问题 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:
#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;
}
并且
#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,因为它已被替换为垃圾),然后查看如何安全地对库文件进行删除。可以检查 /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”被删除后,它很快就会失败,进程也无法被替换。