下面我们来介绍
进程的等待和退出
等待和退出呢实际上
是父子进程之间的一种交互
完成子进程的资源回收
那首先第一个是等待
那个wait()这个系统调用呢
可以用于父进程等待子进程的结束
那具体说起来呢
子进程通过exic()
向父进程返回一个值
父进程呢通过wait()
接受并处理这个返回值
那这时候就有一个问题说
这两个到底谁先谁后
wait父进程先等待
还是子进程先做exit()
这两种情况的不同呢
会导致它下面的处理呢
会有一些区别
wait执行的调用呢
在我们这里就是
如果有子进程存活
也就是说父进程创建的子进程
还有子进程
那这时候呢父进程进入等待状态
等待子进程的返回结果
好那等待的时候呢
这就相当于是wait
父进程先执行wait
等到子进程执行的时候呢
它执行exit ()
这是exit ()是在wait之后执行的
好那么这时候呢
子进程的exit ()退出唤醒父进程
父进程呢就由等待状态
回到就绪状态
等它得到CPU的使用权
可以执行的时候
那么父进程就处理
子进程的返回的这个返回值
这是wait在前exit ()在后的情况
如果不是这样那就有一种情况
就是有僵尸子进程等待
这指什么意思呢
就是子进程先执行exit ()
那这时候说它返回一个值
等待父进程的处理
那这时候呢exit ()在前
好在这个时候如果子进程
还一直处在这个等待的状态
在这里等待父进程的处理
那么这时候呢
父进程的wait就直接返回
如果有多个的话
就从其中一个返回它的值
如果没有子进程在
有可能我一个进程在执行的时候
它执行了wait
实际上它干脆就没有子进程
或者说子进程已经结束了
好那这时候呢它就直接返回
这是呢wait的功能
那接下来说exit
在这里头呢它叫有序退出
它指的是什么意思呢
我执行这个exit
这时候呢
这个进程呢就会在这个
系统调用里头
就会进行资源的回收
那具体的功能是这样的
首先第一个是我们刚才说到的
调用参数返回给父进程结果那exit
然后父进程呢可以拿到它这个结果
比如说我们在做一个编译
你的编译是否成功
这个编译成功之后呢我回复是0
如果不成功我回复错误码
这样的话我下面的
调用编译的这个进程
它依据你编译的结果
来决定后续怎么做
这是它第一项功能
然后第二部分功能是资源回收
你比如说我们占用的
各种资源的回收
占用的打开的文件
分配的内存和创建的
内核相关数据结构
然后检查父进程是否还存活
刚才我们说父进程会检查子进程
子进程也会检查父进程
子进程是执行exit的时候
检查父进程
父进程是在执行wait的时候
检查子进程
好如果这个子进程在exit的时候
检查父进程如果父进程是存活的
那么也就是这时候呢
子进程就保留一个状态
它自己进入僵尸状态
也就是我们退出状态
然后等待父进程对它结果做处理
好如果说没有
也就相当于创建完子进程之后
父进程已经结束了
那么这时候呢它就直接
释放相应的数据结构并且进程结束
好那在这儿呢
同时这个检查完之后会清理
所有的处于等待状态的僵尸进程
好那么这样的话
这个exit退出实际上这是
把进程占用的所有的资源
包括这里头申请了
并没有显式释放的
它都会给它释放掉
这是退出
我们先来看wait
这是wait的实现
那么我们先看有哪些函数会来调
那各种平台上的sys_wait()会到这儿来
然后它在这里头呢
做的工作是在这里头do_exit()
实际上是子进程的收尾工作
然后把它放到队列里头去
schedule()在这个地方
是把它放到队列里头去
好我们也可以通过它的流程图来看
那在这个地方呢实际上就是说
我们把当前进程改成了sleeping的状态
这样的话它就变成阻塞状态了
那也就是我们找到了wait()里头
它是如何进到阻塞状态的
在这儿呢你就可以看到相关的代码
这是最明显的标志
好接下来一个是do_exit()
这是它的实现
它实际上在这里呢
就是保存在这里头呢
是把它把这个进程给杀掉
do_exit()把一个进程或者一个线程关掉
那我们也可以通过
它的调用关系图来看它的执行情况
那也是各种平台上的exit()
最后到这个地方来
它在这里呢
最后都是到这个地方
来释放相关的这些数据结构
这是它的相关的工作
好我们仍然可以来看一下
在这里它做的主要工作都是在干啥
那在这儿呢前面的注释已经说了
它把它地址空间页表
存储的这一部分空间给释放掉
然后把自己的状态改成叫僵尸状态
同时唤醒它的父亲
也就是说如果是wait的话
在那边等着的那个
我们也可以来看看它的流程图
这是它最主要的工作
是关于存储的那一部分的处理
好在这个地方呢
最后这一步是最主要的
我们需要去wakeup_proc()唤醒
处于wait状态的父进程
好除此之外呢我们刚才讲到的
这几个创建加载等待退出
这四个系统调用之外
我们对进程控制
还有一些其它系统调用
比如说优先级控制
那在unix系统呢有一个nice系统调用
它完成对进程的初始优先级的设置
好在unix系统里头进程的优先级
会随着执行时间的延续而衰减
也就是说它的优先级是在不断变化的
这是对优先级的控制
然后还有一个呢
是对调试的支持
在我们目前ucore里头呢
你没有办法在ucore内部
调试你写的应用程序
原因在于在我们这里
这一部分支持还不好
实际上在标准的操作系统里头呢
unix里头它有叫ptrace
这个系统调用呢
完成一个进程对另一个进程的
执行过程的控制
这就有点像我们说的调试
我让一个被调试进程继续执行
在某处设置断点
检查它的内存和寄存器的状态
这是操作系统必须提供的一项功能
然后还有就是定时
我们前面说过sleep
它可以让进程在指定的时间
进入等待状态
然后时间到了它进入就绪状态
然后继续执行这是定时的功能
好到这个地方为止呢
我们就说清楚了
进程控制的几个相关系统调用
它们的实现和它们的功能
那把这些控制功能和我们前面
讲到的进程状态图进行一个联系
我们会看到这样一种情况
这是我们前面讲到过的
三状态进程模型里头
那核心的就绪运行等待
再加上创建和退出这五个状态
我们刚才执行的
说到那几个进程控制的系统调用呢
对这个状态呢都是会有影响的
我们看看它的影响在什么地方
首先是fork fork会有啥影响
fork会导致一个新的进程的创建
好创建在那里准备工作做完
fork准备好了放在就绪队列里头
的时候它就变成就绪了
好然后再有一个是wait
wait会什么 父进程执行wait
会导致父进程由运行状态
进入等待状态
好然后再有一个是exit 退出
退出会导致当前进程
由运行进入退出状态
同时子进程的exit
会导致由于wait进入等待状态的
父进程变成事件发生触发
导致它进入就绪状态
以便于它能再进行执行的时候
对子进程的返回的结果进行处理
那这时候我们还有一个啥没说加载
这个系统调用它在什么地方
实际上加载是你在执行过程当中的
一种状态
准确的来说
那大家可以通过代码
去阅读看看这里头
我在加载可执行文件的过程当中
它还有相应的状态变化
好那到这个地方呢
我们就说清楚了
操作系统当中的进程控制
那从进程的切换
再到跟进程控制相关的
创建加载等待退出
这样主要的这几个系统调用
那这是呢操作系统提供给
应用程序用的最频繁的
最主要的几个功能
好今天的课就上到这里下课