更新共享库而不重新启动进程

如果我的进程正在加载一个.so库,并且如果该库的新版本可用,是否可以切换到新的库而不进行进程重启? 或者答案取决于是否有一个参数更改为库中现有的function之一?

我正在一个运行100个进程的漂亮大系统中工作,每个系统加载10个库。 这些库提供了特定的function,由独立的团队提供。 所以当一个库的变化(对于一个错误修复可以说)理想的事情是将其发布在内部而不会影响正在运行的进程。 可能吗 ?

编辑谢谢! 在我的情况下,当一个新的库可用时,所有正在运行的进程必须开始使用它。 它没有select让他们运行旧版本,并在以后拿起新的。 所以看起来更安全的select是重新加载进程。

正在运行的进程不能立即升级链接库。 你甚至可以尝试,但是如果你成功了(而且你不会失败并显示一个“正在使用的文本文件”的错误信息),你将不得不重新开始这个过程,把它映射到内存中。

您可以使用lsof命令来检查哪些库链接(运行时或链接时间):

lsof -p <process_pid> | grep ' mem ' 

一个有趣的技术,虽然有点在检查点恢复步骤中容易失败,但是做一个不可见的重启。

您的服务器进程或任何它,将所有必要的信息保存到磁盘文件。 包括文件描述符号码和当前状态。 然后,服务器进程执行exec系统调用来执行自己,取代当前的版本。 然后它从磁盘文件中读取它的状态并恢复为其文件描述符提供服务,就像没有任何事情发生一样。

如果一切顺利,重新启动是不可见的,新进程正在使用所有更新的库。

至少,你必须确保库的接口在版本之间不会改变。 如果这是确定的,那么我会试着动态加载与dlopen / dlsym库,看看是否允许您重新加载dlclose。

我自己从来没有做过这样的事情,但这是我首先要追求的道路。 如果你这样做,你能发表结果吗?

Linux提供了几个动态加载器接口,运行时可以加载动态库。 Linux提供的dlopen和dlsysm可以解决你的问题。

ldd你的进程的二进制文件是找出的一种方法。 虽然这在理论上是可行的,但是我们不可能修改正在运行的进程,尽管我相信公用程序存在,比如ksplice可以修改正在运行的linux内核。

您可以简单地升级,运行的过程将继续使用旧版本,并在重新启动时选择新版本,假设您的软件包管理系统是好的,并且知道什么是可兼容的安装。

您可能想了解共享库版本和ld -h选项。

一种使用方法如下:

您在构建系统中维护一个版本计数器。 您使用以下方式构建共享库:

 ld ..... -h mylibrary.so.$VERSION 

不过,你把它放在你的开发树的lib,只是简单的mylibrary.so 。 (还有一个涉及将整个.so文件放入.a文件的黑客攻击)。

现在,在运行时,使用库的进程查找完全版本的名称。 要推出新版本,只需新版本添加到图片即可。 运行与旧版本链接的程序将继续使用它。 随着程序被重新链接并针对新程序进行测试,您将推出新的可执行文件。

有时你可以升级一个正在使用的.so,有时你不能。 这主要取决于你如何去做,而且还取决于你正在运行的内核的安全性。

不要这样做:cat new.so> old.so …因为最终,你的过程可能试图要求页面的东西,并发现它不在正确的位置了。 这是一个问题,因为事物的地址可能会改变,它仍然是相同的inode; 你只是覆盖文件中的字节。

但是,如果你:mv new.so old.so你可以在大多数系统上正常运行,因为你的运行进程可以保存旧库的一个未命名的inode,而你的进程的新调用将获得新文件。 但是,有些内核不喜欢让你使用,也许出于谨慎,也许是为了自己的简单。

如果你期望libaries经常改变,而且期望保持正常运行,那么我认为你的系统应该被重新设计,以便这样的库实际上成为松耦合的组件(例如服务)。

话虽如此,我对这个问题的回答是肯定的:在某些情况下, 可以在不重新启动进程的情况下更新共享库。 在大多数情况下,我认为这是不可能的,例如当你的库的API改变,当你的数据段的安排改变时,当库维护内部线程时。 这个清单很长。

对于代码的很小的错误修复,仍然可以使用ptrace写入进程内存空间,然后从那里重做/lib/ld-linux.so在动态链接方面的功能。 老实说,这是一个非常复杂的活动。