LINUX.ORG.RU

ПисАть в файл из разных процессов


0

1

Добрый день,

Я где-то читал, что писать (добавлять) в файл из разных процессов можно, при этом гарантируется атомарность записи. Т.е. содержимое буфера, переданное в write появится в файле полностью, не прерываемое записями из других процессов.

Безопасно ли таким образом писать лог-файл из разных процессов?

★★

Ответ на: комментарий от pathfinder

> Я не имел дела с блочными устройствами.

Ну, мы пока про файлы, а до блочных устройств еще пилить и пилить.

Там есть какие-то особенности, из-за которых может образоваться дырка?


Ща, погодь, я жду, когда tailgunner произнесет слоги, либо красиво (надеюсь ))) соскочит с темы. ))

LamerOk ★★★★★
()
Ответ на: комментарий от tailgunner

>Если учесть, что все учебники говорят вещи типа «O_APPEND is perfect for log files», я думаю, что я прав :)

Не, ну то, что O_APPEND замечателен для записи логов, это ещё не говорит о том, что вся операция write атомарна.

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

> ну то, что O_APPEND замечателен для записи логов, это ещё не говорит о том, что вся операция write атомарна.

Атомарность записи ту ни при чем :) Ты можешь открыть тот же файл без O_APPEND и «чинить» записи, недописанные из-за сигналов.

tailgunner ★★★★★
()
Ответ на: комментарий от LamerOk

>> filesize+=nbytes, write nbytes

Почему не наоборот? ))

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

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

>Атомарность записи ту ни при чем :)

O_o О чём мы вообще тогда говорим? Ты хоть читал про что дискуссия?

pathfinder ★★★★
()
Ответ на: комментарий от tailgunner

>Атомарность записи ту ни при чем :) Ты можешь открыть тот же файл без O_APPEND и «чинить» записи, недописанные из-за сигналов.

Если будет конкурентная запись нескольких потоков, не починишь.

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

> Ты хоть читал про что дискуссия?

Я - да, а ты?

Если будет конкурентная запись нескольких потоков, не починишь.

Починю.

tailgunner ★★★★★
()
Ответ на: комментарий от LamerOk

>file_offset != new_eof

Тебя разве в детстве не учили, что врать старшим нехорошо?

http://www.opengroup.org/onlinepubs/009695399/functions/pwrite.html

On a regular file or other file capable of seeking, the actual writing of data shall proceed from the position in the file indicated by the file offset associated with fildes. Before successful return from write(), the file offset shall be incremented by the number of bytes actually written. On a regular file, if this incremented file offset is greater than the length of the file, the length of the file shall be set to this file offset.

.............

If the O_APPEND flag of the file status flags is set, the file offset shall be set to the end of the file prior to each write and no intervening file modification operation shall occur between changing the file offset and the write operation.

anonymous
()
Ответ на: комментарий от tailgunner

> запись, испорченная из-за сигнала, нельзя восстановить.

Т.е. ты предполагаешь, что сигнал может прервать ядрёную реализацию write() между filesize+=nbytes и write nbytes и сделать возврат из write() в юзерспейс?

LamerOk ★★★★★
()
Ответ на: комментарий от anonymous

> Тебя разве в детстве не учили, что врать старшим нехорошо?

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

А пока не влезай в тред со своим флудом.

LamerOk ★★★★★
()
Ответ на: комментарий от pathfinder

> Без flock, с одним O_APPEND?

Ты-то сам читаешь дискуссию? Я написал:

открыть тот же файл без O_APPEND

естественно, это сработает только при той реализации, которую я описал. Если она не такая, при сигналах данные будут битые (усеченные).

tailgunner ★★★★★
()
Ответ на: комментарий от anonymous

Единственный, кто в этом треде точно в заблуждении на настоящий момент, - это ты. ))

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

> Т.е. ты предполагаешь, что сигнал может прервать ядрёную реализацию write() между filesize+=nbytes и write nbytes и сделать возврат из write() в юзерспейс?

Нет. Я предполагаю, что filesize += nbytes оставляет в файле место и для байтов, которые не записаны из-за сигнала. Тогда эти незаписанные байты можно дописать через другой дескриптор.

Далеко же мы уклонились от «подходит ли O_APPEND для логов» :)

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> Я предполагаю, что filesize += nbytes оставляет в файле место и для байтов, которые не записаны из-за сигнала.

Ну то есть, filesize += nbytes выполненно, а write nbytes - нет, и это из-за сигнала? Т.е. в юзерспейс мы получаем после write() ситуацию, когда данные из buf не записаны, а размер файла в структурах на стороне ядра был увеличен?

Далеко же мы уклонились


Ну так лор же. )))

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

> Т.е. в юзерспейс мы получаем после write() ситуацию, когда данные из buf не записаны, а размер файла в структурах на стороне ядра был увеличен?

Да. buf записан не полностью, и в файле дырка - штатная ситуация.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> Да. buf записан не полностью, и в файле дырка - штатная ситуация.

Думаю, что нет. write() относительно своих аргументов оставляет систему консистентной. Причину я уже указывал выше - обработка сигналов происходит на границе юзерспейск/кернелспейс. Пока отрабатывает код write() на стороне ядра никакие сигналы, естественно, не фигурируют. Завтра, если будет время, посмотрю, есть ли где примеры rollback'ов в ядре при выходе из или же доставка сигнала просто откладывается.

buf записан не полностью,


Это штатная ситуация и без всяких сигналов, и в этом случае write() возвращает корректное значение.

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

>> Да. buf записан не полностью, и в файле дырка - штатная ситуация.

Думаю, что нет. write() относительно своих аргументов оставляет систему консистентной.

Что значит «консистентной»? Если ты хочешь сказать, что write на блочном устройстве всегда пишет либо весь буфер, либо 0 байт (как указал анонимный брат), то вся дискуссия ни о чем, и O_APPEND иделен для логов без всяких исключений и подводных камней (я так и считаю :)). Если ты о чем-то другом, я тебя не понял.

обработка сигналов происходит на границе юзерспейск/кернелспейс.

Это не так (говорю как действующий драйверописатель).

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> write на блочном устройстве

Ну какое блочное устройство, когда мы говорим о _регулярном файле_?

Что значит «консистентной»?


Это значит, что либо часть данных записана и размер файла, если есть необходимость, изменен соответственно, либо возврат ошибки и размер файла остался не изменным. В соответсвии с этой спецификацией:

On a regular file or other file capable of seeking, the actual writing of data shall proceed from the position in the file indicated by the file offset associated with fildes. Before successful return from write(), the file offset shall be incremented by the number of bytes actually written. On a regular file, if this incremented file offset is greater than the length of the file, the length of the file shall be set to this file offset.


Ссылка в предыдущем моём посте.

Т.е. возникновение sparse file при write() на fd с O_APPEND не возможно (кроме как при баге на стороне ядра).

Кстати, спецификация со мной согласна:

If write() is interrupted by a signal before it writes any data, it shall return -1 with errno set to [EINTR].


If write() is interrupted by a signal after it successfully writes some data, it shall return the number of bytes written.


)) Версия об откладывании обработки сигналов ближе к телу.

O_APPEND иделен для логов без всяких исключений и подводных камней


Нифига он не идеален, так как write() имеет полное право писать по char'у за вызов. ))

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

Это не так


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

Посмотрю код позже на свежую голову.

LamerOk ★★★★★
()

одни теоретики в треде. я так делал, и нормально работало, за тонны логов ни разу не натыкался на то, чтоб одна запись влезла в середину второй.

amomymous ★★★
()
Ответ на: комментарий от LamerOk

> Before successful return from write(), the file offset shall be incremented by the number of bytes actually written. On a regular file, if this incremented file offset is greater than the length of the file, the length of the file shall be set to this file offset.

А, момент про «the length of the file shall be set to this file offset» я упустил. Значит, моя теория неверна.

Еще прошлым летом это было так. ))

Драйверы всегда имели право обрабатывать сигналы (и обязанность тоже).

tailgunner ★★★★★
()
Ответ на: комментарий от amomymous

> одни теоретики в треде. я так делал, и нормально работало

То, что O_APPEND - это рекомендованный способ записи в логи, сказали еще на первой странице :)

tailgunner ★★★★★
()

Всем спасибо!

Kpoxman ★★
() автор топика
Ответ на: комментарий от pathfinder

в противном случае вернется записанное число байт. SA_RESTART должен решить проблему.

man 7 signal

If a blocked call to one of the following interfaces is interrupted by a signal handler, then the call will be automatically restarted after the signal handler returns if the SA_RESTART flag was used; otherwise the call will fail with the error EINTR:

* read(2), readv(2), write(2), writev(2), and ioctl(2) calls on «slow» devices. A «slow» device is one where the I/O call may block for an indefinite time, for example, a terminal, pipe, or socket. (A disk is not a slow device according to this definition.) If an I/O call on a slow device has already transferred some data by the time it is interrupted by a signal handler, then the call will return a success status (normally, the number of bytes transferred).

Ключевые моменты выделены полужирным шрифтом :-) У меня не получилось прервать write сигналом при записи на диск.

Драйверу может вернуть меньше, если ему так хочется, по каким-то своим неведомым причинам.

Да. Но как правило, это сигнализирует о критической ошибке (нет места на устройстве, превышена квота, ошибка ввода/вывода).

sjinks ★★★
()
Ответ на: комментарий от tailgunner

> Потому что тогда запись, испорченная из-за сигнала, нельзя восстановить.

Хорошо, записали бы 512 байт из 1024, после чего система вернула какую-то (не важно, какую) ошибку. Следующий write() сделает lseek к EOF (man 2 write) и запишет (если получится) вторую часть данных. Тогда, если исходить из того, что filesize+=nbytes, то размер файла будет 1536 байт. Нет?

sjinks ★★★
()
Ответ на: комментарий от sjinks

>У меня не получилось прервать write сигналом при записи на диск.

Ковырялся в исходниках ядра. Не нашел там реакции на приходящий сигнал для обычных файлов. Хотя может я плохо искал, для меня много чего в ядре непонятно. Но похоже, что write на обычных файлах действительно кладет на сигналы, чем собственно и упоминается в man 7 signal.

Интересно, зачем это разделение на «slow» и «not a slow»? Сказано, что диск НЕ является медленным устройством.

Но можно ли проводить связь между диском и обычным файлом. Т. е. если мы записываем данные в обычный файл, значит ли это, что мы записываем их на диск? Как должна вести себя система в записи на сетевые файловые системы?

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

O_APPEND при записи в NFS может убить файл.

[quote]O_APPEND may lead to corrupted files on NFS file systems if more than one process appends data to a file at once. This is because NFS does not support appending to a file, so the client kernel has to simulate it, which can't be done without a race condition.[/quote]

sjinks ★★★
()
Ответ на: комментарий от pathfinder

Затрудняюсь ответить. Вероятно, зависит от файловой системы/драйвера.

sjinks ★★★
()
Ответ на: комментарий от pathfinder

>Если мы записываем данные в обычный файл, значит ли это, что мы записываем их на диск

Какую мысль я хотел выразить. Я так понимаю теоретически может существовать файловая система, для которой «носитель» будет именно «slow device». Программа по хорошему не должна делать никаких предположений об особенностях используемой файловой системы. Соответственно не стоит сильно закладываться на O_APPEND.

З.Ы.

Жду контраргументы. :)

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

> Не нашел там реакции на приходящий сигнал для обычных файлов.

да, запись не прерывается и атомарна (под ->i_mutex).
это для «обычных» файлов, напр pipe на сигналы реагирует.

Как должна вести себя система в записи на сетевые файловые системы?


должна соответствовать, я думаю, ведь это из-за стандартов.

но я в этом совсем не разбираюсь и не проверял.

idle ★★★★★
()
Ответ на: комментарий от pathfinder

> теоретически может существовать файловая система,

теоретически, драйвер fs может реализовать fop->write/aio_write
как угодно, да. но правильное поведение, это как
generic_file_aio_write().

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

не стоит сильно закладываться на O_APPEND.


это я не понял

idle ★★★★★
()
Ответ на: комментарий от idle

>сигналы ведь не проверяются не потому, что это сложно сделать.

Если не трудно. Не могли бы более развернуто объяснить почему осознано (как я понял) отказались от сигналов. Производительность?

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

> Программа по хорошему не должна делать никаких предположений об особенностях используемой файловой системы.

Тут, вероятно, всё зависит от точного определения slow device. Если есть дисковод/дискета, попробуйте проверить, отреагирует ли write на сигнал при записи мегабайта данных на дискету? Мне просто интересно.

Тут же еще такая вещь, что далеко не со всеми устройствами O_APPEND пройдет. Сокет/пайп так не открыть, терминал по-моему тоже.

Я так понимаю теоретически может существовать файловая система

Все зависит от реализации и следования стандартам. Если подобное поведение write — это соответствие POSIX'у, то если файловая система соответствует POSIX, она должна поддерживать такое поведение write.

sjinks ★★★
()
Ответ на: комментарий от pathfinder

> Производительность?

нет. стандарты или historical behaviour. те, в любом случае это уже не поменять.

вот именно поэтому read прерывается только фатальным сигналом, что было значительно сложнее реализовать, нужны были изменения в core kernel.

гораздо проще было бы просто реагировать на любой pending_signal(), но это был бы user visible change.

idle ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.