异常控制流(进程、虚拟内存、信号)

异常控制流(进程、虚拟内存、信号)

CPU执行的指令序列称为控制流,异常控制流可以这样理解,CPU本来一条接着一条的顺序执行指令序列,但是突然有异常或者信号使得执行指令的顺序发生改变。有点像汇编和C语言中的跳转,但汇编中虽然跳转了,CPU还是按照顺序执行着指令(CPU还是执行程序中的代码),异常控制流则是代码不再运行,或者操作系统从用户态转为核心态,或者进程上下文切换。
底层上的异常控制流一般是异常引起的,上层的异常控制流一般表现为进程的上下文切换、信号或者C语言中的一些函数。但我觉得上层的这些也是由底层实现的。
异常就是将控制权转移到操作系统内核,异常分为异步异常和同步异常,异步异常是由CPU外部发生的状态变化而引起的。比如:当一段代码要的数据不在缓存中而在磁盘中,一般这种时候系统会把磁盘的数据复制到内存中,这个过程一般在后台进行,在这段时间内CPU会接着执行下面的指令,当数据复制完,会通过中断引脚来通知CPU数据已经转存完。这种就算是异步异常,同时上面说的现象被称为page fault。
在这里插入图片描述
同步异常是CPU内部发生的,最常见的是系统调用,比如当程序要写入一个文件,这时候会调用编程语言的库函数,库函数是已经封装好的函数,其实内部是将操作系统从用户态转换为核心态,让操作系统内核来完成这个过程。
上面说的系统调用属于traps,除了traps还有faults和aborts,traps大多是有意的,faults大多是无意的,但可能恢复,比如说:程序中除了0等。aborts就是终止,比如说:机器本身坏了等。
说完了低层次的异常,再来看一下高层的异常控制流。进程的上下文切换就属于异常控制流,进程是正在运行的程序实例,因为一段代码在运行过程中会将代码和数据存在内存中,寄存器负责拿取代码和数据执行指令(这里过于复杂就不详细说),代码本身和运行的整个环境(占有的内存和寄存器的值)就称为进程,地址空间和寄存器中的值就称为上下文(context)。进程的出现会让程序感觉自己独占CPU和内存。
在这里插入图片描述
CPU在同一时刻只能执行一个程序,操作系统有一个计时器,每过一段时间就打断一下CPU,CPU就会执行下一个程序,将上一个程序进程中的上下文存储起来,直到下回再执行这个程序再取出来接着执行。计时器的打断称为timer interrupt,属于异步异常。
这在一些考研书中被称为时分复用技术,让程序感觉自己独占CPU。空分复用技术通过虚拟内存来实现,在很久之前的内存分配是给每个程序分配一定的内存,但这有很多问题,如果有新的程序加入进来怎么办,或者两段代码共用一些数据怎么办。
虚拟内存其实就是存储在磁盘上的一组连续字节,同时在程序的进程中维护了一个page table。虚拟内存中有一个硬件叫MMU内存管理单元硬件,当CPU执行一条指令,比如说是一个移动指令,产生某个有效地址,这实际上是一个虚拟地址,CPU将该地址发送给MMU,MMU中有一个地址转换的过程,将虚拟地址转换为物理地址。因为磁盘的读取速度过慢,不可能把所有数据都存在磁盘上,所以会把一部分数据存在DRAM中,这也被称为缓存。page table中对应每个虚拟地址的数据是在磁盘中还是DRAM中,这样虚拟内存就有两个优点:1.加速数据的读取;2.更适合内存的管理,当两个程序使用同一段数据时,只要让page table的指针指向同一地址就行。
在这里插入图片描述
再回来,同样是高层次的异常是信号,作为使用过QT的人,我强烈的感觉QT中的signal和slots就是借鉴的这里。
通过一个例子说明,linux启动时会创建init进程,系统上其他所有进程都是init进程的子进程。init进程启动时会创建守护进程,守护进程是一个长期运行的程序,通常用来提供服务,比如说web服务器,或者其他一直在系统上运行的服务。最后会创建登录进程,它为用户提供了命令行接口。
在这里插入图片描述
当在shell中输入命令时,其实是要求shell运行相应的可执行程序,shell会创建一个子进程,然后在这个子进程中执行可执行程序,并且这个子进程也有可能创建其他的子进程。
如果太多子进程在后台运行没有被回收,会发生内存泄露,所以内核会在shell的子进程结束时告知shell,shell会对此作出反应,并放出waitpid,这个通知机制就是信号。每个特定的事件会出发不同的信号,进程自己也可以发送信号到另一个进程,另一个进程可以使用系统默认的操作(default action),也可以自己写catch函数处理信号,也可以忽视信号。这里有点复杂,可以下回说。
同时进程也有很多操作方式,比如Fork(创建一个子进程,子进程和父进程运行同一段代码,子进程可以访问父进程打开的文件),Fork是一个返回两个值的函数,子进程返回0,父进程返回子进程的序号;execve在子进程中运行不同的程序,shell中就用的是这个函数。
以下是shell的实现代码:
在这里插入图片描述
在这里插入图片描述
parseline拆分命令,argv[0]是命令,数组后面的是可选参数,如果命令为空则直接返回;
builtin_command检查该命令是不是shell内建命令,如果不是shell内建命令则创建子进程运行该命令;
shell命令中最后一个字符如果是&则在后台运行,如果不是&父进程会等待子进程结束再接着运行。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值