1. 程式人生 > >(50)LINUX應用編程和網絡編程之五 Linux信號(進程間通信)

(50)LINUX應用編程和網絡編程之五 Linux信號(進程間通信)

sign 變量類型 {0} 退出 指向 示例 div 失效 key

信號實現進程間的通信 3.5.1.什麽是信號 3.5.1.1、信號是內容受限(只是一個int型的數字)的一種異步通信機制 (1)信號的目的:用來通信(進程與進程之間的通信) (2)信號是異步的(對比硬件中斷),信號好像就是一種軟件中斷。 (3)信號本質上是int型數字編號(事先定義好的) 3.5.1.2、信號由誰發出 (1)用戶在終端按下按鍵 (2)硬件異常後由操作系統內核發出信號 (3)用戶使用kill命令向其他進程發出信號 (4)某種軟件條件滿足後也會發出信號,如alarm鬧鐘時間到會產生SIGALARM信號,向一個讀端已經關閉的管道write時會產生SIGPIPE信號
3.5.1.3、信號由誰處理、如何處理 (1)忽略信號 (2)捕獲信號(信號綁定了一個函數) (3)默認處理(當前進程沒有明顯的管這個信號,默認:忽略或終止進程) 3.5.2.常見信號介紹(信號的名字 ) 信號宏 num 信號對應的作用 (1)【SIGINT 】 2 Ctrl+C時OS送給【前臺】進程組中【每個】進程 (2)SIGABRT 6 調用abort函數,進程異常終止
(3)【SIGPOLL / SIGIO 】 8 指示一個異步IO事件,在高級IO中提及 (4)【SIGKILL】 9 殺死進程的終極辦法 (5)SIGSEGV 11 無效存儲訪問時OS發出該信號 (6)【SIGPIPE 】 13 涉及(異步通信的)管道和socket
(7)【SIGALARM】 14 涉及alarm函數的實現 (8)SIGTERM 15 kill命令發送的OS默認終止信號 (9)【SIGCHLD 】 17 子進程終止或停止時OS向其父進程發此信號等待父進程回收 以上所有信號的作用都是事先定義好的。 (10) SIGUSR1 10 用戶自定義信號,作用和意義由應用自己定義 SIGUSR2 12 這兩個名稱是預先已經定義好了的,但是作用是用戶自己定義的。 3.5.3.進程對信號的處理 3.5.3.1、signal函數介紹 signal()函數理解 在<signal.h> 這個頭文件中。 signal(參數1,參數2); 參數1:我們要進行處理的信號。系統的信號我們可以再終端鍵入 kill -l查看(共64個)。其實這些信號就是系統定義的宏。 [email protected]:/mnt/hgfs/Winshare/1.Linux應用編程和網絡編程/7.thread# kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX 參數2:我們處理這些信號的方式(是系統默認還是忽略還是捕獲)。 一般有3中方式進行操作。 1)eg: signal(SIGINT ,SIG_IGN ); //忽略處理 //SIG_ING 代表忽略SIGINT信號,SIGINT信號代表由InterruptKey產生,通常是CTRL +C 或者是DELETE 。發送給所有ForeGround Group的進程。 2)eg: signal(SIGINT ,SIG_DFL ); //默認處理(當前進程沒有明顯的管這個信號,默認:忽略或終止進程) //SIGINT信號代表由InterruptKey產生,通常是CTRL +C或者是DELETE。發送給所有ForeGroundGroup的進程。 SIG_DFL代表執行系統默認操作,其實對於大多數信號的系統默認動作時終止該進程。這與不寫此處理函數是一樣的。 3)捕獲信號並處理(信號綁定了一個函數) 3.5.3.2、用signal函數處理SIGINT信號 (1)忽略處理 #include<stdio.h> #include<signal.h> int main(void) { signal(SIGINT ,SIG_IGN ); while(1) { printf("hello world!\n"); sleep(1); } return 0; } (2)默認處理 signal(SIGINT ,SIG_DFL ) #include<stdio.h> #include<signal.h> int main(void) { signal(SIGINT ,SIG_DFL ); while(1) { printf("hello world!\n"); sleep(1); } return 0; } ----------------------------------------------------------------------------------------------------------------------------------------------------------- (3)捕獲處理 signal函數原型: #include <signal.h> typedef void (*sighandler_t)(int); //typedef sighandler_t這個函數指針變量類型,重命名一種類型 sighandler_t signal(int signum, sighandler_t handler);//第二個參數是一個函數指針類型的變量,將來傳進去一個函數名;返回一個函數指針。 細節: (1)signal函數綁定一個捕獲函數後信號發生後會自動執行綁定的捕獲函數,並且把信號編號s數字作為傳參傳給捕獲函數 (2)signal的返回值在出錯時為SIG_ERR,綁定成功時返回指向我們自定義的捕獲函數的函數指針。 (3)signal()函數(它自己是帶兩個參數,一個整型的信號編號,以及一個指向用戶自定義的信號處理函數的指針。),而這個signal()函數的返回值也為一個函數指針,【這個函數指針指向一個帶一個整型參數,並且返回值為void的一個函數.】(也就是說signal()函數的返回值和我們向它裏面傳遞的第二個參數的類型是一樣的,都是一個函數指針類型的,且這個函數指針指向的函數的返回值為void,參數為int)【相當於我們在signal函數的兩個接收參數中第二個參數是向系統中【綁定】這個要執行的函數】,等到我們第一個參數中註冊的信號真的發生了之後,我們signal函數的返回值就指向了我們用戶自定義的函數去執行它。 (4)signal函數的返回值,它的返回值為一個函數指針,如果signal函數執行錯誤,則返回SIG_ERR,如果執行成功則返回一個指向自定義函數的函數指針。 (5)等於說我們在第二個參數中是向內核註冊(綁定)我們的自定義函數,而當信號真的發生後,返回一個指向我們自定義函數的函數指針,是去執行我們的綁定的自定義函數。 代碼示例: #include <stdio.h> #include <signal.h> #include <stdlib.h> typedef void (*sighandler_t)(int); //重新聲明一個函數指針sighandler_t void func(int sig) { if (SIGINT == sig) printf("func for signal: %d.\n", sig); exit(-1) ; } int main(void) { sighandler_t ret ; //ret為一個函數指針變量,指向一個函數 ret=signal(SIGINT, func); //相當於先向系統中註冊這個函數,SIGINT是Ctrl+C時OS送給【前臺】進程組中【每個】進程 //signal(SIGINT, SIG_DFL); // 指定信號SIGINT為默認處理 //ret = signal(SIGINT, SIG_IGN); // 指定信號SIGINT為忽略處理 if (SIG_ERR == ret) { perror("signal:"); exit(-1); } printf("before loop\n"); while(1) { printf("hello world!\n"); sleep(1); } printf("after loop\n"); return 0; } 3.5.3.3、signal函數的優點和缺點 (1)優點:簡單好用,捕獲信號常用 (2)缺點:無法簡單直接得知之前設置的對信號的處理方法 3.5.3.4、sigaction函數介紹 #include <signal.h> int sigaction(int sig, const struct sigaction *act,struct sigaction *oact); (1)2個都是API,但是sigaction比signal更具有可移植性 (2)用法關鍵是sigaction函數的兩個指針參數 sigaction比signal好的一點:sigaction可以一次得到設置新捕獲函數和獲取舊的捕獲函數(其實還可以單獨設置新的捕獲或者單獨只獲取舊的捕獲函數),而signal函數不能單獨獲取舊的捕獲函數而必須在設置新的捕獲函數的同時才獲取舊的捕獲函數。 sigaction代碼示例: #include <stdio.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> void func(int sig) { if(SIGINT!=sig) return ; printf("信號num是:%d\n",sig); } int main(void) { struct sigaction act; act.sa_handler=func; int res=-1; res=sigaction(SIGINT,&act,NULL); if(res==-1) { perror("sigaction"); _exit(-1); } printf("before loop:\n"); while(1) { printf("hello world\n"); sleep(1); } return 0; } 3.5.4.alarm和pause函數 3.5.4.1、alarm函數:主要是在設定秒數後向signal發送信號,然後執行綁定函數;alarm()函數用於在系統中設置一個定時器,在定時器到時後會向進程發送SIGALRM信號。SIGALRM信號的 默認處理方式是終止進程。 (1)內核以API形式提供的鬧鐘(內核幫一個進程只維護一個alarm時鐘) (2)編程實踐 所需頭文件   #include<unistd.h> 函數原型   unsigned int alarm(unsigned int seconds) 函數參數   seconds:指定秒數 函數返回值   成功:如果調用此alarm()前,進程已經設置了鬧鐘時間,則返回上一個鬧鐘時間的剩余時間,否則返回0。   出錯:-1 註意:alarm只設定一個鬧鐘,時間到達並執行其註冊函數之後,鬧鐘便失效。【如果想循環設置鬧鐘,需在其註冊函數(hander函數中)再調用alarm函數。】 代碼示例: #include<unistd.h> #include<signal.h> #include<stdio.h> void handler() { printf("Hello。。。。。\n"); } void main() { int i; signal(SIGALRM, handler); //讓內核做好準備,一旦接受到SIGALARM信號,就執行 handler alarm(5);//在5秒過後執行handler函數 for(i=1;i<21;i++) { printf("sleep %d ...\n",i); sleep(1); } } 3.5.4.2、pause函數 (1)內核掛起 (2)代碼實踐 pause函數的作用就是讓當前進程暫停運行,交出CPU給其他進程去執行。當當前進程進入pause狀態後當前進程會表現為“卡住、阻塞住”,要退出pause狀態當前進程需要被信號喚醒。 3.5.4.3、使用alarm和pause來模擬sleep pause函數代碼示例: #include <unistd.h> #include <stdio.h> #include <signal.h> void sigroutine(int unused) { printf("Catch a signal SIGINT \n"); } int main() { signal(SIGINT, sigroutine);//SIGINT 程序終止(interrupt)信號, 在用戶鍵入INTR字符(通常是Ctrl-C)時發出 pause();//在沒有收到任何的信號的時候,pause相當於一個死循環,當收到任何一個信號的時候,就執行下一句 sleep(3); printf("receive a signal \n"); } 使用alarm和pause來模擬sleep代碼示例: #include <stdio.h> #include <unistd.h> // unix standand #include <signal.h> void func(int sig) { /* if (sig == SIGALRM) { printf("alarm happened.\n"); } */ } void mysleep(unsigned int seconds); int main(void) { printf("before mysleep.\n"); mysleep(3); printf("after mysleep.\n"); /* unsigned int ret = -1; struct sigaction act = {0}; act.sa_handler = func; sigaction(SIGALRM, &act, NULL); //signal(SIGALRM, func); ret = alarm(5); printf("1st, ret = %d.\n", ret); sleep(3); ret = alarm(5); // 返回值是2但是本次alarm會重新定5s printf("2st, ret = %d.\n", ret); sleep(1); ret = alarm(5); printf("3st, ret = %d.\n", ret); //while (1); pause(); */ return 0; } void mysleep(unsigned int seconds) { struct sigaction act = {0}; act.sa_handler = func; sigaction(SIGALRM, &act, NULL); alarm(seconds); pause(); }

(50)LINUX應用編程和網絡編程之五 Linux信號(進程間通信)