进程管理和调度基础:https://blog.youkuaiyun.com/Jochebed666/article/details/83049299
下面将详细讲解进程创建、进程等待、进程程序替换、shell、进程终止!
一、进程创建
- 进程调用fork,当控制转移到内核中的fork代码后,内核分配新的内存块和内核数据结构给子进程;
- 将父进程部分数据结构内容拷贝至子进程;
- 添加子进程大奥系统进程列表中;
- fork返回,调度器开始调度,是选择的过程;
- 当一个进程调用fork后,将有两个二进制代码相同的进程,他们将运行到相同的地方,但每个进程都可以开始他们自己的旅程;
- fork之前父进程独立执行,fork之后,父子进程分别执行,谁先你真行完全由0号进程调度器决定;
- fork函数之后,子进程返回0,父进程返回子进程的pid。
二、写时拷贝
- fork之后,父子进程代码共享,数据写时拷贝;
- 写时拷贝即当父子进程任意一方进行写入时将以写时拷贝方式各自一份副本;
- 写时拷贝的产生主要考虑两个问题:内存资源和内存性能问题;
三、fork
- 一个父进程希望复制自己,使父子进程同时执行不同的代码段则需要使用fork分流;
- 一个进程要执行一个不同的程序,例如子进程从fork返回后,调用exec函数;
- fork调用失败可能是系统中有太多的进程,也可能是实际用户的进程数超过了限制;
四、vfork
#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);
- vfork创建一个子进程,子进程和父进程共享地址空间,而fork的子进程具有独立的地址空间;
- vfork保证子进程先运行,在它调用exec或exit之后父进程才能被调度;
五、进程终止
- 进程退出有三种场景:运行完毕,结果正确;运行完毕,结果不正确;代码异常终止;
- 正常退出码:0;不正常退出:非0;
- 进程正常终止:五种情况
-
从main函数返回return;(刷新缓冲区)
-
调用exit函数;(与return相似,遇到exit先执行清理函数,关闭所有打开的流,刷新缓冲区,调用_exit退出进程)
-
调用_exit_Exit函数;(不刷新缓冲区,直接退出进程)
-
-
-
进程的最后一个线程在启动例程中执行return语句;
-
进程的最后一个线程调用pthread_exit语句;
-
进程异常退出:四种情况
-
调用abort函数;
-
指针异常;
-
进程接收到某些信号时。如:ctrl + c;
-
最后一个线程对取消请求做出相应;
-
不管进程如何终止,最后都会执行内核中的同一段代码。这段代码为相应进程关闭所有打开描述符,释放它所使用的存储器等;
-
用【echo $?】查看最近一个进程退出码;
六、进程等待
- 父进程通过进程等待的方式,回收子进程资源,避免内存泄漏;获取子进程退出信息;
- 进程等待可以使用wait方法;
- 进程等待可以使用waitpid方法;
- 如果子进程已经退出,调用wait/waitpid时,会立即返回,并释放资源,获取子进程退出信息;
- 如果在任意时刻调用wait/waitpid,子进程存在并且正常运行,进程可能阻塞;
- 如果不存在该子进程,则立即出错返回;
- 进程如果异常退出,一般是收到了信号;
七、status
- status是一个输出型参数,由操作系统填充;
- 如果传递NULL,则表示不关心子进程的退出状态;
- 操作系统会根据该参数,将子进程的退出信息反馈的父进程;
- status可以当做位图来看待,如上图;
八、进程程序替换
- 让一个正在运行的进程去启动硬盘上的一个可执行文件;
- 当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行;
- 调用exec并不创建新进程,所以调用exec前后该进程的ID并未改变;
- l(list):参数列表
- v(vector):参数用数组
- p(path):有p自动搜索环境变量path
- e(envp):自己维护环境变量
- 事实上,只有execve是真正的系统调用,其他的最终都调用execv
九、shell
https://blog.youkuaiyun.com/Jochebed666/article/details/83510733