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