linux應用線程和信號處理

線程和信號處理
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時纔會用他。
發佈了20 篇原創文章 · 獲贊 8 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章