Хотя сначала мне показалось, что вопроса о переполнении стека достаточно, я понимаю из ваших комментариев, почему у вас все еще могут быть сомнения по этому поводу. На мой взгляд, это именно та критическая ситуация , возникающая при взаимодействии двух подсистем UNIX (процессов и файлов).
Как вы, возможно, знаете, системы UNIX обычно делятся на две подсистемы: файловую подсистему и подсистему процессов. Теперь, если в системном вызове не указано иное, ядро не должно иметь этих двух подсистем, взаимодействующих друг с другом. Однако есть одно исключение: загрузка исполняемого файла в текстовые области процесса . Конечно, можно утверждать, что эта операция также запускается системным вызовом ( execve
), но обычно это один случай, когда подсистема процесса делает неявный запрос в файловую подсистему.
Поскольку подсистема процессов, естественно, не имеет возможности обрабатывать файлы (иначе не было бы смысла делить все на две части), она должна использовать все, что файловая подсистема предоставляет для доступа к файлам. Это также означает, что подсистема процесса подчиняется любой мере, которую файловая подсистема принимает в отношении редактирования / удаления файла. По этому поводу я бы рекомендовал прочитать ответ Жиля на этот вопрос U&L . Остальная часть моего ответа основана на более общем ответе Жиля.
Первое, что следует отметить, это то, что внутри файлы доступны только через inodes .Если ядру задан путь, его первым шагом будет преобразование его в индексный дескриптор, который будет использоваться для всех других операций. Когда процесс загружает исполняемый файл в память, он делает это через свой индексный дескриптор, который был предоставлен файловой подсистемой после преобразования пути. Inodes могут быть связаны с несколькими путями (ссылками), а программы могут только удалять ссылки. Чтобы удалить файл и его индексный дескриптор, пользовательское пространство должно удалить все существующие ссылки на этот индексный дескриптор и убедиться, что он полностью не используется. Когда эти условия выполнены, ядро автоматически удалит файл с диска.
Если вы посмотрите на заменяющие исполняемые файлы часть ответа Жиля, вы увидите, что в зависимости от того, как вы редактируете / удаляете файл, ядро будет реагировать / адаптироваться по-разному, всегда с помощью механизма, реализованного в файловой подсистеме.
ETXTBSY
). Никаких последствий. Стратегии 2 и 3 также безопасны для исполняемых файлов: хотя запущенные исполняемые файлы (и динамически загружаемые библиотеки) не являются открытыми файлами в смысле наличия файлового дескриптора, они ведут себя очень похоже. Пока какая-то программа выполняет код, файл остается на диске даже без записи в каталоге.
mv
является атомарной. Это, вероятно, потребует использования системного вызова rename
, а поскольку процессы не могут быть прерваны в режиме ядра, ничто не может помешать этой операции, пока она не завершится (успешно или нет). Опять же, нет изменения inode старого файла: создается новый, и уже запущенные процессы не будут знать о нем, даже если он был связан с одной из ссылок старого inode.В стратегии 3 на этапе перемещения нового файла к существующему имени удаляется запись каталога, ведущая к старому содержимому, и создается запись каталога, ведущая к новому содержимому. Это выполняется за одну атомарную операцию, поэтому эта стратегия имеет большое преимущество: если процесс откроет файл в любое время, он увидит либо старое, либо новое содержимое - нет риска получить смешанный контент или файл не существующий.
Перекомпиляция файла : при использовании gcc
(и поведение, вероятно, аналогично для многих других компиляторов),вы используете стратегию 2. Вы можете увидеть, что запустив strace
процессов вашего компилятора:
stat("a.out", {st_mode=S_IFREG|0750, st_size=8511, ...}) = 0
unlink("a.out") = 0
open("a.out", O_RDWR|O_CREAT|O_TRUNC, 0666) = 3
chmod("a.out", 0750) = 0
stat
и lstat
системные вызовы. a.out
, его индексный дескриптор и содержимое остаются на диске до тех пор, пока они используются уже запущенными процессами. a.out
. Это совершенно новый индексный дескриптор и совершенно новое содержимое, которое уже запущенным процессам не важно. Теперь, когда дело доходит до разделяемых библиотек, будет применяться то же поведение. Пока объект библиотеки используется процессом, он не будет удален с диска, как бы вы ни изменили его ссылки. Всякий раз, когда что-то должно быть загружено в память, ядро будет делать это через индексный дескриптор файла и, следовательно, будет игнорировать изменения, внесенные вами в его ссылки (например, связывание их с новыми файлами).
Мое понимание состоит в том, что благодаря отображению памяти программного процесса, ядро не позволит обновлять зарезервированную часть отображаемого файла. Я думаю, в случае, если процесс работает, то весь его файл зарезервирован, отсюда, обновление его, потому что вы составляете новую версию вашего источника, на самом деле приводит к созданию нового набора inodes. Короче говоря, более старые версии вашего исполняемого файла остаются доступными на диске через события неисправности страницы. Итак, даже если вы обновите огромный файл, его остаются доступными, а ядро должно все еще видно, что нетронутая версия до тех пор, пока процесс работает. Оригинальный файл inodes не должен использоваться повторно в течение длительного времени, так как процесс работает.
Это, конечно, должно быть подтверждено.
Это можно сделать с помощью одиночного sed
:
sed 's/\(.*\)-/\1 /'
или, используя расширенное регулярное выражение:
sed -r 's/(.*)-/\1 /'
точка, что sed
очень жадный, поэтому соответствует как можно большему количеству символов до -
, включая другие -
.
$ echo 'swp-RedHat-Linux-OS-5.5.0.0-03' | sed 's/\(.*\)-/\1 /'
swp-RedHat-Linux-OS-5.5.0.0 03
-121--14343- По соглашению мы проверяем переменную
errno
только при возникновении ошибки (например, при возврате некоторых функций с помощью -1).Вопрос 1: Какое значение
errno
используется для 0 перед запуском программы?
Действительно, мы должны проверять errno
только в случае, когда произошла ошибка. Это связано с тем, что если ошибка отсутствует, то возможно, что errno
будет содержать ненулевое значение (например, если ошибка произошла во время выполнения вызова библиотеки, но ошибка была восстановлена).
Поэтому установка errno
на 0 перед «запуском программы» не является необходимой, и я не стал бы следовать этому совету.
Более того, я прочитал, что лучше сохранить номер ошибки в локальной переменной, а затем проверить его
Да! Ваше наблюдение о том, что printf ()
может засорить errno
, является правильным. Если его значение необходимо сохранить, его следует скопировать в локальную переменную как можно скорее после возникновения ошибки.
Вопрос 2: Применяется ли приведенное выше утверждение к perror () и strerror (), поскольку они также являются системными вызовами, и существует вероятность того, что ошибка произошла и с ними.
perror ()
, вероятно, не вызывает ничего, что изменяет errno
, но если это так, то необходимо быть осторожным, чтобы скопировать значение errno
, прежде чем это произойдет. Уверен, вы можете предположить, что perror ()
правильно работает в вашей системе!
strerror ()
не должен беспокоиться об этом, так как он принимает номер ошибки в качестве параметра, так что, даже если для clobber errno
предыдущее значение уже сохранено.
При расширенном программировании в среде UNIX Ричарда Стивенса (Richard Stevens) мы должны проверить значение
errno
, только если возвращаемое значение функции указывает на то, что произошла ошибка. Я не понимаю, почему?
Потому что системные вызовы и вызовы библиотеки, которые устанавливают errno
при сбое, не обязаны устанавливать его на успех, поэтому он сохраняет любое значение, которое он имел ранее.
Это не всегда происходит при замене JAR-файла. Ресурсы JAR и некоторые загрузчики классов отражения во время выполнения не считываются с диска до тех пор, пока программа явно не запросит информацию.
Это проблема только потому, что банка - это просто архив, а не один исполняемый файл, который отображается в память. Это немного не-стоп, но это все еще ответвление вашего вопроса и то, чем я выстрелил себе в ногу.
Итак, для исполняемых файлов: да. Для баночных файлов: может быть (в зависимости от реализации).