История изменений
Исправление
no-dashi,
(текущая версия)
:
Но, все же интересен механизм.
Этот «механизм» называется MMAP.
До сих пор я думал, что библиотеки загружаются в память и остаются там неизменными до тех пор, пока их использует хотя бы одно приложение.
Не совсем. Бибилиотеки не «загружаются в память», а MMAP'ятся.
А в этом примере происходит какая-то замена уже загруженной в память либы.
Очень грубо говоря, MMAP фактически это подключение страниц кэша в адресное пространство процесса. Как следствие, эти страницы при необходимости (нехватка памяти, ага) можно сплюнуть и когда потребуются, поднять заново. Когда ты переписываешь файл (не удаляешь, а именно обновляешь содержимое), операция записи зацепляет кэш, вследствие чего данные страниц, отображенных в память уже запущенного процесса, меняются. Вдобавок ко всему, при перезаписи файла через cp файл усекается в момент открытия (man fopen), что приводит к удалению MMAP'нутых страниц (размер файла стал 0, страницы освобождены), после чего попытка работающего процесса что-то там сделать автоматом приводит к segfault.
Если бы ты вместо cp использовал dd conv=notrunc при перезаписи файла библиотеки новой копией, вполне возможно что программа пережила бы это (conv=notrunc не приводит к усечению файла и соответственно к освобождению страниц кэша).
Фактически, в линуксе у тебя два swap-пространства: «канонический» swap, куда при необходимости сносятся страницы с данными, и все библиотеки и бинарники. Главное отличие между ними - это то, что во вторую группу запись не производится, и mmap'ятся страницы кэша в которых лежат библиотеки в режиме read-only.
Исходная версия
no-dashi,
:
Но, все же интересен механизм.
Этот «механизм» называется MMAP.
До сих пор я думал, что библиотеки загружаются в память и остаются там неизменными до тех пор, пока их использует хотя бы одно приложение.
Не совсем. Бибилиотеки не «загружаются в память», а MMAP'ятся.
А в этом примере происходит какая-то замена уже загруженной в память либы.
Очень грубо говоря, MMAP фактически это подключение страниц кэша в адресное пространство процесса. Как следствие, эти страницы пр инеобходимости можно сплюнуть и когда потребуются, поднять заново. Когда ты переписываешь файл (не удаляешь, а именно обновляешь содержимое), операция записи зацепляет кэш, вследствие чего данные страниц, отображенных в память уже запущенного процесса, меняются. Вдобавок ко всему, при перезаписи файла через cp файл усекается в момент открытия (man fopen), что приводит к удалению MMAP'нутых страниц (размер файла стал 0, страницы освобождены), после чего попытка работающего процесса что-то там сделать автоматом приводит к segfault.
Если бы ты вместо cp использовал dd conv=notrunc при перезаписи файла библиотеки новой копией, вполне возможно что программа пережила бы это (conv=notrunc не приводит к усечению файла и соответственно к освобождению страниц кэша).