Цепные команды являются атомарными?

8

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

sudo rm somefile; sudo touch somefile

Возможно ли добавление процесса к файлу между этими двумя командами? Если так, есть ли способ гарантировать, что никакая другая команда не будет запущена между?

Дарт Эгрегиус
источник
4
Это может быть проблемой XY . Можете ли вы объяснить основную проблему, которую вы пытаетесь решить?
Нейт Элдридж
1
@NateEldredge: Совсем нет. Я могу понять, почему так кажется, основываясь на моей надуманной гипотезе. У меня даже нет проблем. Просто любопытно.
Дарт Эгредиус
5
Процесс, вероятно, будет продолжать писать в удаленный файл и даже не пытаться открыть новый.
Random832
Мое предположение в гипотетической форме оказалось немного неправильным, но я думаю, вы могли бы изменить «добавить» на «открыть» или «коснуться», и это все равно имело бы смысл.
Дарт Egregious

Ответы:

12

Цепная командная строка в основном представляет собой небольшой сценарий оболочки; он выполнит первую команду, используя обычную процедуру fork + exec, дождется ее завершения, а затем выполнит вторую таким же образом. Между этими двумя командами существует некоторое произвольное количество времени, которое оболочка берет на себя в своей бухгалтерии и обработке, в течение которых происходит обычная многопроцессорная обработка, и произвольные другие процессы могут выполнять произвольные другие действия. Так что ответ «нет». (Если вы действительно сделаете это, вы обнаружите, что запись каталога somefileисчезает, но сам файл остается (так как он открыт процессом) до тех пор, пока он не будет закрыт. Место на диске, используемое файлом, не будет восстановлено, пока это не произойдет. Тем временем , touchкоманда создаст новый, не связанный файл с тем же именем и путем.)

Если вы хотите изменить владельца файла на root, просто сделайте это sudo chown root:root somefile(хотя я не уверен, как это повлияет на процессы с открытым дескриптором файла). Если вы хотите уничтожить содержимое текущего файла, попробуйте truncate -s 0 somefile(запущенный процесс продолжит добавление к пустому файлу). Если это что-то еще, возможно, уточнить, что вы хотите сделать.

Том Хант
источник
Спасибо, Том. Я не был так обеспокоен моей гипотетической ситуацией. Было просто интересно, возможно ли сделать что-то подобное.
Дарт Егрегиус
Что касается общего вопроса, я не думаю, что есть действительно способ обеспечить атомарность в сценарии оболочки. Вы можете использовать файлы блокировки и тому подобное, чтобы гарантировать, что два взаимодействующих процесса не наступают друг на друга, и вы можете использовать разрешения, чтобы гарантировать, что произвольный процесс не может наступить на вашу работу. Но предотвращение запуска чего-либо еще может сильно испортить многопроцессорность ядра; Я не думаю, что код ring-3 может сделать это вообще.
Том Хант
Вы можете использовать обязательные блокировки файлов, если они включены и доступны в вашей файловой системе.
Ройма
5
chownИспользование somefile не повлияет на процессы, которые имеют дескриптор файла для somefile.
PSkocik
Например, вы можете сделать exec 3>somefile; sudo chmod 0600 somefile; echo hello world >&3; sudo cat somefile; exec 3>&-.
PSkocik
6

Нет. rmКоманда, сопровождаемая touchкомандой, вовсе не является атомарной. Между этими двумя командами будет много времени - возможно, в диапазоне миллисекунд. Многое может случиться за это время. Если вам не повезло, ваши учетные данные sudo могут даже истечь.

Одна программа, вызывающая вызовы unlinkи openвызовы, оставила бы намного более короткое окно для гонки, но это все же могло произойти.

Более безопасный подход - создать новый файл с временным именем и использовать renameсистемный вызов. «Перезапись» имени с помощью renameсистемного вызова гарантированно будет атомарной. Это может быть достигнуто с помощью touchи mv.

Но процесс, который открыл старый файл для записи, может продолжать запись в него даже после того, как он был удален. Это будет иметь место как для файла, удаленного с помощью, так unlinkи для удаленного с помощью rename.

kasperd
источник
3

Когда у процесса открыт файловый дескриптор для чтения, не имеет значения, что вы делаете с владельцем или разрешениями: процесс может продолжить доступ к файлу. Вы даже можете удалить файл, и процесс сможет продолжить доступ к нему через дескриптор файла.

Считайте, что разрешения являются контрольным шлюзом для получения дескриптора файла.

Если вы хотите создать файл атомарно, то вместо этого:

sudo rm somefile
sudo touch somefile

Вы могли бы рассмотреть это:

sudo touch anotherfile
sudo perl -e "rename 'anotherfile', 'somefile'"

который будет атомно заменен somefileна anotherfile. (Я бы предпочел использовать, mv -fно я не могу найти утверждение, которое гарантирует, что это вызывает rename(2)системный вызов.


Возможно, вы могли бы обновить свой Вопрос, чтобы объяснить, что вы подразумеваете под «взять под контроль файл». У вас может быть проблема XY здесь.

roaima
источник
Используйте mv -fдля получения rename(2)системного вызова. Вы правы, что lnне будете, потому что он всегда использует link(2)системный вызов, который не может заменить атомарно. ln -fпросто делает unlink(2)на цель первым.
Питер Кордес
@PeterCordes ах да, конечно, спасибо. У меня были lnмозги. Я не был уверен, как получить rename(2)гарантированную причастность, и было слишком поздно, чтобы копать дальше
роайма
POSIX гарантирует, что mv«будет выполнять действия, эквивалентные» rename(old, new). pubs.opengroup.org/onlinepubs/9699919799/utilities/mv.html . mv -fдействует так, как mv -iесли место назначения не доступно для записи, но оно не похоже на cp -fто, ln -fгде оно будет сначала отключено.
Питер Кордес
2

Нет, но

sudo rm somefile; sudo touch somefile

безопасен в большинстве ситуаций.

Большинство процессов открывают файлы и затем используют полученный дескриптор файла для доступа к содержимому файла.

Если процесс открывает какой-то файл, в sh:

 exec 3>somefile

Тогда другой процесс может свободно отсоединить (= удалить) somefile, и файловый дескриптор, для которого somefile был открыт первым процессом (в данном случае 3), продолжит ссылаться на исходное содержимое somefile, который теперь является файлом в подвешенном состоянии.

sudo touch somefile

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

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

Если вы хотите предотвратить повреждение файла несколькими процессами под одним и тем же пользователем (например, root), linux имеет обязательную и рекомендательную блокировку файлов. Вы можете использовать flockкоманду в сценариях оболочки для консультативной блокировки файлов (см. Man-страницу для получения дополнительной информации).

PSkocik
источник