Webserver(2.6)信号
目录
- 信号的概念
- 信号相关的函数
- 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;
}