У меня есть общий объект gateway.so(в Linux/C). И приложение a.out использует его.
Я предполагаю: когда запускается процесс a.out, загрузчик загружает gateway.so(я не использую dl-функции, такие как dlopen
). Таким образом, все разрешения символа времени выполнения на gateway.so будут происходить в памяти. Вам не нужно больше обращаться к gateway.so с диска.
Итак, я не могу заменить gateway.so обновленной версией, в то время как a.out работает, правильно?
Другой связанный с этим вопрос: однажды, когда я заменил и устарел версию файла gateway.so, я получил сообщение
Какой программный компонент (loader/linker...) отправляет этот вывод? Этот компонент выполняется как часть одного и того же контекста процесса?
Ответ 2
Вопрос 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
Что случилось сейчас? Ядро отказалось от изменения примера файла, потому что у него есть процесс, который запускает этот файл.
Теперь попробуйте удалить его:
$ 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, мы получим аналогичную ошибку "Text file busy"? Посмотрим. Сначала запустите основную программу - текущий каталог не находится в пути поиска lib, поэтому скажите динамическому компоновщику для поиска там:
$ LD_LIBRARY_PATH=. ./main
My PID is 5697
Перейдите на другой терминал и замените библиотеку чем-то явно сломанным:
$ echo "junk" > libshared.so
$
Во-первых - не было отказано, как замена двоичной программы.
И в другом терминале произошло что-то интересное, программа перестала работать со следующим сообщением об ошибке:
Segmentation fault
$
Поэтому заменить библиотеку, используемую программой, не запрещено! Но, как видно из приведенного выше примера, это может иметь катастрофические последствия.
К счастью, тот же "трюк", который использовался для замены исполняемого двоичного файла, может быть использован для замены используемого 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)
Основная программа продолжает работать нормально. Опять же, это потому, что просто имя (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" был удален, он не срабатывает раньше и процесс не заменяется.