原文地址:进程间通信--信号(进程间通信唯一的异步方式) 作者:Deem_passion
一、信号的介绍
信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。
信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了那些系统事件。
如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递个它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞取消时才被传递给进程。
二、linux操作系统支持的信号
三、信号的产生
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信号。
四、进程对信号的处理
A.忽略此信号
B.执行该信号的默认处理动作
C.提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式成为捕捉(Catch)一个信号。
五、相关信号API
参数说明:
第一个参数:指定发送信号的接收线程
第二个参数:信号的signum
案例一、
父进程从终端输入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;
}
运行结果如下:
案例二、综合案例
使用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);
}
解释如下: