C использование сигналов для остановки дочерних процессов - PullRequest
0 голосов
/ 17 октября 2011

Моя текущая программа создает дочерние процессы и дает им работу (загрузка процессора).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 resumiu\n", pid[i]);
        }
    }
}

Проблема в том, что иногда я нажимаю CTRL + C, и в консоли ничего не отображается (но процессы останавливаются, потому что я обращаю внимание на менеджера процессов).Другая проблема заключается в том, что после ввода «C» для возобновления работы я получаю ошибки в select (), и дети никогда не возобновляют работу.

Ответы [ 2 ]

4 голосов
/ 17 октября 2011

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

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

3 голосов
/ 17 октября 2011

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

Вы можете сделать следующее:

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

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

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

...