LINUX.ORG.RU

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


0

1

Добрый день,

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

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

★★

>Т.е. содержимое буфера, переданное в write появится в файле полностью

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

pathfinder ★★★★
()

Если файл открыт с O_APPEND, то гарантируется, что запись будет произведена в конец файла, при этом перемещение указателя и запись будут осуществлены атомарно («The adjustment of the file offset and the write operation are performed as an atomic step»).

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

>Если файл открыт с O_APPEND, то гарантируется

А вы умеете пудрить мозги. :) Все правильно, перемещение указателя и запись будут осуществлены атомарно. Но факт атомарности задания смещения и ОДНОЙ операции записи ещё не гарантирует запись ВСЕГО блока данных, который будет передан функции write.

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

> А вы умеете пудрить мозги. :)

Мы просто о разных вещах говорим :-)

Но факт атомарности задания смещения и ОДНОЙ операции записи ещё не гарантирует запись ВСЕГО блока данных, который будет передан функции write

Естественно. ENOSPC/EIO/EFBIG и прочие прелести никто не отменял. Идея в том, что ситуации, когда первый процесс сделал seek, в это время второй записал данные и только после этого первый записал свои данные, перетирая данные второго процесса, быть не должно.

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

>ENOSPC/EIO/EFBIG и прочие прелести никто не отменял

Это не самые главные прелести. А вот EINTR уже интереснее будет.

Если внимательно посмотреть, что говорил ТС:

Т.е. содержимое буфера, переданное в write появится в файле ПОЛНОСТЬЮ, не прерываемое записями из других процессов.

А вот этого O_APPEND гарантировать не сможет.

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

> А вот EINTR уже интереснее будет.

Ну как сказать. Если верить документации, то EINTR возвращается, если the call was interrupted by a signal *before* any data was written.

не прерываемое записями из других процессов

По кра «не перетертое записями из других процессов»

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

EINTR

Вот, нашёл:

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 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.)

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

Ну как сказать. Если верить документации, то EINTR возвращается, если the call was interrupted by a signal *before* any data was written.

Не ладно, давай другой случай. Предположим надо записать сообщение «10:00 Message 1\n». Конкурирующее приложение попытается записать «10:00 Message 2\n». Мы начинаем записывать. Уже успели записать «10:00 Me» и тут внезапно пришел сигнал. После этого write возвращает 8 вместо 16. Остальные надо дозаписать. Собираемся ещё раз вызвать write, для того, чтобы записать оставшиеся 8 байт. Но неожиданно происходит переключение контекста. Второй процесс полностью записывает строку. После этого первый процесс дозаписывает оставшиеся 8 байт. В итоге имеем:

10:00 Me10:00 Message 2
ssage 1
Вместо
10:00 Message 1
10:00 Message 2

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

> и тут внезапно пришел сигнал.

В том-то и дело, что сигнал не прервет запись. См. «A disk is not a slow device» выше — с «быстрыми» устройствами EINTR не будет.

Если мы пишем в сокет/пайп, то да, такая ситуация может иметь место. Но ни сокет, ни пайп не являются seekable devices, и O_APPEND к ним неприменим.

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

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

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

pathfinder ★★★★
()

>ПисАть в файл

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

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

> Попробуй за один заход затолкнуть гигабайт данных.

Сейчас попробую.

Но на самом деле размер ограничен размерностью типа ssize_t.

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

Попробуй за один заход затолкнуть гигабайт данных.

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define BUFSIZE (1024*1048576*sizeof(char))

int main(int argc, char** argv)
{
	int fd;
	char* buf = (char*)malloc(BUFSIZE);
	ssize_t s;
	if (!buf) {
		perror("malloc");
		return EXIT_FAILURE;
	}

	fd = open("text.txt", O_CREAT | O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR);
	if (-1 == fd) {
		perror("open");
		return EXIT_FAILURE;
	}

	s = write(fd, buf, BUFSIZE);
	if  (-1 == s) {
		perror("write");
		close(fd);
		return EXIT_FAILURE;
	}

	printf("Expected to write: %zu, written: %zd\n", BUFSIZE, s);
	close(fd);

	return EXIT_SUCCESS;
}
$ gcc write.c -o write -Wall -Wextra -O2
write.c: In function ‘main’:
write.c:10: warning: unused parameter ‘argc’
write.c:10: warning: unused parameter ‘argv’
$ ./write
Expected to write: 1073741824, written: 1073741824

Полтора гига тоже за раз записалось :-)

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

у меня сдохло на 2 гигабайтах

Expected to write: 6442450944, written: 2147479552

anonymous
()

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

Да.

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

Похоже я действительно был не прав. Я все напутал :(((((. Ограниченность буфера может иметь отношение только к read (и то отчасти). write-у они никак не мешают. Единственная причина, по которой write вынужден вернуть меньшее количество байт, это возникший сигнал. При этом если не было записано ни одного байта, то вернется EINTR, в противном случае вернется записанное число байт. SA_RESTART должен решить проблему.

Правда мне совсем не понятно по части атомарности. Второй перезапуск и последующие так же будут одной атомарной операцией, или как?

Так же я понимаю что write может вернуть меньшее количество байт, чем отправлялось при записи. При этом как я понял не сказано, что это может быть только по причине сигнала. Драйверу может вернуть меньше, если ему так хочется, по каким-то своим неведомым причинам.

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder
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.

В стандарте сказано об атомарности МЕЖДУ изменением и началом записи. Не уверен что делает саму операцию записи всего блока полностью атомарной.

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

> Правда мне совсем не понятно по части атомарности. Второй перезапуск и последующие так же будут одной атомарной операцией, или как?

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

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

> write-у они никак не мешают. Единственная причина, по которой write вынужден вернуть меньшее количество байт, это возникший сигнал.

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



Так ты определись )))

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

>Так ты определись )))

В общем так. Я не вижу других причин возвращать драйверу меньшее количество байт, кроме прихода сигнала. Но как я понял ничто не запрещает вернуть меньшее количество и без сигнала.

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

> увеличение размера файла происходит _до_ начала write

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

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

Зачем знать внутренности ядра? достаточно знать man.

man 2 write

If the file was open(2)ed with O_APPEND, the file offset is first set to the end of the file before writing. The adjustment of the file offset and the write operation are performed as an atomic step.

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

> ничто не запрещает вернуть меньшее количество и без сигнала.

Т.е. ты всё-таки вернулся к своей прежней позиции:

write даже без конкурентного доступа не может гарантировать, что весь буфер будет передан.


?

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

тут кто-нибудь man читает вообще?

man 2 write

The number of bytes written may be less than count if, for example, there is insufficient space on the underlying physical medium, or the RLIMIT_FSIZE resource limit is encountered (see setrlimit(2)), or the call was interrupted by a signal handler after having written less than count bytes. (See also pipe(7).)

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

> увеличение размера файла происходит _до_ начала write

%) Вы про «увеличение размера файла» или про «изменение текущего смещения в файле» ?

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

>целиком и полностью зависит от реализации драйвера.

тебе надо писателем фантастом работать, но лучше попробовать работать читателем манов.

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

>The number of bytes written may be less than count if, for example...

Думаю здесь ключевое слово for example

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

>> ничто не запрещает вернуть меньшее количество и без сигнала.

Т.е. ты всё-таки вернулся к своей прежней позиции:

Да. У меня по прежнему осталось сомнение, что O_APPEND делает всю операцию записи полностью атомарной.

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

> Вы

Не я, а tailgunner

про


сам как думаешь? )

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

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

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

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

> Да. У меня по прежнему осталось сомнение, что O_APPEND делает всю операцию записи полностью атомарной.

Правильное мнение. ))

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

> Оба действия происходят до возврата из вызова, но какое первым, а какое вторым - целиком и полностью зависит от реализации драйвера.

Какого драйвера? Драйвера диска? Точно нет. Драйвера ФС? Тоже нет вряд ли, потому что этот порядок продиктован семантикой.

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

>> увеличение размера файла происходит _до_ начала write

%) Вы про «увеличение размера файла» или про «изменение текущего смещения в файле» ?

Ты точно прочитал то, что я написал?

tailgunner ★★★★★
()

Я бы все таки посоветовал TC использовать flock в любом случае и не искать приключений на свою задницу.

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

>Ты точно прочитал то, что я написал?

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

Что значит «дырка»? Я так понимаю при получении сигнала будет записано столько байт, сколько успели записать к моменту прихода сигнала. И файл увеличится на столько байт, сколько успели записать. «Дырки» здесь никакой не должно быть.

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

> Драйвера ФС? Тоже нет вряд ли

Именно фс. Кто у нас еще отвечает за размер файла? ))

этот порядок продиктован семантикой.


И как же он её диктует? ))

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

> при получении сигнала будет записано столько байт, сколько успели

Что ты понимаешь под словом «записано»?

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

>Что ты понимаешь под словом «записано»?

Если ты знаешь что-то больше меня, говори сразу. Не юли.

Что ты понимаешь под словом «записано»?

Может тебе больше нравится слово «обработано».

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

> Что значит «дырка»?

Ы? В файлах бывают «дырки» - это место, в которое никогда не записывались данные.

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

Это толкование, которое допускает битые записи при наличии сигналов. Если делать так: lseek to EOF, filesize+=nbytes, write nbytes, то после получения сигнала ты сможешь дописать то, что не записано из-за сигнала.

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

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

> Может тебе больше нравится

Мне нравятся write(fd, buf, size) и состояние «до» и «после».

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

>Что ты понимаешь под словом «записано»?

Я не имел дела с блочными устройствами. Писал драйвера для символьных устройств и то для древнего ядра 2.4. Там есть какие-то особенности, из-за которых может образоваться дырка?

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