C语言 信号处理程序和多线程

nnvyjq4y  于 2023-03-17  发布在  其他
关注(0)|答案(3)|浏览(166)

我知道信号是CPU和操作系统内核之间的一种通信方式。一个信号可以中断指令序列,执行处理程序,然后回到指令序列。
下面是对link的一段描述:
当进程处理正在捕获的信号时,该进程正在执行的正常指令序列会被信号处理程序暂时中断。然后,进程会继续执行,但信号处理程序中的指令现在会被执行。如果信号处理程序返回,则进程会继续执行捕获信号时正在执行的正常指令序列。
下面是我的测试:

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

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

我执行它,如果我按ctrl+Cint将被打印,然后我必须等待5秒,无论我在等待5秒时按了多少次ctrl+C,什么也没有发生。
然后我做了另一个测试:

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线程,第三个中断了主线程。
如果有多线程,那么只给中断线程发信号,而不是整个进程?

xdnvmnnf

xdnvmnnf1#

导言

首先,每个线程都有自己的掩码来指定它正在监听哪些信号,当一个线程创建时,它会继承创建它的线程的掩码(让我称之为 * 父线程)*,当pthread_create被调用时,它是活动的。
一般来说,如果一个线程正在侦听信号,那么其他线程就不应该侦听信号,除非您希望有许多线程执行相同的操作(例如,在服务器中处理连接请求时,同时处理多个请求)。通过这种方式,您总是知道哪个线程函数正在处理信号。否则,您根本不知道给定信号时哪个线程函数正在执行。使其无法调试(示例是您自己的问题)。
更改从 * 父线程 * 创建的 * 子线程 * 的掩码,您需要创建一个新掩码,将其设置为活动,使用pthread_create创建一个新线程,然后在 * 父线程 * 中再次将之前的掩码设置为活动(参见答案末尾的代码)。

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

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

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

使用你的处理程序,你会覆盖默认的动作,所以它不会停止程序,但是它会执行你在线程函数中指定的操作。
在第一种情况下,你只有一个线程,主线程,这是一个无限循环,它总是捕捉信号,只要他还活着,这就是为什么行为。如果你重新发送信号,信号暂时被阻止,直到信号处理程序结束执行。然而,如果许多信号被发送,而处理程序正在执行,你可以失去一些信号。事实上,正如解释here,被阻塞的信号被设置为挂起,但不排队。术语 * 挂起 * 意味着操作系统通过设置标志来记住有信号等待在下一个机会被传送,而 * 不排队 * 意味着它通过在某处设置标志来做到这一点,但不通过保持多少信号已经到达的精确记录。因此,如果信号被发送一次,5次或更多次(尝试在程序中多按CTRL+C:我试过了)当信号处理程序执行时,它会产生完全相同的行为。
在第二种情况下,您有3个线程:main一个、t1t2:它们都能看到信号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);
}
rslzwgfq

rslzwgfq2#

无论进程有多少线程,总有一个线程捕获信号并运行信号处理程序,而其他线程(如果有)则继续处理它们的事务。信号被发送到进程,但进程在收到信号时执行的操作取决于进程以及中的线程配置其信号处理程序的方式。
完整的规则相当复杂,我认为可以肯定地说,很少有程序员会费心去完全理解它们,除了非常有限的用途外,通常会避免使用信号。

smtd7mpg

smtd7mpg3#

如果有多线程,那么只给中断线程发信号,而不是整个进程?
是的。一个信号中断一个线程的执行。至少根据POSIX标准。C标准本身没有规定进程或线程的行为。
如果信号是为多线程进程生成的,而不是为特定线程生成的,则该信号将被传送到属于该进程的一个线程(除非该信号被所有线程阻塞)。

相关问题