信号处理程序和多线程

Yve*_*ves 2 c c++ linux multithreading signals

我知道信号是 CPU 和操作系统内核之间的一种通信方式。信号可以中断指令序列,执行处理程序并返回到指令序列。

这是此链接的一段描述:

当一个正在捕获的信号被一个进程处理时,该进程正在执行的正常指令序列会被信号处理程序暂时中断。然后该进程继续执行,但现在执行信号处理程序中的指令。如果信号处理程序返回,进程将继续执行捕获信号时正在执行的正常指令序列。

这是我的测试:

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

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

我执行它,如果我按下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;
}
Run Code Online (Sandbox Code Playgroud)

对于这段代码,我发现我可以ctrl+C连续按三int下,打印三下,然后我必须等待大约5秒钟。

所以看起来第一个ctrl+C中断t1线程,第二个中断t2线程,第三个中断主线程。

那么如果有多线程,信号只会中断线程而不是整个过程?

Fra*_*Boi 7

介绍

首先,每个线程都有自己的掩码,用于指定它正在侦听的信号。创建线程时,它会继承创建它的线程的掩码(让我称其为父线程),该掩码在pthread_create被调用时处于活动状态。

一般来说,如果一个线程正在侦听信号,那么其他线程不应该更好,除非您希望有多个线程执行相同的操作(例如,在服务器中处理连接请求以同时处理多个请求时)。通过这种方式,您始终知道哪个线程正在处理信号。否则你不知道哪个线程正在接收信号以及程序的哪个部分正在执行:调试变得不可能(一个例子是你自己的问题)。

改变了掩盖了的子线程正从创建父线程创建一个新的面具,你把它激活,创建一个新线程pthread_create,然后在父线程重新设置之前的面具活跃(查看代码在答案结束)。

编辑:根据这篇文章,最好使用sigaction()而不是signal. 在现代系统中signal()是这样实现的,sigaction()所以应该没有任何区别。但是,如果使用其旧实现,则可能会出现问题。

回答

那么如果有多线程,信号只会中断线程而不是整个过程?

NO:信号只是信号,它们不做任何事情。与信号相关的动作有能力做一些事情,包括停止程序或终止线程。每个信号都有一个关联的默认操作,默认操作SIGINT是中断进程。

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

在第一种情况下,您只有一个线程,主线程,这是一个无限循环,只要他还活着,它就会始终捕获信号,这就是行为的原因。如果您重新发送信号,信号会暂时被阻塞,直到信号处理程序结束执行。然而,如果在处理程序执行时发送了许多信号,您可能会丢失一些信号。事实上,正如这里所解释的,阻塞的信号被设置为挂起,但不排队。术语挂起意味着操作系统通过设置标志记住有信号等待在下一次传递,而不是排队意味着它通过在某处设置一个标志来实现这一点,而不是通过准确记录到达的信号数量。因此,如果信号被发送一次、5 次或更多(尝试在您的程序中按 CTRL+C 多次:我已经尝试过),而信号处理程序正在执行它会产生完全相同的行为。

在您的第二种情况下,您有 3 个线程:main一个、t1t2: 所有线程都可以查看信号,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);
}
Run Code Online (Sandbox Code Playgroud)