【Linux信号】Linux进程信号(上):信号产生方式和闹钟

【Linux信号】Linux进程信号(上):信号产生方式和闹钟

头像

🎬 个人主页艾莉丝努力练剑
专栏传送门:《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录
Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享

⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平


🎬 艾莉丝的简介:

在这里插入图片描述

文章目录


1 ~> 理解信号是什么,为什么要有?生活中的信号

1.1 信号是什么?

1.1.1 普通信号和实时信号

在这里插入图片描述
  • kill -l可以查看
在这里插入图片描述

常用的只有1~3134~64,没有0号——一共62个,不是64个哦!

前31个是普通信号,后面带RT(real-time)实时信号。我们只考虑1-31就好了。

1.1.2 信号的本质

在这里插入图片描述


什么是通知?什么是异步?

  • 通知:这个通知需要我们理解一下——事件通知。
  • 异步:通知的到来,跟我不同步。
    举个例子,我点了个外卖,然后就打游戏,外卖小哥送货上门,敲门是一个通知,说明外卖到了,我和外卖小哥是不同步的。
    异步关系就是没关系(你做你的,我做我的);同步关系就是有关系(我得等你做完再做)。
在这里插入图片描述
  • 再比如,比如我是一个老师,我讲课,突然快递电话打过来了,我叫张三去帮我取快递,但是我等张三找完快递回来再继续讲,这就是个同步的关系,张三不回来,我就不往下讲。
  • 如果我继续讲课,张三也在找快递,我讲课、他帮我办事,这个就是个异步的关系——我讲我的课,张三找他要帮我找的快递。

1.2 生活中有哪些信号?以及一些结论总结

大抵有如下这些:

在这里插入图片描述


上面这些所有的信号产生的几乎都是异步的。

  • 人能够识别对应的、甚至还没有发生的这些信号

为什么能够识别这些信号?

人是经过教育的,早就知道信号对应处理动作的对应关系。

操作系统给目标进程发送信号,目标进程能不能识别呢?

  • 答案是:进程天然能够识别——进程和信号都是程序员写的代码(进程相当于我们人,已经被程序员教育过了,程序员设计好了)
    进程能够识别信号,并且已经知道怎么处理信号了。
在这里插入图片描述

1.2.1 man 7 signal:查看信号部分的内容

man 7本身不是查看信号的指令,而是指定要查看的手册章节为第 7 章。在 Linux 系统中,信号相关的概述通常位于第 7 章,因此要查看信号的详细信息,可以使用命令:

man7 signal 

会显示信号的概念、列表、默认行为等全局说明。此外,信号相关的系统调用(如 signal()、sigaction())一般在第 2 章,库函数(如 sigsetops())可能在第 3 章,而第 7 章主要提供更上层的概述和标准约定。

在这里插入图片描述

1.2.2 信号没有产生也知道怎么处理

在这里插入图片描述

1.2.3 收到一个信号会立即处理它吗?

当我们收到一个信号,准备处理这个信号,这里的我们即进程,会立即处理这个信号吗?有时候中断不了呢!处理不了信号。

  • 对于信号的处理——不会立即处理——信号可能会立即处理,只有合适的时候会处理。

既然有一定概率不会立即处理,那么得要有把信号临时保存起来的能力——不会立即处理,就得有临时保存的能力。

在这里插入图片描述

比如外卖小哥打电话,答应去取外卖之后如果没有保存信号,是不是就打游戏打着打着就忘了取外卖了。

1.2.4 信号怎么处理?

  • 默认:我后面打完游戏去拿外卖回来吃
  • 忽略:我直接就不想拿了(但是并不是忘了,只是不想)
  • 自定义:比如我拿完外卖直接丢了或者干别的事,自定义行为

我们就以红绿信号灯为例:

  • 红灯——自动停了——默认信号。
  • 收到信号,也处理了,但是处理的方式是忽略——忽略信号。
  • 红灯亮了,别人要么忽略要么停下,而你在跳舞——自定义信号。

1.2.5 总结一些这些小结论

  • 设别信号是内置的,进程能自己识别信号,是内核程序员写的内置特性。
  • 信号的处理方法,在信号产生之前就已经准备好了。
  • 处理信号不是立即处理的,因为我可能正在做优先级更高的事情,会选择合适的时候进行处理。比如我在打游戏,外卖员给我打电话叫拿外卖,那我肯定是先忙完手头的事再去拿。
  • 信号不会被立即处理,所以就注定了进程要有临时保存信号的能力!
  • 信号处理的动作有三种:默认、忽略、自定义,后续都叫信号捕捉

1.2.6 图示

在这里插入图片描述

1.3 信号具体化

1.3.1 为什么是大写?这些大写的名字是什么?

没有0号信号,我们知道,信号是有编号的。

在这里插入图片描述
  • 这些大写的名字是什么呢?这些大写的名字就是宏。

1.3.2 头文件(含内核查看)

在这里插入图片描述

我们查看内核——

在这里插入图片描述
  • 1~31普通信号,我们现在是分时操作系统;34~64实时信号(有RT,就是real-time)。

1.3.3 保存信号

1.3.3.1 进程一定要有临时保存信号的能力

进程一定要有临时保存信号的能力——原因我们前面已经分析了。

  • 信号就是个数字吗?还不够具体。
  • 进程需要使用特定的数据类型来保存对应的信号!

我们要分析两个问题——

  • 1、我们要保存什么信号?哪一个信号?
  • 2、我们要保存信号的侧重点?哪一个信号是否产生了?
1.3.3.2 我们可以用什么样的特定的数据类型来保存信号?位图
  • 1、比特位的位置表示信号编号
  • 2、比特位的内容要么是1要么是0,1或者0——是否收到对应的信号!

用位图表示对应的1~31个编号。

关键是这个位图在哪里?

  • 这个位图应该在哪里?设计在 进程的PCB 里面。

1.3.4 信号的位图在进程PCB里面,谁有资格修改内核数据结构中的字段?

在这里插入图片描述
  • 信号的位图在进程PCB里面,谁有资格修改内核数据结构中的字段?
  • 谁有资格——这个世界上,能够给进程写入信号的家伙只有一个——就是操作系统OS。

操作系统不能决定全部的往进程写入信号(有能力,但是也只是一个办事的)——用户让你办的!

  • 用户通过系统调用让操作系统向目标进程发送信号。
在这里插入图片描述

1.4 为什么要有信号?

  • 我们一看到问为什么,一定要想到没有这个东西会出现怎么样的问题。

Linux之所以需要信号,是为了提供一种 异步且轻量级的进程间通信与事件通知机制,使操作系统能够立即打断进程的正常执行流,以通知其发生了诸如用户中断(Ctrl+C)、硬件异常(段错误)、定时器到期或子进程退出等突发状况。信号机制弥补了管道或共享内存等同步通信方式的不足,让进程能够以一种统一、即时的方式响应外部与内部的高优先级事件,是实现进程控制、异常处理和系统紧急交互的核心基础。

1.5 信号是什么的思维导图

如下图所示:

在这里插入图片描述

1.6 如何自定义捕捉信号?

一个函数,我们写一段代码——

  • 对任何信号进行一下捕捉!
man signal 
在这里插入图片描述

信号是发给进程——处理信号是进程自己去处理的。

回调函数

在这里插入图片描述


signal函数其实是对指定信号未来进行自定义处理的一种延时设定。

我们挑一个信号——这里挑选2号信号:SIGINT

在这里插入图片描述


运行一下:

在这里插入图片描述
kill-2[pid]
在这里插入图片描述
  • 查看信号手册:

我们在手册(man 7 signal)里面查看2号信号——

在这里插入图片描述


要让2号信号不要终止进程,调用我自定义的方法——

在这里插入图片描述

这就叫做信号捕捉

  • Ctrl C(就是给前台发送2号信号)也终止不了了
在这里插入图片描述

我们修改一下代码:

在这里插入图片描述
  • 信号最终是由进程自己去处理的,通过上面的代码可以验证——两个pid一样
在这里插入图片描述

对某个信号进行设定,那这个参数sig是什么?

在这里插入图片描述

我们继续验证一下:

在这里插入图片描述


本来就是设定了是2号信号,为什么还要把2号信号传进来呢?

  • Linux内部,不同的的信号捕捉的处理方法可以是同一个!

所以我们得知道是哪一个信号——我们多个信号设置处理方法,可以是同一个处理方法。

在这里插入图片描述


我们再来验证一下:

在这里插入图片描述

可以通过传入的参数来区分是哪个信号触发了自定义捕捉。

在这里插入图片描述

IGN的参数是1:

在这里插入图片描述

我们验证一下:

  • 所有的2号信号都被忽略了!
在这里插入图片描述
  • 还有一种是恢复默认:SIG_DFL
在这里插入图片描述

我们再运行一下:

在这里插入图片描述
  • 符合预期,就是进程终止,说明恢复默认了!

代码跑完对还是不对由退出码决定。

在这里插入图片描述
  • 运行一下我们发现,进程果然就杀不掉了!
在这里插入图片描述


虽然1~31都能够设定自定义捕捉,但是有两个信号是不能够设定的:9号信号和19 / 20号新号(看系统,具体是哪个)

  • 9号信号很重要:9号信号不能被自定义捕捉、不能被忽略
在这里插入图片描述

2 ~> 信号的产生

  • 让OS给目标进程写信号。

操作系统给目标进程写信号——不管怎么样,必须经过操作系统——因为只有操作系统能够有资格修改内核数据结构。

产生信号这个话题内容有点多。信号的产生是异步产生的,所以不会立即处理。

2.1 信号产生的方式

在这里插入图片描述

2.1.1 使用系统命令产生,kill命令

在【自定义捕捉信号】那里我们已经见识过了。

2.1.2 通过键盘产生信号

放开循环:

我们运行一下:

在这里插入图片描述

我们查看手册,来一一对应一下,看看它对于当前进程的处理动作:

  • term和core都是进程终止

Ctrl C证明我们可以通过键盘产生信号(组合键的方式)。

  • 为什么你如果启动一个进程之后,Ctrl C可以终止这个进程!
    Ctrl C:发送了2号信号,2号信号的处理动作是终止!
  • 我们保留一个问题:键盘怎么能够向目标进程发送信号呢?
    键盘是个硬件啊,进程是个软件啊,怎么发送信号?

2.1.3 产生信号的还有一种方式:系统调用

kill:发送信号给进程。

在这里插入图片描述


第一个参数:发送给哪个进程;第二个参数:发送哪个信号。

系统底层肯定要调用这个kill

  • 管道那里有一个对应的命令:mkfifo
在这里插入图片描述
  • Stat:获取函数的属性
在这里插入图片描述
  • printf:底层也是调用了同名系统调用
在这里插入图片描述

很多命令底层用到了它的同名系统调用!

我们来测试一下——

  • 输出参数不匹配,就给你打印一个使用手册,类似于平常编译时候的报错
  • 类型要转成整数
在这里插入图片描述

然后就可以使用kill系统调用,向目标进程写入信号——

在这里插入图片描述

运行一下,我们发现多了个\n(左边黄色框框):

在这里插入图片描述


我们再修改一下源代码:

\r都去掉

在这里插入图片描述


我们再运行,还是前面的那种运行的图,这次我们看本次运行的结果:

在这里插入图片描述


kill命令底层调用了kill系统调用——让操作系统向目标进程发送指定信号。

  • 如下图,验证了9号信号不能被捕捉
在这里插入图片描述
  • raise:自己给自己可以发任何信号
  • 修改代码,让进程自己给自己发送信号
在这里插入图片描述
  • 自己给自己发,1秒打一次——
在这里插入图片描述


一个系统调用可以给自己发送任意信号。

  • raise底层封装了kill
在这里插入图片描述
  • abort
在这里插入图片描述


我们修改一下代码,加入abort函数:

在这里插入图片描述
  • 运行:当前进程立即终止了
在这里插入图片描述


我们发现信号是6号信号!

  • abort:也是Core(终止进程)
在这里插入图片描述


6号信号通常用来进行异常终止。

既把信号捕捉了,进程也终止了。

  • 6号信号还很特殊:可以理解为不可以被捕捉

可以设置捕捉动作,但是最后还是会终止进程!——异常。

  • 6号信号也可以被算做是不能被捕捉、忽略的
在这里插入图片描述

2.1.4 第四种产生信号的方式:异常

在这里插入图片描述

获取到哪个信号呢?

在这里插入图片描述
  • 我们放开信号捕捉
在这里插入图片描述
  • 运行

信号捕捉函数一直被触发(不是因为while循环)——死循环。

在这里插入图片描述
  • 杀掉进程
在这里插入图片描述

11号信号——

在这里插入图片描述
  • 证明不是因为while(true)循环
在这里插入图片描述

照样还是死循环:

在这里插入图片描述

我们故意来一个/0错误:

在这里插入图片描述
  • 8号信号:SIGFPE(浮点异常)

尽管名字包含“浮点”,但它实际上涵盖了更广泛的算术错误,包括整数运算中的错误。

在这里插入图片描述

观察一下:

在这里插入图片描述

除零错误会被当做信号发出来。

进程自己就会收到对应的信号!

在这里插入图片描述

为什么会把进程异常当成信号?进程异常是如何被OS识别、并且解释成为信号的?!

2.1.5 第五种信号产生的方式:由软件条件产生信号

这里就有点抽象了。

  • 管道:r端关闭,W端打开,W端一直写 --> OS就会把写进程直接就杀掉了

操作系统认为管道不具备写条件——OS就把写进程杀掉了。

你得罪了软件或者硬件,操作系统都要把你杀掉。

进程退出一定是异常了,收到信号了。

关于闹钟(alarm)的相关话题我们在2.4细说。

2.2 三个系统调用层层递进

2.2.1 三个系统调用层层递进

前面的killraiseabort三个系统调用我们发现是层层递进的!

在这里插入图片描述

2.2.2 OS先收到键盘输入,再由操作系统转换成信号发送给目标进程

接下来,先不往后面看,先复盘一个问题:

在这里插入图片描述


我们已经说过啦,必须只能是OS!

只有操作系统可以在内核操作系统里面对PCB内部的位图进行修改。

换句话说,通过键盘产生信号也并不是键盘直接产生的信号,必然是OS先收到键盘
的输入的,再由操作系统转换成信号发送给目标进程!

2.3 键盘怎么能够目标进程发送信号呢?

  • 1、如何理解键盘输入?键盘是基于硬件中断来进行工作的!
  • 2、OS如何解释快捷键?(组合键)ctrl cctrl \,……可能会直接解释成为信号!
  • 3、OS怎么知道信号应该发送给哪一个进程呢?操作系统怎么知道?要补充一点网络时要用的知识!

2.3.1 如何理解键盘输入?

  • 如何理解键盘输入?

我们通过图来理解一下——

在这里插入图片描述


我的操作系统怎么知道用户把键盘按下了、按下了哪个(键)字符?——驱动去处理的。

我们关心的是,OS怎么知道键盘上有数据了?!

计算机的外设很多,操作系统要做各种工作,你能够轮询吗?这种做法在技术上可以实现,但是这会让操作系统变得很慢!

  • 硬件中断
在这里插入图片描述

作为CPU,只要被动等待就行。

  • 中断:是计算机组成原理的最重要的重点,没有之一!没有学懂中断,就是没有学懂操作系统。

操作系统有些东西没有学懂,问题可能不在操作系统上面,可能是在理解计算机组成原理上。

  • 跨学科学习是一个比较重要的思路

除了冯诺依曼告诉我们的外设,还有……

键盘和CPU是一根线上连着的。

  • 针脚如下所示(英特尔的):会和主板相连。
在这里插入图片描述


这些针脚会和主板连上,所以主板走线会和外设勾连。

网卡的例子。

告诉CPU,数据已经到来了,触发中断。

操作系统并不知道键盘上有数据了,而是中断告诉CPU了。

2.3.2 OS如何解释快捷键?

键盘的字符既有“ABCD……”这样的正常的字符,也有“Ctrl C”“Ctrl V”……这样的组合键,操作系统会把组合键解释成信号(会判断,ctrl c是特殊字符,在ASCII码表里存在)——操作系统会有系统调用:给目标进程发送信号——kill(pid,signum)

2.3.3 OS怎么知道信号应该发送给哪一个进程呢?

  • 说到这个,我们先要谈一个进程组和作业的话题。

如下图,我们这里创建了三个进程:

在这里插入图片描述

三个进程的父进程都是同一个:bash;即这三个进程是兄弟进程。

2.3.3.1 进程组
  • 进程是有组的概念
在这里插入图片描述
2.3.3.2 会话ID:SID

这三个兄弟进程的SID一样,属于同一个会话,以bash进程命名:

在这里插入图片描述


一般情况下,同一个组里面大家会使用同一个终端文件(因为我们是远程登录的Xshell,这里的终端文件是虚拟网络文件)。

每一个用户登录,都要建立会话,打开终端文件。

如果在bash命令行里再启动一个进程

  • 我们把代码改一下,把abort注释掉

同一个终端下,在创建一个进程——

在这里插入图片描述


我们发现这个进程和那三个进程不在一个进程组里面,但是在一个会话组里。

本质是一个会话里面创建一个新的进程组(哪怕进程组只有我一个进程)。

  • 我们把for循环也注释掉,方便ctrl c等操作
2.3.3.3 前台进程和后台进程 + 前后台进程切换

进程分前台进程和后台进程:

在这里插入图片描述

我们取地址&,就变成后台进程了——

在这里插入图片描述
  • 我们今天要把这个说法变一变。

我们发现Ctrl C不能干掉进程了——

在这里插入图片描述

再看一下:

在这里插入图片描述

观察运行的打印结果:

在这里插入图片描述
  • 我们在代码里面把休眠时间改长一点

前台进程:

在这里插入图片描述

后台进程:

在这里插入图片描述


我们只能这样删除:

kill-9[pid]
  • 我们把打印也注释掉,只看到时候的任务号
在这里插入图片描述

再创建一大批的任务:

在这里插入图片描述

每一个进程都有自己的进程组,都属于同一个会话:

在这里插入图片描述
  • 启动后台任务,Ctrl C是杀不掉的!
在这里插入图片描述
2.3.3.3.1 前台进程

定义:占用当前终端,必须等该进程执行完毕才能输入其他命令。

示例:直接执行 sleep 100,终端会被占用 100 秒,无法输入新命令。

2.3.3.3.2 后台进程

定义:在后台运行,不占用终端,用户可继续执行其他命令。

启动方式:在命令末尾加 &

sleep100&

输出会显示作业号 [1] 和进程 PID

2.3.3.3.3 作业控制命令
1 jobs
jobs

查看当前终端中的后台作业列表(带作业号)。

输出示例:

[1]+ Running sleep100&
2 fg(foreground)

将后台作业调回前台继续运行。

fg %1 # 将作业号 1 的作业调到前台fg# 默认将最近一个后台作业调到前台
3 bg(background)

将暂停(挂起)的作业放到后台继续运行。

  • 先用 Ctrl+Z 暂停前台进程,此时作业变为“已停止”状态。
  • 执行 bg %1 让它在后台继续运行
sleep200# 执行,然后按 Ctrl+Z 暂停bg %1 # 在后台继续运行jobs# 可看到状态变为 Running
4 Ctrl+Z

暂停当前前台进程,将其放入后台(停止状态)。

5 Ctrl+C

终止前台进程。

进程可以在前台或后台运行,并通过 fgbg 命令进行切换

在这里插入图片描述
2.3.3.4 一些结论
结论1:Ctrl C只能用于终止前台进程!

Ctrl C只能用于终止前台进程!

作业 / 独立的进程组有什么关系?
在这里插入图片描述
  • 进程组是用来完成作业的!

进程组允许是一个进程。

作业是由进程组完成的!

在命令行./进程(启动一个进程)属于上面所说的特殊情况。

task(作业 / 任务)——进程组和作业就像是硬币,一体两面。

如下图所示:

在这里插入图片描述
会话与进程组(作业)的作业之间的关系?

会话内部至少包含一个进程组(就是bash)。

会话: 是由一个或者多个进程组构成的 进程组的集合,通常会有属于自己的终端文件!

通过 **fg [任务号]**把后台任务变成前台之后,就可以Ctrl C删掉了。

在一个会话中,任何时刻,只允许一个进程(组)在前台!

只要一个进程在前台,我的命令就没法被其它进程执行了。

这里存在两个问题:

  • 1、为什么其他命令就无法执行了?

因为在一个会话中,任何时刻,只允许一个进程(组)在前台,能够接受、执行命令的bash在哪里?自动切换成为后台进程组了(为什么bash变成后台了,命令就无法进行了)!获取不到命令,就无法执行命令了!

键盘只有一个,在会话里有这么多进程,谁拥有键盘,谁就是前台进程。

  • 2、什么叫做前后台?
  • 能够直接获取用户输入的基础,叫做前台进程;
  • 否则叫做后台进程

因为键盘只有一个,任何时刻只允许一个人输入(只有一个人,一个输入),谁拥有终端文件(主要是键盘),谁就是前台!获取不到键盘,所以就无法输入了。

  • 终端文件:键盘和显示器的集合体

后台进程可以写数据(向显示器打印),但是后台进程无法从键盘读取数据(读取的时候会被系统直接暂停掉)。
这就叫做 【会话管理】

任何时刻,前台进程(组)只有一个!

  • 键盘输入:操作系统怎么知道要把信号发送给谁?

前台进程只有一个,所以操作系统知道未来要把信号发给哪个进程!

只能发送给前台进程(组),而且前台进程只有一个!


前后台切换就像是 “孔融让梨”

  • Ctrl C严格意义上不是杀掉一个进程,而是杀掉一个进程组。

父进程子进程都被Ctrl C杀掉了。

登录两次、三次、……系统是不是每次都要创建这些东西?
剩下的,在网络讲【守护进程】的时候再介绍。

在这里插入图片描述
后台不允许获取
在这里插入图片描述

代码再重编一下:

在这里插入图片描述

把前台进程被暂停了,前台进程就会自动切换到后台进程。

  • 这里证明了,后台进程能够打印、但是不能获取,一旦获取就会暂停。
在这里插入图片描述
  • 谁拥有键盘,谁就是前台;谁能够获取键盘的输入,谁就是前台。

当你按键盘一定是和某个会话或者某个终端相关联。

2.4 alarm:设置一个若干秒之后的闹钟

学习一个新的函数:alarm

再认识一个信号:14号信号。

在这里插入图片描述


操作系统收到这个信号默认的处理动作是:Term

2.4.1 闹钟的返回值问题

我想看到的现象是收到闹钟前,进程会疯狂打印cnt计数器:

在这里插入图片描述


效果:

  • 计数器才加了1万多次
在这里插入图片描述

为了对比为什么CPU计算速度这么快还是只打印1万多,证明是收到了闹钟信号:

在这里插入图片描述


一万多次肯定对于当前的CPU还是太少了,我们纯在内存当中做++:

在这里插入图片描述

真实情况下是把一个整数加到4亿多次:

在这里插入图片描述


至于有没有回绕呢?应该是没有的。

为什么会有这么大的数量级上的差别的呢?

输出的本质是IO,IO比较慢,这个IO是要经过网络的,要访问外设。

4亿多次是因为没有访问外设,直接访问了内存,内存级别的操作。

这样我们对CPU访问外设速度非常慢有了一个量化的认识。

我们验证了闹钟本身的一些操作。

如果我们设置一个20秒的闹钟,在第五秒的时候就把闹钟提前唤醒了,这种闹钟给一个进程只能设置一个,已经设置了,过了一段时间后悔了,要重设闹钟,下一次返回值就是返回上一个闹钟的剩余时间。

验证一下:

在这里插入图片描述


如果后悔了,设置闹钟为0秒:

在这里插入图片描述

就是返回上一个闹钟的剩余时间,0就是取消闹钟。

如下图,我们先设置alarm(10);,再设置成alarm(0);之后,因为第一个闹钟是10秒,使用返回了10 - 0 = 10秒。

在这里插入图片描述


我们把闹钟取消了,收不到闹钟,就会死循环:

在这里插入图片描述


我们改一下代码:

在这里插入图片描述


再过n个2秒我们就再也收不到闹钟了。

在这里插入图片描述


这就说明,alarm是一个一次性闹钟,而且这个闹钟只对这个进程只会有同一个闹钟(从头到尾调用alarm就一个闹钟)。

在这里插入图片描述


我们可能要对闹钟进行重复设置,时间得卡好!

  • 下面这样写是不行的
在这里插入图片描述


当我调用下一个的时候闹钟就收不到闹钟了——上一个闹钟根本没有响!

重复设置一定要保证上一个闹钟已经被触发了。

一定要写在handlersig函数里:

在这里插入图片描述


此时我们就可以每隔2秒使用一次闹钟:

在这里插入图片描述


这就是闹钟的返回值问题。

在这里插入图片描述

2.4.2 闹钟的应用场景

闹钟功能具有延时触发的能力。

Windows中的关机程序:

shutdown-t10 -s(带选项) 

实现 延迟关机 的功能。

在这里插入图片描述

其实严格意义上这个工作不是闹钟完成的,而是定时器完成的。

有点类似于“看门狗”——

  • 使用 alarm 实现看门狗(B进程)
#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<signal.h>#include<sys/shm.h>#include<sys/ipc.h>#defineMAX_COUNT100#defineSHM_KEY0x1234int*counter;// 共享内存指针voidwatchdog_handler(int signum){// 每次闹钟触发,计数器减1(*counter)--;printf("watchdog: counter = %d\n",*counter);if(*counter <=0){printf("watchdog: no feed, shutting down...\n");// 执行关机命令,如 system("shutdown -h now");exit(0);}// 重新设置1秒后的闹钟alarm(1);}intmain(){// 创建/获取共享内存int shmid =shmget(SHM_KEY,sizeof(int), IPC_CREAT |0666);if(shmid <0){perror("shmget");exit(1);} counter =(int*)shmat(shmid,NULL,0);*counter = MAX_COUNT;// 初始值// 注册信号处理函数signal(SIGALRM, watchdog_handler);// 启动第一个1秒闹钟alarm(1);// 进入无限循环,等待信号while(1){pause();// 等待闹钟信号触发}return0;}
  • A进程喂狗

A进程只需要通过共享内存访问计数器,并定期重置:

#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<sys/shm.h>#defineMAX_COUNT100#defineSHM_KEY0x1234int*counter;intmain(){// 获取共享内存int shmid =shmget(SHM_KEY,sizeof(int),0666);if(shmid <0){perror("shmget");exit(1);} counter =(int*)shmat(shmid,NULL,0);while(1){// 模拟正常工作printf("working...\n");sleep(80);// 每隔80秒喂狗一次(小于100秒即可)// 喂狗:重置计数器*counter = MAX_COUNT;printf("feed watchdog, counter reset to %d\n",*counter);}return0;}
在这里插入图片描述
在这里插入图片描述

2.4.3 理解闹钟

理解闹钟,看看操作系统内同时存在多种闹钟(定时器)?操作系统要不要管理?怎么管理?先描述,再组织。

我们是先描述了:

在这里插入图片描述

怎么再组织呢?

在这里插入图片描述
  • 如果未来5秒的闹钟没有响,那么后面的15秒的50秒的、150秒的一定没响。

只要关注未来超时时间最近的闹钟即可:

我们可以用什么数据结构来管理?

OS给目标进程发送 SIGALARM

未来自己使用定时器应该会使用堆结构,这就是为了方便理解。

堆结构有缺陷,不能遍历,不能很好地支持取消。

  • 闹钟,是单独硬件来计时吗?如果是CPU来计时的话,它不得很忙,一边处理进程,还一边计时。

问题1:闹钟是软件实现的!

问题2:闹钟计时取决于操作系统是如何运行的,操作系统也是个软件,谁运行操作系统?原因是,键盘可以产生中断,操作系统内部也存在一种硬件单元,晶振,也会触发中断,中断向量表,操作系统是基于中断的一种集合。

在这里插入图片描述


一般触发时钟中断的硬件周期是固定的,比如每隔一纳秒,因此在OS内部就可以定义一个全局变量记录中断次数,如果是100,等于说从开机到现在已经过了100纳秒了,次数 = 时间。时钟中断,token中断,操作系统能够算出来从开机到现在过了多少秒,5秒会变成5^9次!如果次数超过了就是超时。计算机里把时间转化成次数,计时不是由CPU计时的,由外部晶振的振动周期决定的。中断越强,CPU响应能力越强。

操作系统并不连续,而是一卡一卡的。

  • 除0死循环,标志位没清,再次上下文时,要先检查core dumped标志位,才运行吗

CPU已经识别出来了,也会走中断,走中断也会走中断向量表,也属于异常。

只要core dump一次就可以了。

自定义捕捉了就没有core dump了,没有走自定义捕捉就没有走系统那一套,就没有core dump了。


结尾

uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!

结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主“一键四连”哦!

往期回顾:

【Linux进程间通信:共享内存】为什么共享内存的 key 值由用户设置

🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡૮₍ ˶ ˊ ᴥ ˋ˶₎ა

Read more

保姆级教程:从零搭建AI系统权限控制系统

保姆级教程:从零搭建AI系统权限控制系统

保姆级教程:从零搭建AI系统权限控制系统 手把手教你,如何在3小时内搭建完整的AI权限安全架构,避免Meta式的数据“裸奔”事故 前言:为什么要学这个? 2026年3月22日,Meta AI发生重大数据泄露事故——敏感数据“全员可见”2小时。如果你也正在开发AI项目,这种事故也可能发生在你身上。 本教程将带你从零开始,一步步搭建一个完整的、可实战的AI权限控制系统。无论你是个人开发者、小团队,还是大型AI项目,都能直接应用。 预计完成时间: 3小时 所需技能: 基础Python、Linux命令行、Git 第一阶段:准备工作(15分钟) 第1步:环境准备 # 1. 安装Python和相关依赖 pip install casbin flask sqlalchemy redis # 2. 安装数据库(推荐PostgreSQL) sudo apt-get install postgresql

By Ne0inhk
文科生封神!Python+AI 零门槛变现:3 天造 App,指令即收入(附脉脉 AI 沙龙干货)

文科生封神!Python+AI 零门槛变现:3 天造 App,指令即收入(附脉脉 AI 沙龙干货)

🎁个人主页:User_芊芊君子 🎉欢迎大家点赞👍评论📝收藏⭐文章 🔍系列专栏:AI 文章目录: * 一、前言:打破“AI是理科生专属”的迷思 * 二、行业新趋势:为什么文科生学Python+AI更有优势? * 2.1 文科生 vs 理科生:AI时代的核心竞争力对比 * 2.2 核心变现逻辑:靠Python+AI,“指令即收入” * 三、Python+AI零基础学习路径(文科生专属版) * 3.1 学习路径流程图 * 3.2 分阶段学习核心内容(新颖且落地) * 阶段1:Python核心基础(7天)—— 只学“AI开发必备” * 阶段2:AI大模型交互(10天)

By Ne0inhk
Flutter 组件 deepseek 的适配 鸿蒙Harmony 实战 - 驾驭国产最强大模型 API、实现鸿蒙端 AI 原生对话与流式渲染的高效集成方案

Flutter 组件 deepseek 的适配 鸿蒙Harmony 实战 - 驾驭国产最强大模型 API、实现鸿蒙端 AI 原生对话与流式渲染的高效集成方案

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 deepseek 的适配 鸿蒙Harmony 实战 - 驾驭国产最强大模型 API、实现鸿蒙端 AI 原生对话与流式渲染的高效集成方案 前言 在 AI 浪潮席卷全球的今天,大模型(LLM)已成为移动应用创新的核心引擎。而在众多的国产模型中,DeepSeek 凭借其卓越的算法效率和极致的性价比,正成为开发者们的“真香”选择。 将 DeepSeek 这种顶尖的认知能力,植入到全面拥抱智能化、万物互联的鸿蒙(OpenHarmony)系统中,将碰撞出怎样的火花? deepseek 库为 Flutter 提供了极简的 API 封装,它完美支持了 SSE(流式事件流)响应,能让你的鸿蒙 App

By Ne0inhk

Playwright携手MCP AI实现自动化浏览器操作(保姆级教程,国内模型搞定!!!)

一、什么是 Playwright MCP 浏览器拓展? 它是连接 AI 大模型与真实浏览器环境的核心桥梁,解决了传统自动化工具需要频繁启动新浏览器的痛点。 ✨ 核心作用 允许 AI “看见” 浏览器内容,并模拟人类行为(点击、输入、滚动),无需每次启动全新的空白浏览器窗口,大幅提升效率。 🎯 最大亮点 通过配套的 Chrome/Edge 拓展插件,AI 可以直接接管你当前已打开的网页,复用现有登录状态(Cookies、Session),无需重新登录即可操作 Gmail、Jira、企业后台等需要鉴权的网站,这是传统无头浏览器无法实现的关键优势。 🛠️ 二、核心功能与特性 功能点核心能力接管现有会话安装插件后,AI 直接操作当前 Chrome/Edge 标签页,保留所有登录态与历史记录,无需重新初始化环境。精准操作支持点击(Click)、输入(Fill)、截图(

By Ne0inhk