1 fork函数
1.1 fork函数用法简介
函数原型: pid_t fork(void) pid_t是返回的一个标识,在不同的平台可能定义不一样,可以是int或者long类型等等,pid_t定义在 sys/types.h中
pid_t pid = fork();
if (pid == 0) {//子进程
}
if(pid>0){ //父进程,此时pid的值是子进程的id号
}
if(pid==-1){ //此时创建子进程失败
}
fork创建进程失败的原因:进程数量超出限制或者内存空间不足
1.2 fork创建进程空间共享问题
使用fork创建的子进程在一开始除了pcb和父进程不一样,其余的空间遵循读时共享写时复制,文件描述符是共享
1.3 创建子进程导致的僵尸进程和孤儿进程的问题
僵尸进程:子进程先于父进程退出后,子进程的PCB需要其父进程释放,但是父进程并没有释放子进程的PCB,这样的子进程就称为僵尸进程,僵尸进程实际上是一个已经死掉的进程,但是仍然占用内存空间
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作,不会像僵尸进程那样占用ID,损害运行系统
解决僵尸进程的方法:
- 杀死父进程,子进程的资源就会被init进程回收,但是一般父进程是一个服务器程序,不可能使用这种暴力的方式
- 在父进程中使用wait或者waitpid函数,使用wait函数回收资源会导致父进程阻塞,合理的使用waitpid函数不会导致父进程阻塞
- 使用信号处理,当子进程退出时会像父进程发送SIGCHLD信号,所以我们可以在程序一开始时就设置处理这种信号的方法,但是当有多个信号同时到来时可能会导致信号丢失的情况
2 wait函数和waitpid函数
2.1 绪论
一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程
2.1 wait
该函数有三个功能:
- 阻塞等待子进程退出
- 回收子进程残留资源
- 获取子进程结束状态(退出原因)。
wait一旦被调用,就会一直阻塞在这里,直到有一个子进程退出出现为止
函数原型:
pid_t wait(int *status); //如果不关心程序退出的状态,可以传入NULL
成功:清理掉的子进程ID;失败:-1 (没有子进程)
使用wait函数传出参数status来保存进程的退出状态 (正常终止→退出值;异常终止→终止信号)。借助宏函数来进一步判断进程终止的具体原因,宏函数具体参考Linux中man命令
2.2 waitpid
函数原型:
pid_t waitpid(pid_t pid,int *status,int options);
//第一个参数
pid<-1 等待进程组号为pid绝对值的任何子进程
pid=-1 等待任何子进程,此时的waitpid()函数就退化成了普通的wait()函数。
pid=0 等待进程组号与目前进程相同的任何子进程,也就是说任何和调用waitpid()函数的进程在同一个进程组的进程。
pid>0 等待进程号为pid的子进程。
//第二个参数需要搭配具体的宏函数来获得其状态,如果不关心具体的状态则传入NULL
//第三个参数,其参数可以用 “|” 运算符连接起来使用。
WNOHANG 如果pid指定的子进程没有结束,则waitpid()函数立即返回0,而不是阻塞在这个函数上等待;如果结束了,则返回该子进程的进程号。
WUNTRACED 如果子进程进入暂停状态,则马上返回。
如果waitpid()函数执行成功,则返回子进程的进程号;如果有错误发生,则返回-1,并且将失败的原因存放在errno变量中。
失败的原因主要有:没有子进程(errno设置为ECHILD),调用被某个信号中断(errno设置为EINTR)或选项参数无效(errno设置为EINVAL)
waitpid(-1, status, 0) 退化等价与wait(status)
3 exec函数簇
参考博客:https://blog.csdn.net/amoscykl/article/details/80354052