обработчик сигналов и многопоточность - PullRequest
0 голосов
/ 19 декабря 2018

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

Вот фрагмент описания этой ссылки :

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

Вот мой тест:

void func(int p)
{
    cout<<"int"<<endl;
    sleep(5);
}

int main()
{
    signal(SIGINT, func);
    while(1)
    {
        cout<<"main"<<endl;
        sleep(2);
    }   
    return 0;
}

Я выполняю егои если я нажму ctrl+C, будет напечатано int, а затем мне придется ждать 5 секунд, независимо от того, сколько раз ctrl+C Я нажал, ожидая 5 секунд, ничего не произошло.

Затем я делаю еще один тест:

void func(int p)
{
    cout<<"int"<<endl;
    sleep(5);
}

int main()
{
    signal(SIGINT, func);
    thread t1([](){while(1){cout<<"t1"<<endl; sleep(2);}});
    thread t2([](){while(1){cout<<"t2"<<endl; sleep(2);}});
    t1.join();
    t2.join();

    return 0;
}

Для этого кода я обнаружил, что могу нажимать ctrl+C три раза подряд, и будет напечатано три int, тогда мне придется ждать около 5 секунд.

Таким образом, кажется, что первый ctrl+C прерывает поток t1, второй прерывает поток t2, а третий прерывает основной поток.

Таким образом, вместо этого сигнализируется только прерывание потокавсего процесса, если есть многопоточность?

Ответы [ 3 ]

0 голосов
/ 19 декабря 2018

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

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

0 голосов
/ 19 декабря 2018

ВВЕДЕНИЕ

Прежде всего каждый поток имеет свою собственную маску, которая определяет, какие сигналы он слушает.Когда создается поток, он наследует маску потока, который его создает (позвольте мне назвать его родительский поток) , который активен при вызове pthread_create.

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

На изменить маску дочернего потока , который создается из родительского потока вы создаете новую маску, вы устанавливаете ее активнойсоздайте новый поток с pthread_create, а затем в родительском потоке снова установите активную предыдущую маску (см. код в конце ответа).

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

ОТВЕТ

Таким образом, сигнализировать только поток прерываний вместо всего процесса, если есть многопоточность?

NO : Сигналы - это просто сигналы, они ничего не делают.Действия, связанные с сигналами, способны выполнять какие-либо действия, включая остановку программы или завершение потока.С каждым сигналом связано действие по умолчанию, и действие по умолчанию для SIGINT состоит в прерывании процесса.

С помощью вашего обработчика вы перезаписываете действие по умолчанию.Поэтому он больше не будет останавливать программу, но будет делать то, что вы указали в функции потока.

В первом случае у вас есть только один поток, основной, который является бесконечным циклом, он всегда перехватываетсигнал пока он жив, вот почему и поведение.Если вы повторно отправляете сигнал, он на мгновение блокируется, пока обработчик сигнала не завершит выполнение.Тем не менее, если много сигналов отправлено во время выполнения обработчика, вы можете потерять некоторые сигналы.Фактически, как объяснено здесь , заблокированные сигналы устанавливаются в ожидании, но не ставятся в очередь.Термин pending означает, что операционная система запоминает наличие сигнала, ожидающего доставки при следующей возможности, путем установки флага, а not queue означает, что она делает это, устанавливаяфлаг где-то, но не путем точного учета того, сколько сигналов поступило.Так что, если сигналы отправляются один, 5 или более раз (попробуйте нажать CTRL + C еще раз с вашей программой: я пробовал), пока обработчик сигнала выполняет его, он будет работать точно так же.

В вашемВо втором случае у вас есть 3 потока: main one, t1 и t2: все они могут видеть сигнал SIGINT, и все они связаны с одним обработчиком сигнала.Если вы нажмете 3 раза один за другим, все три из них запустят обработчик: поэтому вы не видите никакой задержки.Если вы нажмете очень очень быстро, хотя и более 3 раз (количество потоков, слушающих этот сигнал), я думаю, вы увидите нечто похожее на первое поведение.

Я закончу свой ответ код, который я разместил в вопросе , где я устанавливал маски так, чтобы некоторые сигналы были перехвачены только основным потоком:

int main()
{
    int err;
    sigset_t omask, mask;
    pthread_t thread_motionSensor;
    pthread_t thread_tempReading;
    pthread_t thread_platformPost;

    printf("Created threads IDs\n");

    ...
    if (signal(SIGINT, sig_handler)==SIG_ERR)
        printf("Error on recording SIGINT HANDLER\n");

    /*Create a new mask to block all signals for the following thread*/
    sigfillset(&mask);
    pthread_sigmask(SIG_SETMASK, &mask, &omask);
    printf("Trying to create threads\n");
    if ((err = pthread_create (&thread_motionSensor, NULL, task1, NULL))!=0)
    {
    printf("Thread 1 not created: error %d\n", err);
        err_exit((const char)err, "pthread_create error");
    }
    printf("Thread 1 created. Trying to create Thread 2\n");
    if((err = pthread_create (&thread_tempReading,   NULL, task2, NULL))!=0)
    {
    printf("Thread 2 not created: error %d\n", err);
        err_exit((const char)err, "pthread_create error");
    }
    printf("Thread 2 created. Trying to create Thread 3\n");
    if ((err = pthread_create (&thread_platformPost, NULL, task3, NULL))!=0)
    {
     printf("Thread 3 not created: error %d %d\n", err);
         err_exit((const char)err, "pthread_create error");
    }
    printf("Thread 3 created\n");
    /*The main thread must block the SIGALRM but catch SIGINT
    SIGQUIT, SIGTERM, SIgkILL*/
    /*empty the omask set from all signals */
    sigemptyset(&omask);
    /*add the signals to the omask*/
    sigaddset(&omask, SIGINT);
    sigaddset(&omask, SIGQUIT);
    sigaddset(&omask, SIGKILL);
    sigaddset(&omask, SIGTERM);
    /*unblock all signals in omask*/
    pthread_sigmask(SIG_UNBLOCK, &omask, NULL);
    printf("Main thread waiting for signal\n");
    /*pause will stop the main thread until any signal not blocked by omask will be received*/
    pause();
    printf("Exit signal received: cancelling threads\n");

    pthread_cancel(thread_motionSensor);
    pthread_cancel(thread_tempReading);
    pthread_cancel(thread_platformPost);
    pthread_join(thread_motionSensor, NULL);
    pthread_join(thread_tempReading,  NULL);
    pthread_join(thread_platformPost, NULL);
    printf("Exiting from main thread and process\n");
    exit(0);
}
0 голосов
/ 19 декабря 2018

То есть сигнализировать только прерывание потока вместо всего процесса, если есть многопоточность?

Да.Сигнал прерывает один поток выполнения.По крайней мере, в соответствии со стандартом POSIX.Сам по себе стандарт C не определяет поведение процессов или потоков.

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

...