線程和信号處理
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 sigdelset(sigset_t * set);
int sigismember(const sigset_t * set,int signo);
以上這些函數包含在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 函數2009-06-14 18:45pthread_kill:
别被名字吓到,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時才會用他。