问题 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;
}
编译并启动它:
$ gcc -Wall -o example example.c
$ ./example
My PID is 4339
现在它会坐在那里,所以打开一个新终端来执行此操作:
$ gcc -Wall -o example-updated example.c
$ cp example-updated example
cp: cannot create regular file `example': Text file busy
现在发生了什么?内核拒绝更改文件example,因为它有一个进程正在运行该文件。
现在让我们尝试删除它:
$ rm example
什么?那行得通吗?为什么文件可以删除,但不能替换?是的,或者更确切地说,文件并没有真正删除,只是“名称”,内核告诉文件系统保留文件的内容。当文件不再打开时,内容也会被删除。 (dentry 被立即删除,但 inode 在没有用户时被释放,正如人们所说的文件系统)
这可以在 /proc 中看到:(这就是程序打印其 PID 以便您可以轻松检查的原因)
$ readlink /proc/4339/exe
/tmp/t/example (deleted)
无论如何。它这样工作的事实意味着人们可以通过删除旧的二进制文件并将新的二进制文件放在同一位置来安全地升级程序。有一个程序可以处理这个问题: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;
}
和
/* shared.c */
#include <stdio.h>
#include <unistd.h>
void justsit(void) {
for (;;) {
sleep(1);
}
}
像这样编译它们:
$ gcc -Wall --shared -o libshared.so shared.c
$ gcc -Wall -L. -o main main.c -lshared
现在希望如果我们尝试替换 libshared.so 我们会得到类似的“文本文件忙”错误?让我们来看看。首先启动主程序 - 当前目录不在 lib 搜索路径中,所以告诉动态链接器在那里搜索:
$ LD_LIBRARY_PATH=. ./main
My PID is 5697
转到另一个终端并用明显损坏的东西替换库:
$ echo "junk" > libshared.so
$
首先 - 它并没有像替换程序二进制文件那样被拒绝。
在另一个终端发生了一些有趣的事情,程序停止运行并显示以下错误消息:
Segmentation fault
$
因此,不禁止替换程序正在使用的库!但从上面的例子可以看出,它可能会产生灾难性的后果。
幸运的是,用于替换正在运行的二进制文件的相同“技巧”可用于替换正在使用的库。重新启动主程序(不要忘记重新编译 libshared.so,因为它已被 junk 替换)并查看在库上执行 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)
主程序继续正常运行。这又是因为从磁盘中删除的只是名称(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;
}
用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?
发生了什么,它告诉我们什么?首先是error while loading shared libraries,没有exec failed?。没有“exec failed”表明进程被成功替换。这意味着内核将控制权转移给了失败的动态链接器。删除“main”后,它会提前失败,并且不会替换该进程。