1、进程创建
shell命令行启动程序指令皆是创建了进程,我们通常通过调用fork()函数创建子进程。注意进程皆是操作系统创建的,父进程只是通过fork函数去创建子进程,本质还是由操作系统去完成的
1.1、fork()函数用法简介
调用fork后,操作系统内核将:
- 分配新的内存块和内核数据结构给子进程
- 将父进程部分数据结构内容拷贝至子进程
- 添加子进程到系统进程列表当中
- fork返回,开始调度器调度
1.2、fork函数返回值
子进程返回0,父进程返回的是子进程的pid
原因:fork之后进入内核,申请内存构建子进程PCB、虚拟内存、页表,将子进程设置R状态,放入调度队列,由于创建子进程之后父子进程共享代码,所以父子进程都会有return返回值。返回值返回给变量本质发生了写时拷贝,改变了子进程对应页表的指向,数据映射到了其他区域
还有一种理解是,ret一开始是父进程创建的,由于fork之后,返回值有两个都要放入到变量ret中,所以发生写时拷贝(父进程要对ret赋值,子进程也要)。
1.3、写时拷贝
由于进程要独立,代码不可修改,数据可改,所以默认数据各有一份,但是内存是有限度的,如果把父进程数据全部再拷贝一份,那么太浪费内存,甚至导致fork失败。通常通过写时拷贝实现,就是当父或子进程修改数据时,将要修改的数据拷贝一份,让子进程页表指向新的重复数据在发生修改
为啥要有写时拷贝呢?父子进程创建的时候,直接数据各自拷贝一份不就完了?
1、不是所有数据都需要拷贝一份对于只读的数据,父子都拷贝一份浪费资源
2、fork时,创建数据结构,如果还要对数据拷贝一份,fork效率降低
3、fork本身就是向系统要更多的资源,要更多的资源相较于要更少的资源容易失败。
2、进程终止
2.1、进程退出场景
- 代码运行完毕,结果正确
- 代码运行完毕,结果不正确
- 代码异常终止
代码跑完使用退出码标识,命令行使用echo $?查看,没跑完使用信号
2.2、进程退出方法
main函数中,return那个数据就是退出码,通常给系统看,默认0代表正确,非0代表错误。退出码可以人为的定义,也可以使用系统的错误码。不同的错误码映射到不同的字符串
exit表示终止进程,刷新缓冲区,_exit是系统函数,不会刷新,两者返回值就是退出码,return只有在main函数中才能终止进程
2.3、站在OS角度,如何理解进程终止
进程终止的核心思想就是归还资源:
1、释放为了管理进程所维护的所有的数据结构对象
2、释放程序代码和数据占用的内存空间
3、取消改进程的链接关系
第一个释放,通常不是把数据结构和对象销毁,而是设置为不用状态,保存起来,通常保存在数据结构池
第二个释放不是将代码和数据清空,只是把内存设置为无效
3、进程等待
3.1、为啥要等待
1、回收僵尸进程,解决内存泄露,释放空间
2、需要获取子进程的运行结束状态
3、尽量父进程要晚于子进程退出,可以规范化进行资源回收
3.2、进程等待的方法
wait,等待任意一个子进程,当子进程退出,wait就可以返回
成功返回子进程pid,失败返回-1,status为输出型参数,不关心可设置为NULL
waitpid的方法:
pid_t waitpid(pid_t pid,int*status,int options);
status是一个输出型参数,从子进程的task_struct中拿到,低7位是信号,如果信号为0说明正常退出(status& 0x7F),如果退出状态( (status>>8) & 0xff )为0,说明正常退出
1、为啥要使用输出型参数status,是否可通过设置全局变量,告知父进程子进程的退出码?绝对不行,会发生写时拷贝
2、waitpid中的pid是从那里拿到的呢?虽然子进程已经退出了,但是子进程的task_struct并没有立即释放,只有等待成功,内存才释放,所以从控制块中可以拿到子进程的status(这里是退出码)
第一个参数Pid =-1,等待任意一个子进程,与wait等效,Pid>0,等待进程ID与Pid相等的子进程
第三个参数options,WNOHANG:若pid指定的指定的子进程没有结束,waitpid()函数返回0,不予以等待。若正常结束,返回该子进程的id
将WNOHANG传给第三个参数时称为非阻塞式等待,传0称为阻塞式等待
在非阻塞式等待三种状态:1、失败:子进程还没退出,下次在检测 2、成功:子进程退出,已经返回 3、失败:真正的等待失败
4、进程程序替换
fork创建子进程有两个作用:
1、想让子进程执行父进程代码的一部分
2、想让子进程执行与父进程不同的事
进行进程替换时并没有创建新进程,没有PCB,没有改变PID,只是发生了写时拷贝,本质上用该程序的代码和数据修改页表的映射
4.1、替换函数
l(list):表示参数采用列表
v(vector>:参数使用数组
p(path):有p自动搜索环境变量
e(env):表示自己维护环境变量
这里没有替换成功不知道错在哪里了
在proc中使用程序替换execl函数去执行我们写的程序
也可将环境变量由proc传给excmd