目录
进程创建
写时拷贝
fork
进程终止
退出码
进程退出三种情况对应退出信号 :退出码:
进程退出方法
进程等待
两种方式
阻塞等待和非阻塞等待
小知识
进程创建
1.在未创建子进程时,父进程页表对于数据权限为读写,对于代码权限为只读。
2.在创建子进程后
父子进程都变为只读
3.在修改父子进程数据时,操作系统就会“报错”:
分情况:1.如果是野指针等非法情况,终止进程
2.如果是合法的,进行写时拷贝+更改父子进程权限为读写
写时拷贝
好处:因为不一定会修改子进程的所有数据,写时拷贝是按需拷贝,这样节省了时间与空间效率。
fork
常规用法:
1.一个父进程希望复制自己,使父子进程同时执行不同的代码段。
2.子进程要执行一个不同的程序。
失败原因:
1.系统中有过多进程
2.用户的进程数是有限制的。
进程终止
退出码
进程退出时会有退出码,比如return 的数字,退出码会被系统获得,让系统得知该进程执行情况。
$?获取最近一个退出进程的退出码的命令,使用echo打印
0代表程序执行成功,非零表示程序运行失败,不同的值代表不同失败原因。
strerror可以打印出内置错误码对应的错误信息(内置错误码不止100个),错误码可以自定义。
进程退出三种情况对应退出信号 :退出码:
进程退出信号:操作系统发送给进程以通知其终止或执行特定操作的信号。
1.代码运行完,结果正确 0:0
2.代码运行完,结果不正确 0:非零
3.代码运行失败,进程异常 非零:非零
进程退出方法
1.return 退出码 由main函数执行。 return 表示函数调用结束,对于main函数表示进程结束
2.exit(退出码) 直接调用C语言库函数 会主动刷新缓冲区再结束进程,调用后进程结束。
3._exit(退出码) 系统调用 直接终止进程,不会刷新缓冲区。
exit其实是调用的_exit,是对其的封装
结论:
1.进程终止必定调用系统调用
2.操作系统会知道异常的进程,并通过信号杀掉。
3.输出缓冲区在库中(库缓冲区),而不在OS火系统调用中。
进程等待
就是让父进程通过等待的方式,回收子进程的PCB,如果需要,获取子进程的退出信息。
因为如果父进程对子进程不进行管理,子进程会成为僵尸进程,造成内存泄漏,僵尸进程无法杀死,只能靠父进程回收。
两种方式
头文件:<sys/wait.h>
wait和waitpid 本质是获得子进程的task_struct中的退出属性,调用完毕,会让OS释放task_struct
pid_t wait(int *status)
wait成功,返回子进程pid,失败返回-1(该进程无子进程)。
pid_t waitpid(pid_t pid,int* status,int options) 这个比较好用
status的0~6位表示进程退出信号,8~15为表示进程退出码,7位表示coredump()标志位,
退出码可以使用(status>>8)&0xFF得到,退出信号可以使用status&0x7F得到。
阻塞等待和非阻塞等待
阻塞等待指如果子进程未退出,父进程会进入阻塞状态,等待子进程退出。
非阻塞等待是WNOHANG,在waitpid函数中options位置使用,指如果子进程未退出,直接返回0,如果子进程退出,返回>0的值,如果等待的子进程不是该父进程的子进程或该子进程不存在,返回<0的值。
本质是检测子进程状态,不会卡住父进程,让父进程可以在等待事件间隙中做其他事情,所以非阻塞等待会更高效
非阻塞轮询方案:多次非阻塞等待
小知识
1.在malloc和new时,是先开辟虚拟地址空间,等到使用时,再进行物理内存申请,构建完整映射关系,所以其也为惰性申请
2.在task_struct中,有存退出码的成员
3.执行进程时CPU从寄存器中取的是虚拟地址。