Linux:37信号lesson26(未完)

发布于:2025-04-13 ⋅ 阅读:(23) ⋅ 点赞:(0)

1.消息队列,信号量共享内存的底层原理 (最开始的25分钟)

信号量集
第一个成员

*sem.base:起始信号量
可以malloc多个信号量

sem_nsems:有多少个信号量

Linux:进程信号 

1.引入信号

 信号vs信号量 ,没有任何关系

1.信号:闹钟,红绿灯,上课铃,狼烟
2.什么叫信号:中断我们正在做的事情,是一种时间的异步通知机制
信号是一种发给进程的,用来进行异步通知的机制!

信号的产生,相对于进程的运行,是异步的。信号是发给进程的

基本结论

基本结论:
1.信号处理,我在信号没有产生的时候,就知道该如何处理了。
2.信号的处理不是立即处理,而是可以等一会在进行处理,可以等到合适的时候,再进行处理。
3.进程早已经内置了对于信号的识别和处理方式。
4.信号源是非常多的->给进程产生信号的,信号源也非常多 

2.信号产生

产生信号的方式非常多:


1.键盘产生信号

ctrl + c是给目标进程发送信号的。相当一部分信号的处理动作,就是让信号自己终止。

a.信号都有哪些 --- ctrl+c就是给进程发送信号

kill -l:查看信号列表

信号:宏替代
34~64实时信号
1~31普通信号:可以不立即处理
32 33 没有

b.怎么证明“ctrl + c:给进程发送二号信号”

收到信号,处理信号的三种方式:
1.默认处理动作,ctrl + c
2.自定义信号处理动作
3.忽略处理

更改进程默认 信号处理方式的系统调用函数:

signal:系统调用,更改进程的默认信号处理动作

signum:信号
handler:自定义函数指针指向的方法

 

signal:代码例子,更改ctrl + c的默认信号处理动作

 Term:终止
Core:终止,不过有区别
Ign:忽略
Stop:暂停
Cont:continue继续

前后台进程 

ctrl + /:也可以暂停
ctrl + c:给目标进程发送信号
是呢嘛是目标进程:??
前台和后台进程。

./XXX: --- 前台进程,键盘产生的信号,只能发给前台进程,crrl + c可以暂停
./XXX &: ---后台进程,对于ctrl +c 不做处理
命令行shell进程 --- 前台

kill -9 + PID:给目标进程发信号,终止对应的进程,杀掉后台进程

为什么前台进程可以获取键盘输入?
给一个确定的进程,而后台可以有多个,不确定,多以只能是前台
前台进程的本质,就是要从键盘获取数据 

./test:

前台进程进行,bash进程进入后台

键盘输入只能打给前台,所以对ls 等命令不响应

./test &:

./test&编程后台进程,bash变成前台进程,输入命名的话,bash作为前台可以对命令有响应 

 解释

补充命令

jobs:查看所有的后台任务

fg  + 任务号:把后台进程提到前台

ctrl + Z : 暂停,提到后台

bg 1:back ground

什么叫做给进程发信号?

 

什么叫做给进程发信号?
信号产生后不是立即处理,多以要求,进程必须把信号记录下来。

记录在哪?
记录在
struct task_struct
{
unsigned int sigs;--->当做一个位图结构 ,32个比特位,比特位的位置表示信号编号,比特位的内容表示是否收到该信号。
“收到2号信号,记录在sigs里面,设置为1”
}
给一个进程发送信号的本质就是想目标进程写信号
----需要进程PID + 信号

由于task_struct属于内核的数据结构,所以修改位图的本质就是操作系统自己提供发送信号(修改signs的位图)的系统调用

2.系统调用

系统调用产生信号

系统调用发送信号

1.kill函数

2.stoi

3.argv

全部都捕捉了的代码

当对信号进行自定义捕捉的时候,9号新号,无法被自定义捕捉 

可以用就好进行删除

系统调用函数:

raise:自己给自己发信号

abort

abort:给自己发信号,固定的6号信号,用来终止进程。

要求进程必须处理进行终止 

3.产生信号的方式调用系统命令kill 

4.产生信号的方式 -- 【硬件 】异常 

崩掉的两种情况:/0  || 野指针

 

除零错误

CPU上运算,有寄存器,有一个状态寄存器-->

EFLAGS:有个比特位,表示CPU当前计算式是否溢出
CPU属于硬件,操作系统属于软硬件资源的管理者,
寄存器保存当前进程的上下文,当前进程出错,操作系统找到当前进程,发现是硬件错误,诶,找打了。然后OS发送8号信号

野指针:
野指针访问0号地址,再也表中不在,
CPU拿着0号地址,CPU拿的都是虚拟地址
CPU内有寄存器CR3,保存页表的起始地址,
MMU硬件单元,拿着虚拟地址,拿着CR3的物理地址,进行页表映射,映射失败了,还想往0号地址里面写,硬件报错,被OS知道是当前进程硬件报错。返回11号信号

一旦崩掉,进程就会收到错误信号

5.产生信号的方式软件条件 

alarm

 管道只有认读,没有写,bug,错误发型signalpip信号。

alarm:
调用alarm,设定一个闹钟,要求OS给当前进程一个信号,
alarm(0):取消闹钟
返回值:

6.验证闹钟实验 

1.验证闹钟

疯狂打印cnt;

设置闹钟,终止进程,发送信号

捕捉信号 

 14号SIGALRM信号

2.不要IO ,设置成全局,只打印一次。

 

相对于5万多,是数量级别的差距。

纯计算和IO有数量级的差距

3.每隔一秒,发送一个sigalarm信号一次性闹钟
 

一次性闹钟

4.一秒一个闹钟

 

pause  :等待信号,再唤醒

1.每个一秒完成一个任务 

 信号来,每隔一秒执行一个任务

OS处于死循环,操作系统再别人的催租下

OSpause的时间很短,并且一直在等待信号来执行任务

7. 如何快速理解系统闹钟

每调度一次,到0的时候,切换进程

alarm设定闹钟

OS内会同时存在很多闹钟 -> OS要对闹钟进行管理 ->先描述再组织

闹钟结构体,对闹钟的管理编程对链表的增删查改

tiemstmp,执行一次,timestmp++,timestmp * 记录一次的时间 -》记录时间
计数器:计数器是几,那么就可以算出时间


堆:最小堆 -> 记录最短时间的闹钟,超时,闹钟响了,执行function方法,发送SIGALRM信号给进程

都转化成OS发送信号->修改比特位。

3.信号的保存 

1.实际执行信号的处理动作称为信号递达->三种处理方式
2.信号从产生到递达之间的状态,称为信号未决->信号在位图中,还没来得及处理
3.进程可以选择阻塞某个信号
4.解除阻塞,才会递达
5.阻塞和忽略,不同,只要信号被阻塞就不递达,而忽略是递达之后可以选择的一种处理动作

三张表

 SIG_DEL:默认
SIG_IGN:忽略

信号递达,未决在内核中的具体表现:

进程中与信号相关的有三张表:
pending表:unsigned int oending ->保存信号的位图
比特位的位置:表示的是第几个信号
比特位的内容:表示是否收到

block表:unsigned int block本质也是一张位图
比特位的位置:表示第几个信号
比特位的内容:表示是否阻塞

handler表:sighandler_t handler[31]->函数指针数组,数组下标:信号编号

 singal:的本质就是修改handler表

代码验证 

自定义捕捉 

忽略 ,不做处理

自定义后回复默认 ,执行一次回复默认


验证信号保存的话题:


Linux提供信号操作
1.一定是围绕着这三张表展开 

sigset_t:是一个结构体

sigset_t:
阻塞信号集也叫信号屏蔽字。

信号集操作函数

sigprocmask:
谁调用,就获取,设置,更新谁的block表 

调⽤函数 sigprocmask 可以读取或更改进程的信号屏蔽字(阻塞信号集)。

#include <signal.h>

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 

返回值:若成功则为0,若出错则为-1 

对应how里面的操作

sigset_t* set 输入型参数

oldset:输出型参数,输出型参数,改后悔了,返回初始的。

返回值:成功 0 ,失败 - 1

 sigpending:获取pending信号集

sigpending:获取pending信号集

修改pending位图?不同关心,设置有这几种用法

整合lesson27(代码)

实验初步思路:

首先,给二号进行堵塞(block),获取pending,应该全部都是0,

给二号发送一个信号,再次获取pending,由于堵塞,实际上也就是处于未决状态,会看到有一个0 变成 1 。

 第一步:屏蔽二号信号。

1.现将block全部清空,记录老的oblock。

2.sigaddset,添加2号信号,初始化屏蔽信号

3.将当前block信号集,修改成我们设置的blcok.

 第二步:获取pending信号集合

 第三步:打印

 

第四步:

结果:

发送什么信号,就会在什么位置出现

全部屏蔽会怎么样?

 其他信号,发一个,出现一个

 9好信号不可被捕捉,不可被阻塞

2号新号被递达的过程:解除屏蔽

+一个计数器每一次获取打印
计数器 == 10,取消对2号新号的阻塞状态

解除2号信号的阻塞,2号信号被递达:

但是刚开始的时候,是看不到的,因为2号信号一旦被递达,然后就执行默认捕捉,直接终止。
方法:修改默认捕捉,为自定义捕捉

解除屏蔽

 

看不见解除屏蔽后的pending,是因为原因如上

 自定义捕捉

-----------------------------------------------------

pending 是在递达前 1 - > 0 ?√
还是在递达后,处理完 在 1-> 0?

4.信号捕捉 

常规信号产生多次,只记录一次。
实时信号在递达之前产生多次可以一次放在一个队列里。

Core:
核心,会在当前路径下,形成一个文件,进程异常退出的时候,进程在内存中的核心数据从内存拷贝到磁盘,形成一个文件
“核心转储”---支持Debug

Term:进程直接退出。

云服务器上,core dump功能是被禁止掉的。
1.为什么被禁掉?

2.怎么查看?
core file size - > 0 ,关掉

ulimit -c 40960,打开
 

 -g选项->调试

 

#include <iostream>
#include <cstdio>
#include <vector>
#include <functional>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    pid_t id = fork();
    if (id == 0)
    {
        sleep(2);
        printf("hello bit\n");
        printf("hello bit\n");
        printf("hello bit\n");
        printf("hello bit\n");
        printf("hello bit\n");
        int a = 10;
        a /= 0;
        printf("hello bit\n");

        exit(1);
    }
    int status = 0;
    waitpid(id, &status, 0);
    printf("signal: %d, exit code: %d, core dump: %d\n",
           (status & 0x7F), (status >> 8) & 0xFF, (status >> 7) & 0x1);
}

ulimit -a:查看

ulimit -c  size:开多大的core 

size 置为0 ,关掉core.

  • 信号值和退出码是通过 status 变量的位操作获取的:

    • (status & 0x7F):获取低7位,表示信号值。

    • (status >> 8) & 0xFF:获取高8位,表示退出码。

    • (status >> 7) & 0x1:第7位表示是否产生核心转储。