linux下动态库的升级方法

在替换so文件时,如果在不停程序的情况下,直接用 cp new.so old.so 的方式替换程序使用的动态库文件会导致正在运行中的程序崩溃。解决的办法是采用“rm+cp” 或“mv+cp” 来替代直接“cp” 的操作方法。linux系统的动态库有两种使用方法:运行时动态链接库,动态加载库并在程序控制之下使用。

1、为什么在不停程序的情况下,直接用 cp 命令替换程序使用的 so 文件,会使程序崩溃?
很多同学在工作中遇到过这样一个问题,在替换 so 文件时,如果在不停程序的情况下,直接用cp new.so old.so的方式替换程序使用的动态库文件会导致正在运行中的程序崩溃,退出。这与 cp 命令的实现有关,cp 并不改变目标文件的 inode,cp 的目标文件会继承被覆盖文件的属性而非源文件。实际上它是这样实现的:

strace cp libnew.so libold.so 2>&1 |grep open.*lib.*.so

open("libnew.so", O_RDONLY|O_LARGEFILE) = 3

open("libold.so", O_WRONLY|O_TRUNC|O_LARGEFILE) = 4

在 cp 使用“O_WRONLY|O_TRUNC” 打开目标文件时,原 so 文件的镜像被意外的破坏了。这样动态链接器 ld.so 不能访问到 so 文件中的函数入口。从而导致 Segmentation fault,程序崩溃。ld.so 加载 so 文件及“再定位”的机制比较复杂,详情可参见参考文献2。


2、怎样在不停止程序的情况下替换so文件,并且保证程序不会崩溃?

答案是采用“rm+cp” 或“mv+cp” 来替代直接“cp” 的操作方法。

在用新的so文件 libnew.so 替换旧的so文件 libold.so 时,如果采用如下方法:

rm libold.so

cp libnew.so libold.so

采用这种方法,目标文件 libold.so 的 inode 其实已经改变了,原来的 libold.so 文件虽然不能用 ”ls”查看到,但其 inode 并没有被真正删除,直到内核释放对它的引用。同理,mv只是改变了文件名,其 inode 不变,新文件使用了新的 inode。这样动态链接器 ld.so 仍然使用原来文件的 inode 访问旧的 so 文件。因而程序依然能正常运行。
到这里,我们回想在上线操作中在替换可执行程序时,为什么直接使用“cp new old”这样的命令时,系统会禁止这样的操作,并且给出这样的提示“cp: cannot create regular file `old': Text file busy”。这时,我们采用的办法仍然是用“rm+cp”或者“mv+cp”来替代直接“cp”,这跟以上提到的so文件的替换有同样的道理。
但是,为什么系统会阻止 cp 覆盖可执行程序,而不阻止覆盖 so 文件呢?这是因为 Linux 有个 Demand Paging 机制,所谓“Demand Paging”,简单的说,就是系统为了节约物理内存开销,并不会程序运行时就将所有页(page)都加载到内存中,而只有在系统有访问需求时才将其加载。
“Demand Paging”要求正在运行中的程序镜像(注意,并非文件本身)不被意外修改,因此内核在启动程序后会锁定这个程序镜像的 inode。对于 so 文件,它是靠 ld.so 加载的,而ld.so毕竟也是用户态程序,没有权利去锁定inode,也不应与内核的文件系统底层实现耦合。

3、linux动态库的使用方法

Linux 支持两种类型的库静态库和动态库。 静态库包含在编译时静态绑定到一个程序的函数。我们这里关心的是动态库。动态库是在加载应用程序时被加载的,而且它与应用程序是在运行时绑定的。
程序使用动态库的方法有两种:
(1)、在运行时动态链接库
这种方法是动态的将程序和共享库链接并让 Linux 在执行时加载库。
例:
fc@fengchun~/project/so$ cat foo.c
#include<stdio.h>
void foo()
{
printf("infoo\n");
}
fc@fcn~/project/so$cat test1.c
#include <stdio.h>
int main(int argc, char * argv[])
{
loop:
foo();
sleep(1);
goto loop;
return 0;
}
首先将文件 foo.c 编译成动态库 libtest.so:
gcc foo.c -fPIC -shared -o libtest.so
然后将文件 test1.c 编译可执行程序:
gcc test1.c -L. -ltest -o test1
编译参数 “-L.” 指定编译器在当前目录(.)查找动态库文件,编译参数 “-ltest”指定编译器连接库文件 libtest.so。
执行可执行程序 test1
./tsest1
程序将在屏幕上每间隔1秒打印1次“infoo”,此时可以偿试执行一下用直接cp覆盖的方法替换libtest.so文件。
cp libtest.so libtest1.so #即使用完全相同的so文件覆盖
cp libtest1.so libtest.so
可以看到程序立即崩溃退出。
fc@fengchun~/project/so$ ./test1
infoo
infoo
Segmentation fault
(2)、在运行时动态加载库并在程序控制之下使用它们。
通过这种方法使用动态库,对于库文件本身的编写和编译与方法1是相同的。但在可执行程序中需要使用一个称为动态加载的过程,这样程序可以有选择地调用库中的函数。动态加载(Dynamic Loading,DL)API 就是为了动态加载而存在的,它允许共享库对用户空间程序可用。尽管非常小,但是这个 API 提供了所有需要的东西,而且很多困难的工作是在后台完成的。
动态加载API主要包括以下函数:
#include <dlfcn.h>
void *dlopen(const char *filename, int flag);
char *dlerror(void);
void *dlsym(void *handle, const char *symbol);
int dlclose(void *handle);
以下是一个使用动态加载API的例子:
fc@fengchun~/project/so$ cat foo.c
#include<stdio.h>
void foo()
{
printf("infoo\n");
}
fc@fengchun~/project/so$ cat test2.c
#include <stdio.h>
#include <dlfcn.h>
#include <string.h>
int main( )
{
void *dl_handle;
float (*func)();
char *error;
/* Open the shared object */
dl_handle = dlopen( "./libtest.so", RTLD_LAZY );
if (dl_handle) {
printf( "!!! %s\n", dlerror() );
return;
}
while (1) {
/* Resolve the symbol (method) from the object */
func = dlsym( dl_handle, "foo" );
error = dlerror();
if (error = NULL) {
printf( "!!! %s\n", error );
return;
}
/* Call the resolved method and print the result */
(*func)();
sleep(1);
}
/* Close the object */
dlclose( dl_handle );
return;
}
首先将文件 foo.c 编译成动态库 libtest.so:
gcc foo.c -fPIC -shared -o libtest.so
然后将文件 test2.c 编译可执行程序:
gcc -rdynamic -o test2 test2.c -ldl
编译参数“-rdynamic” 用来通知链接器将所有符号添加到动态符号表中(目的是能够通过使用 dlopen 来实现向后跟踪),编译参数 “-ldl”指定编译器连接库 libdl
执行可执行程序 test2:
./tsest2
程序将在屏幕上每间隔1秒打印1次“infoo”,此时可以偿试执行一下用直接cp覆盖的方法替换libtest.so文件。
cp libtest.so libtest1.so #即使用完全相同的so文件覆盖
cp libtest1.so libtest.so
可以看到程序立即崩溃退出。
fc@fengchun~/project/so$ ./test2
infoo
infoo
Segmentation fault

转自http://hi.baidu.com/ops_bd/blog/item/bcb8fc2dd392272b5243c1b5.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux动态链接库是一种在程序运行时动态加载的库文件,它包含了一些可被程序调用的函数和变量。与静态链接库不同,动态链接库在程序运行时才会被加载,可以减小程序的体积,提高程序的灵活性和可移植性。动态链接库可以被多个程序共享,从而节省系统资源。在Linux系统中,动态链接库的文件名通常以“.so”结尾。 ### 回答2: Linux动态链接库是一种在运行时加载的共享库,它可以被多个程序共享使用,提供可重用的代码和功能。与静态链接库相比,动态链接库允许程序在运行时加载,并在内存中共享已加载的库。这种方式可以减少内存占用,提高系统性能,并降低可执行文件的大小。 Linux动态链接库分为两种类型:系统级动态链接库和用户级动态链接库。系统级动态链接库例如libc,libpthread等,它们提供了操作系统的基本功能和系统调用的封装。用户级动态链接库则是由开发者自行编写的共享库,它包含了特定功能的代码,可以被多个程序调用和重用。 动态链接库的使用有以下优势: 1. 代码重用:多个程序可以共享同一个动态链接库,避免了重复代码的编写和维护。 2. 系统升级:当动态链接库的代码需要更时,只需要替换库文件,而不需要重编译所有使用该库的程序。 3. 空间节省:动态链接库在内存中只需加载一次,并被多个程序共享使用,减少了内存占用和可执行文件的大小。 4. 运行时动态性:程序可以在运行时加载或卸载动态链接库,增加了程序的灵活性和扩展性。 然而,使用动态链接库也存在一些注意事项: 1. 依赖关系:程序需要正确的动态链接库版本,否则会导致运行错误或崩溃。 2. 性能开销:动态链接库的加载和链接需要额外的时间和开销,可能会稍微降低程序的执行速度。 3. 安全性:动态链接库易受到恶意代码的攻击,因此需要确保库的来源和完整性。 总之,Linux动态链接库是一种强大的机制,可以提高代码的重用性和灵活性,在系统开发和维护中起到了重要的作用。 ### 回答3: Linux动态链接库(Dynamic Link Library,简称DLL)是指一种在Linux操作系统中,将不同的代码库以动态链接的方式组合在一起,供程序调用的机制。 在Linux中,动态链接库是以共享库(shared library)的形式存在的。它与静态链接库(static library)相比,可以在程序运行时动态加载和卸载,并且可以被多个程序同时共享使用。 Linux动态链接库的特点有以下几点: 1. 节省内存空间:相比静态链接库,动态链接库只需在内存中加载一次,就可以被多个程序共享使用,节省了内存空间。 2. 灵活更:由于动态链接库是以单独的文件存在的,所以可以通过替换或更动态链接库文件,实现对其功能的升级或修复,而不用重编译和链接整个程序。 3. 高度可移植性:动态链接库可以跨平台使用,只需在目标系统中存在相同的动态链接库文件即可。 4. 动态加载和卸载:在程序运行时,可以通过动态链接库的加载和卸载机制,动态添加或删除某个功能模块,实现程序的动态扩展和精简。 5. 提高开发效率:动态链接库将一些常用的函数和代码库进行打包,供多个程序调用,可以提高开发效率,并且减少了代码重复性。 总之,Linux动态链接库是一种方便、灵活、可共享的代码组织方式,提高了程序的开发效率和可维护性,也为程序的升级和优化提供了便利。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值