mq_receive () не возвращается после генерации сигнала - PullRequest
1 голос
/ 04 февраля 2020

В настоящее время я работаю над встроенным приложением (работающим на linux 4.14), которое реализует два потока (основной и коммуникационный поток), используя потоки POSIX.

Поток связи создает очередь POSIX, которая обрабатывает запросы команды из основного потока (путем вызова mq_send ()). Он также может обрабатывать входящие данные из последовательной линии, которая генерирует сигнал SIGIO.

Вот пример кода

Основной поток:

pthread_t   com_thread;
mqd_t       cmd_queue;

void main (void) 
{
    struct mq_attr attr;

    init_serial();        // Does serial line init and set a sigaction() with SIGIO that store serial data

    // Create queue
    attr.mq_flags   =   0;
    attr.mq_maxmsg  =   100;
    attr.mq_msgsize =   sizeof(struct Dummy);
    attr.mq_curmsgs =   0;

    cmd_queue = mq_open(QUEUE_NAME,                         // Queue name
                        O_RDWR | O_CREAT,                   // Flags
                        S_IRWXU|S_IRWXG|S_IRWXO,            // Mode
                        &attr);                             // Attributes

    // Create thread
    pthread_create(&com_thread, NULL, com_fw_handler, NULL);

    while(1) 
    {
        // Do some stuff ...

        // Send command request
        mq_send(cmd_queue, (const char*)cmd_request, sizeof(struct Dummy), 0);
    }
}

Com Thread:

static void * com_fw_handler (void * ptr) 
{
    struct Dummy request_from_queue;
    sigset_t sig_set;
    int ret = 0;

    // Allow SIGIO signal
    sigemptyset(&sig_set);
    sigaddset(&sig_set, SIGIO);
    sigprocmask(SIG_UNBLOCK, &sig_set, NULL);

    while(1) 
    {
        // Wait for a command request or SIGIO
        do 
        {
            ret = mq_receive(cmd_queue, (char*)request_from_queue, sizeof(request_from_queue), NULL);

            printf("mq_received() returned %d\n", ret);

            if(ret > 0)
            {
                // Handle command request
            }
        }while(ret > 0);

        // If mq_receive() exited because SIGIO has been raised
        if((ret < 0) && (errno == EINTR))
        {
            // Handle incoming data from serial
        }
    }
}

Когда я пытаюсь отладить свое приложение с помощью GDB, все работает нормально, mq_received () завершается каждый раз, когда система получает данные из последовательной линии. Вот пример вывода на консоль:

mq_received() returned 64
mq_received() returned -1
mq_received() returned 64
mq_received() returned -1
......

64 - размер структуры Dummy, -1 - возвращаемое значение при поднятии SIGIO.

Если приложение запускается непосредственно система, SIGIO повышена (я вижу отладочную печать на консоли), но кажется, что mq_receive () никогда не завершается. Только вывод на консоль:

mq_received() returned 64
mq_received() returned 64
.......

Сеанс GDB начинается со следующих «опций»:

handle all nostop pass noprint

Я не могу определить, связано ли наблюдаемое мной поведение с обработкой сигнала GDB или проблема гонки / времени или просто проблема разработчика!

Что-то не так в приведенном мною образце кода?

Спасибо!

1 Ответ

0 голосов
/ 05 февраля 2020

У вас есть две отдельные проблемы: обработка SIGIO в предсказуемом потоке и прерывание длинного или блокирующего вызова.

Для начала вам нужно явно скрыть SIGIO во всех, кроме одного нить. POSIX указывает, что сигнал "доставляется ровно в один из тех потоков внутри процесса, который ... не заблокировал доставку сигнала." Таким образом, реализация выбирает, какой поток получает SIGIO, и ему не нужно выбирать один и тот же поток каждый раз, когда генерируется SIGIO. Если только один поток способен принимать сигнал, этот поток получит его.

Вы можете сделать это с помощью pthread_sigmask(...). (Не используйте sigprocmask в многопоточной программе, поскольку ее поведение в этом случае не указано .)

Во-вторых, вы хотите надежно прервать mq_receive. Это не может быть сделано, и это классическая c гонка обработки сигналов, как написано:

while (1) {
  ....                // < - SIGIO here or prior.  What happens?
  r = mq_receive(...)
  ....                // < - SIGIO here or later.  What happens?
}

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

Теперь эта гонка может быть хорошо для вашей программы: особенно если вы примените тайм-аут к mq_receive и бросите Отметьте в обработчике сигналов, вы заметите, что SIGIO «в конце концов», и это может быть достаточно для вас.

Если нет, вам нужно будет изучить другие варианты. Linux реализует mqd_t как дескриптор файла , что явно разрешено, но не требуется spe c. При этом ваша проблема становится смешанными сигналами и вводом / выводом, что может быть сделано с некоторой осторожностью (например, pselect или self-pipe ). mq_notify - другой подход. Действительно, вы могли бы даже mq_send написать специальное сообщение, которое гласит: «Я видел SIGIO - сделайте что-нибудь с этим». Et c.

...