二 Linux進程間通信( 六 )

運行結果如下:

二 Linux進程間通信

文章插圖
代碼示例:舊的信號處理函數
sa_flags標志為0代表使用的是舊的信號處理函數
#include <stdio.h>#include <unistd.h>#include <signal.h>void handler(int signo){printf("catch a signal: %d\n", signo);}int main(){int ret = -1;struct sigaction act;//標志為0,代表使用的是舊的信號處理函數指針act.sa_flags = 0;//給阻塞集初始化sigfillset(&act.sa_mask);act.sa_handler = handler;// 信號注冊ret =sigaction(SIGINT, &act, NULL);if(ret == -1){perror("sigaction");return 1;}printf("按下任意鍵退出.....\n");getchar();return 0;}運行結果如下:
二 Linux進程間通信

文章插圖
代碼示例:新的信號處理函數
#include <stdio.h>#include <unistd.h>#include <signal.h>void handler(int signo,siginfo_t *info,void *context){printf("catch a signal: %d\n", signo);}int main(){int ret = -1;struct sigaction act;//使用新的信號處理函數指針act.sa_flags = 0;//給阻塞集初始化sigfillset(&act.sa_mask);act.sa_handler = handler;// 信號注冊ret =sigaction(SIGINT, &act, NULL);if(ret == -1){perror("sigaction");return 1;}printf("按下任意鍵退出.....\n");getchar();return 0; }
二 Linux進程間通信

文章插圖
不可重入函數和可重入函數先看下面一段代碼:
#include <stdio.h>#include <signal.h>int a = 10;void SelfAdd(int n){ a = a + n; a = a + n;}void handler(int signo){ SelfAdd(signo);}int main(){ signal(2, handler); SlefAdd(2); printf("%d\n", a); return 0;}上面我寫了一個比較簡單的代碼,我們慢慢分析,當我們在主函數中執行調用SelfAdd時 , 進入該函數,執行完函數中int a = a + n這句代碼后,a變成了12 , 此時收到2號信號,發生中斷
二 Linux進程間通信

文章插圖
最后打印a結果是16,其實正常調用該函數的話,打印的應該是18 。像上面這樣的因為重入導致結果錯亂的函數就叫做不可重入函數 。其中a是一個全局變量 。如果一個函數值訪問自己的局部變量或參數,那么這樣的函數就叫做可重入函數 。
說的通俗點,不可重入的意思是,如果你定義了一個全局變量,在函數1里面這個變量應該是10,但是有一個函數2改變了這個變量的值,此時本來函數1用的是10  , 你把他改變了,這就是不安全的,這就是不可重入函數 。
思考一個問題:為什么兩個不同的控制流程調用同一個函數,訪問同一個局部變量或參數不會造成錯亂?
在多線程中,每個線程雖然是資源共享,但是他們的棧卻是獨有的,所以說局部變量不會造成錯亂 。
如果一個函數符合以下條件之一則是不可重入的:
  • 調用了malloc或free,因為malloc也是用全局鏈表來管理堆的 。
  • 調用了標準I/O庫函數 。標準I/O庫的很多實現都以不可重入的方式使用全局數據結構 。
  • 函數體內使用了靜態的數據結構 。
保證函數可重入性的方法:
  • 在寫函數的時候盡量使用局部變量 。(例如寄存器和棧中的變量)
  • 對于要使用的全局變量要加以保護(采取中斷、信號量互斥的方法),這樣構成的函數就一定是可重入函數 。
使用信號避免僵尸進程SIGCHLD信號
產生條件:1)子進程終止時
?2)子進程接收到SIGSTOP信號停止的時候
?3)子進程處于停止態,接收到SIGCONT后喚醒時
如何避免僵尸進程:
1)最簡單的方法 , 父進程通過wait和waitpid等待子進程函數結束,但是,這會導致父進程掛起 。
2)如果父進程要處理的事情很多,不能掛起,通過signal()函數人為處理信號SIGCHLD , 只有在子進程退出自動調用制定好的回調函數,因為子進程結束后,父進程會收到信號SIGCHLD,可以在回調函數里面用wait或waitpid回收資源 。
#include <stdio.h>#include <unistd.h>#include <signal.h>#include<sys/types.h>#include<sys/wait.h>void sig_child(int signo){ pid_t pid; //處理僵尸進程,-1代表等待任意一個子進程,WNOHANG代表不阻塞 while((pid=waitpid(-1,NULL,WNOHANG))>0) {printf("孩子進程被殺死 %d\n",pid); }}int main(){pid_t pid;//創建捕捉子進程退出信號//只要子進程退出,觸發SIGSIGCHLD,自動調用sig_child()signal(SIGCHLD,sig_child());//創建進程pid = fork();if(pid<0){perror("fork");exit(1);}else if(pid == 0){//子進程printf("我是子進程,pid id :%d.我正在退出\n",getpid());exit(0);}else if(pid>0){//父進程sleep(2);//保證子進程先運行printf("我是父親,我正在退出\n");system("ps -ef|grep defunct");//查看有沒有僵尸進程}return 0;}

推薦閱讀