C — синхронизация файлов процедур Linux/отложенная запись

У меня есть простой сторожевой механизм, сделанный следующим образом:

  • Модуль ядра, хранящий список счетчиков потоков в файле /proc. Модуль увеличивает каждый счетчик каждую секунду. Когда счетчик достигает максимального значения, это означает, что соответствующий поток каким-то образом застрял.
  • Программа пользовательского пространства, генерирующая несколько потоков, контролируемых модулем ядра. Каждый поток отправляет команду сброса счетчика каждые 10 секунд, записывая определенную строку в файл /proc:
time_t timer = time(NULL);
int n = 0;
while (true) {
    if (time(NULL) >= timer + 10) {
        timer = time(NULL);
        char szData[64];
        memset(szData, 0, 64);
        sprintf(szData, "s|%d|%s", tid, function_name);
        if ((n = strlen(szData)) > 0) {
            int t = 0;
            int fp = 0;
            while ((fp = open("/proc/counters", O_WRONLY | O_EXCL)) == -1 && errno == EACCESS) {
                if (++t > 4) {
                    break;
                }
                usleep(260000);
            }
            if (fp == -1) {
                return 1;
            }
            write(fp, szData, n);
            close(fp);
        }
    }
}

В этом состоянии каждый счетчик должен достигать максимального значения 10, но моя проблема в том, что иногда некоторые из них достигают максимального значения 20, затем 30 и так далее. На стороне модуля ядра я вижу, что действительно команда сброса не приходит вовремя и на следующем раунде получает две команды за одну секунду, как будто первая задержалась. Пример:

Содержимое файла счетчиков /proc

    *856 7 20/600 thread_function_name

Отладочные распечатки модуля ядра

26 августа 11:16:38 XWEB-PRO ядро ​​kern.info: [114.086453] зарегистрируйте 856
26 августа 11:16:38 XWEB-PRO ядро ​​kern.info: [124.138523] зарегистрируйте 856
26 августа 11:16:58 XWEB-PRO ядро ​​kern.info: [134.190508] зарегистрируйте 856
26 августа 11:16:58 XWEB-PRO ядро ​​kern.info: [144.242274] зарегистрируйте 856
26 августа 11:16:58 XWEB-PRO ядро ​​kern.info: [144.242277] зарегистрируйте 856
26 августа 11:17:08 XWEB-PRO ядро ​​kern.info: [154.294433] зарегистрируйте 856
26 августа 11:17:28 XWEB-PRO ядро ​​kern.info: [164.346516] зарегистрируйте 856
26 августа 11:17:28 XWEB-PRO ядро ​​kern.info: [174.398552] зарегистрируйте 856
26 августа 11:17:38 XWEB-PRO ядро ​​kern.info: [184.468022] зарегистрируйте 856
26 августа 11:17:48 XWEB-PRO ядро ​​kern.info: [194.522241] регистр 856

Как видите, я пропустил команду в 11:16:48, но у меня 3 в 11:16:58. Потом я пропустил один в 11:17:18, но есть 2 в 11:17:28. Я уже пробовал fflush, fsync и sync, но безуспешно. Кто-нибудь может указать мне в правильном направлении? Спасибо

Что такое n? (тип и значение)

Wiimm 26.08.2024 12:08

Извините, я отредактировал вопрос. Это длина команды сброса.

bui3 26.08.2024 12:26

Почему вы открываете файл с помощью O_EXCL? Насколько я понимаю, файл предоставляется вашим модулем, а значит существует всегда. Более того, вы не используете O_CREAT, который вам почти всегда нужен вместе с O_EXCL. Поведение вашего вызова open() с указанными флагами не определено.

John Bollinger 26.08.2024 15:02

А почему вы не проверяете, действительно ли удается открыть файл? Даже если вы решите проблему с помощью O_EXCL, вполне возможно, что open() иногда может дать сбой. Надежный код должен это распознавать и обрабатывать соответствующим образом.

John Bollinger 26.08.2024 15:06

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

John Bollinger 26.08.2024 15:07

Я использую O_EXCL, потому что у меня есть много асинхронных потоков, которые выполняют одну и ту же запись в этот файл, каждый со своим собственным tid. Я не использую флаг O_CREAT, поскольку файл уже существует и содержит другую информацию, которую я должен сохранить. Результат открытия проверяется и повторяется 3 раза, я пропустил эту часть, но теперь добавил ее.

bui3 26.08.2024 15:11

Вы упускаете суть. O_EXCL имеет смысл только при использовании вместе с O_CREAT (или в Linux, в особом случае, который здесь не применяется). Поведение не определено, если в противном случае используется O_EXCL. Я думаю, вы думаете, что это предотвращает одновременное открытие файла несколькими потоками, но это не так.

John Bollinger 26.08.2024 15:13

Хорошо, извините, теперь более понятно. Я заметил, что если я добавляю печать в файл журнала непосредственно перед записью в файл /proc, счетчики сбрасываются каждые 10 секунд.

bui3 26.08.2024 15:16

Конечно, вполне вероятно, что запись потока может быть отложена — в конце концов, в этом и есть смысл сторожевого таймера. Но я не вижу в представленном коде причин для задержки на полный 10-секундный цикл или для записи трех отдельных операций записи в одном цикле с пропуском только одного цикла. Я думаю, что основная проблема, вероятно, в модуле, а не в клиенте.

John Bollinger 26.08.2024 15:19

Нам нужно увидеть код вашего модуля ядра. Что такое файл /proc? (например) /proc/counters или /proc/pid/watchdog? Как получить счетчик потоков, если вы не добавляете поле в task_struct?

Craig Estey 26.08.2024 19:38
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
10
69
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Наконец я заметил проблему. Похоже, это была проблема с состоянием гонки. В файл /proc одновременно записывают 10 потоков, поэтому, возможно, какая-то операция записи была пропущена модулем ядра (действительно, сброс всегда происходил кратно 10 секундам). Я окружил последовательность открытия/записи/закрытия в пользовательском пространстве мьютексом, и проблема, кажется, исчезла.

Другие вопросы по теме