在vscode中,创建signal.c文件
故意在while中没有写代码块,让编译器认为在main中,quit只会被检测
运行可执行程序后,当输入 2号信号时,调用自定义方法将quit置为1,跳出while循环
编译器优化
编译器有对应的编译优化级别 -O1 -O2 -O3
在makefile中,添加-O2的优化级别
再次执行可执行程序时,输入2号信号,只调用了对应的自定义方法,说明进入main中的while循环 无法停止
全局变量被加载到内存中
while循环判断实际上是一种计算,会在CPU去执行的
进行计算时,将内存中的数据load到CPU中的寄存器上,然后才对quit进行真假判断
内存中有当前进程的代码和数据,CPU中有对应的PC指针去指向
若while循环条件满足,pc指针继续指向while循环的代码
若while循环条件不满足,则pc指针会向下移动,指向下一条语句,并向后执行
正常来说,每次都要尝试数据从内存load到CPU的过程
在main函数中 quit是没有被修改的,只是被检测,编译器发现quit变量没有被修改,就不会重复把数据从内存load到CPU中
因此编译器会优化,只需第一次把数据从内存load到CPU中,后续只需要检测寄存器中的数据即可
所以刚开始quit为0,将0传给CPU中,后续输入2号信号后,调用自定义方法,quit变为1,
但是在CPU中依旧quit为0,修改了内存中的quit,那CPU中quit就无法影响内存的quit了
一直使用quit为0,所以while循环无法退出
所以要告诉编辑器,保证每次检测,都要从内存中进行数据读取,不要用寄存器中的数据
为了解决这个问题,使用volatile
使quit变为volatile修饰的全局变量
volatile作用:杜绝对quit变量进行寄存器级别的优化,保证内存可见性
再次运行可执行程序,输入2号信号,跳出while循环,执行main中的printf打印
子进程在运行时会退出,若父进程不关心子进程退出,子进程就会变成僵尸状态
父进程要使用 wait/waitpid去等待子进程 回收僵尸,获取子进程的退出结果
即父进程进行阻塞式等待(什么都不干,就等待子进程的退出结果)
父进程主动检测--------因为子进程退出了,父进程暂时不知道
子进程要退出时,会向父进程发信号 SIGCHLD
父进程对于该信号的处理动作是SIG_DFL 什么都不做
验证SIGCHLD的存在
#include #include #include #include #include pid_t id;void handler(int signo){ sleep(5); printf("捕捉到一个信号:%d,who:%d\n",signo,getpid()); //-1代表等待任意一个子进程 pid_t ret=waitpid(-1,NULL,0); if(ret>0) { printf("wait success,ret:%d,id:%d\n",ret,id); }}int main(){ signal(SIGCHLD,handler);//自定义捕捉 id=fork(); if(id==0) { //子进程 int cnt=5; while(cnt) { printf("我是子进程,我的pid是:%d,ppid:%d\n",getpid(),getppid()); sleep(1); cnt--; } exit(1); } //父进程 while(1) { sleep(1); } return 0;}
实现一个自定义方法,当子进程退出时,会向父进程发送信号SIGCHLD
调用对应的自定义方法,打印出对应的信号以及父进程的pid值
运行可执行程序后,who的pid值就是父进程的pid
17号信号就是SIGCHLD
同时通过waitpid返回的pid值与子进程的pid值相同
通过for循环创建出10个子进程,若10个子进程发送信号,处理信号需要一个一个处理,所以当发送一个信号时,可能暂时被保留下来,但是父进程只有一个比特位 pending位图保留信号,当再次保留信号时,pending位图再次被置为1
,把上次信号覆盖掉,造成信号丢失,最后处理信号时可能比发送信号的数量少
有多少子进程,就回收几次,若没有子进程就退出即可
多线程概念
1.线程是一个执行分支,执行粒度比进程更细,调度成本更低
2.线程是进程内部的一个执行流
3.线程是CPU调度的基本单位,进程是承担分配系统资源的基本实体
下面将会对于这些概念进行解析
理解概念
什么是多线程
创建子进程时,只创建PCB,创建出来的PCB继续指向父进程的地址空间
代码区假设有很多函数存在,让不同的PCB执行不同的函数
相当于在一个进程内部包含多个执行流,指向同一个进程内的不同代码区域
每个PCB都是单独的线程
线程在地址空间内运行,所以该线程属于进程
调度成本低
多个线程之间使用的是同一个地址空间和页表
若为新的进程,则还需再次找到新的地址空间和页表并进行切换
局部性原理
CPU内部存在一个硬件cache
把一部分数据预先加载到缓冲区里,提高整机的效率
如CPU正在访问第100行代码,未来有很大概率访问101行,
所以一旦访问到第100行就把100行附近的数据全部load到内存中或者CPU的cache中
多线程在执行代码和数据时,依旧属于这个进程,CPU里面的cache会缓存各种各样的数据
若进行线程切换,因为都属于同一个进程,cache中缓存的数据是不变的
若进行进程切换,把当前缓存的数据设为失效,cache要重新加载当前的代码和数据
调度成本更低,体现在不用对cache进行切换
什么叫做进程
task_struct 叫做执行流
进程包含 一大堆的执行流、地址空间、页表以及该进程对应的代码和数据
所以进程是承担分配系统资源的基本实体
来源地址:https://blog.csdn.net/qq_62939852/article/details/130693696