2012-03-26 4 views
8

Можно ли одновременно работать с несколькими процессами для доступа (записи) к одному и тому же файлу? Используя следующий код, он работает, но у меня есть сомнения.Несколько процессов, обращающихся к одному и тому же файлу

Использование экземпляра в экземпляре - это исполняемый файл, который вызывается каждый раз при получении электронной почты и записывает его в центральный файл.

if (freopen(console_logfile, "a+", stdout) == NULL || freopen(error_logfile, "a+", stderr) == NULL) { 
    perror("freopen"); 
} 
printf("Hello World!"); 

Это работает на CentOS и скомпилирован как C.

+0

Возможный (лучший) дубликат [fopen two processes] (http://stackoverflow.com/questions/1842909/fopen-two-processes). – blahdiblah

+0

См. Также [Может ли несколько процессов присоединяться к файлу с помощью fopen без каких-либо проблем с параллелизмом?] (Http://stackoverflow.com/questions/7552451/can-multiple-processes-append-to-a-file-using-fopen- без-любой-параллелизм-вероит). – blahdiblah

+2

Я не знаю контекста использования ваших журналов, но я бы рекомендовал взглянуть на 'syslog'. Может, тебе это подходит. Работа с ним очень проста. http://www.gnu.org/software/libc/manual/html_node/Submitting-Syslog-Messages.html –

ответ

8

Использование стандартного объекта C IO представляет новый уровень сложности; файл изменяется только через write(2) -семейство системных вызовов (или сопоставлений памяти, но это не используется в этом случае). C-стандартные обертки IO могут отложить запись в файл некоторое время и не могут отправлять полные запросы в одну систему вызов.

write(2) сам вызов должен вести себя хорошо:

[...] 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. 

    POSIX requires that a read(2) which can be proved to occur 
    after a write() has returned returns the new data. Note that 
    not all file systems are POSIX conforming. 

Таким образом, ваши базовые write(2) вызовы будут вести себя должным образом.

Для потоков ввода-вывода стандартного уровня C вам также необходимо позаботиться о буферизации. Функция setvbuf(3) может использоваться для запроса небуферизованного вывода, вывода с линейной буферизацией или блочного буферизации. Поведение по умолчанию изменяется от потока к потоку - если стандартный вывод и стандартная ошибка записываются на терминал, то по умолчанию они буферизируются по строке и не загружаются. В противном случае блокировка по умолчанию является значением по умолчанию.

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

Если вы пишете более BUFSIZ байт за один раз, ваши записи могут чередоваться. Функция setvbuf(3) может помочь предотвратить чередование.

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

+0

Отличный совет о 'setvbuf()' и его вариантах 'setbuf()', 'setbuffer()' и 'setlinebuf()'. Они были именно то, что мне нужно. Спасибо, @sarnold. –

+0

Спасибо, что указали, что O_APPEND необходим для обеспечения атомной настройки смещения файла. Я опустил его из первого процесса, который открывает файл, потому что он также создает его (так что «append» не кажется подходящим ..) – RobM

1

Этот ответ был неправильным. Это делает работу:

Так состояние гонки будет:

  1. процесс 1 открывает его для Append, то
  2. позже процесс 2 открывает его для Append, то
  3. еще позже 1 запись и закрывается, то
  4. finally 2 записывает и закрывает.

Я был бы впечатлен, если бы это «сработало», потому что мне непонятно, что должно означать . Я предполагаю, что «рабочий» означает, что все байты, написанные двумя процессами, находятся в файле журнала? Я бы ожидал, что оба они начинаются с того же байтового смещения, поэтому заменяют остальные байтов. Все будет хорошо, и в том числе шаг 3. и только покажут как проблему на шаге 4. Похоже, легкий тест для записи: open getchar ... напишите close.

Важно ли, чтобы файл мог открываться одновременно? A более очевидное решение, если запись выполняется быстро, заключается в открытии эксклюзивного.

Для быстрой проверки на вашей системе, попробуйте:

/* write the first command line argument to a file called foo 
* stackoverflow topic 9880935 
*/ 

#include <stdio.h> 
#include <fcntl.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 

int main (int argc, const char * argv[]) { 
    if (argc <2) { 
     fprintf(stderr, "Error: need some text to write to the file Foo\n"); 
     exit(1); 
    } 

    FILE* fp = freopen("foo", "a+", stdout); 

    if (fp == NULL) { 
     perror("Error failed to open file\n"); 
     exit(1); 
    } 

    fprintf(stderr, "Press a key to continue\n"); 
    (void) getchar();  /* Yes, I really mean to ignore the character */ 

    if (printf("%s\n", argv[1]) < 0) { 
     perror("Error failed to write to file: "); 
     exit(1);   
    } 

    fclose(fp); 

    return 0; 
} 
+0

Они не перезаписывают друг друга, из 'man freopen':' a + ... Последующая запись в файл всегда будет заканчиваться на текущем конце файла '. – blahdiblah

+0

@blahdiblah - может быть, я чего-то не хватает, но как они могут ** не переписать в моем примере? Оба процесса открываются для добавления, но не записывают ни одного байта на этом этапе, поэтому файл имеет одинаковую длину для обоих. Затем они оба пишут. Разве файл не смещает атрибут fd, а не файл? – gbulmer

+0

Я сообщаю только информацию на странице руководства и результаты моего собственного тестирования. Я не могу говорить с основными деталями реализации. – blahdiblah

Смежные вопросы