Linux学习之系统编程5(信号)

写在前面:

我的Linux的学习之路非常坎坷。第一次学习Linux是在大一下的开学没多久,结果因为不会安装VMware就无疾而终了,可以说是没开始就失败了。第二次学习Linux是在大一下快放暑假(那个时候刚刚过完考试周),我没什么事做就又重拾Linux,不服输的我选择再战Linux,这一次学习还算顺利,虽然中间有些小插曲但是不影响整体学习进度, 我看着B站上的视频一点点学习Linux,基本上把Linux的基础指令学完了。学完之后我又遇到问题了,视频基本上到这就结束了,而我却不知道下一步该学什么,于是就没怎么碰Linux,结果没过多长时间我就把学的Linux指令忘的一干二净。现在是我第三次学习Linux,我决定重新开始学Linux,同时为了让自己学习的效果更好,我选择以写blog的形式逼迫自己每天把学习到的Linux知识整理下来。这也就是我写这个系列blog的原因。


信号的相关的概念

信号的共性

  • 简单
  • 不能携带大量信息
  • 满足条件才能 发送

信号的特质

信号是软件层面的“中断”。一旦信号产生,无论程序执行到什么位置,必须立即停止,处理信号,处理结束后,再继续执行后续指令。

所有信号的产生及处理全部都是由内核完成的

信号的产生

  1. 按键产生。如CTRL+c,CTRL+z,CTRL+\.
  2. 系统调用产生。如kill,raise,abort
  3. 软件条件产生。如定时器alarm,setitimer,sleep
  4. 硬件异常产生。如非法访问内存(段错误),内存对齐错误(总线错误)
  5. 命令产生。如kill命令

信号的两种状态

  • 未决:产生与递达之间的状态。
  • 递决:产生并且送达到进程,直接被内核处理掉。

信号的处理方式

  • 执行默认动作
  • 忽略
  • 捕捉(自定义)

信号屏蔽字和未决信号集

1

  • 阻塞信号集(信号屏蔽字):本质是位图。用来记录信号的屏蔽状态。一旦被屏蔽的信号,在解除屏蔽前,一直处于未决态。
  • 未决信号集:本质是位图。用来记录信号的处理状态。该信号集中的信号,表示,已经产生,但尚未被处理。

信号四要素

  • 编号
  • 名称
  • 对应事件
  • 默认处理动作

信号使用之前,应先确定其4要素,而后再用!!!

常规信号

编号名称对应事件默认处理动作
1SIGHUP本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端不再关联。挂断
2SIGINT在用户键入INTR字符(通常是Ctrl+C)时发出通知前台进程组终止进程
3SIGQUIT由QUIT字符(通常是Ctrl+)来控制进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号。
4SIGILL通常是因为可执行文件本身出现错误, 或者试图执行数据段. 堆栈溢出也有可能产生这个信号。终止程序
5SIGTRAP由断点指令或其它陷阱(trap)指令产生
6SIGABRT调用abort函数生成的信号。
7SIGBUS访问非法地址终止程序
8SIGFPE在发生致命的算术运算错误时发出终止程序
9SIGKILL发现某个进程终止不了立即结束程序的运行
10SIGUSR1留给用户自定义留给用户自定义
11SIGSEGV试图访问未分配给自己的内存,或试图往没有写权限的内存地址写数据终止程序
12SIGUSR2留给用户自定义留给用户自定义
13SIGPIPE通常在进程间通信产生终止程序
14SIGALRM时钟定时信号终止程序
15SIGTERM程序结束(terminate)信号终止程序
16SIGCHLD子进程(child)状态变化时, 父进程会收到这个信号忽略
17SIGCONT让一个停止(stopped)的进程继续执行
18SIGSTOP需要暂停某一程序暂停(stopped)进程的执行,注意它和terminate以及interrupt的区别:该进程还未结束
19SIGTSTP用户键入SUSP字符时(通常是Ctrl+Z)发出这个信号终止程序
20SIGTTIN当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN信号. 缺省时这些进程会停止执行.终止程序
21SIGTTOU类似于SIGTTIN, 但在写终端(或修改终端模式)时收到终止程序
22SIGURG有”紧急”数据或out-of-band数据到达socket时产生.终止程序
23SIGXCPU超过CPU时间资源限制终止程序
24SIGXFSZ当进程企图扩大文件以至于超过文件大小资源限制。终止程序
25SIGVTALRM虚拟时钟信号终止程序
26SIGPROF类似于SIGALRM/SIGVTALRM, 但包括该进程用的CPU时间以及系统调用的时间.终止程序
27SIGWINCH窗口大小改变时发出.终止程序
28SIGIO文件描述符准备就绪, 可以开始进行输入/输出操作.终止程序
29SIGPWRPower failure终止程序
30SIGSYS非法的系统调用。终止程序

由于初学,后面的一些信号我不敢保证正确性(后面的都是我查资料cv过来的,等我学明白之后我会来更新这个表格
上面列出的信号中,程序不可捕捉,阻塞,忽略的是SIGKILL,SIGSTOP


kill函数

int kill(pid_t pid, int signum

参数

  • pid:
    • >0,发送信号给指定的进程
    • =0,发送信号给跟调用kill函数的那个进程处于同一进程组的进程。
    • <-1,取绝对值,发送信号给该绝对值所对应的进程组的所有组员。
    • =-1,发送信号给,有权限发送的所有进程。
  • signum,待发送的信号

返回值

  • 成功,0
  • 失败,-1,errno被设置。

举个栗子

我们写一个程序,用子进程调用kill杀死父进程
源代码:

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

int main()
{
	pid_t pid;
	pid=fork();
	if(0==pid)
{
		printf("I am child,i will kill my parent\n");
		sleep(1);
		int res=kill(getppid(),SIGKILL);
		if(res==-1)
			perror("kill error");
	}
	else 
	{
		for(int i=0;;i++)	
			printf("%d-------I am parent, i will be killed by my child\n",i);
	}
	return 0;
}

效果:
1


alarm函数

每个进程都有唯一的闹钟
unsigned int alarm(unsigned int seconds)

参数

  • second,定时的秒数,单位是s

返回值

  • 上次定时剩余的时间

取消闹钟

alarm(0);

举个栗子

我们使用alarm来计时,统计计算机一秒钟能打印多少。
源代码

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

int main()
{
	alarm(1);
	int i;
	for(i=0;;i++)
		printf("%d\n",i);
}

效果:
1


setitimer函数

设置闹钟,可以替代alarm函数,精度微秒us,可以实现周期定时
int setitimer(int which,const struct itimerval * new_value,struct itimerval * old_val)

参数

  • which:选择计时方式
    • ITIMER_REAL:采用自然计时。(一般用这个)——>SIGALRM
    • ITIMER_VIRTUAL:采用用户空间计时——>SIGVTALRM
    • ITIMER_PROF:采用内核+用户空间计时——>SIGPROF
  • new_value:定时秒数
  • old_value:传出参数,上次定时剩余的时间.(如果不关心的话,可以直接传NULL)

返回值

  • 成功,0
  • 失败,-1,errno被设置

struct itimerval类型

struct itimerval
{
	struct timeval 
	{
		time_t tv_sec;			/*second*/
		suseconds_t tv_usec;	/*microsecond*/
	}it_interval;				/*用于设定两个任务之间的间隔时间*/
	struct timerval
	{
		time_t tv_sec;			/*second*/
		susecond_t tv_usec ;	/*microsecond*/
	}it_value;					/*第一次定时秒数*/
}

可以理解有两个定时器

  • 一个用于第一个闹钟什么时候触发
  • 第一个闹钟触发后间隔多少时间再次触发闹钟。

举个栗子

计数

功能和上面的alarm一样
源代码:

#include<unistd.h>
#include<stdio.h>
#include<sys/time.h>

int main()
{
	struct itimerval t;
	t.it_interval.tv_sec=0;
	t.it_interval.tv_usec=0;

	t.it_value.tv_sec=1;
	t.it_value.tv_usec=0;
	int res=setitimer(ITIMER_REAL,&t,NULL);
	for(int i=0;;i++)
		printf("%d\n",i);
	return 0;
}

效果:
1

向屏幕打印信息

源代码:

#include<unistd.h>
#include<stdio.h>
#include<sys/time.h>
#include<signal.h>

void fun(int signal)
{
	printf("hellow linux\n");
}

int main()
{
	signal(SIGALRM,fun);
	struct itimerval t;
	t.it_interval.tv_sec=2;
	t.it_interval.tv_usec=0;

	t.it_value.tv_sec=1;
	t.it_value.tv_usec=0;
	int res=setitimer(ITIMER_REAL,&t,NULL);
	while(1);
	return 0;
}

效果:
1


信号集操作函数

  • 信号集set函数
    • sigset_t set; :自定义信号集(这是一种类型)
    • sigemptyset(sigset_t* set);:清空信号集,全部置为0
    • sigfillset(sigset_t * set);:填满信号集,全部置为1
    • sigaddset(sigset_t* set);:将指定信号添加到信号集中,即将指定信号置为1
    • sigdelset(sigset_t* set);:将指定信号移除信号集,即将指定信号置为0
    • sigismember(const sigset_t* set,int signum);:判断指定信号是否在集合中。在—>1,不在—>0.
  • sigprocmask函数
    int sigprocmask(int how,const sigset_t * set,sigset* oldset);
    • how:
      • SIG_BLOCK:设置阻塞(位与)
      • SIG_UNBLOCK:取消阻塞(取反位与)
      • SIG_SETMASK:用自定义set填充mask (不推荐)
    • set:自定义的set\
    • oldset:旧有的mask(不用的话可以传NULL
  • sigpending函数
    int sigpending(sigset* set)
    读取未决信号集,参数是传出的未决信号集。

1

举个栗子

源代码:

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

void print_set(sigset_t* pset)
{
	for(int i=1;i<33;i++)
		if(sigismember(pset,i))
			printf("1");
		else 
			printf("0");
	printf("\n");
}

int main()
{
	sigset_t set,pset;
	sigemptyset(&set);
	sigaddset(&set,SIGINT);
	sigaddset(&set,SIGQUIT);
	sigaddset(&set,SIGKILL);
	sigaddset(&set,SIGHUP);
	sigprocmask(SIG_BLOCK,&set,NULL);
	while(1)
	{
		sigpending(&pset);
		print_set(&pset);
		sleep(1);
	}
	return 0;
}

效果
2


signal实现信号捕捉

注册一个信号捕捉函数,ANS设置,不同操作系统存在差异建议使用sigaction函数
sighandler_t signal(int signum,sighandler_t handler)

参数:

  • signum :捕捉信号
  • handler:捕捉信号后的操作函数

返回值

  • 成功,返回操作函数的返回值
  • 失败,返回SIG_ERR,errno被设置。

举个栗子

源代码:

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

void fun(int signal)
{
	printf("hellow linux,SIGQUIT has been not work\n");
}

int main()
{
	signal(SIGQUIT,fun);
	while(1);
}

效果:
1


sigaction实现信号捕捉

注册一个信号捕捉函数
int sigaction(int signum,const struct sigaction * act,struct sigaction * oldact)

struct sigaction类型

struct sigaction {
	void     (*sa_handler)(int);		//操作函数名
	void     (*sa_sigaction)(int, siginfo_t *, void *);		//一般不用管
	sigset_t   sa_mask;		//只作用于函数捕捉期间
	int        sa_flags;	//一般置为0,表示用默认的
	void     (*sa_restorer)(void);
};

举个栗子

源代码:

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

void fun(int signal)
{
	printf("catch you SIGQUIT\n");
}

int main()
{
	struct sigaction act;
	act.sa_handler=fun;
	sigemptyset(&act.sa_mask);
	act.sa_flags=0;
	sigaction(SIGQUIT,&act,NULL);
	while(1);
}

效果:
1


信号捕捉

信号捕捉的特性

  • 捕捉函数执行期间,信号屏蔽字 由 mask --> sa_mask , 捕捉函数执行结束。 恢复回mask
  • 捕捉函数执行期间,本信号自动被屏蔽(sa_flgs = 0).其他信号不屏蔽,如需屏蔽则调用sigsetadd函数修改
  • 捕捉函数执行期间,被屏蔽信号多次发送,解除屏蔽后只处理一次!

内核实现信号捕捉简析

3


综合练习

我们借助信号捕捉回收子进程
源代码:

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<signal.h>
#include<sys/wait.h>

void fun(int signal)
{
	while(waitpid(-1,NULL,0)!=-1)
		printf("wait successfully\n");

}

int main()
{
	int i;
	pid_t pid;
 	sigset_t set;
	sigemptyset(&set);
	sigaddset(&set,SIGCHLD);
	sigprocmask(SIG_BLOCK,&set,NULL);
	for(i=0;i<15;i++)
	{
		pid=fork();
		if(0==pid)
			break;
	}
	if(15==i)
	{
		struct sigaction act;
		act.sa_handler=fun;
		sigemptyset(&act.sa_mask);
//		sigdelset(&set,SIGCHLD);
		sigprocmask(SIG_UNBLOCK,&set,NULL);
		act.sa_flags=0;
		sigaction(SIGCHLD,&act,NULL);
		printf("i am parent,i wait my all child\n");
		while(1);
	}
	else
	{
		printf("i am %dth child,i will be waited\n",i+1);
	}
	return 0;
}

效果:
4


写在最后

个人亲身经验:我们学习的一系列Linux命令,一定要自己亲手去敲。不要只是看别人敲代码,不要只是停留在眼睛看,脑袋以为自己懂了,等你实际上手去敲会发现许许多多的这样那样的问题。正可谓“键盘敲烂,月薪过万


如果你觉得我写的题解还不错的,请各位王子公主移步到我的其他题解看看

  1. 数据结构与算法部分(还在更新中):
  • C++ STL总结 - 基于算法竞赛(强力推荐
  • 动态规划——01背包问题
  • 动态规划——完全背包问题
  • 动态规划——多重背包问题
  • 动态规划——分组背包问题
  • 动态规划——最长上升子序列(LIS)
  • 二叉树的中序遍历(三种方法)
  • 最长回文子串
  • 最短路算法——Dijkstra(C++实现)
  • 最短路算法———Bellman_Ford算法(C++实现)
  • 最短路算法———SPFA算法(C++实现)
  • 最小生成树算法———prim算法(C++实现)
  • 最小生成树算法———Kruskal算法(C++实现)
  • 染色法判断二分图(C++实现)
  1. Linux部分(还在更新中):
  • Linux学习之初识Linux
  • Linux学习之命令行基础操作
  • Linux学习之基础命令(适合小白)
  • Linux学习之权限管理和用户管理
  • Linux学习之制作静态库和动态库
  • Linux学习之makefile
  • Linux学习之系统编程1(关于读写系统函数)
  • Linux学习之系统编程2(关于进程及其相关的函数)
  • Linux学习之系统编程3(进程及wait函数)
  • Linux学习之系统编程4(进程间通信)

✨🎉总结

“种一颗树最好的是十年前,其次就是现在”
所以,
“让我们一起努力吧,去奔赴更高更远的山海”
在这里插入图片描述
如果有错误❌,欢迎指正哟😋

🎉如果觉得收获满满,可以动动小手,点点赞👍,支持一下哟🎉

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

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

相关文章

2024年初级会计资格考试报名照片要求,千万不要直接上传哦

2024年初级会计资格考试照片要求&#xff0c;千万不要直接上传哦。 第一步&#xff1a;支付宝搜索"亿鸣证件照"或者微信搜索”随时照“&#xff0c;然后进入小程序的搜索列表搜索"初级会计"&#xff0c;然后上传一张生活照或者自拍一张&#xff0c;就能制…

【计算机毕业设计】SSM企业工资管理系统

项目介绍 本项目包含管理员与普通员工两种角色&#xff0c; 管理员角色包含以下功能&#xff1a; 管理员登录,员工管理,部门管理,岗位管理,职称管理,工龄奖金管理,工资项管理,考勤管理,工资查询,统计图表等功能。 员工角色包含以下功能&#xff1a; 员工登录,个人信息管理…

lotus-farcaster 搭建

lotus 开源监控仪表盘 安装修改配置文件监听钱包地址指定安装目录可以修改脚本指定miner配置文件路径执行安装脚本 修改修改端口为9200修改安装脚本 9100改为9200&#xff0c;重新安装修改检查脚本 端口 9200&#xff0c;重新安装 安装成功prometheus 安装创建存储目录 配置文件…

SESV:通过预测和纠错实现精确的医学图像分割

SESV: Accurate Medical Image Segmentation by Predicting and Correcting Errors SESV&#xff1a;通过预测和纠错实现精确的医学图像分割背景贡献实验方法Thinking SESV&#xff1a;通过预测和纠错实现精确的医学图像分割 286 IEEE TRANSACTIONS ON MEDICAL IMAGING, VOL. …

[C#]基于deskew算法实现图像文本倾斜校正

【算法介绍】 让我们开始讨论Deskeweing算法的一般概念。我们的主要目标是将旋转的图像分成文本块&#xff0c;并确定它们的角度。为了让您详细了解我将使用的方法&#xff1a; 照常-将图像转换为灰度。应用轻微的模糊以减少图像中的噪点。现在&#xff0c;我们的目标是找到带…

2023 hnust 湖南科技大学 大四上 商务智能 课程 期末考试 复习资料

前言 《听了课就能及格》由于老师发的复习PPT内容过多&#xff08;近两万字&#xff09;&#xff0c;因此在此大幅删减由于老师透露太少&#xff0c;删减全凭主观意志&#xff0c;请谨慎参考&#xff01;&#xff01;&#xff01;猜测逻辑 过于细碎概念的不考&#xff08;不算…

Vue2 - 数据响应式原理

目录 1&#xff0c;总览2&#xff0c;Observer3&#xff0c;Dep4&#xff0c;Watcher5&#xff0c;Schedule 1&#xff0c;总览 vue2官网参考 简单介绍下上图流程&#xff1a;以 Data 为中心来说&#xff0c; Vue 会将传递给 Vue 实例的 data 选项&#xff08;普通 js 对象&a…

HackTheBox - Medium - Linux - Awkward

Awkward Awkward 是一款中等难度的机器&#xff0c;它突出显示了不会导致 RCE 的代码注入漏洞&#xff0c;而是 SSRF、LFI 和任意文件写入/追加漏洞。此外&#xff0c;该框还涉及通过不良的密码做法&#xff08;例如密码重用&#xff09;以及以纯文本形式存储密码来绕过身份验…

Python轴承故障诊断 (九)基于VMD+CNN-BiLSTM的故障分类

往期精彩内容&#xff1a; Python-凯斯西储大学&#xff08;CWRU&#xff09;轴承数据解读与分类处理 Python轴承故障诊断 (一)短时傅里叶变换STFT Python轴承故障诊断 (二)连续小波变换CWT_pyts 小波变换 故障-CSDN博客 Python轴承故障诊断 (三)经验模态分解EMD_轴承诊断 …

算法训练day60|单调栈part0

参考&#xff1a;代码随想录 84.柱状图中最大的矩形 要求当前柱形的左右两边第一个比他小的位置 对于高度为5的柱子&#xff08;index为2&#xff09; mid 他的左边第一个比他小的柱子为1&#xff0c;index为1 left 他的右边第一个比他小的柱子高度为2&#xff0c;index为4…

场景识别与词袋模型

目录 1. 任务要求2. 数据集3. 实现算法3.1 目标实现3.2 Tiny images representation3.3 SIFT特征词袋表示3.4 相关算法 4. 实验结果4.1 基础结果展示4.2 算法超参的影响4.2.1 Tiny images size4.2.2 Vocabulary size 4.3 其他结果 5. 源代码 1. 任务要求 输入&#xff1a;给定…

redis黑马点评项目启动指南(含mac m1pro | windows11 wsl2 ubuntu环境配置 持续更新中~)

redis黑马点评项目学习笔记 mac m1pro windows 含项目配置教学 mac M1pro环境配置windows11 wsl2 ubuntu 环境配置一.短信登录1. 1发送验证码1.2短信登录注册1.3登录校验拦截器补缺Cookie Session Token1.4基于redistoken认证实现短信登陆1.5完善token认证的刷新机制 2.商户查询…

Redis 给集合元素单独设置过期

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、场景 1.1 消费队列 1.2 Redis实现 二、常见的方案 2.1 为单独的 field 设置过期 2.2 设置整体过期时间 2.3 zset 结合 sc…

项目实战:数字孪生可视化大屏幕,实现生产过程实时监控

项目介绍 智慧工厂数据可视化系统&#xff0c;融合工业大数据、物联网、人工智能等各类信息技术&#xff0c;整合厂区现有信息系统的数据资源&#xff0c;实现数字孪生工厂、设备运维监测、智能管网监测、综合安防监测、便捷通行监测、能效管理监测、生产管理监测、仓储物流监…

【数据分析实战】冰雪大世界携程景区评价信息情感分析采集词云

文章目录 引言数据采集数据集展示数据预处理 数据分析评价总体情况分析本人浅薄分析 各游客人群占比分析本人浅薄分析 各评分雷达图本人浅薄分析 差评词云-可视化本人浅薄分析 好评词云-可视化本人浅薄分析 综合分析写在最后 今年冬天&#xff0c;哈尔滨冰雪旅游"杀疯了&q…

技术学习|CDA level I 业务分析方法

业务分析方法有三个主要构成部分&#xff1a;业务指标分析、业务模型分析及业务分析方法。 业务指标分析是发现业务问题的核心方法&#xff1a;用于通用指标和场景指标的计算及分析方法&#xff0c;以及指标体系的设计与应用方法。业务模型是从一系列业务行为中抽象出来的信息…

请你列出逻辑电路中的24种表达式

随着时代发展&#xff0c;数字电路的使用频率越来越高&#xff0c;完全不低于模拟电路&#xff0c;因此从事数字电路的工程师越来越多&#xff0c;如果你想成为一名优秀的数字工程师&#xff0c;一定要学会下面的逻辑电路表达式&#xff01; 1、基本逻辑运算与运算 (AND): A AN…

04 帧 Frame

文章目录 04 帧 Frame4.1 相机相关信息4.2 特征点提取4.2.1 特征点提取 ExtractORB()4.3 ORB-SLAM2对双目/RGBD特征点的预处理4.3.1 双目视差公式4.3.2 双目图像特征点匹配 ComputeStereoMatches()4.3.3 根据深度信息构造虚拟右目图像&#xff1a;ComputeStereoFromRGBD() 4.4 …

Python中的h5py包使用

h5py是一个非常强大的工具&#xff0c;可以用于存储和处理大量科学数据。它可以帮助我们提高数据处理的效率和可靠性。 目录 一、h5py1.1 特点1.2 主要功能1.3 常用场景 二、安装h5py三、示例代码3.1 运行结果 四、总结 一、h5py h5py是Python中的一个库&#xff0c;提供了对H…

JS函数实现数字转中文大写

JS函数实现数字转中文大写 1. 数字转字符,分割,去除空字符2. 遍历分割字符,替换为中文3. 增加四位数单位4. 处理零5. 拼接四位数据和单位 项目中,JS将万亿以下正整数转为中文大写 1. 数字转字符,分割,去除空字符 function toChineseNumber(num){const strs num.toString().re…