Является ли файл append атомарным в UNIX?
=========================================

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

 

A напишите, что размер PIPE\_BUF должен быть атомарным. Это должно быть не менее 512 байт, хотя оно может быть легко увеличено (для Linux, похоже, установлено значение 4096).

Предположим, что вы говорите все полностью совместимые с POSIX компоненты. Например, это неверно для NFS.

Но если вы напишете файл журнала, который вы открыли в режиме "O\_APPEND", и сохраните свои строки (включая новую строку) в байтах "PIPE\_BUF", вы должны иметь возможность записывать несколько файлов в файл журнала без каких-либо проблем с коррупцией. Любые прерывания будут поступать до или после записи, а не посередине. Если вы хотите, чтобы целостность файла выдержала перезагрузку, вам также нужно вызвать `fsync(2)` после каждой записи, но это ужасно для производительности.

Уточнение: прочитайте комментарии и [ответ Озона Соломона](/questions/82187/is-file-append-atomic-in-unix/541733#541733). Я не уверен, что `O_APPEND` должен иметь атомарность размера `PIPE_BUF`. Вполне возможно, что это именно то, как Linux реализовал `write()`, или это может быть связано с размерами блоков основной файловой системы.

 

 **Изменить:** Обновлен в августе 2017 года с последними результатами Windows.

  Я собираюсь дать вам ответ со ссылками на тестовый код и результаты в качестве автора предложенного [Boost.AFIO,](https://ned14.github.io/afio/index.html) который реализует асинхронную файловую систему и библиотеку файлового ввода-вывода C++.

  Во-первых, O\_APPEND или эквивалентный FILE\_APPEND\_DATA в Windows означает, что приращения максимального экстента файла ("длины" файла) являются **атомарными** при одновременной записи. Это гарантируется POSIX, и Linux, FreeBSD, OS X и Windows все реализуют это правильно. Samba также реализует это правильно, NFS до v5 нет, так как ему не хватает возможности форматирования проводов для атомарного добавления. Таким образом, если вы откроете свой файл только для добавления, одновременные записи не будут разрываться по отношению друг к другу на любой основной ОС, если не задействована NFS.

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

 

---

 Нет O\_DIRECT/FILE\_FLAG\_NO\_BUFFERING:
-----------------------------------------

  Microsoft Windows 10 с NTFS: атомарность обновления = 1 байт до и включительно 10.0.10240, с 10.0.14393 не менее 1 МБ, возможно, бесконечное (*).

  Linux 4.2.6 с ext4: атомарность обновления = 1 байт

  FreeBSD 10.2 с ZFS: атомарность обновления = не менее 1 МБ, возможно, бесконечна (*)

  O\_DIRECT/FILE\_FLAG\_NO\_BUFFERING:
-------------------------------------

  Microsoft Windows 10 с NTFS: обновлять атомарность = до и включительно с 10.0.10240 до 4096 байт, только если страница выровнена, в противном случае 512 байт, если FILE\_FLAG\_WRITE\_THROUGH выключен, иначе 64 байта. Обратите внимание, что эта атомарность, вероятно, является особенностью PCIe DMA, а не предназначена для. Начиная с 10.0.14393, по крайней мере, 1 МБ, вероятно, бесконечное (*).

  Linux 4.2.6 с ext4: атомарность обновления = не менее 1 МБ, возможно, бесконечность (*). Обратите внимание, что более ранние версии Linux с ext4 определенно не превышали 4096 байт, XFS, конечно, раньше имела пользовательскую блокировку, но, похоже, недавний Linux наконец исправил это.

  FreeBSD 10.2 с ZFS: атомарность обновления = не менее 1 МБ, возможно, бесконечна (*)

 

---

 Вы можете увидеть необработанные результаты эмпирических испытаний по адресу <https://github.com/ned14/afio/tree/master/programs/fs-probe>. Обратите внимание, что мы тестируем разрывы только на 512 байтов, поэтому я не могу сказать, разорвется ли частичное обновление сектора 512 байт во время цикла чтения-изменения-записи.

  Таким образом, чтобы ответить на вопрос OP, записи O\_APPEND не будут мешать друг другу, но при чтении одновременно с записями O\_APPEND, вероятно, будут наблюдаться разрывы записей в Linux с ext4, если не включен O\_DIRECT, после чего ваши записи O\_APPEND должны быть кратны размеру сектора.

 

---

 (*) "Вероятно, бесконечное" проистекает из этих пунктов в спецификации POSIX:

 
>   Все следующие функции должны быть атомарными по отношению друг к другу в эффектах, указанных в POSIX.1-2008, когда они работают с обычными файлами или символическими ссылками... [много функций]... read()... write ()... Если два потока каждый вызывают одну из этих функций, каждый вызов должен видеть все указанные эффекты другого вызова, или ни одного из них. [[Источник]](https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_07%23tag_15_09_07)
> 
> 

  а также

 
>   Записи могут быть сериализованы относительно других операций чтения и записи. Если read() файловых данных может быть доказано (любым способом) после write() данных, это должно отражать этот write(), даже если вызовы выполняются разными процессами. [[Источник]](https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html)
> 
> 

  но наоборот:

 
>   Этот том POSIX.1-2008 не определяет поведение одновременной записи в файл из нескольких процессов. Приложения должны использовать некоторую форму управления параллелизмом. [[Источник]](https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html)
> 
> 

  [Вы можете прочитать больше о значении этого в этом ответе](/questions/82190/atomicity-of-write2-to-a-local-filesystem/541745#541745)

 

Я написал script, чтобы эмпирически проверить максимальный атомный размер append. script, написанный в bash, порождает несколько рабочих процессов, которые все пишут записи, относящиеся к конкретным работникам, в один и тот же файл. Затем он считывает файл, просматривая перекрывающиеся или поврежденные подписи. Вы можете увидеть источник для script в этом [сообщении в блоге](http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/).

  **Фактический максимальный атомный размер append зависит не только от ОС, но и от файловой системы.**

В Linux + ext3 размер составляет 4096, а в Windows + NTFS - 1024. Подробнее см. комментарии ниже.

 

Вот что говорит стандарт: [http://www.opengroup.org/onlinepubs/009695399/functions/pwrite.html](https://www.opengroup.org/onlinepubs/009695399/functions/pwrite.html).

>  Если установлен флаг `O_APPEND` флагов состояния файла, смещение файла должно быть установлено до конца файла перед каждой записью, и между изменением смещения файла и операции записи не должно происходить промежуточная операция изменения файла.
> 
> 
>