signal handler and multithreading

时间:2018-12-19 11:22:22

标签: c++ c linux multithreading signals

I know that signal is a way of communication between CPU and OS kernel. A signal can interrupt the sequence of instructions, execute the handler and go back to the sequence of instructions.

Here is a piece of description of this link:

When a signal that is being caught is handled by a process, the normal sequence of instructions being executed by the process is temporarily interrupted by the signal handler. The process then continues executing, but the instructions in the signal handler are now executed. If the signal handler returns, the process continues executing the normal sequence of instructions it was executing when the signal was caught.

Here is my test:

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

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

I execute it and if I press ctrl+C, int will be printed and then I have to wait for 5 seconds, no matter how many times ctrl+C I pressed while waiting for the 5 seconds, nothing happened.

Then I make another test:

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;
}

For this code, I found that I could press ctrl+C three times continually and three int would be printed, then I have to wait for about 5 seconds.

So it seems that the first ctrl+C interrupts the t1 thread, the second interrupts the t2 thread and the third interrupts the main thread.

So signal only interrupt thread instead of the whole process if there are multi-threading?

3 个答案:

答案 0 :(得分:3)

简介

首先,每个线程都有其自己的掩码,该掩码指定其正在侦听的信号。创建线程时,它将继承创建线程的掩码(我称其为“父线程”)。当调用pthread_create时,该掩码是活动的。

通常最好是,如果一个线程正在侦听一个信号,那么其他线程就不应该这样做,除非您希望有多个线程执行相同的操作(例如,在服务器中处理连接请求以同时处理多个请求时) )。这样,您始终知道哪个线程正在处理信号。否则,您将不知道哪个线程正在接收信号,而正在执行程序的哪一部分:将无法调试(例如,您自己的问题)。

更改从父线程创建的子线程的掩码,请创建一个新的掩码,将其设置为活动状态,使用pthread_create创建一个新线程,然后在父线程中将先前的掩码再次设置为活动(请参见答案末尾的代码)。

编辑:根据this post,最好使用sigaction()而不是signal。在现代系统中,signal()sigaction()一起实现,因此应该没有任何区别。但是,如果使用旧的实现,则可能会出现问题。

答案

  

因此,如果有信号,则仅通知中断线程,而不通知整个过程   是多线程的?

:信号只是信号,它们什么也没做。与信号关联的动作具有执行操作的能力,包括停止程序或终止线程。每个信号都有一个关联的默认操作,SIGINT的默认操作是中断该过程。

使用处理程序,您将覆盖默认操作。因此它将不再停止程序,但会执行您在线程函数中指定的操作。

在第一种情况下,您只有一个线程(主线程),它是一个无限循环,只要他还活着,它就始终捕获信号,这就是行为的原因。如果重新发送信号,则信号会暂时阻塞,直到信号处理程序结束执行为止。但是,如果在处理程序执行期间发送了许多信号,则可以放宽一些信号。 实际上,如here所述,阻塞的信号被设置为未决,但未排队。术语待处理表示操作系统通过设置标志来记住有一个信号等待下一次机会传递,而未排队则表示它通过以下方式进行操作:在某处设置一个标志,而不是通过准确记录已到达多少信号来实现。 因此,如果在信号处理程序执行期间将信号发送一次,发送5次或多次(尝试使用程序按CTRL + C更多次:我已经尝试过),则信号的行为将完全相同。

在第二种情况下,您有3个线程:maint1t2:所有这些线程都可以查看信号SIGINT,并且所有它们具有关联的相同信号处理程序。如果您一个接一个地按3次,则三个按钮都将执行处理程序:这就是为什么您看不到任何延迟的原因。如果您按得非常快并且超过3次(侦听该信号的线程数),我想您会看到与第一个行为类似的东西。

我将以the code I posted in a question结尾,在此我设置了掩码,以便某些信号仅由主线程捕获:

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);
}

答案 1 :(得分:1)

So signal only interrupt thread instead of the whole process if there are multi-threading?

Yes. A signal interrupts a single thread of execution. At least according to the POSIX standard. C standard itself doesn't specify behaviour of processes or threads.

If a signal is generated for a multi threaded process as opposed to being generated for particular thread, the signal will be delivered to exactly one of the threads belonging to that process (unless the signal is blocked by all threads).

答案 2 :(得分:1)

No matter how many threads the process has, some thread catches the signal and runs the signal handler while the other threads (if any) go on about their business. The signal is sent to the process, but what the process does when it gets the signal is dependent on how the process, and the threads in, configured its signal handlers.

The full rules are quite complicated and I think it's safe to say that very few programmers bother to full understand them and generally avoid signals except for very limited purposes. See here for more details.