原文地址:進程間通訊--信號(進程間通訊惟一的異步方式) 做者:Deem_passionhtml
1、信號的介紹 linux
信號是在軟件層次上對中斷機制的一種模擬,是一種異步通訊方式。異步
信號能夠直接進行用戶空間進程和內核進程之間的交互,內核進程也能夠利用它來通知用戶空間進程發生了那些系統事件。ide
若是該進程當前並未處於執行態,則該信號就由內核保存起來,直到該進程恢復執行再傳遞個它;若是一個信號被進程設置爲阻塞,則該信號的傳遞被延遲,直到其阻塞取消時才被傳遞給進程。函數
2、linux操做系統支持的信號oop
3、信號的產生spa
A.用戶在終端按下某些鍵時,終端驅動程序會發送信號給前臺進程,例如ctr+c產生SIGINT, ctr + \產生SIGQUI信號,ctr + z產生SIGTSTP。操作系統
B.硬件異常產生信號,這些條件由硬件檢測到並通知內核,而後內核向當前進程發送適當的信號。例如當前進程執行了除以0的指令,CPU的運算單元會產生異常,內核將這個異常解釋爲SIGFPE信號發送給進程。再好比當前進程訪問了非法內存地址,MMU會產生異常,內核將這個異常解釋爲SIGSEGV信號發送給當前進程 。
C.一個進程調用int kill(pid_t pid,int sig)函數能夠給另外一個進程發送信號
D.能夠用kill命令給某個進程發送信號,若是不明確指定信號則發送SIGTERM信號,該信號的默認處理動做是終止進程。
E.當內核檢測到某種軟件條件發生時也能夠經過信號通知進程,例如鬧鐘超時產生SIGALRM信號,向讀端已關閉的管道寫數據時產生SIGPIPE信號。
4、進程對信號的處理
A.忽略此信號
B.執行該信號的默認處理動做
C.提供一個信號處理函數,要求內核在處理該信號時切換到用戶態執行這個處理函數,這種方式成爲捕捉(Catch)一個信號。
5、相關信號API
參數說明:
第一個參數:指定發送信號的接收線程
第二個參數:信號的signum
案例1、
父進程從終端輸入signum,而後發給子進程
點擊(此處)摺疊或打開
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
int main()
{
int pid;
if((pid = fork()) < 0)
{
perror("Fail to fork");
exit(EXIT_FAILURE);
}else if(pid == 0){
while(1);
}else{
int signum;
while(scanf("%d",&signum) == 1)
{
kill(pid,signum);
system("ps -aux | grep a.out");
}
}
return 0;
}
咱們通常都是用第一個,也就是經過typedef改寫過的。
注意:signal函數我通常認爲其是向內核註冊當前進程收到信號的處理的方式。
signal(SIGINT,handler);
參數說明:
signum : 指定信號
handler : SIG_IGN忽略該信號,SIG_DFL採用系統默認方式處理信號,自定義的信號處理函數指針。
案例探究:
經過異步方式,給子進程收屍
注意:子進程在終止時會給父進程發SIGCHLD,該信號的默認處理動做是忽略,父進程能夠自定義SIGCHLD信號的處理函數,這樣父進程只須要專心處理本身的工做,沒必要關心子進程了,子進程終止時會通知父進程,父進程在信號處理函數中調用wait清理子進程便可。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
void child_exit_handler(int signum)
{
if(signum == SIGCHLD)
{
printf("Child exit.\n");
wait(NULL);
}
}
int main()
{
int pid;
int i = 0;
//想內核註冊,處理 SIGCHLD信號的方式
signal(SIGCHLD,child_exit_handler);
if((pid = fork()) < 0)
{
perror("Fail to fork");
exit(EXIT_FAILURE);
}else if(pid == 0){
for(i = 0;i < 5;i ++)
{
printf("child loop.\n");
sleep(1);
}
}else{
for(i = 0;i < 5;i ++)
{
printf("Father loop.\n");
sleep(2);
}
}
exit(EXIT_SUCCESS);
}
C.鬧鐘函數alarm
larm()也稱爲鬧鐘函數,它能夠在進程中設置一個定時器。當定時器指定的時間到時,內核就向進程發送SIGALARM信號。
seconds:指定的秒數,若是參數seconds爲0,則以前設置的鬧鐘會被取消,並將剩下的時間返回。
成功:若是調用此alarm()前,進程中已經設置了鬧鐘時間,則放回上一個鬧鐘時間的剩餘時間,不然返回0。
alarm(100);
........
......
alarm(5);
出錯:-1
案例探究:
點擊(此處)摺疊或打開
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
void handler(int signum)
{
if(signum == SIGALRM)
{
printf("Recv SIGALARM.\n");
}
exit(EXIT_SUCCESS);
}
int main()
{
int count = 0;
int n = 0;
signal(SIGALRM,handler);
n = alarm(10);
printf("n = %d.\n",n);
sleep(2);
n = alarm(5);
printf("n = %d.\n",n);
while(1)
{
printf("count = %d.\n", ++count);
sleep(1);
}
return 0;
}
運行結果以下:
案例2、綜合案例
使用FIFO實現clientA與clientB之間聊天
A.輸入quit後,兩個進程退出
B.若是在20秒內,沒有等到另外一端發來的消息,則認爲對方已不在,此時終止。
clientA:
點擊(此處)摺疊或打開
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#define MAX 100
void signal_handler(int signum)
{
static int flag = 0;
switch(signum)
{
case SIGALRM:
if(flag == 0)
{
printf("The people is leaving,the system is closed in 10 seconds \
and you can input 'ctrl + c' cancel.\n");
alarm(10);
}else{
kill(getppid(),SIGKILL);
usleep(500);
exit(EXIT_SUCCESS);
}
flag = 1;
break;
case SIGINT:
printf("The alarm is cancel.\n");
alarm(0);
break;
}
}
int child_recv_fifo(char *fifo_name)
{
int n,fd;
char buf[MAX];
if((fd = open(fifo_name,O_RDONLY)) < 0)
{
fprintf(stderr,"fail to open %s : %s.\n",fifo_name,strerror(errno));
return -1;
}
signal(SIGALRM,signal_handler);
signal(SIGINT,signal_handler);
alarm(15);//璁劇疆瀹氭椂鍣?
while(1)
{
n = read(fd,buf,sizeof(buf));
buf[n] = '\0';
printf("Read %d bytes : %s.\n",n,buf);
if(strncmp(buf,"quit",4) == 0 || n == 0)
{
kill(getppid(),SIGKILL);
usleep(500);
exit(EXIT_SUCCESS);
}
alarm(15);
}
return 0;
}
int father_send_fifo(char *fifo_name,int pid)
{
int n,fd;
char buf[MAX];
if((fd = open(fifo_name,O_WRONLY)) < 0)
{
fprintf(stderr,"Fail to open %s : %s.\n",fifo_name,strerror(errno));
return -1;
}
signal(SIGINT,SIG_IGN);
while(1)
{
getchar();
printf(">");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1] = '\0';
write(fd,buf,strlen(buf));
if(strncmp(buf,"quit",4) == 0)
{
kill(pid,SIGKILL);
usleep(500);
exit(EXIT_SUCCESS);
}
}
return 0;
}
int main(int argc,char *argv[])
{
int pid;
if(argc < 3)
{
fprintf(stderr,"usage %s argv[1].\n",argv[0]);
exit(EXIT_FAILURE);
}
if(mkfifo(argv[1],0666) < 0 && errno != EEXIST)
{
perror("Fail to mkfifo");
exit(EXIT_FAILURE);
}
if(mkfifo(argv[2],0666) < 0 && errno != EEXIST)
{
perror("Fail to mkfifo");
exit(EXIT_FAILURE);
}
if((pid = fork()) < 0)
{
perror("Fail to fork");
exit(EXIT_FAILURE);
}else if(pid == 0){
child_recv_fifo(argv[2]);
}else{
father_send_fifo(argv[1],pid);
}
exit(EXIT_SUCCESS);
}
client B
點擊(此處)摺疊或打開
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#define MAX 100
void signal_handler(int signum)
{
static int flag = 0;
switch(signum)
{
case SIGALRM:
if(flag == 0)
{
printf("The people is leaving,the system is closed in 10 seconds \
and you can input 'ctrl + c' cancel.\n");
alarm(10);
}else{
kill(getppid(),SIGKILL);
usleep(500);
exit(EXIT_SUCCESS);
}
flag = 1;
break;
case SIGINT:
printf("The alarm is cancel.\n");
alarm(0);
break;
}
}
int child_recv_fifo(char *fifo_name)
{
int n,fd;
char buf[MAX];
if((fd = open(fifo_name,O_RDONLY)) < 0)
{
fprintf(stderr,"fail to open %s : %s.\n",fifo_name,strerror(errno));
return -1;
}
signal(SIGALRM,signal_handler);
signal(SIGINT,signal_handler);
alarm(15);//璁劇疆瀹氭椂鍣?
while(1)
{
n = read(fd,buf,sizeof(buf));
buf[n] = '\0';
printf("Read %d bytes : %s.\n",n,buf);
if(strncmp(buf,"quit",4) == 0 || n == 0)
{
kill(getppid(),SIGKILL);
usleep(500);
exit(EXIT_SUCCESS);
}
alarm(15);
}
return 0;
}
int father_send_fifo(char *fifo_name,int pid)
{
int n,fd;
char buf[MAX];
if((fd = open(fifo_name,O_WRONLY)) < 0)
{
fprintf(stderr,"Fail to open %s : %s.\n",fifo_name,strerror(errno));
return -1;
}
signal(SIGINT,SIG_IGN);
while(1)
{
getchar();
printf(">");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1] = '\0';
write(fd,buf,strlen(buf));
if(strncmp(buf,"quit",4) == 0)
{
kill(pid,SIGKILL);
usleep(500);
exit(EXIT_SUCCESS);
}
}
return 0;
}
int main(int argc,char *argv[])
{
int pid;
if(argc < 3)
{
fprintf(stderr,"usage %s argv[1].\n",argv[0]);
exit(EXIT_FAILURE);
}
if(mkfifo(argv[1],0666) < 0 && errno != EEXIST)
{
perror("Fail to mkfifo");
exit(EXIT_FAILURE);
}
if(mkfifo(argv[2],0666) < 0 && errno != EEXIST)
{
perror("Fail to mkfifo");
exit(EXIT_FAILURE);
}
if((pid = fork()) < 0)
{
perror("Fail to fork");
exit(EXIT_FAILURE);
}else if(pid == 0){
child_recv_fifo(argv[1]);
}else{
father_send_fifo(argv[2],pid);
}
exit(EXIT_SUCCESS);
}
解釋以下: