主頁(yè) > 知識(shí)庫(kù) > 淺談Linux信號(hào)機(jī)制

淺談Linux信號(hào)機(jī)制

熱門(mén)標(biāo)簽:百度競(jìng)價(jià)點(diǎn)擊價(jià)格的計(jì)算公式 檢查注冊(cè)表項(xiàng) 美圖手機(jī) 硅谷的囚徒呼叫中心 阿里云 網(wǎng)站建設(shè) 智能手機(jī) 使用U盤(pán)裝系統(tǒng)

一、信號(hào)列表

root@ubuntu:# kill -l

 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP

 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1

11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM

16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP

21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ

26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR

31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3

38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8

43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13

48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12

53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7

58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2

63) SIGRTMAX-1 64) SIGRTMAX

其中最常見(jiàn)的:

  • Ctrl + C 觸發(fā)的是 SIGINT;
  • Ctrl + \ 觸發(fā)的是SIGQUIT;
  • 但是特別說(shuō)明下 Ctrl + D 并不是觸發(fā)信號(hào),而是產(chǎn)生一個(gè) EOF,這也是為什么在 Python 交互模式按下這個(gè)組合會(huì)退出 Python 的原因。

1.1、實(shí)時(shí)信號(hào)非實(shí)時(shí)信號(hào)

如上,kill列舉出所有信號(hào)。實(shí)時(shí)信號(hào)與非實(shí)時(shí)信號(hào)又叫做可靠信號(hào)與不可靠信號(hào)。SIGRTMIN 及其以后的是實(shí)時(shí)信號(hào),之前的是非實(shí)時(shí)信號(hào)。區(qū)別是實(shí)時(shí)信號(hào)支持重復(fù)排隊(duì),但是非實(shí)時(shí)信號(hào)不支持。非實(shí)時(shí)信號(hào)在排隊(duì)時(shí)候會(huì)默認(rèn)只出現(xiàn)一次,意思就是即使多次發(fā)送也終將只收到一個(gè)。在隊(duì)列的取出順序上也有區(qū)別,即最先取出的信號(hào)一定是實(shí)時(shí)信號(hào)。

PS:

  • kill、killall 默認(rèn)發(fā)送SIGTERM 信號(hào)。
  • linux下 SIGKILL不能被阻塞、或忽略。
  • 默認(rèn)情況下 SIGCHLD 不被忽略,編程時(shí)候需要注意這個(gè)(要么設(shè)置 SIG_IGN 或者主動(dòng) wait)。
  • 所有未定義處理函數(shù)的信號(hào),默認(rèn)退出進(jìn)程。
  • 信號(hào)被設(shè)置block后仍存在于隊(duì)列中只是不被處理,如果放開(kāi)屏蔽將會(huì)被處理。
  • 信號(hào)可以中斷sleep調(diào)用引起睡眠的進(jìn)程。

1.2、信號(hào)狀態(tài)

信號(hào)的”未決“是一種狀態(tài),指的是從信號(hào)的產(chǎn)生到信號(hào)被處理前的這一段時(shí)間;信號(hào)的”阻塞“是一個(gè)開(kāi)關(guān)動(dòng)作,指的是阻止信號(hào)被處理,但不是阻止信號(hào)產(chǎn)生。

例如在sleep前用 sigprocmask 阻塞了退出信號(hào),然后sleep,然后在sleep的過(guò)程中產(chǎn)生一個(gè)退出信號(hào),但是此時(shí)退出信號(hào)被阻塞過(guò),(中文的”阻塞”在這里容易被誤解為一種狀態(tài),實(shí)際上是一種類(lèi)似于開(kāi)關(guān)的動(dòng)作,所以說(shuō)“被阻塞過(guò)”,而不是“被阻塞”)所以處于“未決”狀態(tài),在 sleep后又用sigprocmask關(guān)掉退出信號(hào)的阻塞開(kāi)關(guān),因?yàn)橹爱a(chǎn)生的退出信號(hào)一直處于未決狀態(tài),當(dāng)關(guān)上阻塞開(kāi)關(guān)后,馬上退出“未決”狀態(tài),得到處理,這一切發(fā)生在sigprocmask返回之前。

1.3、信號(hào)生命周期

對(duì)于一個(gè)完整的信號(hào)生命周期(從信號(hào)發(fā)送到相應(yīng)的處理函數(shù)執(zhí)行完畢)來(lái)說(shuō),可以分為三個(gè)重要的階段,這三個(gè)階段由四個(gè)重要事件來(lái)刻畫(huà):

1.信號(hào)誕生;

2. 信號(hào)在進(jìn)程中注冊(cè)完畢;

3.信號(hào)在進(jìn)程中的注銷(xiāo)完畢;

4.信號(hào)處理函數(shù)執(zhí)行完畢。相鄰兩個(gè)事件的時(shí)間間隔構(gòu)成信號(hào)生命周期的一個(gè)階段。

下面闡述四個(gè)事件的實(shí)際意義:

  • 信號(hào)"誕生"。信號(hào)的誕生指的是觸發(fā)信號(hào)的事件發(fā)生(如檢測(cè)到硬件異常、定時(shí)器超時(shí)以及調(diào)用信號(hào)發(fā)送函數(shù)kill()或sigqueue()等)。
  • 信號(hào)在目標(biāo)進(jìn)程中"注冊(cè)";進(jìn)程的task_struct結(jié)構(gòu)中有關(guān)于本進(jìn)程中未決信號(hào)的數(shù)據(jù)成員:
struct sigpending pending;
struct sigpending
{
    struct sigqueue *head, **tail;
    sigset_t signal;
};

信號(hào)在進(jìn)程中注冊(cè)指的就是信號(hào)值加入到進(jìn)程的未決信號(hào)集中(sigpending結(jié)構(gòu)的第二個(gè)成員sigset_t signal),并且信號(hào)所攜帶的信息被保留到未決信號(hào)信息鏈的某個(gè)sigqueue結(jié)構(gòu)中。只要信號(hào)在進(jìn)程的未決信號(hào)集中,表明進(jìn)程已經(jīng)知道這些信號(hào)的存在,但還沒(méi)來(lái)得及處理,或者該信號(hào)被進(jìn)程阻塞。

1.信號(hào)在進(jìn)程中的注銷(xiāo)。在目標(biāo)進(jìn)程執(zhí)行過(guò)程中,會(huì)檢測(cè)是否有信號(hào)等待處理(每次從系統(tǒng)空間返回到用戶(hù)空間時(shí)都做這樣的檢查)。如果存在未決信號(hào)等待處理且該信號(hào)沒(méi)有被進(jìn)程阻塞,則在運(yùn)行相應(yīng)的信號(hào)處理函數(shù)前,進(jìn)程會(huì)把信號(hào)在未決信號(hào)鏈中占有的結(jié)構(gòu)卸掉。是否將信號(hào)從進(jìn)程未決信號(hào)集中刪除對(duì)于實(shí)時(shí)與非實(shí)時(shí)信號(hào)是不同的。對(duì)于非實(shí)時(shí)信號(hào)來(lái)說(shuō),由于在未決信號(hào)信息鏈中最多只占用一個(gè)sigqueue結(jié)構(gòu),因此該結(jié)構(gòu)被釋放后,應(yīng)該把信號(hào)在進(jìn)程未決信號(hào)集中刪除(信號(hào)注銷(xiāo)完畢);而對(duì)于實(shí)時(shí)信號(hào)來(lái)說(shuō),可能在未決信號(hào)信息鏈中占用多個(gè)sigqueue結(jié)構(gòu),因此應(yīng)該針對(duì)占用gqueue結(jié)構(gòu)的數(shù)目區(qū)別對(duì)待:如果只占用一個(gè)sigqueue結(jié)構(gòu)(進(jìn)程只收到該信號(hào)一次),則應(yīng)該把信號(hào)在進(jìn)程的未決信號(hào)集中刪除(信號(hào)注銷(xiāo)完畢)。否則,不在進(jìn)程的未決信號(hào)集中刪除該信號(hào)(信號(hào)注銷(xiāo)完畢)。進(jìn)程在執(zhí)行信號(hào)相應(yīng)處理函數(shù)之前,首先要把信號(hào)在進(jìn)程中注銷(xiāo)。

2.信號(hào)生命終止。進(jìn)程注銷(xiāo)信號(hào)后,立即執(zhí)行相應(yīng)的信號(hào)處理函數(shù),執(zhí)行完畢后,信號(hào)的本次發(fā)送對(duì)進(jìn)程的影響徹底結(jié)束。

1.4、信號(hào)的執(zhí)行和注銷(xiāo)

內(nèi)核處理一個(gè)進(jìn)程收到的軟中斷信號(hào)是在該進(jìn)程的上下文中,因此,進(jìn)程必須處于運(yùn)行狀態(tài)。當(dāng)其由于被信號(hào)喚醒或者正常調(diào)度重新獲得CPU時(shí),在其從內(nèi)核空間返回到用戶(hù)空間時(shí)會(huì)檢測(cè)是否有信號(hào)等待處理。如果存在未決信號(hào)等待處理且該信號(hào)沒(méi)有被進(jìn)程阻塞,則在運(yùn)行相應(yīng)的信號(hào)處理函數(shù)前,進(jìn)程會(huì)把信號(hào)在未決信號(hào)鏈中占有的結(jié)構(gòu)卸掉。當(dāng)所有未被屏蔽的信號(hào)都處理完畢后,即可返回用戶(hù)空間。對(duì)于被屏蔽的信號(hào),當(dāng)取消屏蔽后,在返回到用戶(hù)空間時(shí)會(huì)再次執(zhí)行上述檢查處理的一套流程。

處理信號(hào)有三種類(lèi)型:進(jìn)程接收到信號(hào)后退出;進(jìn)程忽略該信號(hào);進(jìn)程收到信號(hào)后執(zhí)行用戶(hù)設(shè)定用系統(tǒng)調(diào)用signal的函數(shù)。當(dāng)進(jìn)程接收到一個(gè)它忽略的信號(hào)時(shí),進(jìn)程丟棄該信號(hào),就象沒(méi)有收到該信號(hào)似的繼續(xù)運(yùn)行。如果進(jìn)程收到一個(gè)要捕捉的信號(hào),那么進(jìn)程從內(nèi)核態(tài)返回用戶(hù)態(tài)時(shí)執(zhí)行用戶(hù)定義的函數(shù)。而且執(zhí)行用戶(hù)定義的函數(shù)的方法很巧妙,內(nèi)核是在用戶(hù)棧上創(chuàng)建一個(gè)新的層,該層中將返回地址的值設(shè)置成用戶(hù)定義的處理函數(shù)的地址,這樣進(jìn)程從內(nèi)核返回彈出棧頂時(shí)就返回到用戶(hù)定義的函數(shù)處,從函數(shù)返回再?gòu)棾鰲m敃r(shí),才返回原先進(jìn)入內(nèi)核的地方。這樣做的原因是用戶(hù)定義的處理函數(shù)不能且不允許在內(nèi)核態(tài)下執(zhí)行(如果用戶(hù)定義的函數(shù)在內(nèi)核態(tài)下運(yùn)行的話(huà),用戶(hù)就可以獲得任何權(quán)限)。

eg:

#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

void myHandler(int num)
{
    int ret = 0;
    
    if (SIGUSR1 == num)
    {
        sigset_t set;
        ret = sigemptyset(&set);
        assert(!(-1 == ret));
        ret = sigaddset(&set, SIGINT);
        assert(!(-1 == ret));
        ret = sigaddset(&set, SIGRTMIN);
        assert(!(-1 == ret));
        ret = sigprocmask(SIG_UNBLOCK, &set, NULL);
        assert(!(-1 == ret));
        printf("解除阻塞 recv sig num: %d\n", num);
    }
    else if (num == SIGINT || num == SIGRTMIN)
    {
        printf("recv sig num: %d\n", num);
    }
    else
    {
        printf(" 其他信號(hào)recv sig num: %d\n", num);
    }
}

int main(void)
{
    pid_t pid;
    int ret = 0;
    // 設(shè)置回調(diào)函數(shù)
    struct sigaction act;
    act.sa_handler = myHandler;
    act.sa_flags = SA_SIGINFO;
    // 注冊(cè)非實(shí)時(shí)信號(hào)的處理函數(shù)
    ret = sigaction(SIGINT, &act, NULL);
    assert(!(-1 == ret));
    // 注冊(cè)實(shí)時(shí)信號(hào)的處理函數(shù)
    ret = sigaction(SIGRTMIN, &act, NULL);
    assert(!(-1 == ret));
    // 注冊(cè)用戶(hù)自定義信號(hào)
    ret = sigaction(SIGUSR1, &act, NULL);
    assert(!(-1 == ret));
    
    // 把 SIGINT  SIGRTMIN 軍添加到阻塞狀態(tài)字中
    sigset_t set;
    ret = sigemptyset(&set);
    assert(!(-1 == ret));
    ret = sigaddset(&set, SIGINT);
    assert(!(-1 == ret));
    ret = sigaddset(&set, SIGRTMIN);
    assert(!(-1 == ret));
    ret = sigprocmask(SIG_BLOCK, &set, NULL);
    assert(!(-1 == ret));
    
    pid = fork();
    assert(!(-1 == ret));
    if (0 == pid)
    {
        union sigval value;
        value.sival_int = 10;
        int i = 0;
        // 發(fā)三次不穩(wěn)定信號(hào)
        for (i = 0; i < 3; i++)
        {
            ret = sigqueue(getppid(), SIGINT, value);
            assert(!(-1 == ret));
            printf("發(fā)送不可靠信號(hào) ok\n");
        }
        
        // 發(fā)三次穩(wěn)定信號(hào)
        value.sival_int = 20;
        for (i = 0; i < 3; i++)
        {
            ret = sigqueue(getppid(), SIGRTMIN, value);
            assert(!(-1 == ret));
            printf("發(fā)送可靠信號(hào)ok\n");
        }
        // 向父進(jìn)程發(fā)送 SIGUSR1 解除阻塞
        ret = kill(getppid(), SIGUSR1);
        assert(!(-1 == ret));
    }
    while (1)
    {
        sleep(1);
    }
    return 0;
}

二、信號(hào)掩碼和信號(hào)處理函數(shù)的繼承

2.1、信號(hào)處理函數(shù)的繼承

信號(hào)處理函數(shù)是進(jìn)程屬性,所以進(jìn)程里的每個(gè)線程的信號(hào)處理函數(shù)是相同的。通過(guò)fork創(chuàng)建的子進(jìn)程會(huì)繼承父進(jìn)程的信號(hào)處理函數(shù)。execve 后設(shè)置為處理的信號(hào)處理函數(shù)會(huì)被重置為默認(rèn)函數(shù),設(shè)置為忽略的信號(hào)保持不變。意思是如果父進(jìn)程里信號(hào)設(shè)置處理為SIG_IGN,那么等到子進(jìn)程被exec了,這個(gè)信號(hào)的處理還是被忽略,不會(huì)重置為默認(rèn)函數(shù)。

eg:

// test.c --> test
#include <stdlib.h>
  
typedef void (*sighandler_t)(int);
static sighandler_t old_int_handler;
  
static sighandler_t old_handlers[SIGSYS + 1];
  
void sig_handler(int signo)
{
    printf("receive signo %d\n",signo);
    old_handlers[signo](signo);
}
  
int main(int argc, char **argv)
{
    old_handlers[SIGINT] = signal(SIGINT, SIG_IGN);
    old_handlers[SIGTERM] = signal(SIGTERM, sig_handler);
  
    int ret;
  
    ret = fork();
    if (ret == 0) {
        //child
        // 這里execlp將運(yùn)行 test2 作為子進(jìn)程。
        execlp("/tmp/test2", "/tmp/test2",(char*)NULL);
    }else if (ret > 0) {
        //parent
        while(1) {
            sleep(1);
        }
    }else{
        perror("");
        abort();
    }
  
}
  
================================================
test2.c --> test2
#include <stdio.h>
int main(int argc, char **argv)
{
    while(1) {
        sleep(1);
    }
    return 0;
}

結(jié)論:test換成test2后,SIGINT的處理方式還是忽略,SIGTERM被重置為默認(rèn)的方式。

2.2、信號(hào)掩碼的繼承

信號(hào)掩碼有以下規(guī)則:

1.每個(gè)線程可以有自己信號(hào)掩碼。

2.fork出來(lái)的子進(jìn)程會(huì)繼承父進(jìn)程的信號(hào)掩碼,exec后信號(hào)掩碼保持不變。如果父進(jìn)程是多線程,那么子進(jìn)程只繼承主線程的掩碼。

3.針對(duì)進(jìn)程發(fā)送的信號(hào),會(huì)被任意的沒(méi)有屏蔽該信號(hào)的線程接收,注意只有一個(gè)線程會(huì)隨機(jī)收到。linux下如果都可以所有線程都可以接收信號(hào),那么信號(hào)將默認(rèn)發(fā)送到主線程,posix系統(tǒng)是隨機(jī)發(fā)送。

4.fork之后子進(jìn)程里pending的信號(hào)集初始化為空,exec會(huì)保持pending信號(hào)集。

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
  
typedef void (*sighandler_t)(int);
  
static void *thread1(void *arg)
{
    sigset_t set;
      
    printf("in thread1\n");
  
    sigemptyset(&set);
    sigaddset(&set, SIGTERM);
    pthread_sigmask(SIG_BLOCK, &set, NULL);

    while(1) {
        sleep(1);
    }
}
  
static void sigset_print(sigset_t *set)
{
    int i;
  
    for (i = 1; i <= SIGSYS; i++) {
        if (sigismember(set, i)) {
            printf("signal %d is in set\n",i);
        }
    }
}
  
int main(int argc, char **argv)
{
    int ret;
    sigset_t set;
    pthread_t pid;
  
    pthread_create(&pid, NULL, thread1, NULL);
    sleep(1);
  
    sigemptyset(&set);
    sigaddset(&set, SIGINT);
    pthread_sigmask(SIG_BLOCK, &set, NULL);
  
    ret = fork();
    if (ret == 0) {
        //child
        pthread_sigmask(SIG_BLOCK, NULL, &set);
        sigset_print(&set);
  
        while(1) {
            sleep(1);
        }
    }else if (ret > 0) {
        //parent
        while(1) {
            sleep(1);
        }
    }else{
        perror("");
        abort();
    }
  
}

結(jié)論:只有在主線程里設(shè)置的掩碼才被子進(jìn)程繼承了。這里面的原因在于linux里的fork只是復(fù)制了調(diào)用fork()的那個(gè)線程,因此在子進(jìn)程里只有父進(jìn)程的主線程被拷貝了,當(dāng)然信號(hào)掩碼就是父進(jìn)程的主線程的信號(hào)掩碼的復(fù)制了。再次驗(yàn)證證明,如果是在thread1里調(diào)用fork,那么子進(jìn)程的信號(hào)掩碼就會(huì)是thread1的拷貝了。

2.3、sigwait 與多線程

sigwait函數(shù):sigwait等一個(gè)或者多個(gè)指定信號(hào)發(fā)生。

它所做的工作只有兩個(gè):

第一,監(jiān)聽(tīng)被阻塞的信號(hào);

第二,如果所監(jiān)聽(tīng)的信號(hào)產(chǎn)生了,則將其從未決隊(duì)列中移出來(lái)。sigwait并不改變信號(hào)掩碼的阻塞與非阻塞狀態(tài)。

在POSIX標(biāo)準(zhǔn)中,當(dāng)進(jìn)程收到信號(hào)時(shí),如果是多線程的情況,我們是無(wú)法確定是哪一個(gè)線程處理這個(gè)信號(hào)。而sigwait是從進(jìn)程中pending的信號(hào)中,取走指定的信號(hào)。這樣的話(huà),如果要確保sigwait這個(gè)線程收到該信號(hào),那么所有線程含主線程以及這個(gè)sigwait線程則必須block住這個(gè)信號(hào),因?yàn)槿绻约翰蛔枞蜎](méi)有未決狀態(tài)(阻塞狀態(tài))信號(hào),別的所有線程不阻塞就有可能當(dāng)信號(hào)過(guò)來(lái)時(shí),被其他的線程處理掉。

PS:

在多線程代碼中,總是使用sigwait或者sigwaitinfo或者sigtimedwait等函數(shù)來(lái)處理信號(hào)。而不是signal或者sigaction等函數(shù)。因?yàn)樵谝粋€(gè)線程中調(diào)用signal或者sigaction等函數(shù)會(huì)改變所以線程中的信號(hào)處理函數(shù),而不是僅僅改變調(diào)用signal/sigaction的那個(gè)線程的信號(hào)處理函數(shù)。

2.4、多進(jìn)程下的信號(hào)

多進(jìn)程下鍵盤(pán)觸發(fā)的信號(hào)會(huì)同時(shí)發(fā)送到當(dāng)前進(jìn)程組的所有進(jìn)程。如果一個(gè)程序在執(zhí)行時(shí) fork 了多個(gè)子進(jìn)程,那么按鍵觸發(fā)的信號(hào)將會(huì)被這個(gè)程序的所有進(jìn)程收到。

但是與多線程不一樣,多進(jìn)程下的信號(hào)掩碼和信號(hào)處理函數(shù)是獨(dú)立的。每個(gè)進(jìn)程都可以選擇處理或者不處理,也可以設(shè)置自己的信號(hào)掩碼。

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>

int main(int argc, char **argv)
{
    pid_t pid = fork();

    signal(SIGCHLD, SIG_IGN);
    if (pid < 0)
        printf("error fork\n");
    else if (pid == 0)
    {
        signal(SIGINT, SIG_IGN); // 忽略 SIGINT,這樣 ctrl+c 后子進(jìn)程能活下來(lái); 不設(shè)置的話(huà),收到信號(hào)將退出
        printf("child gid = %ld\n", getpgid(getpid()));
        do
        {
            sleep(1);
        } while (1);
    }
    else
    {
        printf("parent gid = %ld\n", getpgid(getpid()));
        do
        {
            sleep(1);
        } while (1);
    }

    return 0;
}

如上圖,可以看到,收到SIGINT 后父進(jìn)程退出,子進(jìn)程因?yàn)樵O(shè)置了忽略 SIGINT 所以子進(jìn)程沒(méi)有受到影響。

三、apis

3.1、信號(hào)發(fā)生函數(shù)

1.kill(pid_t pid, int signum);

2.int sigqueue(pid_t pid, int sig, const union sigval value);

3.pthread_kill(pthread_t tid, int signum);

4.raise(int signum);// 發(fā)送信號(hào)到自己

5.void alarm(void);

6.void abort(void);

7.int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);

PS:

sigqueue()比kill()傳遞了更多的附加信息,但sigqueue()只能向一個(gè)進(jìn)程發(fā)送信號(hào),而不能發(fā)送信號(hào)給一個(gè)進(jìn)程組。如果signo=0,將會(huì)執(zhí)行錯(cuò)誤檢查,但實(shí)際上不發(fā)送任何信號(hào),0值信號(hào)可用于檢查pid的有效性以及當(dāng)前進(jìn)程是否有權(quán)限向目標(biāo)進(jìn)程發(fā)送信號(hào)。

3.2、信號(hào)處理函數(shù)

1.signal(int signum, void (*handler)(int signum))

2.sigaction(int signum, struct sigaction* newact, sigaction* oldact)

sigaction act;
act.sa_handler = handler;
act.sa_flags = SA_SIGINFO;
// 注冊(cè)信號(hào)的處理函數(shù)
sigaction(SIGINT, act, NULL);

3.3、信號(hào)掩碼函數(shù)

1.sigprocmask(int how, struct sigaction* set,struct sigaction* oldset)

2.pthread_sigmask(int how, struct sigaction* set,struct sigaction* oldset)

sigprocmask用于設(shè)置進(jìn)程的信號(hào)掩碼,pthread_sigmask用于設(shè)置線程的信號(hào)掩碼,二者參數(shù)相同。第一個(gè)參數(shù)有SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK。

3.4、信號(hào)集合變量

sigset_t set

sigemptyset(&set) //清空阻塞信號(hào)集合變量

sigfillset(&set)  //添加所有的信號(hào)到阻塞集合變量里

sigaddset(&set,SIGINT) //添加單一信號(hào)到阻塞信號(hào)集合變量

sigdelset(&set,SIGINT) //從阻塞信號(hào)集合變量中刪除單一信號(hào)

sigismember(&set,int signum) //測(cè)試信號(hào)signum是否包含在信號(hào)集合set中,如果包含返回1,不包含返回0,出錯(cuò)返回-1。錯(cuò)誤代碼也只有一個(gè)EINVAL,表示signum不是有效的信號(hào)代碼。

3.5、信號(hào)屏蔽函數(shù)

1.int sigpending(sigset_t *set); // 返回阻塞的信號(hào)集

2.int sigsuspend(const sigset_t *mask);

sigsuspend表示臨時(shí)將信號(hào)屏蔽字設(shè)為mask,并掛起進(jìn)程直到有信號(hào)產(chǎn)生(非屏蔽信號(hào)才能喚醒或終止進(jìn)程),如果信號(hào)處理函數(shù)返回,那么siguspend將恢復(fù)之前的信號(hào)屏蔽字(temporarily)

假設(shè)sisuspend阻塞進(jìn)程時(shí)產(chǎn)生了信號(hào)A,且A不是mask內(nèi)的屏蔽信號(hào),那么A的信號(hào)處理函數(shù)有兩種情形,

一:直接終止進(jìn)程,此時(shí)進(jìn)程都不存在了,那么sigsuspend當(dāng)然無(wú)須返回了(不存在進(jìn)程了sigsuspend也不存在了,函數(shù)棧嘛);

二:如果信號(hào)A的處理函數(shù)返回,那么信號(hào)屏蔽字恢復(fù)到sigsuspend之前的(sigsuspend調(diào)用時(shí)將信號(hào)屏蔽字設(shè)為mask,所以要恢復(fù)到sigsuspend調(diào)用之前的),然后sigsuspend返回-1并將error置為EINTR.

以上就是淺談Linux信號(hào)機(jī)制的詳細(xì)內(nèi)容,更多關(guān)于Linux信號(hào)機(jī)制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

標(biāo)簽:懷化 黃山 煙臺(tái) 通遼 湘潭 賀州 湖北 山南

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《淺談Linux信號(hào)機(jī)制》,本文關(guān)鍵詞  ;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話(huà)咨詢(xún)

    • 400-1100-266