fork()
为什么要fork()创建子进程?
1 一个进程希望能执行不同的代码段
2 一个进程希望复制自己,同时父子进程执行不同的代码段。
规则: 父进程为模板 ,创建子进程
1 将父进程的PCB复制一份 稍加修改作为子进程的PCB
2 将父进程的虚拟地址空间空间拷贝一份,作为子进程的地址空间
采用写时拷贝的方式,写时拷贝效率高,用到哪,拷贝哪。其实就是偷懒的拷贝方式。
开销 进程 >线程 > 协程
fork函数为调用函数,上边肯定还有库
3 fork会在父子进程中分别返回,
**父进程返回子进程的Pid ,子进程返回0.**
即 我们的 if else 判断 fork()的返回值
父子进程执行顺序由调度情况决定。
4对于 父子进程的理解
父进程先结束 所以先打印两个 ,子进程后结束,打印6个
假入我们fflush(stdout)一下 ,
刷新了缓冲区
缓冲区被刷新(清空),当然6个了。
进程的终止:
1 代码执行完,结果正常退出
2 代码自行完,结果不正常退出
3 代码未执行完,异常退出
执行完时,main 函数的返回值作为进程的返回码
默认 0 为进程正常退出,默认非0异常退出。
$?是bash的一个特殊变量 ,表示上个命令对应进程的退出码
如何查看 echo $?
这个码不能为负,范围 0-255
exit也能终止进程
也可以判断进程的正常或者异常退出
头文件为stdlib.h
注意 : exit表示让进程结束 ,而return 代表让函数结束。
exit() 库函数
_exit() 系统调用 头文件"unistd.h",他不会刷新缓冲区
exit 本质上调用了_exit,但也有点区别;
0 调用用户函数
1 exit退出时会关闭流 并 fflush () ;
2 调用结束函数
刷新缓冲区的操作 :
1、\n
2 main 函数推出
3 ffush(stdout);
4 调用exit()
mian()函数结束,也可以通过特殊操作再执行一些过程;
atexit() 它先告诉操作系统给本进程注册一个退出清理函数,
atexit()这个函数 进程退出之前再执行;
;当main函数结束,再执行一个函数,它的参数为一个函数指针(函数名);
下面看一看解决僵尸进程的科学的方法
进程等待
wait
头文件为"sys/wait.h"
1 参数是输出型参数,表示退出码(正常/异常)退出
2 返回值是子进程的PID
3 阻塞式等待,一直等到子进程结束才返回
获取子进程的status
1 wait 和waitpid 都有一个status参数,该参数是一个出型参数,由操作系统填充
2 如果传递NULL,表示不关心子进程的退出状态信息,否则 ,系统就会根据参数将状态信息反馈给父进程;
3 status 不能简单的当作整数来看,可以当作位图来看,我们只研究status的低16位
正常终止 : 0-7位 均为0 , 8-15位表示状态信息
被信号所杀 (异常终止): 0-6位存终止信号 第7位是一个core Dump标志 8-15 位未用
1 一个wait()只回收一个进程,wait少了 ,会造成僵尸进程(内存泄漏 );ps aux |grep test可以看到 Z状态的进程,
wait()调用次数要等与子进程个数
wait少了 ——————————僵尸进程(内存泄漏)
wait多了——————————wait()出错
2如果有多个子进程,任何一个子进程结束都会触发wait的返回
waitpid
waitpid 只等特指的子进程,行为和wait非常相似,当参数为(-1,NULL,0)就和wait()一样了;
pid_t waitpid(pid_t pid,int* status,int options=0)
非阻塞式:
waitpid 的第三个参数为(WNOHANG)
waitpid(ret,NULL,WNOHANG);
轮询式的wait好处: 灵活控制代码, 利用等待时间做别的事(阻塞式是一直在等)
坏处: 是代码写起来复杂