Безопасно ли читать () из файла, как только функция write () возвращается? - PullRequest
1 голос
/ 20 декабря 2010



У меня есть очень специфическое приложение, где мне нужна переменная с автоинкрементом и постоянным хранилищем

Чтобы быть точным, я храню десятичное представление переменной int в файле. Чтобы сгенерировать следующее число, I read() из файла, преобразовать содержимое обратно в int, добавить 1 и write() обратно в файл. Мне НЕ нужен одновременный доступ к этим данным. Только один поток из одного процесса вызывает функции для получения номера автоинкремента. Программа работает во встроенной среде, где никто не будет иметь доступа к консоли, поэтому безопасность не должна быть проблемой. Если это имеет значение, он работает на Linux 2.6.24 на MIPS.
Проблема в том, что я не получаю 100% воспроизводимых результатов. Иногда я получаю повторные номера, что неприемлемо для моего заявления.

Моя реализация заключается в следующем.

При запуске приложения у меня есть:

int fd = open("myfile", O_RDWR|O_CREAT|O_SYNC, S_IRWXU|S_IRWXG|S_IRWXO);

И функции автоинкремента:

int get_current(int fd)
{
    char value[SIZE];
    lseek(fd, 0, SEEK_SET);
    read(fd, value, SIZE);
    return atoi(value);
}

int get_next(int fd)
{
    char value[SIZE];
    int cur = get_current(fd);
    memset(value, 0, SIZE);
    sprintf(value, "%d", cur + 1);
    lseek(fd, 0, SEEK_SET);
    write(fd, value, SIZE);
    //fsync(fd);  /* Could inserting this be the solution? */
    return (cur + 1);
}

Я специально исключил проверку ошибок выше для удобства чтения кода. У меня есть код для проверки возвращаемых значений всех системных вызовов.

Код был изначально написан другим человеком, и теперь, когда я обнаружил эту проблему, первый шаг к ее решению - выяснить, что могло ее вызвать. Я обеспокоен тем, что это может быть связано с кэшированием доступа к файлам. Я знаю, что когда у меня write() нет никакой гарантии, что данные вообще когда-либо достигали физического носителя, но безопасно ли звонить на read(), не позвонив на fsync(), и все же получить предсказуемые результаты? Если это так, то у меня нет идей;)

Спасибо за прочтение.

Ответы [ 3 ]

5 голосов
/ 20 декабря 2010

Да, безопасно читать сразу после записи. В Unix-подобной системе данные находятся в безопасном пуле буферов ядра, когда возвращается write(), и будут возвращены другим процессам, которые должны прочитать данные. Аналогичные комментарии применяются при использовании O_SYNC, O_DSYNC, O_FSYNC (которые обеспечивают запись данных на диск) и в системах Windows. Ясно, что асинхронная запись не будет завершена, когда вызов aio_write() завершится, но будет завершена, когда будет сообщено о завершении.

Однако ваша проблема возникает из-за того, что вы не гарантируете, что у вас есть один процесс или поток, обращающийся к файлу за раз. Вы должны убедиться, что вы получаете последовательный доступ, чтобы вы не получили двух процессов (или потоков), считывающих файл одновременно. Это проблема «потерянных обновлений» в терминах СУБД.

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

0 голосов
/ 20 декабря 2010

Содержимое файла - это действительно плохой способ реализовать атомный счетчик.Насколько большим будет ваш счет?Если он не велик, одним простым методом было бы написать один байт (неважно, что) для увеличения счетчика и использовать fstat (st_size) для считывания счетчика.ftrunc может сбросить счетчик до нуля.

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

Другой способ, которым вы можете использовать mmap, - это если у вас есть атомарные типы C1x (_Atomic int), но вам придется подождать 5-10 лет.:-) Или вы можете использовать встроенные функции gcc или asm для атомарных операций.Это решение обладает лучшими характеристиками (слегка лучше, чем подход pthread_mutex_t, и в сотни раз быстрее, чем подход write).

0 голосов
/ 20 декабря 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...