天天看點

線程和信号處理

線程和信号處理

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時才會用他。