linux应用线程和信号处理

来源:互联网 发布:知乎 小米笔记本ro 编辑:程序博客网 时间:2024/05/20 06:24
线程和信号处理
Pthread库也对线程和信号的处理提供了一些函数,这些函数包括:
int pthread_sigmask(int how,const sigset_t * newmask, sigset_t * oldmask);
int pthread_kill(pthread_t thread,int signo);
int sigwait(const sigset_t * set, int * sig);

以上这些函数包含在signal.h中。

这三个函数如果正确执行,返回值都为0。如果发生错误,则pthread_sigmask()和pthread_kill()函数返回值不为0,并且相应的错误代码被设置;sigwait()是线程取消点,总是返回0.

pthread_sigmask()函数用来改变或者设置线程的信号屏蔽(signal mask)。

    newmask用来执行信号屏蔽,设置信号屏蔽之前的信号屏蔽被存放到oldmask指向的位置。改变的方式由第一个参数how决定。如果how是SIG_SETMASK则把信号屏蔽值设置为newmask;如果how是SIG_BLOCK,那么在newmask中指定的信号就添加到了当前信号的屏蔽中;如果how是SIG_UNBLOCK,那么newmask中指定的信号从当前信号屏蔽中被删除。


pthread_sigkill()函数可以向其它线程发送信号。

sigwait()挂起调用sigwait()的线程,直到收到第一个参数set指向的信号集中指定的信号,且等待到信号被存放到第二个参数sig指向的位置。这里需要注意的是,在多线程情况下,执行sigwait()的时侯,sigwait()的第一个参数指向的信号集中的信号必须被阻塞。如果sigwait()等待的信号有相应的信号处理函数将不被调用。


在linux中,使用sigset_t数据类型存放信号集合。对信号集合的操作的GNU C library提供了一些函数:

int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
int sigismember(sigset_t *set, int signum);
以上这些函数包含在signal.h中


其中:

sigemptyset把set指向的信号集清空。

sigfillset初始化信号集让其包括所有的信号。

sigaddset把信号signo添加到信号集中。

sigdelset是从信号集删除信号signo。

sigismember用来判断某个信号signo是否在信号集中。

例子:
下面给出的例子创建了两个线程,线程thread2向线程thread1发送信号SIGUSR1和SIGUSR2。
线程thread2先向thread1发送SIGUSR1信号,然后再发送SIGUSR2信号。
线程thread1收到SIGUSR1信号的时侯,线程thread1使用pthread_sigmask()对SIGUSR1进行阻塞,
故SIGUSR1信号在解除SIGUSR1阻塞之前并没有调用信号处理函数sig_handler()处理信号SIGUSR1,
此时SIGUSR1信号变成未决信号(pending signal),当线程thread1调用pthread_sigmask(SIG_SETMASK,&oldmask,NULL)时,
恢复线程thread1原来的信号屏蔽,这个时侯未决信号SIGUSR1被处理。
线程thread1收到SIGUSR2时,并没有调用信号处理函数sig_handler(),
尽管SIGUSR1和SIGUSR2都是用signal()函数进行了注册,但是线程thread1使用sigwait()等待SIGUSR2信号。
#include <iostream>#include <signal.h>#include <pthread.h>using namespace std;const int THREAD_NUMBER = 2;pthread_t pt[THREAD_NUMBER];void sigHandler(int signo){    switch (signo)    {    case SIGUSR1:    {        cout<<"thread "<<pthread_self()<<" : in signal handler: get signal SIGUSR1"<<endl;        break;    }    case SIGUSR2:    {        cout<<"thread "<<pthread_self()<<" : in signal handler: get signal SIGUSR2"<<endl;        break;    }    default:    {        break;    }    }}void * thread1(void * arg){    sigset_t newmask,oldmask,waitset;    int ret_val;    int signo;    sigemptyset(&waitset);    sigemptyset(&newmask);    //initialize the signal set,and block signal SIGUSR1 while waiting for signal SIGUSR2    sigaddset(&newmask,SIGUSR1);    //initialize the signal set,and wait for SIGUSR2    sigaddset(&waitset,SIGUSR2);    cout<<"thread 1 ID is :"<<pthread_self()<<endl;    //block signal SIGUSR1    ret_val = pthread_sigmask(SIG_BLOCK,&newmask,&oldmask);    if (ret_val)    {        cout<<"pthread_sigmask error"<<endl;        pthread_exit(0);    }    else    {        cout<<"pthread_sigmask done"<<endl;    }    cout<<"pthread 1 calls sigwait"<<endl;    sigwait(&waitset,&signo);    if (SIGUSR2 == signo)    {        cout<<"in thread 1 routing: thread 1 received signal SIGUSR2"<<endl;    }    ret_val = pthread_sigmask(SIG_SETMASK,&oldmask,NULL);    if (ret_val)    {        cout<<"pthread_sigmask SIG_SETMASK error!"<<endl;        pthread_exit(0);    }    pthread_exit(0);}void * thread2(void * arg){    sleep(1);    cout<<"the ID of thread 2 is:"<<pthread_self()<<endl;    pthread_kill(pt[0],SIGUSR1);    cout<<"thread 2 has sent SIGUSR1 to thread 1"<<endl;    pthread_kill(pt[0],SIGUSR2);    cout<<"thread 2 has send SIGUSR2 to thread 2"<<endl;    pthread_exit(0);}int main(){    int i;    int retValue;    signal(SIGUSR1,sigHandler);    signal(SIGUSR2,sigHandler);    retValue = pthread_create(&pt[0],NULL,thread1,NULL);    if (retValue)    {        cout<<"pthread create error!"<<endl;        exit(1);    }    else    {        cout<<"pthread create done!"<<endl;    }    retValue = pthread_create(&pt[1],NULL,thread2,NULL);    if (retValue)    {        cout<<"pthread create error!"<<endl;        exit(1);    }    else    {        cout<<"pthread create done!"<<endl;    }    for (i = 0; i< THREAD_NUMBER ; i++ )    {        retValue = pthread_join(pt[i],NULL);        if (retValue)        {            cout<<"pthread "<<i<<" join error!"<<endl;            exit(1);        }        else        {            cout<<"pthread "<<i<<" join done!"<<endl;        }    }    return 0;}


pthread_kill 和 sigaction 函数:

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

int pthread_kill(pthread_t thread, int sig);

向指定ID的线程发送sig信号,如果线程代码内不做处理,则按照信号默认的行为影响整个进程,也就是说,如果你给一个线程发送了SIGQUIT,但线程却没有实现signal处理函数,则整个进程退出。

pthread_kill(threadid, SIGKILL)也一样,杀死整个进程。
如果要获得正确的行为,就需要在线程内实现signal(SIGKILL,sig_handler)了。

所以,如果int sig的参数不是0,那一定要清楚到底要干什么,而且一定要实现线程的信号处理函数,否则,就会影响整个进程。


OK,如果int sig是0呢,这是一个保留信号,一个作用是用来判断线程是不是还活着。

我们来看一下pthread_kill的返回值:
成功:0
线程不存在:ESRCH
信号不合法:EINVAL

所以,pthread_kill(threadid,0)就很有用啦。

int kill_rc = pthread_kill(thread_id,0);

if(kill_rc == ESRCH)
    printf("the specified thread did not exists or already quit/n");
else if(kill_rc == EINVAL)
    printf("signal is invalid/n");
else
    printf("the specified thread is alive/n");

上述的代码就可以判断线程是不是还活着了。



sigaction :

信号安装函数sigaction(int signum,const struct sigaction *act,struct sigaction *oldact)


sigaction函数的功能是检查或修改与指定信号相关联的处理动作(可同时两种操作)。

他是POSIX的信号接口,而signal()是标准C的信号接口(如果程序必须在非POSIX系统上运行,那么就应该使用这个接口)

给信号signum设置新的信号处理函数act, 同时保留该信号原有的信号处理函数oldact

int sigaction(int signo,const struct sigaction *restrict act, 

              struct sigaction *restrict oact);
 

sigaction函数是把SIGINT中断信号改变,通过act.sa_handler转到新的操作上。

结构sigaction定义如下:

struct sigaction{
  void (*sa_handler)(int);
   sigset_t sa_mask;
  int sa_flag;
  void (*sa_sigaction)(int,siginfo_t *,void *);
}; 
 

sa_handler字段包含一个信号捕捉函数的地址

sa_mask字段说明了一个信号集,在调用该信号捕捉函数之前,这一信号集要加进进程的信号屏蔽字中。仅当从信号捕捉函数返回时再将进程的信号屏蔽字复位为原先值。

sa_flag是一个选项,主要理解两个

SA_INTERRUPT 由此信号中断的系统调用不会自动重启
SA_RESTART 由此信号中断的系统调用会自动重启

SA_SIGINFO 提供附加信息,一个指向siginfo结构的指针以及一个指向进程上下文标识符的指针
 

最后一个参数是一个替代的信号处理程序,当设置SA_SIGINFO时才会用他。
0 0
原创粉丝点击