Является ли операция записи в unix-атоме?

Я читал APUE (расширенное программирование в среде UNIX) и сталкивался с этим вопросом, когда вижу $ 3.11:

if (lseek(fd, 0L, 2) < 0) /* position to EOF */
err_sys("lseek error");
if (write(fd, buf, 100) != 100) /* and write */
err_sys("write error")

APUE говорит:

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

Он просто говорит, что cpu будет переключаться между вызовами функций между lseek и write, я хочу знать, будет ли он также переключаться на операцию с половинной write? Вернее, write атом? Если threadA пишет "aaaaa", threadB пишет "bbbbb", будет ли результат "aabbbbbaaa"?

Что еще, после этого APUE говорит, что pread и pwrite - это все атомарные операции, означает ли это, что эти функции используют mutex или lock внутренне, чтобы быть атомарными?

+4
источник поделиться
2 ответа

Вызов семантики Posix "атомный", возможно, является упрощением. Posix требует, чтобы записи и записи выполнялись в некотором порядке:

Записи могут быть сериализованы по отношению к другим чтениям и записи. Если read() данных файла может быть доказано (каким-либо образом) после write() данных, оно должно отражать эту write(), даже если вызовы выполняются разными процессами. Аналогичное требование применяется к нескольким операциям записи в одну и ту же позицию файла. Это необходимо для гарантии распространения данных из вызовов write() на последующие вызовы read(). (из раздела Обоснование спецификации pwrite для pwrite и write)

Гарантия атомарности, упомянутая в APUE, относится к использованию флага O_APPEND, который заставляет записи выполняться в конце файла:

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

Что касается pread и pwrite, APUE говорит (правильно, конечно), что эти интерфейсы позволяют приложению искать и выполнять ввод/вывод атомарно; другими словами, операция ввода-вывода будет выполняться в указанной позиции файла независимо от того, что делает какой-либо другой процесс. (Поскольку позиция указана в самом вызове и не влияет на постоянную позицию файла.)

Гарантия последовательности Posix выглядит следующим образом (из описания функций write() и pwrite()):

После успешного возврата write() в обычный файл:

  • Любое успешное read() из каждой позиции байта в файле, которое было изменено этой записью, должно возвращать данные, указанные в записи() для этой позиции, до тех пор, пока такие позиции байтов снова не будут изменены.

  • Любая последующая успешная write() в одну и ту же позицию байта в файле должна перезаписывать эти данные файла.

Как упоминалось в Обосновании, эта формулировка гарантирует, что два одновременных вызова write (даже в разных несвязанных процессах) не будут чередовать данные, потому что, если данные будут чередоваться во время записи, которая в конечном итоге преуспеет, вторая гарантия будет невозможна. Как это делается, зависит от реализации.

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

Это требование особенно важно для сетевых файловых систем, где некоторые схемы кэширования нарушают эту семантику.

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

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

В рамках однопоточного процесса Posix требует, чтобы read(), write(), pread() и pwrite() были атомарными, когда они работают с обычными файлами (или символическими ссылками). См. " Взаимодействие потоков с обычными файловыми операциями" для полного списка интерфейсов, которые должны соответствовать этому требованию.

+2
источник

В Linux есть блокирующие и неблокирующие системные вызовы. write является примером блокировки системного вызова, что означает, что выполнение поток будет заблокирован до тех пор, write не будет завершена. Поэтому, когда пользовательский процесс называется write, он не может выполнить ничего другого, пока системный вызов не будет завершен. Поэтому с точки зрения пользовательского потока он будет вести себя как атомный [хотя на уровне ядра многое может случиться, и выполнение системного вызова ядра может быть прервано много раз].

-1
источник

Посмотрите другие вопросы по меткам или Задайте вопрос