
Вопрос
Предположим, у меня есть некий некаталог (файл, именованный канал/сокет, что угодно) по пути /tmp/foo
и некий другой некаталог по пути /tmp/bar
. Затем два (или более) процесса начинают выполняться одновременно:
Процесс 1 выполняет:
unlink('/tmp/foo') /* or rename('/tmp/foo', '/tmp/removed') */
unlink('/tmp/bar') /* or rename('/tmp/bar', '/tmp/removed') */
Процесс два (и т. д.) выполняет:
link('/tmp/foo', '/tmp/bar')
Насколько я понимаю, процесс два никак не может быть успешным (либо link(2)
is предпринят, пока /tmp/foo
все еще присутствует, в этом случае /tmp/bar
также присутствует, поэтому он должен завершиться неудачей с EEXIST
, либо /tmp/foo
is исчез, поэтому он должен завершиться неудачей с ENOENT
).
Но эта интуиция основана на предположении, что системные вызовы unlink(2)
and/or rename(2)
по своей сути последовательны в своих эффектах расцепления, поэтому я ищу подтверждение своего понимания: существует ли какая-либо *nix-подобная система, ядро которой позволяет двум вызовам unlink(2)
and/or rename(2)
быть успешными, но одновременно заставляет их link(2)
также быть успешными (будь то из-за переупорядочивания расцепления /tmp/foo
and /tmp/bar
и отсутствия абстрагирования/сокрытия этого от вызывающего процесса link(2)
, или из-за какого-то другого странного состояния гонки/ошибки)?
Текущее понимание
Я прочитал man-страницы для unlink(2)
, rename(2)
, и link(2)
для Linux и нескольких BSD, а также спецификацию POSIX для этих функций. Но я не думаю, что они действительно содержат что-либо обнадеживающее по этому вопросу, после тщательного рассмотрения. По крайней мере, в случае с rename(2)
, нам обещают, чтоместо назначенияатомарно заменяется, если он уже присутствует (ошибки в самой ОС в сторону), но ничего больше.
Я виделпретензиичто несколько одновременных выполнений rename(foo, qux)
будут атомарно и переносимо иметь все, кроме одного переименования, с ошибкой ENOENT
- так что это многообещающе! Я просто не уверен, можно ли это распространить на получение link(foo, bar)
ошибки с ENOENT
при тех же обстоятельствах.
Предпочтительные ответы
Я понимаю, что это одна из тех ситуаций, когда «невозможно доказать отрицательное» — в лучшем случае мы можем только отметить, что нет никаких доказательств того, что link(2)
существует *nix-подобная система, которая позволит двум процессам успешно завершиться.
Поэтому я ищу ответы, охватывающие как можно больше систем *nix (по крайней мере Linux, OS X и различные BSD, но в идеале также и проприетарные, все еще используемые системы, такие как Solaris 10) — от людей, которые достаточно хорошо знакомы с этими системами и этим узким набором проблем (атомарные/хорошо упорядоченные операции файловой системы), и они уверены (насколько это вообще возможно), что они знали бы о таких проблемах, как вышеупомянутая ошибка Mac OS X, которая на самом деле не является rename(2)
атомарной, если бы они существовали на платформах, с которыми они знакомы. Это дало бы мне достаточно уверенности в том, что это работает так, как я думаю, в достаточно переносимой манере, чтобы на это можно было положиться.
Заключительное замечание
Это не вопрос «проблемы X/Y» — нет никакой глубинной проблемы, на которую можно было бы ответить, отослав меня к различным механизмам блокировки/IPC или чему-то еще, что работает с неопределенностью относительно того, как взаимодействуют эти конкретные системные вызовы: я конкретно хочу узнать, можно ли полагаться на то, что вышеуказанные системные вызовы будут переносимо взаимодействовать, как и ожидается, между *nix-подобными системами, используемыми сегодня на практике.
решение1
Посмотрите на такие стандарты, какPOSIXдля гарантий переносимости. На практике большинство систем, совместимых с POSIX, имеют незначительные отклонения от спецификаций, но в целом вы можете положиться на гарантии, данные в спецификации. Большинство современных unice-систем соответствуют спецификации, даже если они не были официально протестированы. Возможно, их нужно запустить в режиме POSIX, например, настроив POSIXLY_CORRECT=1
с помощью bash или убедившись, что /usr/xpg4/bin
он опережает /bin
и /usr/bin
находится PATH
в Solaris.
Один Unix v2(старое расширение POSIX) говорит следующее оlink
:
Функция
link()
автоматически создаст новую ссылку для существующего файла, а количество ссылок файла увеличится на единицу.
Оrename
:
Если ссылка, названнаяновыйаргумент существует, он удаляется истарыйпереименован вновый. В этом случае ссылка с именем new останется видимой для других процессов на протяжении всей операции переименования и будет ссылаться либо на файл, на который ссылаетсяновыйилистарыйдо начала операции.
POSIX явно утверждает, что если место назначения существует, его замена должна быть атомарной. Однако он не утверждает, что само переименование должно быть атомарным, т.е. что нет момента времени, когда обастарыйиновыйссылаются на файл в вопросе, или когда ни то, ни другое не делает. На практике эти свойства верны в системах unix, по крайней мере с локальными файловыми системами.
Более того, порядок операций гарантируется: в C ;
гарантирует последовательное выполнение; в sh ;
/newline гарантирует последовательное выполнение (как &&
и т. д.); другие языки программирования предлагают аналогичные гарантии. Так в
unlink("/tmp/foo");
unlink("/tmp/bar");
гарантируется, что не существует момента времени, когда /tmp/foo
существует, но не существует /tmp/bar
(предполагая, что /tmp/bar
существует изначально). Поэтому параллельное выполнение процесса link("/tmp/foo", "/tmp/bar")
не может быть успешным.
Обратите внимание, что атомарность не гарантируетустойчивость. Атомарность — это наблюдаемое поведение в работающей системе. Устойчивость в контексте файловых систем — это то, что происходит в случае сбоя системы. Многие файловые системы жертвуют устойчивостью ради производительности, поэтому если выполнение unlink("foo"); unlink("bar");
прерывается (при текущем каталоге на дисковом хранилище), возможно, что bar
будет удален и foo
останется позади.
Некоторые сетевые файловые системы дают меньше гарантий, когда операции происходят на разных клиентах. Старые реализации NFS были печально известны этим. Я думаю, что современные реализации лучше, но у меня нет опыта работы с современными NFS.