目录
- 信号的概念
- 信号相关的函数
- kill
- raise
- abort
- alarm
- 1s钟电脑能数多少个数?
- setitimer
- 过3s以后,每隔2s定时一次
- 信号捕捉函数
- signal
- sigaction
- 信号集
- sigprocmask
- 编写一个程序,把所有的常规信号未决状态打印到屏幕
- sigchld信号
信号的概念
比如ctrl+c会进程发送一个中断信号
硬件发生异常,比如被0除,野地址
系统状态变化,alarm定时器、超过cpu时间片,子进程退出
kill命令
信号相关的函数
kill
给某个进程pid发送某个信号sig
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>int main(){pid_t pid=fork();if(pid==0){//子进程for(int i=0;i<5;i++){printf("child process\n");sleep(1);}}else if(pid>0){//父进程printf("parent process\n");sleep(2);printf("kill child process now\n");kill(pid,SIGINT);}return 0;
}
raise
给当前进程发送信号(kill可以给任何进程)
abort
alarm
设置定时器
开始倒计时,当倒计时为0时终止当前进程
每一个进程只有唯一的一个定时器,如果第二次出现alarm(重新倒计时),返回值是上一个剩余的时间
该函数不阻塞
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>int main(){int secondes=alarm(5);printf("seconds:%d\n",secondes);sleep(2);secondes=alarm(10);printf("seconds:%d\n",secondes);while(1){}return 0;
}
1s钟电脑能数多少个数?
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>int main(){alarm(1);int i=0;while(1){printf("%i\n",i++);}return 0;
}
实际上更多,因为在这边写到终端上i/o缓冲也占了时间,就是没全部打印出来,就已经显示闹钟了
有五百万次
实际的时间=内核时间+用户时间+消耗的时间
进行文件IO操作的时候比较浪费时间
alarm是自然计时,不受sleep的影响
setitimer
设置定时器,且精度更高,到微秒
每个阶段发送信号
过3s以后,每隔2s定时一次
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>int main(){struct itimerval new_value;//设置间隔的时间new_value.it_interval.tv_sec=2;new_value.it_interval.tv_usec=0;//设置延迟的时间,3s之后开始第一次定时new_value.it_value.tv_sec=3;new_value.it_value.tv_usec=0;int ret=setitimer(ITIMER_REAL,&new_value,NULL);printf("定时器开始了...\n");if(ret==-1){perror("setitimer");exit(0);}getchar();return 0;
}
信号捕捉函数
signal
sigkill(9号信号) sigstop不能被捕捉,不能被忽略
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>int main(){//注册信号捕捉signal(SIGALRM,SIG_IGN);struct itimerval new_value;//设置间隔的时间new_value.it_interval.tv_sec=2;new_value.it_interval.tv_usec=0;//设置延迟的时间,3s之后开始第一次定时new_value.it_value.tv_sec=3;new_value.it_value.tv_usec=0;int ret=setitimer(ITIMER_REAL,&new_value,NULL);printf("定时器开始了...\n");if(ret==-1){perror("setitimer");exit(0);}getchar();return 0;
}
捕捉到信号之后,信号忽略,原先终止,现在啥都不做
signal(SIGALRM,SIG_DFL);
换成默认,默认就是终止进程,倒计时结束就结束
sigaction
SIGKILL和SIGSTOP不能被忽略也不能被捕捉
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>void myalarm(int num){printf("捕捉到了信号的编号是:%d\n",num);printf("xxxx\n");
}int main(){struct sigaction act;act.sa_flags=0;act.sa_handler=myalarm;sigemptyset(&act.sa_mask);//清空零时阻塞信号集//注册信号捕捉sigaction(SIGALRM,&act,NULL);struct itimerval new_value;//设置间隔的时间new_value.it_interval.tv_sec=2;new_value.it_interval.tv_usec=0;//设置延迟的时间,3s之后开始第一次定时new_value.it_value.tv_sec=3;new_value.it_value.tv_usec=0;int ret=setitimer(ITIMER_REAL,&new_value,NULL);printf("定时器开始了...\n");if(ret==-1){perror("setitimer");exit(0);}while(1);return 0;
}
每两秒捕捉一次定时器的alarm信号,然后调用回调函数
信号集
#include<signal.h>
#include<stdio.h>int main(){//创建一个信号集sigset_t set;//清空信号集的内容sigemptyset(&set);//判断sigint是否在信号集set里int ret=sigismember(&set,SIGINT);if(ret==0){printf("SIGINT 不阻塞\n");}else if(ret==1){printf("SIGINT 阻塞\n");}//添加几个信号到信号集中sigaddset(&set,SIGINT);sigaddset(&set,SIGQUIT);//判断sigint是否在信号集set里ret=sigismember(&set,SIGINT);if(ret==0){printf("SIGINT 不阻塞\n");}else if(ret==1){printf("SIGINT 阻塞\n");}//判断sigquit是否在信号集set里ret=sigismember(&set,SIGQUIT);if(ret==0){printf("SIGQUIT 不阻塞\n");}else if(ret==1){printf("SIGQUIT 阻塞\n");}//从信号集中删除一个信号sigdelset(&set,SIGQUIT);//判断sigquit是否在信号集set里ret=sigismember(&set,SIGQUIT);if(ret==0){printf("SIGQUIT 不阻塞\n");}else if(ret==1){printf("SIGQUIT 阻塞\n");}return 0;
}
sigprocmask
对内核的信号集进行操作
编写一个程序,把所有的常规信号未决状态打印到屏幕
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>int main(){//设置2号、3号信号阻塞sigset_t set;sigemptyset(&set);//将2号和3号信号添加到信号集中sigaddset(&set,SIGINT);sigaddset(&set,SIGQUIT);//修改内核中的阻塞信号sigprocmask(SIG_BLOCK,&set,NULL);while(1){//获取当前的未决信号集的数据sigset_t pendingset;sigemptyset(&pendingset);sigpending(&pendingset);//遍历前32位for(int i=1;i<=32;i++){if(sigismember(&pendingset,i)==1){printf("1");}else if(sigismember(&pendingset,i)==0){printf("0");}else{perror("sigismember");exit(0);}}printf("\n");}return 0;
}
未决信号集和阻塞信号集
sigchld信号
子进程终止时,产生
子进程接收到SIGSTOP信号(暂停进程,不是停止)
子进程处于停止态(暂停),接受到SIGCONT唤醒时
以上三种情况,内核会发送SIGCHLD信号给父进程,父进程默认忽略该信号。
用于解决僵尸进程问题,不需要父进程来wait,子进程结束之后发送信号。
然后父进程捕捉该信号,再去调用pid来wait回收资源,之后父进程继续做自己的事情。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include<sys/stat.h>int main(){//创建一些子进程pid_t pid;for(int i=0;i<20;i++){pid=fork();if(pid==0){break;}}if(pid>0){//父进程while(1){printf("parent process pid:%d\n",getpid());sleep(2);}}else if(pid==0){//子进程printf("child process pid:%d\n",getpid());}return 0;
}
创建20个子进程,产生了僵尸进程
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include<sys/stat.h>
#include <signal.h>void myFun(int num){printf("捕捉到的信号:%d\n",num);
}int main(){//创建一些子进程pid_t pid;for(int i=0;i<20;i++){pid=fork();if(pid==0){break;}}if(pid>0){//父进程//捕捉子进程死亡时发送的sigchld信号struct sigaction act;act.sa_flags=0;act.sa_handler=myFun;sigemptyset(&act.sa_mask);sigaction(SIGCHLD,&act,NULL);while(1){printf("parent process pid:%d\n",getpid());sleep(2);}}else if(pid==0){//子进程printf("child process pid:%d\n",getpid());}return 0;
}
子进程死了之后,就捕捉到了17号信号(SIGCHLD)
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include<sys/stat.h>
#include <signal.h>
#include<sys/wait.h>void myFun(int num){printf("捕捉到的信号:%d\n",num);//回收子进程PCB的资源while(1){int ret=waitpid(-1,NULL,WNOHANG);if(ret>0){printf("child die,pid=%d\n",ret);}else if(ret==0){//说明还有子进程活着break;}else if(ret==-1){//所有子进程结束了break;}}}int main(){//创建一些子进程pid_t pid;for(int i=0;i<20;i++){pid=fork();if(pid==0){break;}}if(pid>0){//父进程//捕捉子进程死亡时发送的sigchld信号struct sigaction act;act.sa_flags=0;act.sa_handler=myFun;sigemptyset(&act.sa_mask);sigaction(SIGCHLD,&act,NULL);while(1){printf("parent process pid:%d\n",getpid());sleep(2);}}else if(pid==0){//子进程printf("child process pid:%d\n",getpid());}return 0;
}
全部被捕捉并处理,没有僵尸进程
还有个问题就是,如果在捕捉信号注册还没完成的时候,子进程就已经全部结束了,那这个时候就不会再发生sigchld了,那就不会被回收了。
如何让信号注册结束在子进程之前
设置阻塞信号集,信号注册完之前,子进程阻塞,信号发不了。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include<sys/stat.h>
#include <signal.h>
#include<sys/wait.h>void myFun(int num){printf("捕捉到的信号:%d\n",num);//回收子进程PCB的资源while(1){int ret=waitpid(-1,NULL,WNOHANG);if(ret>0){printf("child die,pid=%d\n",ret);}else if(ret==0){//说明还有子进程活着break;}else if(ret==-1){//所有子进程结束了break;}}}int main(){//提前设置好阻塞信号集,阻塞sigchld因为有可能子进程很快结束,父进程还没有注册完sigset_t set;sigemptyset(&set);sigaddset(&set,SIGCHLD);sigprocmask(SIG_BLOCK,&set,NULL);//创建一些子进程pid_t pid;for(int i=0;i<20;i++){pid=fork();if(pid==0){break;}}if(pid>0){//父进程//捕捉子进程死亡时发送的sigchld信号struct sigaction act;act.sa_flags=0;act.sa_handler=myFun;sigemptyset(&act.sa_mask);sigaction(SIGCHLD,&act,NULL);//注册完信号捕捉以后,解除阻塞sigprocmask(SIG_UNBLOCK,&set,NULL);while(1){printf("parent process pid:%d\n",getpid());sleep(2);}}else if(pid==0){//子进程printf("child process pid:%d\n",getpid());}return 0;
}