Linux信号处理机制基础

什么是信号

  • 信号在最早的UNIX系统中即被引入,已有30多年的历史,但只有很小的变化。
  • 信号是提供异步事件处理机制的软件中断
  • 进程之间可以相互发送信号,这使信号成为一种进程间通信(Inter-ProcessCommunication,lPC)的基本手段

信号的名称与编号

  • 信号是很短的消息,本质就是一个整数,用以区分代表不同事件的不同信号。为了便于记忆,在signum.h头文件中用一组名字前缀为SIG的宏来标识信号,即为信号的名字。
  • 通过kill -l命令可以查看信号
  • 一共有62个信号,其中前31个信号为不可靠的非实时信号后31个为可靠的实时信号

 常用信号

信号处理 

  • 忽略:什么也不做,SIGKILL(9)和SIGSTOP(19)不能被忽略
  • 默认:在没有人为设置的情况,系统缺省的处理行为。
  • 捕获:接收到信号的进程会暂停执行,转而执行一段事先编写好的处理代码,执行完毕后再从暂停执行的地方继续运行,

信号处理函数 

signal函数

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

功能:设置调用进程针对特定信号的处理方式

参数:signum         信号编号
        handler 信号的处理方式,可以如下取值
                SIG_IGN        -忽略
                SIG_DFL       - 默认
                信号处理函数指针        -捕获 
返回值:成功返回原信号处理方式,如果之前未处理过则返回NULL,失败返回SIG_ERR

 代码演示

//信号处理
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
//信号处理函数
void sigfun(int signum){printf("%d进程:捕获到%d号信号\n",getpid(),signum);}
int main(){// 对2号信号进行忽略处理//接下来程序就有了忽略2号信号的能力if(signal(SIGINT,SIG_IGN) == SIG_ERR){perror("signal");return -1;}//对2号信号进行捕捉处理if(signal(SIGINT,sigfun) == SIG_ERR){perror("signal");return -1;}// if(signal(SIGINT,SIG_DFL) == SIG_ERR){//     perror("signal");//     return -1;// }for(;;);return 0;
}

 信号处理流程

        当有信号到来时,内核会保存当前进程的栈帧,然后再执行信号处理函数。当信号处理函数结束后,内核会恢复之前保存的进程的栈帧,使之继续执行

太平间信号

        无论一个进程是正常终止还是异常终止,都会通过系统内核向其父进程发送SIGCHLD(17)信号。父进程完全可以在针对SIGCHLD(17)信号的信号处理函数中,异步地回收子进程的僵尸,简洁而又高效

        在信号处理函数执行期间,如果有多个相同的信号到来,则只保留一个,其余统统丢弃。如果我们在一次信号处理函数执行期间只进行一次收尸,就会导致漏网的僵尸。那我们又该如何对这些僵尸进程进行回收,我们可以在一次信号处理函数期间尽可能多的回收僵尸进程。此外为防止长时间等待回收子进程影响父进程的执行,我们可以采用非阻塞方式回收僵尸进程。

代码演示 

//太平间信号
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<sys/wait.h>
#include<errno.h>//信号捕获处理函数
void sigchild(int signum){printf("%d进程:捕获到%d号信号\n",getpid(),signum);sleep(2);//假装信号处理函数很耗时for(;;){pid_t pid = waitpid(-1,NULL,WNOHANG);if(pid == -1){if(errno == ECHILD){printf("%d进程:没有子进程了\n",getpid());break;}else{perror("waitpid");return ;}}else if(pid == 0){printf("子进程正在运行,回收不了\n");   break;}else{printf("%d进程:回收了%d进程的僵尸\n",getpid(),pid);}}
}/*for(;;){pid_t pid = wait(NULL);if(pid == -1){if(errno == ECHILD){printf("%d进程:没有子进程了\n",getpid());break;}else{perror("wait");return ;}}printf("%d进程:回收了%d进程的僵尸\n",getpid(),pid);}
}*/
int main(){if(signal(SIGCHLD,sigchild) == SIG_ERR){perror("signal");return -1;}//创建多个子进程for(int i = 0; i < 5;i++){pid_t pid = fork();if(pid == -1){perror("fork");return -1;}if(pid == 0){printf("%d进程:我是子进程\n",getpid());//sleep(1 + i);sleep(1);return 0;}}//创建老六pid_t oldsix = fork();if(oldsix == -1){perror("fork");return -1;}if(oldsix == 0){printf("%d进程:我是老六\n",getpid());sleep(15);return 0;}//父进程代码for(;;);return 0;
}

信号的继承与恢复

  • fork函数创建的子进程会继承父进程的信号处理方式
    • 父进程中对某个信号进行捕获,则子进程中对该信号依然捕获
    • 父进程中对某个信号进行忽略,则子进程中对该信号依然忽略

代码演示 

//验证子进程是否继承父进程的信号处理方式
#include <stdio.h>
#include<unistd.h>
#include<signal.h>
#include<sys/wait.h>void sigfun(int signum){printf("%d进程:捕获到%d号信号\n",getpid(),signum);
}
int main(){//忽略2号信号if(signal(SIGINT,SIG_IGN) == SIG_ERR){perror("signal");return -1;}//捕获3号信号if(signal(SIGQUIT,sigfun) == SIG_ERR){perror("signal");return -1;}//创建子进程pid_t pid = fork();if(pid == -1){perror("fork");return -1;}//子进程暂时不结束if(pid == 0){printf("%d进程:我是子进程\n",getpid());while(1){}return 0;}//父进程printf("%d进程:我是父进程\n",getpid());sleep(1);return 0;
}

 由于父子进程共用终端设备我们需要在vi上进行,开启两个窗口,向子进程发送2号和3号信号。

窗口1 
day06$./fork
18338进程:我是父进程
18339进程:我是子进程
day06$kill -2 18317
day06$
day06$kill -3 18317
day06$kill -3 18317
day06$kill -3 18317
窗口2
day06$./fork
18316进程:我是父进程
18317进程:我是子进程
day06$18317进程:捕获到3号信号
18317进程:捕获到3号信号
18317进程:捕获到3号信号
  • exec家族函数创建的新进程对信号的处理方式和原进程稍有不同
    • 原进程中被忽略的信号,在新进程中依然被忽略
    • 原进程中被捕获的信号,在新进程中被默认处理

 代码演示

execl.c

//新进程是否继承旧进程的信号处理方式
#include <stdio.h>
#include<unistd.h>
#include<signal.h>
//信号处理函数
void sigfun(int signum){printf("%d进程:捕获到%d号信号\n",getpid(),signum);return 0;
}
int main(){//忽略SIGINT信号if(signal(SIGINT,SIG_IGN) == SIG_ERR){perror("signal");return -1;}//捕获SIGQUIT信号if(signal(SIGQUIT,sigfun) == SIG_ERR){perror("signal");return -1;}//变身if(execl("./new","new",NULL) == -1){perror("execl");return -1;}return 0;
}

new.c

//变身目标
#include<stdio.h>
int main(){while(1){}return 0;
}
day06$./execl
^C^C^C^C^C^C^C^C^C^\退出 (核心已转储)
day06$

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

splunk Enterprise 的HTTP收集器-windows

1.创建HTTP收集器 2.使用HTTP收集器 然后打开全局设置&#xff1a;把ssl给去掉&#xff0c;点保存&#xff08;保存之后&#xff0c;可以看到这些状态全部都是已启用了&#xff09;&#xff1a; 3.测试&#xff1a; curl --location --request POST http://192.168.11.131:808…

u盘pe怎么安装系统_u盘pe安装系统详细步骤

u盘pe怎么安装系统&#xff1f;u盘pe安装系统需要准备一个u盘&#xff0c;然后将u盘制作成pe&#xff0c;进入pe后再安装系统&#xff0c;下面小编就教大家u盘pe安装系统详细步骤教程。 u盘pe启动盘是什么&#xff1f; u盘pe启动盘是一种可引导的USB存储设备&#xff0c;其中包…

10:Logic软件原理图中添加电源与GND

Logic软件原理图中添加电源与GND

续:docker 仓库数据传输加密

上一个实验&#xff1a;非加密的形式在企业中是不被允许的。 示例&#xff1a;【为Registry 提供加密传输】 因为传输也是https&#xff0c;所以与ssh一样的加密。 ## 这种方式就不用写这个了。 [rootdocker ~]# cat /etc/docker/daemon.json #{ # "insecure-registrie…

7个流行的开源数据治理工具

数字化时代&#xff0c;数据是已经成为最宝贵的资产之一。数据支撑着我们的政府、企业以及各类组织的所有流程&#xff0c;并为决策以及智能化服务提供支撑。大数据有大用途&#xff0c;但是也可能隐藏着巨大的风险&#xff0c;特别是如果我们对数据的情况不是很了解的时候&…

计算机组成原理:实验四常规型微程序控制器组成实验

一、实验目的 1.掌握时序产生器的组成原理。 2.掌握微程序控制器的组成原理。 3.掌握微指令格式的化简和归并。 二、实验电路 1.时序发生器 TEC-4计算机组成原理实验系统的时序电路如图4.1所示。电路采用2片GAL22V10&#xff08;U6、U7&#xff09;&#xff0c;可产生两极…

钓鱼特辑(四)安全较量,摆脱“麻瓜”标签

时至今日&#xff0c;尽管员工们对网络安全有所了解&#xff0c;却往往因缺乏足够的安全意识而对攻防没有直观感知。在红队看来&#xff0c;普通员工可能犹如“麻瓜”&#xff0c;防御薄弱&#xff0c;易于突破。 现在红队以求职者或合作方等“人畜无害”的身份在日常沟通中发动…

Windows中pip换源

step1&#xff1a;检查是否安装 输入如下&#xff0c;出现版本号&#xff0c;就是安装好了 pip -V或pip --version pip3 -V pip3 --version step2&#xff1a;找到&#xff08;创建&#xff09;配置文件 对于 Windows 用户&#xff0c;配置文件在【%APPDATA%\pip\pip.ini】文…

使用LinkedHashMap实现固定大小的LRU缓存

使用LinkedHashMap实现固定大小的LRU缓存 1. 什么是LRU&#xff1f; LRU是"Least Recently Used"的缩写&#xff0c;意为"最近最少使用"。LRU缓存是一种常用的缓存淘汰算法&#xff0c;它的核心思想是&#xff1a;当缓存满时&#xff0c;优先淘汰最近最少…

Mac下的压缩包和Win看到的不一样怎么办 Mac压缩后Win电脑看文件名会乱码

在当今多平台的数字工作环境中&#xff0c;Mac和Windows用户常常需要交换文件&#xff0c;但有时候会遇到一些兼容性问题。特别是在处理压缩文件时&#xff0c;Mac用户创建的压缩包在Windows系统中打开时&#xff0c;常常会遇到文件名乱码的问题。本文将详细讨论“Mac下的压缩包…

C语言基础(二十八)

1、冒泡排序&#xff1a; #include "date.h" #include <stdio.h> #include <stdlib.h> #include <time.h> // 函数声明 void bubbleSort(int *arr, int n); int* createRandomArray(int n, int *size); int main() { int time getTi…

22行为型设计模式——解释器模式

一、解释器模式 解释器模式&#xff08;Interpreter Pattern&#xff09;是一种行为型设计模式&#xff0c;主要用于解析和解释特定的语言或表达式。它的核心思想是为语言中的每种语法规则定义一个解释器&#xff0c;通过这些解释器将语言的表示形式转换为可执行的操作。解释器…

【Python机器学习】NLP词频背后的含义——距离和相似度

我们可以使用相似度评分&#xff08;和距离&#xff09;&#xff0c;根据两篇文档的表示向量间的相似度&#xff08;或距离&#xff09;来判断文档间有多相似。 我们可以使用相似度评分&#xff08;和举例&#xff09;来查看LSA主题模型与高维TF-IDF模型之间的一致性。在去掉了…

STM32基于HAL库串口printf使用和接收

我们这里使用HAL库直接用cubemx生成代码配置串口 1.打开cubemx&#xff0c;选择MCU型号 2.我这里使用的是STM32F103C8T6&#xff0c;根据自己的型号选择&#xff0c;这里不限制型号 3.选择时钟源 4.系统设置 5时钟配置 5.选择和配置串口 5.配置中断和中断优先级 6.工程设置…

node环境安装、vue-cli搭建过程、element-UI搭建使用过程

vue-cli 官方提供的一个脚手架&#xff0c;用于快速生成一个 vue 的项目模板&#xff1b;预先定义好的目录结构及基础代码&#xff0c;就好比咱们在创建 Maven 项目时可以选择创建一个骨架项目&#xff0c;这个骨架项目就是脚手架&#xff0c;我们的开发更加的快速 前端项目架…

探索Python测试的奥秘:nose库的魔法之旅

文章目录 探索Python测试的奥秘&#xff1a;nose库的魔法之旅1. 背景&#xff1a;为什么要用nose&#xff1f;2. nose是什么&#xff1f;3. 如何安装nose&#xff1f;4. 五个简单的库函数使用方法4.1 nose.tools.assert_true4.2 nose.tools.assert_equal4.3 nose.tools.raises4…

html2canvas、pdf-lib、file-saver将html页面导出成pdf

html2canvas、pdf-lib、file-saver将html页面导出成pdf 项目背景 需要根据用户的账号信息&#xff0c;生成一个pdf报告发给客户&#xff0c;要求报告包含echart饼图、走势图等。 方案 使用html2canvas&#xff0c;将页面转成图片&#xff0c;再通过pdf-lib将图片转成pdf文件…

Visual Studio Code离线汉化

从官网下载Visual Studio Code安装包后&#xff0c; 下载Visual Studio Code&#xff1a;https://code.visualstudio.com/ 若因网络等问题无法在线安装语言包&#xff0c;可以尝试离线安装&#xff1a; 从官网下载语言包&#xff1a; Extensions for Visual Studio family …

Stable Diffusion majicMIX_realistic模型的介绍及使用

一、简介 majicMIX_realistic模型是一种能够渲染出具有神秘或幻想色彩的真实场景的AI模型。这个模型的特点是在现实场景的基础上&#xff0c;通过加入一些魔法与奇幻元素来营造出极具画面效果和吸引力的图像。传统意义的现实场景虽然真实&#xff0c;但通常情况下缺乏奇幻性&a…

【网络世界】网络层

目录 &#x1f308;前言&#x1f308; &#x1f4c1; 网络层 &#x1f4c1; IPV4 &#x1f4c2; 什么是IP地址 &#x1f4c2; 网段划分 &#x1f4c2; 特殊IP &#x1f4c2; 内网和公网 &#x1f4c2; IPV4的危机 &#x1f4c1; IP协议格式 &#x1f4c1; 路由 &#x1f…