线程和信号处理
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.
下面给出的例子创建了两个线程,线程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信号。
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时才会用他。
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中指定的信号从当前信号屏蔽中被删除。
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时才会用他。