Linux多线程信号处理浅谈

linux 多线程信号总结(一)  

        1. 在多线程环境下,产生的信号是传递给整个进程的,一般而言,所有线程都有机会收到这个信号,进程在收到信号的的线程上下文执行信号处理函数,具体是哪个线程执行的难以获知。

  2 signal函数BSD/Linux的实现并不在信号处理函数调用时,恢复信号的处理为默认,而是在信号处理时阻塞此信号,直到信号处理函数返回。其他实现可能在调用信号处理函数时,恢复信号的处理为默认方式,因而需要在信号处理函数中重建信号处理函数为我们定义的处理函数,在这些系统中,较好的方法是使用sigaction来建立信号处理函数。

  3 发送信号给进程,哪个线程会收到?APUE说,在多线程的程序中,如果不做特殊的信号阻塞处理,当发送信号给进程时,由系统选择一个线程来处理这个信号。

  4 如果进程中,有的线程可以屏蔽了某个信号,而某些线程可以处理这个信号,则当我们发送这个信号给进程或者进程中不能处理这个信号的线程时,系统会将这个信号投递到进程号最小的那个可以处理这个信号的线程中去处理。

  5 如果我们同时注册了信号处理函数,同时又用sigwait来等待这个信号,谁会取到信号?经过实验,Linux上sigwait的优先级高。

  6 在Linux中的posix线程模型中,线程拥有独立的进程号,可以通过getpid()得到线程的进程号,而线程号保存在pthread_t的值中。而主线程的进程号就是整个进程的进程号,因此向主进程发送信号只会将信号发送到主线程中去。如果主线程设置了信号屏蔽,则信号会投递到一个可以处理的线程中去。

  7 当调用SYSTEM函数去执行SHELL命令时,可以放心的阻塞SIGCHLD,因为SYSTEM会自己处理子进程终止的问题。

  8 使用sleep()时,要以放心的去阻塞SIGALRM信号,目前sleep函数都不会依赖于ALRM函数的SIGALRM信号来工作。

  linux 多线程信号总结(二)

  1. 默认情况下,信号将由主进程接收处理,就算信号处理函数是由子线程注册的

  2. 每个线程均有自己的信号屏蔽字,可以使用sigprocmask函数来屏蔽某个线程对该信号的响应处理,仅留下需要处理该信号的线程来处理指定的信号。

  3. 对某个信号处理函数,以程序执行时最后一次注册的处理函数为准,即在所有的线程里,同一个信号在任何线程里对该信号的处理一定相同

  4. 可以使用pthread_kill对指定的线程发送信号

  APUE的说法:每个线程都有自己的信号屏蔽字,但是信号的处理是进程中所有的线程共享的,

  这意味着尽管单个线程可以阻止某些信号,但当线程修改了与某个信号相关的处理行为后,所

  有的线程都共享这个处理行为的改变。这样如果一个线程选择忽略某个信号,而其他线程可

  以恢复信号的默认处理行为,或者为信号设置一个新的处理程序,从而可以撤销上述线程的

  信号选择。

  进程中的信号是送到单个线程的,如果信号与硬件故障或者计时器超时有关,该型号就被发

  送到引起该事件的线程中去,而其他的信号则被发送到任意一个线程。

  sigprocmask的行为在多线程的进程中没有定义,线程必须使用pthread_sigmask

  总结:一个信号可以被没屏蔽它的任何一个线程处理,但是在一个进程内只有一个多个线程共用的处理函数。……

  linux 多线程信号总结(三)

  1 Linux 多线程应用中,每个线程可以通过调用pthread_sigmask() 设置本线程的信号掩码。一般情况下,被阻塞的信号将不能中断此线程的执行,除非此信号的产生是因为程序运行出错如SIGSEGV;另外不能被忽略处理的信号SIGKILL 和SIGSTOP 也无法被阻塞。

  2 当一个线程调用pthread_create() 创建新的线程时,此线程的信号掩码会被新创建的线程继承。

  3 信号安装最好采用sigaction方式,sigaction,是为替代signal 来设计的较稳定的信号处理,signal的使用比较简单。signal(signalNO,signalproc);

  不能完成的任务是:1.不知道信号产生的原因;

  2.处理信号中不能阻塞其他的信号

  而signaction,则可以设置比较多的消息。尤其是在信号处理函数过程中接受信号,进行何种处理。

  sigaction函数用于改变进程接收到特定信号后的行为。

  4 sigprocmask函数只能用于单线程,在多线程中使用pthread_sigmask函数。

  5 信号是发给进程的特殊消息,其典型特性是具有异步性。

  6 信号集代表多个信号的集合,其类型是sigset_t。

  7 每个进程都有一个信号掩码(或称为信号屏蔽字),其中定义了当前进程要求阻塞的信号集。

  8 所谓阻塞,指Linux内核不向进程交付在掩码中的所有信号。于是进程可以通过修改信号掩码来暂时阻塞特定信号的交付,被阻塞的信号不会影响进程的行为直到该信号被真正交付。

  9 忽略信号不同于阻塞信号,忽略信号是指Linux内核已经向应用程序交付了产生的信号,只是应用程序直接丢弃了该信号而已。

一、发送信号的函数

int pthread_kill(pthread_t thread, int sig);


1、别被名字吓到,pthread_kill可不是kill,而是向线程发送signal。还记得signal吗,大部分signal的默认动作是终止进程的运行,所以,我们才要用sigaction()去抓信号并加上处理函数。

​ 2、向指定ID的线程发送sig信号,如果线程代码内不做处理,则按照信号默认的行为影响整个进程,也就是说,如果你给一个线程发送了SIGQUIT,但线程却没有实现signal处理函数,则整个进程退出。如果要获得正确的行为,就需要在线程内实现sigaction了。所以,如果int sig的参数不是0,那一定要清楚到底要干什么,而且一定要实现线程的信号处理函数,否则,就会影响整个进程。如果int sig是0呢,这是一个保留信号,其实并没有发送信号,作用是用来判断线程是不是还活着。

二、信号处理
1、进程信号处理:

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);


给信号signum设置一个处理函数,处理函数在sigaction中指定
act.sa_mask 信号屏蔽字
act.sa_handler 信号集处理程序

​ 2、信号集的处理

​    int sigemptyset(sigset_t *set); //清空信号集
​    int sigfillset(sigset_t *set);  //将所有信号加入信号集
​    int sigaddset(sigset_t *set, int signum); //增加一个信号到信号集
​    int sigdelset(sigset_t *set, int signum); //删除一个信号到信号集


​ 3、多线程信号屏蔽处理

/* int sigprocmask(int how, const sigset_t *set, sigset_t oldset); 这是进程的信号屏蔽处理*/
int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
how = SIG_BLOCK:向当前的信号掩码中添加set,其中set表示要阻塞的信号组。
SIG_UNBLOCK:向当前的信号掩码中删除set,其中set表示要取消阻塞的信号组。
SIG_SETMASK:将当前的信号掩码替换为set,其中set表示新的信号掩码。


在多线程中,新线程的当前信号掩码会继承创造它的线程的信号掩码。
一般情况下,被阻塞的信号将不能中断此线程的执行,除非此信号的产生是因为程序运行出错如 SIGSEGV;另外不能被忽略处理的信号 SIGKILL 和 SIGSTOP 也无法被阻塞。三、实例

#include 
#include 
#include 
#include 

#include 
#include 

#include "include/pthread.h"

#ifndef _WIN64
#pragma comment(lib,".\\lib32\\pthreadVC2.lib")
#pragma comment(lib,".\\lib32\\pthreadVCE2.lib")
#pragma comment(lib,".\\lib32\\pthreadVSE2.lib")
#else
#pragma comment(lib,".\\lib64\\pthreadVC2.lib")
#endif 


/*
WINDOWS 缺少库函数,得在Linux下运行


*DECRIPTION:    正确到处理信号

*    int pthread_kill(pthread_t thread, int sig);
*        向线程thread发送sig信号,成功返回0,失败返回错误码
*
*    int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
*        为信号signum设置处理函数,处理函数在sigaction中指定
*        act.sa_mask 信号屏蔽字
*        act.sa_handler 信号集处理程序
*
* int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
*        多线程信号屏蔽函数
*        how = SIG_BLOCK:向当前的信号掩码中添加set,其中set表示要阻塞的信号组。
*        SIG_UNBLOCK:向当前的信号掩码中删除set,其中set表示要取消阻塞的信号组。
* SIG_SETMASK:将当前的信号掩码替换为set,其中set表示新的信号掩码。
* 在多线程中,新线程的当前信号掩码会继承创造它的线程的信号掩码
*/


void sig_handler1(int arg)
{
    printf("thread1 get signal\n");
    return;
}
void sig_handler2(int arg)
{
    printf("thread2 get signal\n");
    return;
}
void* thread_fun1(void* arg)
{
    printf("new thread 1\n");

    struct sigaction act;
    

    memset(&act, 0, sizeof(act));
    sigaddset(&act.sa_mask,SIGQUIT);
    act.sa_handler = sig_handler1;
    sigaction(SIGQUIT,&act,NULL);

    pthread_sigmask(SIG_BLOCK,&act.sa_mask,NULL);
    Sleep(2);

    return (void*)0;
}
void *thread_fun2(void *arg)
{
    printf("new thread 2\n");
    
    //WINDOWS 缺少库函数,得在Linux下运行
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    sigaddset(&act.sa_mask, SIGQUIT);
    act.sa_handler = sig_handler2;
    sigaction(SIGQUIT, &act, NULL);

    //    pthread_sigmask(SIG_BLOCK, &act.sa_mask, NULL);
    Sleep(2);
}

int main()
{
    pthread_t tid1, tid2;
    int err;
    int s;

    err = pthread_create(&tid1,NULL,thread_fun1,NULL);
    if (err!=0)
    {
        printf("create new thread 1 failed\n");
        return 0;
    }
    err = pthread_create(&tid2,NULL,thread_fun2,NULL);
    if (err != 0)
    {
        printf("create new thread 2 failed\n");
        return 0;
    }

    Sleep(1);
    s = pthread_kill(tid1, SIGQUIT);
    if (s!=0)
    {
        printf("send signal to thread1 failed\n");
    }

    s = pthread_kill(tid2, SIGQUIT);
    if (s != 0)
    {
        printf("send signal to thread2 failed\n");
    }
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    return 0;
}

你可能感兴趣的:(嵌入式开发,linux,信号处理,网络)