进程控制
一、fork函数初识(见之前)
-
写实拷贝
二、fork常规用法 -
一个父进程希望复制自己,使父子进程同时执行不同的代码段
-
一个进程要执行不同的程序
三、fork调用失败的原因
- 系统中有太多的进程
- 实际用户的进程超过了限制
创建进程是需要很大成本的
进程终止
一、进程退出场景
- 代码运行完毕,结果正确
- 代码运行完毕,结果错误
- 代码异常终止(一般程序崩溃后退出码也就没有意义了)
为什么main总会return 0?意义在哪?
- main函数的return值是进程的退出码
- 查看退出码:echo $?(输出最近一次进程退出时的退出码)
- 一般用0表示:success
- !0:failed(错误)
- strerror() :可以查看退出码所对应的内容头文件string.h
进程退出的方式
- main函数return,代表进程退出,非main函数return叫做函数返回
- exit():进程终止,头文件stdlib,在任意地方调用都代表终止进程,参数是退出码(main return or exit 本身就会要求系统,进行缓冲区刷新!)
- _exit():终止进程,头文件unistd,强制终止进程,不要进行进程的后续收尾工作,比如刷新缓冲区(用户级缓冲区)
进程等待
-
进程为什么要等待?
父进程fork之后,需要通过wait/waitpid等待子进程退出
-
为什么要父进程等待?
- 通过获取子进程退出信息,能够得知子进程执行结果
- 可以保证:时序问题,子进程先退出,父进程后退出
- 进程退出的时候,会先进入僵尸状态,会造成内存泄露的问题,需要通过父进程wait,释放该子进程占用的资源
进程等待的方法
wait方法
-
返回值: 成功返回被等待进程pid,失败返回-1。
-
参数: 输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
-
例子:
可以观察到一开始有两个进程同时运行,之后有一个进程变成了僵尸进程,又过了一段时间这个僵尸进程也结束了,这个就可以说明首先wait是可以回收僵尸进程的,同时wait的返回值如果正常返回就是等待那个进程的pid,如果异常返回就是返回-1
waitpid方法
-
头文件与wait相同:
pid_ t waitpid(pid_t pid, int *status, int options);
-
返回值:
- 当正常返回的时候waitpid返回收集到的子进程的进程ID
- 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0
- 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在
-
参数:
-
pid:
- Pid=-1,等待任一个子进程。与wait等效。
- Pid>0.等待其进程ID与pid相等的子进程
-
status:
-
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
-
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
-
例子:(一定要让父进程通过status得到进程执行的结果)
这里的status就不是简单的一个整型了,我们知道它有32个比特位,但是判断代码运行结果:只使用低16个比特位!高16比特位不用(如果没有收到信号代表代码是运行完成的,没有异常终止);次低8位代表的就是进程退出的退出码,前7位就是进程的终止信号,如果程序没有异常终止那么就为0 -
用status来获取进程的退出码或者退出信号
-
操作系统给我们提供了两个宏,可以就是让我们每次查看退出码的时候都进行位操作,一个是WIFEXITED如果没有收到退出信号就为真,就可以用WEXITSTATUS来获取退出码
-
-
options:
-
options的值为0:默认行为,阻塞等待;设置为WNOHANG:设置等待方式为非阻塞
-
阻塞的本质:其实就是进程的PCB被放入了等待队列,并将进程的状态改为S状态
-
返回的本质:进程的PCB从等待队列拿到R队列,从而被CPU调度
-
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的PID。
-
例:
-
-
进程程序替换
进程不变,仅仅替换当前进程的代码和数据的技术叫做进程的程序替换
-
为什么要进行程序替换?
想让子进程执行一个全新的程序
-
什么是程序替换?(原理?)
用新进程的代码和数据替换掉原来的代码和数据
-
进程的程序替换使用----怎么办?----阶段一
- 现象:就是替换之后后续的代码不会执行,如果是子进程发生了程序替换,那么只会改变子程序的代码和数据不会影响父进程
- exec* 如果有返回值那么就是调用失败
- 现象:就是替换之后后续的代码不会执行,如果是子进程发生了程序替换,那么只会改变子程序的代码和数据不会影响父进程
-
阶段二
各个程序替换函数的基本使用
替换函数
C/C++程序要运行,必须要先加载到内存中---->用到的就是加载器(也就是exec*程序替换函数),进程程序替换会更改代码区的代码,这也会发生写时拷贝
-
execl函数
int execl(count char*path,count char *arg,...); //path 就是要执行的文件全路径一定要是路径+文件名 // ... 意思是可变参数列表,要执行的目标程序在命令行上怎么执行这里就怎么一个一个传递进去() //必须以NULL作为传入的参数的结束
PS:剩下的函数在下篇