一、信号的介绍
信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。
信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了那些系统事件。
如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递个它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞取消时才被传递给进程。
二、linux操作系统支持的信号
A. kill -l
B.常用信号的含义
![](https://i-blog.csdnimg.cn/blog_migrate/69fd9f6388ae6a3938d2508fa4d10403.png)
B.常用信号的含义
![](https://i-blog.csdnimg.cn/blog_migrate/045dcaf7ac4d96b380278d29f762d163.png)
![](https://i-blog.csdnimg.cn/blog_migrate/20181fbb41f0e2ea93343d44d4d9f722.png)
三、信号的产生
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
A.通过系统调用向一个指定的进程发送信号
运行结果如下:
B.捕捉一个信号
对应的API
其原型:
![](https://i-blog.csdnimg.cn/blog_migrate/837d7c4d97053dc540e1fdcbf6867191.png)
参数说明:
第一个参数:指定发送信号的接收线程
第二个参数:信号的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;
- }
运行结果如下:
![](https://i-blog.csdnimg.cn/blog_migrate/d350301e1af9642200f48be9f987ffb2.png)
B.捕捉一个信号
![](https://i-blog.csdnimg.cn/blog_migrate/a8428917a0d3900ee09b22de93bb6e72.png)
对应的API
![](https://i-blog.csdnimg.cn/blog_migrate/d12e3dbd21a7ecd9d4f7aa0c019087e0.png)
其原型:
![](https://i-blog.csdnimg.cn/blog_migrate/75583af0b85da048baccfbca786d62ec.png)
我们一般都是用第一个,也就是通过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
![](https://i-blog.csdnimg.cn/blog_migrate/bf899fb707987ddf47f1bc39c5fbf9a3.png)
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;
- }
![](https://i-blog.csdnimg.cn/blog_migrate/5143b48a3c1b946b8d37d122007091d2.png)
案例二、综合案例
使用FIFO实现clientA与clientB之间聊天
A.输入quit后,两个进程退出
B.如果在20秒内,没有等到另一端发来的消息,则认为对方已不在,此时终止。
clientA:
client B
D.将进程挂起函数pause
案例如下:
点击(此处)折叠或打开
- #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);
- }
D.将进程挂起函数pause
![](https://i-blog.csdnimg.cn/blog_migrate/ff6ee353c685c6aac807df6610740a33.png)
解释如下:
![](https://i-blog.csdnimg.cn/blog_migrate/3224e130dd0d93b1db7d18482173a84d.png)
案例如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8200d7ba37175c1c1baac7da69e69a1f.png)