Unix信号处理

信号的基本概念我已经在上一节中简单介绍了,大家可以去看我的上一篇博客:

Unix中的进程和线程-2-CSDN博客

1.信号的产生

kill函数:

#include <signal.h>
#include <fcntl.h>
#include<t_stdio.h>
//自定义信号处理函数,n为信号编号
void handle(int n){
    printf("this is a test..\n");
    return;

}

int main(void){
    //从父进程继承信号处理函数
    //采用默认处理方式
    signal(2,SIG_IGN);//改变2号进程的处理函数,忽略2号信号
    signal(3,handle);//对3号信号使用自定义处理函数
    pid_t pid = fork();
    if(pid==-1)E_MSG("fork",-1);
    while(1);
    return 0;
}

  1. signal(2,SIG_IGN):使用signal()函数,将2号信号(通常是SIGINT,即Ctrl+C)的处理方式设置为忽略(SIG_IGN)。这意味着当程序接收到Ctrl+C时,不会终止程序,而是忽略该信号。
  2. signal(3,handle):将3号信号的处理方式设置为调用自定义的信号处理函数handle。这表示当程序接收到3号信号时(例如Ctrl+\),会执行handle函数中定义的操作。
  3. pid_t pid = fork();:创建一个子进程。
  4. if(pid==-1)E_MSG("fork",-1);:如果fork()调用失败,输出错误消息并退出程序。
  5. while(1);:在这个循环中,父进程和子进程都会进入一个无限循环,使得程序一直处于运行状态。

这个程序主要演示了信号处理的基本用法,以及父子进程之间对信号处理函数的继承。通过signal()函数可以设置特定信号的处理方式,可以是忽略该信号、采用默认的处理方式,或者调用自定义的信号处理函数。

 kill函数的仿写:

#include<t_stdio.h>
#include<t_file.h>
#include <sys/types.h>
#include <signal.h>

int main(int argc , char * argv[]){
    //将命令行的参数转化为整数类型
     kill (atoi(argv[2]),atoi(argv[1]));
    return 0;
}

 alarm函数:

alarm函数是一个用于在指定时间后发送信号的函数。它允许你在程序中设置一个定时器,当定时器计时时间到达后,系统会向当前进程发送一个 SIGALRM 信号。这通常用于实现超时控制或者周期性任务。

以下是 alarm 函数的声明:

#include <unistd.h>

unsigned int alarm(unsigned int seconds);

alarm 函数接受一个 unsigned int 类型的参数 seconds,表示定时器的计时时间,单位是秒。当调用 alarm 函数后,系统会在指定的秒数之后向当前进程发送 SIGALRM 信号。

如果之前已经设置过定时器,调用 alarm 函数会取消之前的定时器,并设置新的定时器。如果 seconds 参数为 0,则之前设置的定时器会被取消,且不会设置新的定时器。

下面是一个简单的示例代码,演示了如何使用 alarm 函数实现一个定时器:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void handler(int signum) {
    printf("Received SIGALRM signal.\n");
    // 处理定时器触发后的逻辑
}

int main() {
    // 注册信号处理函数
    signal(SIGALRM, handler);

    printf("Setting up alarm for 5 seconds.\n");
    // 设置定时器为 5 秒
    alarm(5);

    // 进入一个无限循环
    while (1) {
        // 这里可以执行一些其他任务
        // 例如:等待用户输入等
    }

    return 0;
}

在这个示例中,首先注册了一个信号处理函数 handler,用于处理 SIGALRM 信号。然后调用 alarm(5) 设置了一个定时器,时间为 5 秒。当定时器计时时间到达时,系统会发送 SIGALRM 信号,进而触发信号处理函数 handler,在该处理函数中打印了一条消息。

  2.信号的暂停

如下:

#include<stdio.h>
#include <unistd.h>
#include<signal.h>
//信号处理函数
void handle(int n){}

unsigned int t_sleeps(unsigned int seconds){
    alarm(2);//设置一个两秒闹钟
    pause();//停止进程
    return alarm(0);//取消原来的脑子,返回未决时间

}

int main(void){
    //不能忽略SIGALRM信号,这样pause会一直起作用,不会停止
    //signal(SIGALRM,SIG_IGN);
    signal(SIGALRM,handle);

    while(1){
        t_sleeps(2);
        printf("nihao...\n");
    }
    return 0;
}

  1. #include<stdio.h>:包含了标准输入输出的头文件。
  2. #include <unistd.h>:包含了UNIX标准的头文件,其中包括了alarm()pause()函数的声明。
  3. #include<signal.h>:包含了信号处理相关的头文件。
  4. void handle(int n){}:定义了一个空的信号处理函数handle(),用于处理信号。在这个示例中,它并没有实际功能。
  5. unsigned int t_sleeps(unsigned int seconds):定义了一个自定义的睡眠函数t_sleeps(),它接受一个参数seconds,表示睡眠的秒数,并返回一个unsigned int类型的值,表示剩余的未决定时间。
  6. alarm(2);:调用alarm()函数,设置一个两秒的定时器。这会在两秒后向进程发送一个SIGALRM信号。
  7. pause();:调用pause()函数,使进程暂停,等待信号的到来。当接收到信号后,进程会恢复执行。
  8. return alarm(0);:取消之前设置的闹钟,并返回未决定的时间。

main()函数中:

  • 注册了信号处理函数,将SIGALRM信号的处理方式设置为handle函数。这一步通常是为了确保程序在接收到SIGALRM信号时不会终止。
  • 使用一个无限循环,调用t_sleeps(2)函数,表示每隔两秒打印一次"nihao..."。

这个程序的主要目的是演示如何使用alarm()pause()函数实现一个自定义的睡眠函数。当调用t_sleeps()函数时,程序会暂停执行两秒钟,然后继续执行后续的代码。

 3.信号阻塞和信号集

 3.1信号集的常用操作:

#include<signal.h>
#include<t_stdio.h>

int main(void){
    //定义信号集类型变量set
   __sigset_t set;
    //初始化set
    sigemptyset(&set);
    //将2号信号添加到信号集set中
    sigaddset(&set,2);
    sigaddset(&set,3);
    //测试3号信号是不是信号集中的一员
    int is=sigismember(&set,3);
    if(is==-1)E_MSG("sigismember",-1);
    if(is) 
        printf("signum is member of set..\n");
    else printf("signum is not member of set..\n");

    //将3号信号从信号集中移除
    sigdelset(&set , 3);
    is=sigismember(&set,3);
    if(is==-1)E_MSG("sigismember",-1);
    if(is) 
        printf("del signum is member of set..\n");
    else printf("del signum is not member of set..\n");

    return 0;
}

 信号的阻塞:

3.2进程的信号掩码集:

#include<t_stdio.h>
#include <asm-generic/signal-defs.h>
#include <bits/sigset.h>

int main(void){
    //定义信号集的变量set
    __sigset_t set;
    //初始化set
    sigemptyset(&set);
    //将2,3,9号信号添加到信号集set中
    sigaddset(&set,2);
    sigaddset(&set,3);
    sigaddset(&set,9);

    //将set设置为进程的信号掩码集
    int sm=sigprocmask(SIG_SETMASK,&set,NULL);
    if(sm==-1)E_MSG("sigprocmask",-1);
    while(1){}
    return 0;
}
  1. __sigset_t set;:定义了一个变量 set,类型为 __sigset_t,这是一个用于表示信号集的数据结构。
  2. sigemptyset(&set);:使用 sigemptyset() 函数将信号集 set 清空,确保其中不包含任何信号。
  3. sigaddset(&set,2);sigaddset(&set,3); 和 sigaddset(&set,9);:使用 sigaddset() 函数向信号集 set 中添加信号。在这里,将信号2、3和9添加到 set 中,分别代表 SIGINTSIGQUIT 和 SIGKILL 信号。
  4. int sm=sigprocmask(SIG_SETMASK,&set,NULL);:使用 sigprocmask() 函数将 set 中的信号设置为进程的信号掩码集。SIG_SETMASK 表示设置当前信号掩码为 set 中包含的信号集合,即阻塞信号2、3和9。如果设置成功,sigprocmask() 返回0;否则返回-1,并且相应的错误信息会被设置到 errno 中。
  5. while(1){}:进入一个无限循环,使得程序一直处于运行状态,以保持信号掩码的设置。

3.3未决信号集:

#include<t_stdio.h>
#include<signal.h>
#include <asm-generic/signal-defs.h>
#include<unistd.h>

int main(void){
    //定义信号集类型变量
    __sigset_t set , pset;
    //初始化信号集
    sigemptyset(&set);

    //添加信号到set
    sigaddset(&set,2);

    //将set设置为进程的信号掩码集
        int sm =sigprocmask(SIG_BLOCK,&set,NULL);
        if(sm==-1)E_MSG("sigismember",-1);
        while(1){
            sleep(1);
            //获取进程的未决信号集,pset存放的就是进程的未决信号集
            sigpending(&pset);
            int is=sigismember(&pset,2);
            if(is==-1)E_MSG("sigismemeber",-1);
            is? printf("yes..\n") : printf("no..\n");

        }

    return 0;
}
  1. 信号掩码集(signal mask): 信号掩码集是用于阻塞一组信号的,也就是说,被加入到该进程的信号掩码集中的信号,都会被阻塞,直到被从掩码集中移除。在你的代码中,通过sigprocmask(SIG_BLOCK,&set,NULL);将2号信号(SIGINT,即Ctrl+C触发的中断信号)加入到了掩码集中,从而阻塞了该信号。
  2. 未决信号集(pending signal): 未决信号集是指当前被阻塞,并且尚未处理的信号集合。只要这个信号在掩码集中,那么该信号一旦产生,就会被追加到未决信号集中,等待处理。在代码中,未决信号集的状态被周期性检查,并且每秒输出一次结果,显示2号信号是否存在于未决信号集中。 应用场景: 信号掩码集主要用于在处理某个信号时临时屏蔽其它会对当前处理过程产生干扰的信号,比如在处理某些复杂的业务逻辑或者关键数据更新时,防止被其它信号打断。 未决信号集则帮助我们得知哪些信号被阻塞且尚未得到处理,这对于调试程序,以及编写稳健的并发处理代码很有帮助。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/504376.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript基础语法–变量

文章目录 认识JavaScript变量程序中变量的数据&#xff08;记录&#xff09;–变量变量的命名格式在Java script中变量定义包含两部分1. 变量声明&#xff08;高级JS引擎接下来定义一个变量&#xff09;2. 其他的写法 变量命名的规范&#xff08;遵守&#xff09;变量的练习a. …

【Docker】Windows中打包dockerfile镜像导入到Linux

【Docker】Windows中打包dockerfile镜像导入到Linux 大家好 我是寸铁&#x1f44a; 总结了一篇【Docker】Windows中打包dockerfile镜像导入到Linux✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 前言 今天遇到一个新需求&#xff0c;如何将Windows中打包好的dockerfile镜像给迁移…

【Linux】进程程序替换 做一个简易的shell

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 文章目录 前言 进程程序替换 替换原理 先看代码和现象 替换函数 第一个execl()&#xff1a; 第二个execv()&#xff1a; 第三个execvp()&#xff1a; 第四个execvpe()&a…

【MySQL】DQL-查询语句全解 [ 基础/条件/分组/排序/分页查询 ](附带代码演示&案例练习)

前言 大家好吖&#xff0c;欢迎来到 YY 滴MySQL系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C Linux的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的…

瑞吉外卖实战学习--8、人员禁用和启用

前言 1、通过前端页面查看接口 会发现请求方式是put 请求接口是employee 2、检查页面传值 根据浏览器的请求可以看到传值为id和status 2、写put请求&#xff0c;添加修改时间和修改人的id然后传回给后台 /*** 启用和禁用员工账号* param request* param employee* return…

为何有时坚持很苦,而有时坚持很酷

坚持很苦 大部分情况下是被动的&#xff0c;被迫的&#xff0c;坚持去做的事情并不是自己发自内心的。 比如一部分学生考研或者读书&#xff0c;都是随大流盲从而已&#xff0c;自己想做啥都不清楚。 坚持很酷 追求自己的理想是这个蓝色星球上最酷炫的事情啦&#xff0c;没有…

二维码门楼牌管理应用平台建设:实现智能化与人性化的数据治理

文章目录 前言一、二维码门楼牌管理应用平台的建设背景二、人工数据审核的重要性三、地址匹配校验的实现四、人工修正的流程与机制五、人工数据审核的挑战与对策六、展望未来 前言 在数字化时代的浪潮下&#xff0c;二维码门楼牌管理应用平台的建设成为了城市管理的新宠。本文…

【嵌入式智能产品开发实战】(七)—— 政安晨:通过ARM-Linux掌握基本技能【环境准备:树莓派】

目录 Raspberry Pi OS 下载系统镜像 使用SSH客户端登陆 升级更新 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: 嵌入式智能产品开发实战 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正…

详解Linux进程

进程 1.什么是进程2.创建进程2.1进程标识符2.2初时fork&#xff08;&#xff09;函数&#xff0c;创建进程 3.进程状态3.1进程状态的描述3.2Linux中具体的进程状态 4 僵尸状态5 孤儿进程6进程优先级 1.什么是进程 进程在我们的电脑和手机上是无处不在的。例如我们windows系统下…

基于SpringBoot+Vue前后端分离的停车场管理系统设计与实现+毕业论文(12000字)

介绍 本系统主要包含普通用户与管理员两个用户角色&#xff1a;普通用户功能模块&#xff1a;可以方便地对车位进行查询&#xff0c;车位申请和个人缴费。 管理员功能模块: 管理系统用户&#xff0c;停车位&#xff0c;用户缴费信息管理&#xff0c;登录日志管理。 普通用户…

白色微立体的智能界面,就喜欢这种简洁白净。

本次发一些微立体风格的智能家居界面&#xff0c;风格为微立体&#xff0c;也叫轻拟物风格&#xff0c;或者新拟态风格。

25Ramdisk 启动模式简介

Ramdisk 启动模式简介 ramdisk是一种虚拟磁盘技术,我们的PE系统几乎都是使用ramdisk方式从计算机启动的.那么,ramdisk有哪些特点呢? Ramdisk 将内存虚拟为一个磁盘 Ramdisk技术会将你的一部分内存虚拟成一块磁盘分区.使用U盘启动pe系统时,打开pe系统里的文件资源管理器,你会看…

(文章复现)考虑分布式电源不确定性的配电网鲁棒动态重构

参考文献&#xff1a; [1]徐俊俊,吴在军,周力,等.考虑分布式电源不确定性的配电网鲁棒动态重构[J].中国电机工程学报,2018,38(16):4715-47254976. 1.摘要 间歇性分布式电源并网使得配电网网络重构过程需要考虑更多的不确定因素。在利用仿射数对分布式电源出力的不确定性进行合…

云防护是怎么能帮助用户做好网络安全

在数字化时代&#xff0c;网络安全威胁呈现出愈发复杂和多样化的趋势。 无论是个人用户、小型企业还是大型企业&#xff0c;都面临着来自全球各地的网络攻击风险。这些攻击可能导致数据泄露、服务中断、财务损失甚至声誉受损。因此&#xff0c;采取有效的安全防护措施变得至关…

数据处理的两个基本问题

文章目录 数据处理的两个基本问题bx、si、di、bp机器指令处理的数据所在位置汇编语言中数据位置的表达寻址方式指令要处理的数据有多长&#xff1f;div 指令伪指令 dddup 数据处理的两个基本问题 我们知道&#xff0c;计算机是进行数据处理、运算的机器&#xff0c;那么有两个基…

基于Tampermonkey 实现自动答题和视频播放

目录 一、环境准备 二、下载Tampermonkey 三、安装脚本 四、启用脚本 一、环境准备 微软自带的 edge 浏览器(电脑端) 二、下载Tampermonkey 安装地址&#xff1a;Tampermonkey 篡改猴(油猴脚本) 下载完成会在浏览器拓展中自动生成一个插件&#xff0c;此时点击管理拓展&…

linux 内核模块入门

内核模块可以动态地被安装到内核&#xff0c;从而扩展内核的功能&#xff0c;使用内核模块时不需要重新编译内核。内核模块常用的场景是驱动&#xff0c;随着芯片种类的增加&#xff0c;硬件种类的增加&#xff0c;这些芯片或者硬件(比如网卡) 的驱动可以以模块的方式进行开发&…

ONT60 旋转链表 思路分享

题干链接&#xff1a;ONT60 旋转链表 ​ 这道题是反转链表题的pro升级版&#xff0c;但比反转链表略微复杂一些。如果有做过旋转数组那道题&#xff08;链接在这里&#xff1a;https://blog.csdn.net/wyd_333/article/details/126712919&#xff0c;但当时刷这道题的时候我用的…

Linux|centos7-postgresql数据库|yum安装数据库和配置repmgr高可用集群以及repmgr的日常管理工作

一、 前言 postgresql 的yum部署其实还是有点东西的&#xff0c;本文就做一个小小的记录&#xff0c;高可用方面repmgr插件还是非常不错的&#xff0c;但如何部署以及部署后如何使用也是一个难点&#xff0c;因此&#xff0c;也在本文里做一个记录 环境介绍&#xff1a; 第…

【Redis教程0x0A】详解Redis哨兵机制

1. 引言 Redis的哨兵机制是基于主从架构的。 在 Redis 的主从架构中&#xff0c;由于主从模式是读写分离的&#xff0c;如果主节点&#xff08;master&#xff09;挂了&#xff0c;那么将没有主节点来服务客户端的写操作请求&#xff0c;也没有主节点给从节点&#xff08;slav…