C, использующий сигналы для остановки дочерних процессов

#c #handler #signals

#c #обработчик #сигналы

Вопрос:

Моя текущая программа создает дочерние процессы и дает им работу (интенсивная работа с процессором). Main() сидит там и ждет, пока дочерние процессы отправят данные по каналам (используя select).

Что я хотел сделать, так это когда программа обрабатывает данные, я мог бы нажать CTRL C, чтобы остановить работу дочерних процессов и спросить пользователя, хочет ли он прекратить или возобновить работу.

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

У меня уже есть код на месте, но он работает не совсем правильно.

В основном я должен signal(SIGINT, pausar); обрабатывать SIGINT (CTRL C).

Это функция pausar():

 void pausar(int signum){
    signal(SIGINT, pausar);

    int i;
    // pid[] contains all the child processes
    for(i = 0; i<CORES; i  )
    {
        kill(pid[i], SIGSTOP);
    }

    char option[2];
    printf("n Computacao pausada.n'S' para sair ou 'C' para continuar: ");

    scanf("%1s", option);
    if (option[0] == 's' || option[0] == 'S') {
        printf("A desligar...n");

        //if user wants to quit, kill all the child processes
        for(i = 0; i<CORES; i  )
        {
            kill(pid[i], SIGKILL);
        }

        exit(0);
    }
    else
    {
        printf("[%d] A resumir computacao...n",getpid());
        kill(getpid(), SIGCONT);

        //if user wants to resume work, send signal to continue
        for(i = 0; i<CORES; i  )
        {
            kill(pid[i], SIGCONT);
            printf("%d resumiun", pid[i]);
        }
    }
}
  

Проблема в том, что иногда я нажимаю CTRL C, и в консоли ничего не отображается (но процессы ОСТАНАВЛИВАЮТСЯ, потому что я обращаю внимание на диспетчер процессов). Другая проблема заключается в том, что после ввода ‘C’ для возобновления работы я получаю ошибки в select (), и дочерние элементы никогда не возобновляют работу.

Ответ №1:

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

Если вы используете Linux: создайте сокет события с signalfd() помощью и добавьте этот сокет в набор чтения, переданный select() . Затем сигналы обрабатываются в фиксированной точке вашего кода, и вам не нужно беспокоиться об условиях гонки.

Комментарии:

1. Может быть, вы имеете в виду signalfd() вместо eventfd() ?

Ответ №2:

Во-первых, для того, что вы пытаетесь сделать, ваш обработчик сигналов слишком сложен. Во-вторых, вызов signal() внутри вашего обработчика сигналов — не очень хорошая идея… это не асинхронная функция, защищенная от сигналов.

Что вы можете сделать, так это следующее:

  1. В вашем основном, установите функцию обработчика сигналов, используя signal() , как вы это сделали.
  2. Заблокируйте сигнал SIGINT через sigprocmask() . Это предотвращает поступление ложного сигнала до вызова pselect() .
  3. Внутри вашего обработчика сигналов установите только простой глобальный флаг, который является sig_atomic_t
  4. Используйте pselect() вместо select() . Это позволит вам изменить маску сигнала процесса, чтобы разрешить поступление сигнала SIGINT, и это будет сделано атомарным образом по отношению к сигналам. В противном случае ваш SIGINT может появиться до вызова to select() , и тогда вы «потеряете» этот сигнал, даже если он устанавливает флаг в обработчике.
  5. Когда pselect() вызов возвращается, определите, был ли установлен флаг.
  6. Если sig_atomic_t был установлен глобальный флаг, и вы вернулись из pselect -за перехваченного сигнала, затем запустите другую функцию, которая фактически выполнит все завершение дочерних процессов и запросит пользователя и т. Д.

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

Если вам нужна дополнительная информация pselect() , здесь есть хорошая статья об этом.

Комментарии:

1. это решение без signalfd() , хотя это невозможно сделать безопасно, не pselect() включив SIGINT только во время pselect() вызова, потому что всегда будет время непосредственно перед select() вызовом, но глобальный флаг уже установлен. если сигнал возникает в это время, Strg C не будет обрабатываться до select() возврата.

2. Хорошо, я обновлю вышеприведенный пост… спасибо, что указали на это