【发布时间】:2011-08-25 07:38:32
【问题描述】:
我想知道...当我启动一个使用共享库 A 的程序 X 时,当程序运行时,我修改了磁盘上的共享库,并运行另一个依赖于该共享库的程序 Y共享库。该程序 Y 将使用共享库的已在内存中的版本,还是会加载具有后续修改的共享库的不同实例?
如何确定是否共享已加载的库或从磁盘重新加载?
【问题讨论】:
标签: unix posix shared-libraries
我想知道...当我启动一个使用共享库 A 的程序 X 时,当程序运行时,我修改了磁盘上的共享库,并运行另一个依赖于该共享库的程序 Y共享库。该程序 Y 将使用共享库的已在内存中的版本,还是会加载具有后续修改的共享库的不同实例?
如何确定是否共享已加载的库或从磁盘重新加载?
【问题讨论】:
标签: unix posix shared-libraries
动态加载器只执行常规的旧 open(2) 和 mmap(2) 调用,并且内存映射会像打开的 fd 一样增加 inode 引用计数。因此,如果您对库执行通常的原子文件替换技巧,将您的更改写在文件的新副本中,然后 rename(2) 覆盖旧名称,此后开始的任何内容都将获取新的 inode和新的内容,但是运行的程序会继续使用旧的inode和旧的内容。
如果您就地修改库,那么在write 调用之后启动的任何程序自然都会获取您的更改。更有趣的问题是已经映射的进程会发生什么。答案可能是“系统不会让你这样做”或“未指定,取决于页面缓存实现的细节”。随便戳一下 Linux 实现(这是我必须要做的):glibc 动态加载器对其所有共享库映射使用MAP_DENYWRITE,这没有记录,但听起来就像它的意思是“制作这个映射存在时文件不可修改”。但是,我在内核源代码中找不到任何使MAP_DENYWRITE 做 任何事情的东西;它可能是历史遗迹之类的。
它也使用MAP_PRIVATE。 http://pubs.opengroup.org/onlinepubs/7908799/xsh/mmap.html 表示“未指定在建立 MAP_PRIVATE 映射后对基础对象所做的修改是否可以通过 MAP_PRIVATE 映射可见。”因此,您可能会也可能无法修改正在运行的进程下的共享库图像,具体取决于页面缓存实现的细节。
【讨论】:
动态加载器使用mmap(2) 来加载共享库,所以所有的魔法都在mmap(2) 中。
在Linux的特定情况下,文件映射调用fs特定的mmap()操作,通常连接到mm/filemap.c:generic_file_mmap(),它将文件映射的vm_ops.fault设置为mm/filemap.c:filemap_fault(),所以神奇的是延迟到缺页时间。 filemap_fault() 尝试使用 find_lock_page() 在页面缓存中查找页面。
mmap() → fs_file_ops.mmap() → generic_file_mmap() → file_vm_ops.fault = filemap_fault()
page fault → filemap_fault() → find_lock_page()
【讨论】: