信號實現進程間的通訊
3.5.1.什麼是信號
3.5.1.一、信號是內容受限(只是一個int型的數字)的一種異步通訊機制
(1)信號的目的:用來通訊(進程與進程之間的通訊)
(2)信號是異步的(對比硬件中斷),信號好像就是一種軟件中斷。
(3)信號本質上是int型數字編號(事先定義好的)
3.5.1.二、信號由誰發出
(1)用戶在終端按下按鍵
(2)硬件異常後由操做系統內核發出信號
(3)用戶使用kill命令向其餘進程發出信號
(4)某種軟件條件知足後也會發出信號,如alarm鬧鐘時間到會產生SIGALARM信號,向一個讀端已經關閉的管道write時會產生SIGPIPE信號
3.5.1.三、信號由誰處理、如何處理
(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.一、signal函數介紹
signal()函數理解
在<signal.h> 這個頭文件中。
signal(參數1,參數2);
參數1:咱們要進行處理的信號。系統的信號咱們能夠再終端鍵入 kill -l查看(共64個)。其實這些信號就是系統定義的宏。
root@ubuntu:/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.二、用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.三、signal函數的優勢和缺點
(1)優勢:簡單好用,捕獲信號經常使用
(2)缺點:沒法簡單直接得知以前設置的對信號的處理方法
3.5.3.四、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.一、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.二、pause函數
(1)內核掛起
(2)代碼實踐
pause函數的做用就是讓當前進程暫停運行,交出CPU給其餘進程去執行。噹噹前進程進入pause狀態後當前進程會表現爲「卡住、阻塞住」,要退出pause狀態當前進程須要被信號喚醒。
3.5.4.三、使用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();
}