標籤:style class blog c code java
今天繼續探討訊號相關的東東,話不多說,正入正題:
訊號在核心中的表示:下面用圖來進一步描述這種訊號從產生到遞達之間的狀態(
訊號阻塞與未訣): 那是怎麼來決定的呢?下面慢慢來舉例分解:所以,通過這些圖,可以描述訊號從產生到遞達的一個過程,上面的理解起來可能有點難,下面會用代碼來進一步闡述,在進行實驗之前,還需瞭解一些函數的使用,這些函數在實驗中都會被用到,也就是訊號集操作函數。
訊號集操作函數:其中解釋一下sigset_t,百度百科解釋為:而這個函數的意義就是將這64位清0這個函數的意義是將這屏蔽字的64位都變為1將這個訊號所對應的位置為1將這個訊號所對應的位置為0檢測這一個訊號所對應的位當前是0還是1 以上是操作訊號集的五個相關的函數,但是注意:
這五個函數僅僅是改變這個訊號集變數,如set,並非真正改變進程訊號當中的屏蔽字,所以接下來介紹的函數就是改變訊號當中的屏蔽字的
sigprocmask:好了,理論說了很多,下面正式開始實驗,來體會一下一個訊號從產生到遞達的一個狀態轉換過程:首先,我們列印出系統的未訣訊號,目的是為了觀察之後的狀態:
#include <unistd.h>#include <sys/stat.h>#include <sys/wait.h>#include <sys/types.h>#include <fcntl.h>#include <stdlib.h>#include <stdio.h>#include <errno.h>#include <string.h>#include <signal.h>#define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); } while(0)void handler(int sig);void printsigset(sigset_t *set)//列印出訊號集的狀態,其中參數set為未訣狀態的訊號集{ int i; for (i=1; i<NSIG; ++i)//NSIG表示訊號的最大值,也就是等於64 { if (sigismember(set, i))//說明是未訣狀態的訊號 putchar(‘1‘); else putchar(‘0‘);//說明不是未訣狀態的訊號 } printf("\n");}int main(int argc, char *argv[]){ sigset_t pset; for (;;) { sigpending(&pset);//該函數是擷取進程當中未訣狀態的訊號集 ,儲存在pset當中 printsigset(&pset);//列印訊號集的狀態,看有沒有未訣狀態的訊號產生 sleep(1); } return 0;}
【說明】:sigpending是用來擷取進程中所有的未訣訊號集:
這時看一下運行效果:
可以發現,目前狀態沒有未訣的訊號,因為還沒有被阻塞的訊號過,訊號也沒有產生過,所以不可能有未訣的狀態。
這時,我們來安裝一個SIGINT訊號:
#include <unistd.h>#include <sys/stat.h>#include <sys/wait.h>#include <sys/types.h>#include <fcntl.h>#include <stdlib.h>#include <stdio.h>#include <errno.h>#include <string.h>#include <signal.h>#define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); } while(0)void handler(int sig);void printsigset(sigset_t *set){ int i; for (i=1; i<NSIG; ++i) { if (sigismember(set, i)) putchar(‘1‘); else putchar(‘0‘); } printf("\n");}int main(int argc, char *argv[]){ sigset_t pset; if (signal(SIGINT, handler) == SIG_ERR)//安裝一個SIGINT訊號 ERR_EXIT("signal error"); for (;;) { sigpending(&pset); printsigset(&pset); sleep(1); } return 0;}void handler(int sig){ printf("recv a sig=%d\n", sig);}
這時再看下效果:
從結果來看,訊號被直接遞達了,所以這次也沒有看到有1的未訣狀態的訊號,因為訊號必須被阻塞才會出現未訣狀態,所以接下來將SIGINT訊號利用上面介紹到的函數來將其阻塞掉:
#include <unistd.h>#include <sys/stat.h>#include <sys/wait.h>#include <sys/types.h>#include <fcntl.h>#include <stdlib.h>#include <stdio.h>#include <errno.h>#include <string.h>#include <signal.h>#define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); } while(0)void handler(int sig);void printsigset(sigset_t *set){ int i; for (i=1; i<NSIG; ++i) { if (sigismember(set, i)) putchar(‘1‘); else putchar(‘0‘); } printf("\n");}int main(int argc, char *argv[]){ sigset_t pset; sigset_t bset; sigemptyset(&bset);//將訊號集清0 sigaddset(&bset, SIGINT);//將SIGINT所對應的位置1 if (signal(SIGINT, handler) == SIG_ERR) ERR_EXIT("signal error"); sigprocmask(SIG_BLOCK, &bset, NULL);//更改進程中的訊號屏蔽字,其中第三個參數傳NULL,因為不關心它原來的訊號屏蔽字 for (;;) { sigpending(&pset); printsigset(&pset); sleep(1); } return 0;}void handler(int sig){ printf("recv a sig=%d\n", sig);}
編譯運行:
從結果來看,將SIGINT訊號來了,由於添加到了訊號屏蔽字為1,所以會被阻塞掉,並且可以看到SIGINT對應的位也列印為1了。
【說明】:SIGINT對應的位是指:
下面,我們做一件事情,就是當我們按下ctrl+\解除阻塞,這樣處於未訣狀態的訊號就會被遞達,則對應的未訣狀態位也會還原成0,具體代碼如下:
#include <unistd.h>#include <sys/stat.h>#include <sys/wait.h>#include <sys/types.h>#include <fcntl.h>#include <stdlib.h>#include <stdio.h>#include <errno.h>#include <string.h>#include <signal.h>#define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); } while(0)void handler(int sig);void printsigset(sigset_t *set){ int i; for (i=1; i<NSIG; ++i) { if (sigismember(set, i)) putchar(‘1‘); else putchar(‘0‘); } printf("\n");}int main(int argc, char *argv[]){ sigset_t pset; sigset_t bset; sigemptyset(&bset); sigaddset(&bset, SIGINT); if (signal(SIGINT, handler) == SIG_ERR) ERR_EXIT("signal error"); if (signal(SIGQUIT, handler) == SIG_ERR)//註冊一個ctrl+c訊號 ERR_EXIT("signal error"); sigprocmask(SIG_BLOCK, &bset, NULL); for (;;) { sigpending(&pset); printsigset(&pset); sleep(1); } return 0;}void handler(int sig){ if (sig == SIGINT) printf("recv a sig=%d\n", sig); else if (sig == SIGQUIT) { sigset_t uset;//當按下ctrl+\時,則對SIGINT訊號解除阻塞 sigemptyset(&uset); sigaddset(&uset, SIGINT); sigprocmask(SIG_UNBLOCK, &uset, NULL); }}
編譯運行:
從中可以看到,當我們按下ctrl+\時,並沒有退出,而是解除了阻塞,所以對應的SIGINT位也變為0了。
另外,看下這種情況:
多次按了ctrl+c,可在按ctrl+\解除阻塞時,只響應了一次訊號處理函數,這也由於SIGINT是不可靠訊號,不支援排隊。
另外,由於我們捕獲了ctrl+\訊號,所以沒辦法退出這個進程了,那怎麼辦呢,可以利用shell命令將其強制殺掉如下:
好了,今天學的東西可能有些生澀,需好好消化,下節再見!