От новичка до гуру: Курсы программирования на CyberDuff

Как написать тестовый пример для проверки EINTR, возвращаемого функцией sem_wait в Linux

Я только что написал приведенную ниже процедуру для обработки ошибки EINTR. Процедура приведена ниже,

while((s = sem_wait(&w4compl)) == -1)
{
        if (errno == EINTR)
        {
                perror("call interrupted by sig. handler\n");
                continue;
        }
        else
                printf("Other Error Generated\n");
}

Итак, здесь я не могу увидеть оператор печати «вызов прерван sig. handler\n». Как я могу проверить это, чтобы он печатал то же самое (как я могу выполнить часть if (errno == EINTR)).

07.08.2018

  • Привет П.П. Можете ли вы дать мне ссылку, если на этот вопрос уже был дан ответ. 07.08.2018
  • Это связано с вашим вопросом. 07.08.2018
  • @ П.П. Если присмотреться, то на самом деле это не очень хороший дубликат. Конечно, заголовок красивый и общий, но во всех основных ответах говорится: «Не звонить повторно fclose()», что здесь не очень помогает. 07.08.2018
  • Согласен с Джонатаном. Мне это мало помогает. 07.08.2018
  • @JonathonReinhart Связанный вопрос предназначен для модульного тестирования EINTR, о чем OP тоже спрашивал здесь. Там достойные ответы. Конечно, в качестве примера использовался fclose (здесь это sem_post). Я не уверен, что нам нужен отдельный вопрос для каждого системного вызова, который может возвращать EINTR, и как его тестировать. 07.08.2018
  • @ П.П. Я согласен с тем, что нам не нужны разные вопросы для каждого системного вызова, но fclose() — это библиотечная функция с дополнительной семантикой (отключающая связь с базовым файловым дескриптором), так что этот вопрос был просто плохим примером IMO. Я не видел ответов, которые предлагали перевести системный вызов в заблокированное состояние (что легко для семафоров) и отправить сигнал в поток. 07.08.2018

Ответы:


1

Установите обработчик сигнала и вызовите доставку сигнала (используя alarm(), setitimer() или timer_create()+timer_settime()), чтобы доставка сигнала прервала вызов sem_wait().


Рассмотрим этот пример программы:

#define  _POSIX_C_SOURCE  200809L
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <semaphore.h>
#include <stdio.h>
#include <errno.h>

static void dummy_handler(int signum)
{
}

static int install_dummy_handler(int signum)
{
    struct sigaction  act;
    memset(&act, 0, sizeof act);
    sigemptyset(&act.sa_mask);
    act.sa_handler = dummy_handler;
    act.sa_flags = 0;
    return sigaction(signum, &act, NULL);
}

static const char *errname(const int errnum)
{
    switch (errnum) {
    case EINTR:  return "EINTR";
    case EINVAL: return "EINVAL";
    default:     return "(other)";
    }
}

int main(void)
{
    sem_t  s;

    if (install_dummy_handler(SIGALRM) == -1) {
        fprintf(stderr, "Cannot install ARLM signal handler: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    sem_init(&s, 0, 0);

    alarm(1);

    if (sem_wait(&s) == -1) {
        const int  errnum = errno;
        printf("sem_wait() failed with errno == %s (%d; %s).\n",
               errname(errnum), errnum, strerror(errnum));
    } else
        printf("sem_wait() succeeded.\n");

    return EXIT_SUCCESS;
}

В main() мы устанавливаем обработчик сигнала SIGALRM. Неважно, делает ли вообще что-либо функция обработчика сигнала, потому что именно доставка сигнала приводит к тому, что "медленные" системные вызовы возвращаются с ошибкой EINTR. (Поскольку флаг SA_RESTART не использовался при установке этого обработчика. Если вы посмотрите на act.sa_mask в install_dummy_handler(), вы увидите, что мы вообще не использовали флаги. Все флаги и sigaction() использование описаны на справочной странице man 2 sigaction.)

В main() мы сначала инициализируем наш семафор, затем устанавливаем будильник на одну секунду. По истечении реального времени настенных часов поднимается сигнал SIGALRM. Обратите внимание, что хотя SIGALRM отлично подходит для этого примера и подобных целей, вы, вероятно, захотите использовать таймеры POSIX для каждого процесса вместо этого.

Затем мы просто вызываем sem_wait() на семафоре и проверяем результат. На практике, если вы скомпилируете и запустите приведенный выше example.c, используя, например,

gcc -Wall -O2 example.c -lpthread -o example
./example

программа будет выводить

sem_wait() failed with errno == EINTR (4; Interrupted system call).

через одну секунду.

07.08.2018
  • @NiravPatel: обратите внимание, что если у вас несколько потоков, сигнал должен быть направлен на поток, выполняющий вызов sem_wait(), и что именно доставка сигнала функции обработчика пользовательского пространства, а не сам сигнал, вызывает прерывание блокирующий вызов. В некоторых случаях вам может понадобиться пара сигналов, при этом первичный сигнал заблокирован во всех потоках, кроме одного, чтобы его обработчик сигналов мог пересылать сигнал (используя незаблокированный вторичный сигнал) каждому существующему другому потоку, чтобы прервать несколько потоков. . Дайте мне знать, если вам нужна дополнительная информация или пример. 08.08.2018

  • 2

    Почти любой системный вызов в Linux может вернуть EINTR, если системный вызов прерван.

    Из справочной страницы (выделено мной):

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

    Чтобы вызвать этот случай, вы должны убедиться, что системный вызов sem_wait заблокирован (ожидание), а затем отправить сигнал (у которого есть обработчик) потоку.

    Какой-то псевдокод:

    sigint_handler:
        return
    
    thread2:
        <Your while loop from the question>
    
    main:
        signal(SIGINT, sigint_handler)  // Setup signal handler
    
        sem_wait(&w4compl)
        t2 = start_thread(thread2)
        sleep(5)                        // Hack to make sure thread2 is blocked
    
        pthread_kill(t2, SIGINT)
    
    07.08.2018
  • Спасибо @Джонатан Рейнхарт. Здесь подпрограмма вызывается в одном из потоков. Итак, как я могу передать сигнал SIGINT потоку? 07.08.2018
  • Используйте pthread_kill, чтобы отправить сигнал потоку. 07.08.2018
  • Просто чтобы понять, какой системный вызов должен блокировать OP? А почему СИГИНТ? 07.08.2018
  • @Jonathan Я попробую псевдокод в моем существующем коде и проверю. 07.08.2018
  • 1. Использование signal не является хорошей идеей (следует предпочесть подписание). 2. Помимо того, что sleep является плохим выбором в качестве механизма синхронизации, сам sleep может быть прерван сигналом (я до сих пор не понимаю, как sleep в main обеспечивает его блокировку в потоке). 07.08.2018
  • 3. Насколько я понимаю, вопрос ОП касается модульного тестирования сценария, в котором системный вызов возвращает EINTR. Поэтому я ожидаю прозрачного механизма (например, ответ, в котором упоминалось использование указателей функций в связанном дублировании). 4. Я бы не стал использовать обработчики сигналов, блокировку/приостановку сигналов и т.д. только для модульного тестирования, т.е. модульное тестирование должно тестировать, а не влиять/изменять/вводить дополнительное поведение и т.д. 07.08.2018
  • Новые материалы

    Основы Spring: Bean-компоненты, контейнер и внедрение зависимостей
    Как лего может помочь нашему пониманию Когда мы начинаем использовать Spring, нам бросают много терминов, и может быть трудно понять, что они все означают. Итак, мы разберем основы и будем..

    Отслеживание состояния с течением времени с дифференцированием снимков
    Время от времени что-то происходит и революционизирует часть моего рабочего процесса разработки. Что-то более забавное вместо типичного утомительного и утомительного процесса разработки. В..

    Я предполагаю, что вы имеете в виду методы обработки категориальных данных.
    Я предполагаю, что вы имеете в виду методы обработки категориальных данных. Пожалуйста, проверьте мой пост Инструментарий специалиста по данным для кодирования категориальных переменных в..

    Игра в прятки с данными
    Игра в прятки с данными Я хотел бы, чтобы вы сделали мне одолжение и ответили на следующие вопросы. Гуглить можно в любое время, здесь никто не забивается. Сколько регионов в Гане? А как..

    «Раскрытие математических рассуждений с помощью Microsoft MathPrompter и моделей больших языков»
    TL;DR: MathPrompter от Microsoft показывает, как использовать математические рассуждения с большими языковыми моделями; 4-этапный процесс для улучшения доверия и рассуждений в математических..

    Раскройте свой потенциал в области разработки мобильных приложений: Абсолютная бесплатная серия
    Глава 6: Работа в сети и выборка данных Глава 1: Введение в React Native Глава 2: Основы React Native Глава 3: Создание пользовательского интерфейса с помощью React Native Глава 4:..

    Все о кейсах: Camel, Snake, Kebab & Pascal
    В программировании вы сталкивались с ними при именовании переменной, класса или функции. Поддержание согласованности типов и стилей случаев делает ваш код более читабельным и облегчает совместную..