Linux進程間通信――使用信號
睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接
一、什么是信號
用過Windows的我們都知道,當我們無法正常結束一個程序時,可以用任務管理器強制結束這個進程,但這其實是怎么實現的呢?同樣的功能在Linux上是通過生成信號和捕獲信號來實現的,運行中的進程捕獲到這個信號然后作出一定的操作并最終被終止。
信號是UNIX和Linux系統響應某些條件而產生的一個事件,接收到該信號的進程會相應地采取一些行動。通常信號是由一個錯誤產生的。但它們還可以作為進程間通信或修改行為的一種方式,明確地由一個進程發送給另一個進程。一個信號的產生叫生成,接收到一個信號叫捕獲。
二、信號的種類
信號的名稱是在頭文件signal.h中定義的,信號都以SIG開頭,常用的信號并不多,常用的信號如下:
更多的信號類型可查看附錄表。
三、信號的處理——signal函數
程序可用使用signal函數來處理指定的信號,主要通過忽略和恢復其默認行為來工作。signal函數的原型如下:
[cpp] view plaincopyprint?
可以看到,第一次按下終止命令(ctrl+c)時,進程并沒有被終止,面是輸出OUCH! - I got signal 2,因為SIGINT的默認行為被signal函數改變了,當進程接受到信號SIGINT時,它就去調用函數ouch去處理,注意ouch函數把信號SIGINT的處理方式改變成默認的方式,所以當你再按一次ctrl+c時,進程就像之前那樣被終止了。
四、信號處理——sigaction函數
前面我們看到了signal函數對信號的處理,但是一般情況下我們可以使用一個更加健壯的信號接口——sigaction函數。它的原型為:
[cpp] view plaincopyprint?
此外,現在有一個這樣的問題,我們使用signal或sigaction函數來指定處理信號的函數,但是如果這個信號處理函數建立之前就接收到要處理的信號的話,進程會有怎樣的反應呢?它就不會像我們想像的那樣用我們設定的處理函數來處理了。sa_mask就可以解決這樣的問題,sa_mask指定了一個信號集,在調用sa_handler所指向的信號處理函數之前,該信號集將被加入到進程的信號屏蔽字中,設置信號屏蔽字可以防止信號在它的處理函數還未運行結束時就被接收到的情況,即使用sa_mask字段可以消除這一競態條件。
承接上面的例子,下面給出用sigaction函數重寫的例子代碼,源文件為signal2.c,代碼如下:
[cpp] view plaincopyprint?
在代碼中我們使用fork調用復制了一個新進程,在子進程中,5秒后向父進程中發送一個SIGALRM信號,父進程中捕獲這個信號,并用ouch函數來處理,變改alarm_fired的值,然后退出循環。從結果中我們也可以看到輸出了5個Hello World!之后,程序就收到一個SIGARLM信號,然后結束了進程。
注:如果父進程在子進程的信號到來之前沒有事情可做,我們可以用函數pause()來掛起父進程,直到父進程接收到信號。當進程接收到一個信號時,預設好的信號處理函數將開始運行,程序也將恢復正常的執行。這樣可以節省CPU的資源,因為可以避免使用一個循環來等待。以本例子為例,則可以把while循環改為一句pause();
下面再以一個小小的例子來說明alarm函數和pause函數的用法吧,源文件名為,signal4.c,代碼如下:
[cpp] view plaincopyprint?
進程在5秒后接收到一個SIGALRM,進程恢復運行,打印信息并退出。
六、信號處理函數的安全問題
試想一個問題,當進程接收到一個信號時,轉到你關聯的函數中執行,但是在執行的時候,進程又接收到同一個信號或另一個信號,又要執行相關聯的函數時,程序會怎么執行?
也就是說,信號處理函數可以在其執行期間被中斷并被再次調用。當返回到第一次調用時,它能否繼續正確操作是很關鍵的。這不僅僅是遞歸的問題,而是可重入的(即可以完全地進入和再次執行)的問題。而反觀Linux,其內核在同一時期負責處理多個設備的中斷服務例程就需要可重入的,因為優先級更高的中斷可能會在同一段代碼的執行期間“插入”進來。
簡言之,就是說,我們的信號處理函數要是可重入的,即離開后可再次安全地進入和再次執行,要使信號處理函數是可重入的,則在信息處理函數中不能調用不可重入的函數。下面給出可重入的函數在列表,不在此表中的函數都是不可重入的,可重入函數表如下:
七、附錄——信號表
如果進程接收到上面這些信號中的一個,而事先又沒有安排捕獲它,進程就會終止。
還有其他的一些信號,如下:
- #include <signal.h>
- void (*signal(int sig, void (*func)(int)))(int);
- #include <signal.h>
- #include <stdio.h>
- #include <unistd.h>
- void ouch(int sig)
- {
- printf("\nOUCH! - I got signal %d\n", sig);
- //恢復終端中斷信號SIGINT的默認行為
- (void) signal(SIGINT, SIG_DFL);
- }
- int main()
- {
- //改變終端中斷信號SIGINT的默認行為,使之執行ouch函數
- //而不是終止程序的執行
- (void) signal(SIGINT, ouch);
- while(1)
- {
- printf("Hello World!\n");
- sleep(1);
- }
- return 0;
- }

- #include <signal.h>
- int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
- #include <unistd.h>
- #include <stdio.h>
- #include <signal.h>
- void ouch(int sig)
- {
- printf("\nOUCH! - I got signal %d\n", sig);
- }
- int main()
- {
- struct sigaction act;
- act.sa_handler = ouch;
- //創建空的信號屏蔽字,即不屏蔽任何信息
- sigemptyset(&act.sa_mask);
- //使sigaction函數重置為默認行為
- act.sa_flags = SA_RESETHAND;
- sigaction(SIGINT, &act, 0);
- while(1)
- {
- printf("Hello World!\n");
- sleep(1);
- }
- return 0;
- }
- #include <sys/types.h>
- #include <signal.h>
- int kill(pid_t pid, int sig);
- #include <unistd.h>
- unsigned int alarm(unsigned int seconds);
- #include <unistd.h>
- #include <sys/types.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <signal.h>
- static int alarm_fired = 0;
- void ouch(int sig)
- {
- alarm_fired = 1;
- }
- int main()
- {
- pid_t pid;
- pid = fork();
- switch(pid)
- {
- case -1:
- perror("fork failed\n");
- exit(1);
- case 0:
- //子進程
- sleep(5);
- //向父進程發送信號
- kill(getppid(), SIGALRM);
- exit(0);
- default:;
- }
- //設置處理函數
- signal(SIGALRM, ouch);
- while(!alarm_fired)
- {
- printf("Hello World!\n");
- sleep(1);
- }
- if(alarm_fired)
- printf("\nI got a signal %d\n", SIGALRM);
- exit(0);
- }

- #include <unistd.h>
- #include <sys/types.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <signal.h>
- static int alarm_fired = 0;
- void ouch(int sig)
- {
- alarm_fired = 1;
- }
- int main()
- {
- //關聯信號處理函數
- signal(SIGALRM, ouch);
- //調用alarm函數,5秒后發送信號SIGALRM
- alarm(5);
- //掛起進程
- pause();
- //接收到信號后,恢復正常執行
- if(alarm_fired == 1)
- printf("Receive a signal %d\n", SIGALRM);
- exit(0);
- }

RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成