Что происходит, когда файл, который на 100% помещен в кэш страницы, изменяется другим процессом


14

Я знаю, что при изменении страницы кэша страниц она помечается как грязная и требует обратной записи, но что происходит, когда:

Сценарий: файл / apps / EXE, который является исполняемым файлом, полностью помещается в кеш страниц (все его страницы находятся в кеше / памяти) и выполняется процессом P

Непрерывный выпуск заменяет / apps / EXE новым исполняемым файлом.

Предположение 1: Я предполагаю, что процесс P (и любой другой пользователь с файловым дескриптором, ссылающимся на старый исполняемый файл) продолжит использовать старый, в памяти / apps / EXE без проблем, и любой новый процесс, который попытается выполнить этот путь, получит новый исполняемый файл.

Предположение 2: Я предполагаю, что если не все страницы файла будут отображены в памяти, все будет хорошо, пока не произойдет сбой страницы, требующий страниц из файла, который был заменен, и, возможно, произойдет сегфоут?

Вопрос 1: Если вы скомбинируете все страницы файла с чем-то вроде vmtouch, это вообще изменит сценарий?

Вопрос 2: Если / apps / EXE находится на удаленной NFS, будет ли это иметь какое-то значение? (Я предполагаю, что нет)

Пожалуйста, исправьте или подтвердите мои 2 предположения и ответьте на мои 2 вопроса.

Давайте предположим, что это коробка CentOS 7.6 с ядром 3.10.0-957.el7

Обновление: Думая об этом дальше, мне интересно, если этот сценарий ничем не отличается от любого другого сценария грязной страницы ..

Я полагаю, что процесс, который записывает новый двоичный файл, выполнит чтение и получит все страницы кэша, так как он весь разбит на страницы, и тогда все эти страницы будут помечены как грязные. Если они заблокированы, они будут просто бесполезными страницами, занимающими основную память после того, как число ссылок станет равным нулю.

Я подозреваю, что когда текущие программы заканчиваются, все остальное будет использовать новый двоичный файл. Предполагая, что это все правильно, я думаю, что это интересно только тогда, когда в файл вставлена ​​только часть файла.


Просто чтобы сделать это явным, замена файла не будет большой вещью (в зависимости от того, открывается ли она приложением и как приложение реагирует на измененное содержимое), но изменение mmaped файлов может привести к серьезным сбоям приложений (это общая проблема в мире Java, когда изменен zip-файл, содержащий запись в каталоге mmaped). Это, однако, зависит от платформы, не гарантируется, видят ли измененные области изменения или нет.
Eckes

Ответы:


12

Непрерывный выпуск заменяет / apps / EXE новым исполняемым файлом.

Это важная часть.

Способ выпуска нового файла - это создание нового файла (например /apps/EXE.tmp.20190907080000), запись содержимого, установка разрешений и владельца и, наконец, переименование (2) его окончательного имени /apps/EXE, замена старого файла.

В результате у нового файла новый номер инода (это означает, что это другой файл).

И у старого файла был свой собственный номер инода, который на самом деле все еще существует, хотя имя файла больше не указывает на него (или нет имен файлов, указывающих на этот инод больше).

Итак, ключевой момент в том, что когда мы говорим о «файлах» в Linux, мы чаще всего действительно говорим о «инодах», так как после открытия файла инод является ссылкой, которую мы храним на файл.

Предположение 1 : Я предполагаю, что процесс P (и любой другой пользователь с файловым дескриптором, ссылающимся на старый исполняемый файл) продолжит использовать старый, в памяти / apps / EXE без проблем, и любой новый процесс, который попытается выполнить этот путь, получит новый исполняемый файл.

Верный.

Предположение 2 : Я предполагаю, что если не все страницы файла будут отображены в памяти, все будет хорошо, пока не произойдет сбой страницы, требующий страниц из файла, который был заменен, и, возможно, произойдет сегфоут?

Неправильно. Старый инод все еще существует, поэтому сбои страниц в процессе, использующем старый двоичный файл, все равно смогут найти эти страницы на диске.

Вы можете увидеть некоторые эффекты этого, посмотрев /proc/${pid}/exeсимволическую ссылку (или, что эквивалентно, lsofвыходные данные) для процесса, выполняющего старый двоичный файл, который покажет, /app/EXE (deleted)что имя больше не существует, но индекс все еще существует.

Вы также можете видеть, что дисковое пространство, используемое двоичным файлом, будет освобождено только после того, как процесс завершится (при условии, что это единственный процесс с открытым индексом). Проверьте выходные данные dfдо и после завершения процесса, вы увидите, что он уменьшается на размер о том старом бинарнике, который, как ты думал, больше не был

Кстати, это касается не только двоичных файлов, но и любых открытых файлов. Если вы откроете файл в процессе и удалите его, файл будет храниться на диске до тех пор, пока этот процесс не закроет файл (или не прекратит работу). Аналогично тому, как жесткие ссылки хранят счетчик количества имен, указывающих на индекс на диске, Драйвер файловой системы (в ядре Linux) хранит счетчик количества ссылок на этот индекс в памяти и освобождает индекс от диска только после того, как все ссылки из работающей системы также будут освобождены.

Вопрос 1 : Если вы блокируете все страницы файла чем-то вроде vmtouch, это меняет сценарий

Этот вопрос основан на неверном предположении 2, что отсутствие блокировки страниц приведет к ошибкам в работе сегмента. Не будет

Вопрос 2 : Если / apps / EXE находится на удаленной NFS, будет ли это иметь какое-то значение? (Я предполагаю, что нет)

Он должен работать так же и большую часть времени, но с NFS есть некоторые "ошибки".

Иногда вы можете увидеть артефакты удаления файла, который все еще открыт в NFS (отображается как скрытый файл в этом каталоге).

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

Но основная идея та же. Драйвер клиента NFS по-прежнему использует inode и будет пытаться хранить файлы (на сервере), пока на inode все еще есть ссылки.


1
Блокирует ли rename (2), пока счетчик ссылок файла старого имени не станет равным нулю?
Грегг Левенталь

2
Нет, переименовать (2) не будет блокировать. Старый инод хранится потенциально очень долго.
filbranden

1
Посмотрите ответ @ mosvy на вопрос, почему вы не можете написать исполняемый файл (вы получаете ETXTBSY). Отмена связи и создание новых имеет тот же эффект переименования: в итоге вы получите новый индекс. (Переименовать лучше, потому что тогда нет момента, когда имя файла не существует, это атомарная операция, заменяющая имя, указывающее на новый
индекс

4
@GreggLeventhal: «Каково ваше предположение о процессе непрерывного выпуска, который я использую, который гарантирует, что он использует временные файлы?» - Поскольку Unix существует, это единственный разумный способ сделать это. renameэто в значительной степени единственная операция с файлом и файловой системой, которая гарантированно является атомарной (при условии, что мы не пересекаем границы файловой системы или устройства), поэтому «создайте временный файл и затем rename» - это стандартный шаблон для обновления файлов. Это также то, что каждый текстовый редактор в Unix использует, например.
Йорг Миттаг

1
@ grahamj42: renameявляется частью POSIX. Конечно, он включен в качестве ссылки на ISO C (раздел 7.21.4.2 в текущем проекте), но он там.
Йорг Миттаг

6

Предположение 2: Я предполагаю, что если не все страницы файла будут отображены в памяти, все будет хорошо, пока не произойдет сбой страницы, требующий страниц из файла, который был заменен, и, вероятно, произойдет сегфоут?

Нет, этого не произойдет, потому что ядро ​​не позволит вам открыть для замены что-либо внутри файла, который в данный момент выполняется. Такое действие потерпит неудачу с ETXTBSY[1]:

cp /bin/sleep sleep; ./sleep 3600 & echo none > ./sleep
[9] 5332
bash: ./sleep: Text file busy

Когда dpkg и т. Д. Обновляет двоичный файл, он не перезаписывает его, а использует, rename(2)который просто указывает запись в каталоге на совершенно другой файл, и любые процессы, у которых все еще есть сопоставления или открытые дескрипторы для старого файла, продолжат использовать его без проблем. ,

[1] такая защита не распространяется на другие файлы, которые также можно считать «текстовыми» (живой код / ​​исполняемый файл): общие библиотеки, классы Java и т. Д .; изменение такого файла во время отображения другим процессом приведет к его аварийному завершению. В Linux динамический компоновщик покорно передает MAP_DENYWRITEфлаг mmap(2), но не ошибается - он никак не влияет.


1
В сценарии dpkg, в какой момент переименование завершается так, что dentry для / apps / EXE будет ссылаться на inode нового двоичного файла? Когда больше нет ссылок на старый? Как это работает?
Грегг Левенталь

2
rename(2)является атомным; как только он завершится, запись dir ссылается на новый файл. Процессы, которые все еще использовали старый файл в этой точке, могли бы получить к нему доступ только через существующие сопоставления или через открытые дескрипторы к нему (которые могут ссылаться на потерянный зубной протез, более недоступный, кроме как через /proc/PID/fd).
Мосви

1
Мне больше нравится ваш ответ, потому что ваше упоминание ETXTBSY привело меня к этому utcc.utoronto.ca/~cks/space/blog/unix/WhyTextFileBusyError, который отвечает на все мои вопросы.
Грегг Левенталь

4

Ответ filbranden верен, если предположить, что процесс непрерывного выпуска правильно заменяет файлы с помощью rename. Если это не так, но изменяет файл на месте, все иначе. Однако ваша ментальная модель все еще ошибается.

Невозможно что-либо изменить на диске и не совместить с кешем страниц, потому что кеш страниц является канонической версией и той, которая изменена. Любая запись в файл происходит через кеш страницы. Если он уже присутствует, существующие страницы будут изменены. Если это еще не сделано, попытки изменить частичную страницу приведут к кешированию всей страницы с последующей модификацией, как если бы она уже была кеширована. Записи, занимающие целую страницу или более, могут (и почти наверняка) оптимизируют шаг чтения, разбивая их на страницы. В любом случае, существует только одна каноническая модифицируемая версия файла, когда-либо существовавшая (*), та, которая находится в кеше страницы. ,

(*) Я слегка соврал. Для NFS и других удаленных файловых систем их может быть больше одной, и они обычно (в зависимости от того, какая из них и какие параметры монтирования и на стороне сервера) не правильно реализуют атомарность и упорядочение семантики для записей. Вот почему многие из нас считают их в корне сломанными и отказываются использовать их в ситуациях, когда записи будут выполняться одновременно с использованием.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.