当前位置: 首页 > article >正文

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;
}

http://www.kler.cn/a/374823.html

相关文章:

  • 地址踩踏问题
  • windows11家庭版安装docker无法识别基于wsl2的Ubuntu
  • 基于Spring Boot的九州美食城商户一体化系统
  • LLaMA-Factory(二)界面解析
  • 知识图谱+RAG学习
  • 【pycharm】远程服务器之后如何打开终端
  • 信号完整性SI总结【小登培训】
  • OpenCV基础知识
  • DB-GPT系列(二):DB-GPT部署(镜像一键部署、源码部署)
  • C++ 代码工程化
  • 微信小程序,打开新的项目,调试遇见[ app.json 文件内容错误] app.json: 在项目根目录未找到 app.json
  • 【行业应用-工业防爆】本安型工业防爆网关,实现安全高效的数据传输与监控
  • 如何解决docker镜像下载失败问题
  • nfs作业
  • Docker | 容器数据卷在docker中的角色:持久化存储
  • WebSocket简单使用
  • Python实现图像(边缘)锐化:梯度锐化、Roberts 算子、Laplace算子、Sobel算子的详细方法
  • 【PythonWeb开发】Flask-RESTful字段格式化
  • C语言常见进制 (二进制、八进制、十进制、十六进制)详解
  • uniapp使用echart
  • Litctf-web
  • Docker(二):Docker的基本使用
  • 从0攒一个分布大数据/人工智能/数据库 处理引擎
  • 躺平成长-下一个更新的数据(躺平成长数据显示核心)
  • 【计算机网络 - 基础问题】每日 3 题(六十)
  • Make a Windows service publicly accessible