本文共 1774 字,大约阅读时间需要 5 分钟。
scp 动态库导致core的原因分析:
我们写的业务代码,编译出来是so文件,用exe进行dlopen加载so动态库。 在服务器上面,我们会使用 scp 把新编译的so拷贝到服务器上面。 这样导致的问题是core的位置不固定,杂乱无章。
(from 和 to 都是我们编译的动态库,有正在运行的程序引用它):
cp from to // to文件是已经存在的。 被覆盖文件 to的inode依旧不变(属性也不变),内容变为from的 scp 172.172.xx.xx:/from to // to文件是已经存在的。 被覆盖文件 to的inode依旧不变(属性也不变),内容变为from的 mv from to // to的inode变为from的,相应的,to的属性也成了from的我们来看看cp/scp 系统干了什么:
strace cp test2 test 2>&1 | grep open.*test open(“test2”, O_RDONLY|O_LARGEFILE) = 3 open(“test”, O_WRONLY|O_TRUNC|O_LARGEFILE) = 4 可见,cp的实现逻辑不是“rm + open(O_CREAT)”,而是 O_TRUNC。 执行mv的时候,老的to文件因为有进程在引用它,所以老的to文件并没有被真正删除,只是被系统给隐藏起来了;在进程重启后,系统会把隐藏的to文件删除,引用新的to文件(从from改名过来)为什么文件inode不变,使用 O_TRUNC方式拷贝动态库就core呢?
这里解释下虚拟内存:
虚拟内存:用户编译生成的程序时使用的地址称为虚拟地址。使程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。 虚存空间的用户程序按照虚地址编程并存放在辅存(硬盘)中。程序运行时,由MMC依据当时分配给该程序的实地址空间把程序的一部分调入实存。每次访存时,首先判断该虚地址所对应的部分是否在实存中。如果是,则进行地址转换并用实地址访问主存;否则,发出缺页中断,将辅存中的部分程序调度进物理内存,再按同样的方法访问物理内存。 虚拟内存空间 和 物理内存的映射关系,存储在物理内存某个(或者某些)页上面,这里简称为 虚存的页。0.Linux由于Demand Paging机制的关系,必须确保正在运行中的程序镜像(注意,并非文件本身)不被意外修改,因此内核会绑定这个动态库的inode到该进程,不会变更。内核也在启动程序后会绑定 内存页 到这个so的inode,而一旦此inode文件被open函数O_TRUNC掉,则kernel会把so文件对应在 虚存的页 清空。 由于这个虚拟内存空间 和 物理内存的映射关系没有了,导致已经加载到物理内存中的部分数据也没办法访问,系统会自动回收这块物理内存。 这样当运行到so里面的代码时,因为物理内存中不再有实际的数据(仅存在于虚存空间内),会产生一次缺页中断。Kernel从so文件中copy一份到内存中去。
1.应用程序通过dlopen打开so的时候,kernel通过mmap把so加载到进程地址空间,对应于vma里的几个page.
2.在这个过程中loader会把so里面引用的外部符号例如malloc printf等解析成真正的虚存地址。 3.当so被cp覆盖时,确切地说是被trunc时,kernel会把so文件在虚拟内的页purge 掉。 4.当运行到so里面的代码时,因为物理内存中不再有实际的数据(仅存在于虚存空间内),会产生一次缺页中断。 5.Kernel从so文件中copy一份到内存中去,a)但是这时的全局符号表并没有经过解析,当调用到时就产生segment fault , b)如果需要的文件偏移大于新的so的地址范围,就会产生bus error. 全局符号表在dlopen的时候已经解析重定位过了,trunc后不会再重定位一次。所以,如果用相同的so去覆盖
A) 如果so 里面依赖了外部符号,coredump B) 如果so里面没有依赖外部符号,运气不错,不会coredump转载地址:http://mlrti.baihongyu.com/