Обработка очереди из файла, в который выполняется запись в C - PullRequest
1 голос
/ 25 марта 2020

Я пишу C код, который берет свои задачи из файла (на linux). В этот файл параллельно записывается несколько процессов (с использованием echo "COMMAND" >> "file_queue_input"). До сих пор эта логика c использовалась:

  rename (file_queue_input, file_queue_process);
  queue_file = fopen (file_queue_process,"r");
  while (!feof (queue_file))
  {
      <process file line by line>
  } // end of while reading queue file
  fclose (queue_file);
  remove (file_queue_process);

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

Мое предположение: дескриптор записи открывается перед операцией перемещения, остается открытым для нового имени, но еще не записал свои данные, файл читается без данных а затем дескриптор записи записывает данные, но поскольку чтение l oop уже завершено, читать данные некому, и файл удаляется. Как это предотвратить?

Как правильно читать файл очереди, в который также часто пишут? Я совершенно уверен, что я не первый человек, заинтересованный в этом, но не могу найти правильный вопрос для Google, чтобы найти что-то полезное.

Как и просили в комментариях, здесь добавлен минимальный пример (обратите внимание, что у меня есть все проверки, ведение журнала и т. д. c ... но хотелось бы, чтобы код был как можно короче, чтобы все, что не было абсолютно необходимым, было удалено): https://drive.google.com/open?id=1U9vh7DEUPopuyTJ5j4J8T8FqVj812AzV

Это содержит 2 файла: Queue_read_test.c (файл, который я пишу, и который контролирует, как это сделать), bash_load_generator.sh, который представляет собой файл, имитирующий записи, где я абсолютно не контролирую.

Сначала мы проверяем числа и запускаем генератор, когда генератор заканчивается sh, мы запускаем программу чтения очереди:

ice@center:/usr/src/Demo$ ./bash_load_generator.sh
All commands started waiting for finish
DONE
ice@center:/usr/src/Demo$ ./Queue_read_test # Stop with CTRL+C
^CFinished with 10000 queue items read: ice@center:/usr/src/Demo$

Затем мы запускаем первую программу чтения очереди, а затем мы запускаем генератор на 2-м экране, после того, как все закончено и файл очереди исчезает, мы останавливаемся читатель очереди (это сценарий из реальной жизни):

ice@center:/usr/src/Demo$ ./Queue_read_test # Run in screen 1
ice@center:/usr/src/Demo$ ./bash_load_generator.sh # Run in screen 2
All commands started waiting for finish
DONE
ice@center:/usr/src/Demo$ ls -ltra /tmp/ | grep queue # Check all was processed (no file should be found) in screen 2
ice@center:/usr/src/Demo$
ice@center:/usr/src/Demo$ # return to screen 1 and CTRL+C
^CFinished with 9905 queue items read: ice@center:/usr/src/Demo$

И мы видим, что 95 команд потеряно. Это для иллюстрации описанной проблемы, мой вопрос теперь, возможно, благодаря вашим комментариям более точным: Что я могу сделать как автор средства чтения очереди, чтобы предотвратить потерю команд? (без возможности изменять средства записи очереди) Могу ли я проверить, есть ли дескрипторы открытых файлов (не думаю, что я не root).

1 Ответ

1 голос
/ 25 марта 2020

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

По поводу показанного кода ...
Непонятно, что содержится в переменной file_queue_process. Это должен быть действительный filespe c. (то есть, как описано в ссылке, и что файл, на который он указывает, существует.)
Учитывая, что это правда, следующая потенциальная проблема заключается в том, что вы пытаетесь (1) ввести плохо контролируемый l oop (2) без предварительной проверки, что функция fopen () вернула допустимый дескриптор файла. Я бы предложил использовать fgets () в качестве элемента управления l oop в качестве гораздо более безопасного подхода:

char line[80] = {0};
queue_file = fopen (file_queue_process,"r");
if(queue_file)
{
    while (fgets(line, 80, queue_file)
    {
        <process file line by line>
    } // end of while reading queue file
  fclose (queue_file);
}
remove (file_queue_process);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...