在Linux中,信號(hào)是進(jìn)程間通訊的一種方式,它采用的是異步機(jī)制。當(dāng)信號(hào)發(fā)送到某個(gè)進(jìn)程中時(shí),操作系統(tǒng)會(huì)中斷該進(jìn)程的正常流程,并進(jìn)入相應(yīng)的信號(hào)處理函數(shù)執(zhí)行操作,完成后再回到中斷的地方繼續(xù)執(zhí)行。 需要說(shuō)明的是,信號(hào)只是用于通知進(jìn)程發(fā)生了某個(gè)事件,除了信號(hào)本身的信息之外,并不具備傳遞用戶數(shù)據(jù)的功能。 1 信號(hào)的響應(yīng)動(dòng)作每個(gè)信號(hào)都有自己的響應(yīng)動(dòng)作,當(dāng)接收到信號(hào)時(shí),進(jìn)程會(huì)根據(jù)信號(hào)的響應(yīng)動(dòng)作執(zhí)行相應(yīng)的操作,信號(hào)的響應(yīng)動(dòng)作有以下幾種:
用戶可以通過(guò) 2 信號(hào)類型Linux支持的信號(hào)類型可以參考下面給出的列表。 2.1 在POSIX.1-1990標(biāo)準(zhǔn)中的信號(hào)列表
注:其中 2.2 在SUSv2和POSIX.1-2001標(biāo)準(zhǔn)中的信號(hào)列表
注:在Linux 2.2版本之前, 2.3 其它信號(hào)
注意:列表中有的信號(hào)有三個(gè)值,這是因?yàn)椴糠中盘?hào)的值和CPU架構(gòu)有關(guān),這些信號(hào)的值在不同架構(gòu)的CPU中是不同的,三個(gè)值的排列順序?yàn)椋?,Alpha/Sparc;2,x86/ARM/Others;3,MIPS。 例如 3 信號(hào)機(jī)制文章的前面提到過(guò),信號(hào)是異步的,這就涉及信號(hào)何時(shí)接收、何時(shí)處理的問(wèn)題。 我們知道,函數(shù)運(yùn)行在用戶態(tài),當(dāng)遇到系統(tǒng)調(diào)用、中斷或是異常的情況時(shí),程序會(huì)進(jìn)入內(nèi)核態(tài)。信號(hào)涉及到了這兩種狀態(tài)之間的轉(zhuǎn)換,過(guò)程可以先看一下下面的示意圖: 接下來(lái)圍繞示意圖,將信號(hào)分成接收、檢測(cè)和處理三個(gè)部分,逐一講解每一步的處理流程。 3.1 信號(hào)的接收接收信號(hào)的任務(wù)是由內(nèi)核代理的,當(dāng)內(nèi)核接收到信號(hào)后,會(huì)將其放到對(duì)應(yīng)進(jìn)程的信號(hào)隊(duì)列中,同時(shí)向進(jìn)程發(fā)送一個(gè)中斷,使其陷入內(nèi)核態(tài)。 注意,此時(shí)信號(hào)還只是在隊(duì)列中,對(duì)進(jìn)程來(lái)說(shuō)暫時(shí)是不知道有信號(hào)到來(lái)的。 3.2 信號(hào)的檢測(cè)進(jìn)程陷入內(nèi)核態(tài)后,有兩種場(chǎng)景會(huì)對(duì)信號(hào)進(jìn)行檢測(cè):
當(dāng)發(fā)現(xiàn)有新信號(hào)時(shí),便會(huì)進(jìn)入下一步,信號(hào)的處理。 3.3 信號(hào)的處理信號(hào)處理函數(shù)是運(yùn)行在用戶態(tài)的,調(diào)用處理函數(shù)前,內(nèi)核會(huì)將當(dāng)前內(nèi)核棧的內(nèi)容備份拷貝到用戶棧上,并且修改指令寄存器(eip)將其指向信號(hào)處理函數(shù)。 接下來(lái)進(jìn)程返回到用戶態(tài)中,執(zhí)行相應(yīng)的信號(hào)處理函數(shù)。 信號(hào)處理函數(shù)執(zhí)行完成后,還需要返回內(nèi)核態(tài),檢查是否還有其它信號(hào)未處理。如果所有信號(hào)都處理完成,就會(huì)將內(nèi)核?;謴?fù)(從用戶棧的備份拷貝回來(lái)),同時(shí)恢復(fù)指令寄存器(eip)將其指向中斷前的運(yùn)行位置,最后回到用戶態(tài)繼續(xù)執(zhí)行進(jìn)程。 至此,一個(gè)完整的信號(hào)處理流程便結(jié)束了,如果同時(shí)有多個(gè)信號(hào)到達(dá),上面的處理流程會(huì)在第2步和第3步驟間重復(fù)進(jìn)行。 4 信號(hào)的使用4.1 發(fā)送信號(hào)用于發(fā)送信號(hào)的函數(shù)有 raise函數(shù):向進(jìn)程本身發(fā)送信號(hào) 函數(shù)聲明如下: #include <signal.h> int raise(int sig); 函數(shù)功能是向當(dāng)前程序(自身)發(fā)送信號(hào),其中參數(shù) kill函數(shù):向指定進(jìn)程發(fā)送信號(hào) 函數(shù)聲明如下: #include <sys/types.h> #include <signal.h> int kill(pid_t pid, int sig); 函數(shù)功能是向特定的進(jìn)程發(fā)送信號(hào),其中參數(shù) 在這里的參數(shù)
另外,當(dāng) 4.2 等待信號(hào)被捕獲等待信號(hào)的過(guò)程,其實(shí)就是將當(dāng)前進(jìn)程(線程)暫停,直到有信號(hào)發(fā)到當(dāng)前進(jìn)程(線程)上并被捕獲,函數(shù)有 pause函數(shù):將進(jìn)程(或線程)轉(zhuǎn)入睡眠狀態(tài),直到接收到信號(hào) 函數(shù)聲明如下: #include <unistd.h> int pause(void); 該函數(shù)調(diào)用后,調(diào)用者(進(jìn)程或線程)會(huì)進(jìn)入睡眠(Sleep)狀態(tài),直到捕獲到(任意)信號(hào)為止。該函數(shù)的返回值始終為-1,并且調(diào)用結(jié)束后,錯(cuò)誤代碼(errno)會(huì)被置為EINTR。 sigsuspend函數(shù):將進(jìn)程(或線程)轉(zhuǎn)入睡眠狀態(tài),直到接收到特定信號(hào) 函數(shù)聲明如下: #include <signal.h> int sigsuspend(const sigset_t *mask); 該函數(shù)調(diào)用后,會(huì)將進(jìn)程的信號(hào)掩碼臨時(shí)修改(參數(shù) 4.3 修改信號(hào)的響應(yīng)動(dòng)作用戶可以自己重新定義某個(gè)信號(hào)的處理方式,即前面提到的修改信號(hào)的默認(rèn)響應(yīng)動(dòng)作,也可以理解為對(duì)信號(hào)的注冊(cè),可以通過(guò) 首先看一下函數(shù)聲明: #include <signal.h> typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); 第一個(gè)參數(shù) 下面為示例代碼: #include <stdio.h> #include <signal.h> #include <unistd.h> /* 信號(hào)處理函數(shù) */ void sig_callback(int signum) { switch (signum) { case SIGINT: /* SIGINT: Ctrl+C 按下時(shí)觸發(fā) */ printf("Get signal SIGINT. \r\n"); break; /* 多個(gè)信號(hào)可以放到同一個(gè)函數(shù)中進(jìn)行 通過(guò)信號(hào)值來(lái)區(qū)分 */ default: /* 其它信號(hào) */ printf("Unknown signal %d. \r\n", signum); break; } return; } /* 主函數(shù) */ int main(int argc, char *argv[]) { printf("Register SIGINT(%u) Signal Action. \r\n", SIGINT); /* 注冊(cè)SIGINT信號(hào)的處理函數(shù) */ signal(SIGINT, sig_callback); printf("Waitting for Signal ... \r\n"); /* 等待信號(hào)觸發(fā) */ pause(); printf("Process Continue. \r\n"); return 0; } 源文件下載:鏈接 例子中,將 ./linux_signal_example Register SIGINT(2) Signal Action. Waitting for Signal ... ^CGet signal SIGINT. Process Continue. 進(jìn)程收到 |
|