Lun*_*oms 16 c linux shared-libraries
我有一个共享对象gateway.so(在Linux/C中).而a.out应用程序正在使用它.
我猜:当进程a.out启动时,加载器加载gateway.so(我没有使用dl函数dlopen).因此,对gateway.so的所有运行时符号解析都将在内存中进行.它不再需要从磁盘访问gateway.so.
我对吗?
所以我无法用更新版本替换gateway.so,而a.out正在运行,对吧?
另一个相关的问题:一旦我替换了版本的gateway.so文件,我收到了消息
"a.out:无法解析符号'Test_OpenGateway'"
哪个程序组件(加载器/链接器......)发送此输出?该组件是作为相同进程上下文的一部分执行的吗?
And*_*org 30
问题A.
如果以正确的方式执行,您可以在应用程序使用时替换库.
在我们到达之前,让我们看一下主程序二进制文件.这是一个示例程序:
#include <unistd.h>
void justsit(void) {
for (;;) {
sleep(1);
}
}
int main(int argc, char **argv) {
printf("My PID is %d\n", getpid());
justsit();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
编译并启动它:
$ gcc -Wall -o example example.c
$ ./example
My PID is 4339
Run Code Online (Sandbox Code Playgroud)
现在它只会坐在那里,所以打开一个新的终端来做到这一点:
$ gcc -Wall -o example-updated example.c
$ cp example-updated example
cp: cannot create regular file `example': Text file busy
Run Code Online (Sandbox Code Playgroud)
现在发生什么事?内核拒绝更改文件示例,因为它有一个运行该文件的进程.
现在让我们尝试删除它:
$ rm example
Run Code Online (Sandbox Code Playgroud)
什么?那有效吗?为什么文件可以删除,但不能替换?是的,或者更确切地说,文件并没有真正删除,只是"名称",内核告诉文件系统保留文件的内容.当没有任何文件打开时,内容也会被删除.(dentry被立即删除,但是当没有用户时,inode被释放,因为人们会说文件系统)
这可以在/ proc中看到:(这就是程序打印其PID以便您可以轻松检查的原因)
$ readlink /proc/4339/exe
/tmp/t/example (deleted)
Run Code Online (Sandbox Code Playgroud)
无论如何.这样工作的事实意味着可以通过删除旧的二进制文件并将新的二进制文件放在同一位置来安全地升级程序.有一个程序来处理这个:install(1).
好的,回到你的问题 - 共享对象.
让我们将示例分为两部分,main.c和shared.c:
/* main.c */
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void justsit(void);
int main(int argc, char **argv) {
printf("My PID is %d\n", getpid());
justsit();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
和
/* shared.c */
#include <stdio.h>
#include <unistd.h>
void justsit(void) {
for (;;) {
sleep(1);
}
}
Run Code Online (Sandbox Code Playgroud)
像这样编译它们:
$ gcc -Wall --shared -o libshared.so shared.c
$ gcc -Wall -L. -o main main.c -lshared
Run Code Online (Sandbox Code Playgroud)
现在希望如果我们尝试替换libshared.so我们会得到类似的"文本文件忙"错误?让我们来看看.首先启动主程序 - 当前目录不在lib搜索路径中,所以告诉动态链接器在那里搜索:
$ LD_LIBRARY_PATH=. ./main
My PID is 5697
Run Code Online (Sandbox Code Playgroud)
转到另一个终端并用明显破坏的东西替换库:
$ echo "junk" > libshared.so
$
Run Code Online (Sandbox Code Playgroud)
首先 - 它没有像更换程序二进制文件那样被拒绝.而在另一个终端发生了一些有趣的事情,程序停止运行时出现以下错误消息:
Segmentation fault
$
Run Code Online (Sandbox Code Playgroud)
所以不禁止用程序替换正在使用的库!但从上面的例子可以看出,它可能带来灾难性的后果.
幸运的是,用于替换正在运行的二进制文件的相同"技巧"可用于替换正在使用的lib.重新启动主程序(不要忘记重新编译libshared.so,因为它被垃圾代替)并看看如何安全地在库上执行rm.可以检查/ proc/PID/maps以查看进程正在使用的共享对象:
$ cat /proc/5733/maps | grep libshared.so
008a8000-008a9000 r-xp 00000000 08:01 2097292 /tmp/t/libshared.so
008a9000-008aa000 r--p 00000000 08:01 2097292 /tmp/t/libshared.so
008aa000-008ab000 rw-p 00001000 08:01 2097292 /tmp/t/libshared.so
$ rm libshared.so
$ cat /proc/5733/maps | grep libshared.so
008a8000-008a9000 r-xp 00000000 08:01 2097292 /tmp/t/libshared.so (deleted)
008a9000-008aa000 r--p 00000000 08:01 2097292 /tmp/t/libshared.so (deleted)
008aa000-008ab000 rw-p 00001000 08:01 2097292 /tmp/t/libshared.so (deleted)
Run Code Online (Sandbox Code Playgroud)
主程序继续运行良好.这又是因为只是从磁盘中删除了名称(dentry),而不是实际的内容(inode).删除后,可以安全地创建名为libshared.so的新文件,而不会影响正在运行的程序.
因此,总结一下 - 只需使用install命令安装程序和二进制文件.
问题B
是的,由动态链接器在用户空间中打印.
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv) {
execl("./main", "main", NULL);
printf("exec failed?\n");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
用它编译gcc -Wall -o execit execit.c.请记住,execl使用指定的命令替换当前进程.
$ ./execit
main: error while loading shared libraries: libshared.so: cannot open shared object file: No such file or directory
$ rm main
$ ./execit
exec failed?
Run Code Online (Sandbox Code Playgroud)
发生了什么事,它告诉我们什么?首先是error while loading shared libraries没有exec failed?.没有"执行失败"表示该过程已成功替换.这意味着内核将控制转移到失败的动态链接器.删除"main"后,它会提前失败并且不会替换该过程.
Bla*_*iev 10
不,一旦运行时链接程序(ld.so)将其映射到进程的地址空间,可能仍需要从磁盘读取该文件.这种映射的发生方式是通过mmap(2)系统调用和PROT_EXEC允许执行的标志.
映射后映射不会将整个文件放入内存,但实际上会创建一个内存区域,如果所请求的内存尚未复制,则会按需调用页面错误,并且会在通过读取文件中的适当偏移量来获取内核空间.
关于第二个问题,运行时链接器(ld.so)抱怨这一点.加载的ld.so代码由编译时链接器(ld)作为程序启动代码发出,因此在main调用之前在用户空间中执行.