LINUX.ORG.RU

Удаление загруженной динамической библиотеки


1

1

Безопасно ли удалять с диска библиотеку, если та в это время используется каким либо приложением?

Интуиция и проведенный эксперимент говорят что можно. Но хотелось бы подтверждения экспертов.

Ответ на: комментарий от anonymous

Спасибо.

Тогда еще один вопрос.

Если обновить файл либы уже после того, как приложение было запущено, то функции будут вызываться уже из новой версии.

Неужто при каждом вызове функции из библиотеки проводится проверка на время модификации файла?

who_cares
() автор топика
Ответ на: комментарий от who_cares

если библиотека уже загружена в память, то она уже в памяти, незачем проверять время файла.

anonymous
()
Ответ на: комментарий от who_cares

Если обновить файл либы уже после того, как приложение было запущено, то функции будут вызываться уже из новой версии.

Нет.

legolegs ★★★★★
()
Ответ на: комментарий от legolegs

Я тоже так считал.

Тогда как объяснить это:

libstup.cpp:

#include <iostream>

#include "libstup.h"

using namespace std;

void foo() {
    cout << "foo" << endl;
}

libstup.h:

void foo();

libstup-empty.cpp:

main.cpp:

#include <unistd.h>

#include "libstup.h"

int main() {
    foo();
    sleep(2);
    foo();

    return 0;
}

g++ -fPIC -shared -o libstup.so libstup.cpp g++ -fPIC -shared -o libstup-empty.so libstup-empty.cpp LD_LIBRARY_PATH=. ./a.out & sleep 1 && cp libstup-empty.so libstup.so

Итого: [1] 15794 foo [1]+ Ошибка сегментирования LD_LIBRARY_PATH=. ./a.out

who_cares
() автор топика
Ответ на: комментарий от quiet_readonly

Отнюдь. В фоне запускается a.out. Далее sleep 1 для того чтобы успела загрузиться либа и выполниться функция, затем подменяем библиотеку. Итог — запущенная в фоне a.out падает на втором вызове foo().

who_cares
() автор топика
Ответ на: комментарий от quiet_readonly
g++ -fPIC -shared -o libstup.so libstup.cpp 
g++ -fPIC -shared -o libstup-empty.so libstup-empty.cpp 
g++ -L. -lstup main.cpp
LD_LIBRARY_PATH=. ./a.out & sleep 1 && cp libstup-empty.so libstup.so
who_cares
() автор топика
Ответ на: комментарий от who_cares

может быть в inode причина. при cp сохраняется старый

g++ -fPIC -shared -o libstup.so libstup.cpp
g++ -fPIC -shared -o libstup-empty.so libstup-empty.cpp
g++ -fPIC main.cpp -L. -lstup

cp libstup.so libstup.so.tmp
ls -i lib*so
LD_LIBRARY_PATH=. ./a.out & sleep 1 && cp libstup-empty.so libstup.so
sleep 2
ls -i lib*so

echo ""

cp libstup.so.tmp libstup.so
ls -i lib*so
LD_LIBRARY_PATH=. ./a.out & sleep 1 && mv libstup-empty.so libstup.so
sleep 2
ls -i lib*so
./run.sh
2505165 libstup-empty.so  2505160 libstup.so
foo
./run.sh: строка 10:  6390 Ошибка сегментирования                   LD_LIBRARY_PATH=. ./a.out
2505165 libstup-empty.so  2505160 libstup.so

2505165 libstup-empty.so  2505160 libstup.so
foo
foo
2505165 libstup.so

anonymous
()
Ответ на: комментарий от anonymous

Да, взаимосвязь здесь прослеживается.

Но, все же интересен механизм.

До сих пор я думал, что библиотеки загружаются в память и остаются там неизменными до тех пор, пока их использует хотя бы одно приложение.

А в этом примере происходит какая-то замена уже загруженной в память либы.

Понимаю, RTFM и т.п., но может кто нибудь подсказал бы куда копать.

who_cares
() автор топика
Ответ на: комментарий от who_cares

кстати, ещё:

cp libstup.so.tmp libstup.so
ls -i lib*so
LD_LIBRARY_PATH=. ./a.out & sleep 1 && shred libstup.so
sleep 2
ls -i lib*so
похоже в память загружается не библиотека, а данные о ней.

anonymous
()
Ответ на: комментарий от who_cares

в файле /proc/$PID/maps, в 5-том столбце указывается inode файла библиотеки, может с этим что связано.

anonymous
()

Вам нужно удалить старую библиотеку, затем создать новую (cp, mv) с тем же именем. Если вы делаете копирование в существующий файл, но он открывается на запись и изменяются его данные (не очень понятно почему, т.к. запись файл, который в данные момент выполняется должна быть запрещена, по-крайней мере мне не удалось перезаписать выполняемый файл (но не библиотеку), когда он работает, даже будучи рутом, можно лишь удалить его и создать новый с тем же именем).

x_hash
()
Ответ на: комментарий от who_cares

До сих пор я думал, что библиотеки загружаются в память

Вся загвоздка в расплывчатом «загружаются». Если бы читались, то cp ничего бы не портил. А они mmap'аются и получается, что cp, переписывая файл, меняет его отображения.

const86 ★★★★★
()
Ответ на: комментарий от who_cares

Но, все же интересен механизм.

Этот «механизм» называется MMAP.

До сих пор я думал, что библиотеки загружаются в память и остаются там неизменными до тех пор, пока их использует хотя бы одно приложение.

Не совсем. Бибилиотеки не «загружаются в память», а MMAP'ятся.

А в этом примере происходит какая-то замена уже загруженной в память либы.

Очень грубо говоря, MMAP фактически это подключение страниц кэша в адресное пространство процесса. Как следствие, эти страницы при необходимости (нехватка памяти, ага) можно сплюнуть и когда потребуются, поднять заново. Когда ты переписываешь файл (не удаляешь, а именно обновляешь содержимое), операция записи зацепляет кэш, вследствие чего данные страниц, отображенных в память уже запущенного процесса, меняются. Вдобавок ко всему, при перезаписи файла через cp файл усекается в момент открытия (man fopen), что приводит к удалению MMAP'нутых страниц (размер файла стал 0, страницы освобождены), после чего попытка работающего процесса что-то там сделать автоматом приводит к segfault.

Если бы ты вместо cp использовал dd conv=notrunc при перезаписи файла библиотеки новой копией, вполне возможно что программа пережила бы это (conv=notrunc не приводит к усечению файла и соответственно к освобождению страниц кэша).

Фактически, в линуксе у тебя два swap-пространства: «канонический» swap, куда при необходимости сносятся страницы с данными, и все библиотеки и бинарники. Главное отличие между ними - это то, что во вторую группу запись не производится, и mmap'ятся страницы кэша в которых лежат библиотеки в режиме read-only.

no-dashi ★★★★★
()
Последнее исправление: no-dashi (всего исправлений: 1)
Ответ на: комментарий от const86

А они mmap'аются и получается, что cp, переписывая файл, меняет его отображения.

Дело не в замене данных, дело в сбросе кэша (обнуление файла в момент открытия его по записи в cp). Используй dd conv=notrunc вместо cp - и segfault'а не будет (если конечно бинарник неизменен):

В первой консоли:

console1$ cp /lib/libc.so.6 ~
console1$ LD_PRELOAD=~/libc.so.6 bash

Во второй:

console2$ cat /lib/libc.so.6 >~/libc.so.6

Возвращаемся в первую, жмем что угодно - Segmentation fault

В первой консоли:

console1$ cp /lib/libc.so.6 ~
console1$ LD_PRELOAD=~/libc.so.6 bash

Во второй:

console2$ dd if=/lib/libc.so.6 of=~/libc.so.6 conv=notrunc

Возвращаемся в первую - всё работает.

no-dashi ★★★★★
()
Ответ на: комментарий от no-dashi

Спасибо за толковый ответ.

Если вместо cp использовать

dd conv=notrunc if=libstup-empty.so of=libstup.so
то приложение при втором вызове foo() выдает уже:

./a.out: relocation error: ./libstup.so: symbol __cxa_finalize, version not defined in file with link time reference

Идем дальше и в libstup-empty.cpp добавляем:

#include <iostream>

#include "libstup-new.h"

using namespace std;

void foo() {
    cout << "new foo" << endl;
}

После этого компилим и запускаем:

g++ -fPIC -shared -o libstup.so libstup.cpp 
g++ -fPIC -shared -o libstup-empty.so libstup-empty.cpp 
g++ -L. -lstup main.cpp
LD_LIBRARY_PATH=. ./a.out & sleep 1 && dd conv=notrunc if=libstup-empty.so of=libstup.so

В консоли видим следующее:

[1] 4323
foo
19+1 записей считано
19+1 записей написано
 скопировано 10063 байта (10 kB), 8,7186e-05 c, 115 MB/c

new foo

who_cares
() автор топика
Ответ на: комментарий от who_cares

Не сношай нам моск. Причину я тебе объяснил. Если тебе этого недостаточно, и ты хочешь подменять бинарник выполняющейся программы, то это надо делать через libdl (dlopen и т.п.).

</thread>

no-dashi ★★★★★
()
Ответ на: комментарий от no-dashi

приводит к удалению MMAP'нутых страниц (размер файла стал 0, страницы освобождены), после чего попытка работающего процесса что-то там сделать автоматом приводит к segfault.

вышеописанное приводит к SIGBUS

redbaron ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.