概述
目录
- 一、相关函数
- 二、详解
- 1、fork的使用
- 2、孤儿进程
- 3、僵尸进程
一、相关函数
fork函数:创建一个新的进程,当前进程是新进程的父进程
通过man 2 fork
命令可以查看linux手册中第二节关于fork函数的介绍
#include <unistd.h>
pid_t fork(void);
fork函数会返回两次,一次在父进程、一次在子进程。它有3中可能的返回结果:
- -1:创建失败,在父进程中返回-1
- 0:创建成功,当前进程是子进程
- 其他:创建成功,在父进程中返回子进程的pid(进程 ID)
需要注意:fork的操作比较重,fork出来的子进程会获得父进程的完整副本,当然了Linux不是在子进程一fork出来就将父进程的资源复制出一个副本给子进程的,Linux采用了写时复制,一开始两个进程是共享数据的,但是当子进程尝试修改数据,就会触发写时复制,为子进程创建一个该数据的副本。
linux系统还提供了一些函数,用来获取进程 ID,查看手册man 2 getpid
结果如下:
#include <unistd.h>
//获取当前进程的pid
pid_t getpid(void);
//获取父进程的pid
pid_t getppid(void);
二、详解
1、fork的使用
编写 forkdemo.c,代码如下:
#include <unistd.h>
#include <stdio.h>
int main(){
printf("before forkn");
pid_t pid=fork();
if(pid == -1){
printf("创建子进程失败");
}else if(pid == 0){
printf("我是子进程,我的pid是:%d,父进程的pid是:%dn",getpid(),getppid());
}else{
sleep(1);
printf("我是父进程,我的pid是:%d,子进程的pid是:%dn",getpid(),pid);
}
printf("after forkn");
}
编译 gcc forkdemo.c -o forkdemo
后执行./forkdemo
,结果如下:
before fork
我是子进程,我的pid是:28473,父进程的pid是:28472
after fork
我是父进程,我的pid是:28472,子进程的pid是:28473
after fork
从结果可以看出,fork函数会返回两次,在子进程中返回0,父进程中返回的是子进程的pid,这里让父进程sleep 1s是为了保证子进程先执行完。
- 问题:子进程中为什么也是从fork出开始执行,为什么不从main函数开始执行?
通过执行 fork系统调用,子进程获得父进程中数据空间,堆和栈等的副本,但它们并不共享存储空间,只共享代码段。由于把当前运行的位置都复制到子进程 ,那么子进程当然也是接着fork继续执行。
2、孤儿进程
现在如果我让子进程 sleep(1), 让父进程先执行完会如何呢?修改后代码如下:
#include <unistd.h>
#include <stdio.h>
int main(){
printf("before forkn");
pid_t pid=fork();
if(pid == -1){
printf("创建子进程失败");
}else if(pid == 0){
//子进程休眠1s
sleep(1);
printf("我是子进程,我的pid是:%d,父进程的pid是:%dn",getpid(),getppid());
}else{
printf("我是父进程,我的pid是:%d,子进程的pid是:%dn",getpid(),pid);
}
printf("after forkn");
}
编译执行后结果如下:
before fork
我是父进程,我的pid是:28513,子进程的pid是:28514
after fork
我是子进程,我的pid是:28514,父进程的pid是:1
after fork
- 问题:父进程先结束了,然后子进程的父进程不再是创建它的那个进程了,这是为何?
要知道为什么有这个结果,首先要知道什么是孤儿进程?
孤儿进程:父进程先于子进程结束,则子进程会成为孤儿进程。
借助现实的例子:作为一个单亲父亲的孩子,如果他的父亲死亡了,那么这个孩子就会成为孤儿,会有专门的机构"孤儿院"来收养这个孩子。
Linux中托孤的行为和这个例子很相似,当父进程运行结束,但子进程还在运行,那么这个子进程就会变为孤儿进程,为避免孤儿进程退出时无法释放相关的资源(进程描述符)而变为僵尸进程,所以孤儿进程会被 init 进程(pid 为 1 )所 “收养”,由init进程来进行“善后”。
3、僵尸进程
当一个进程完成它的工作终止后,它的所有资源都会被回收,但是该进程的进程描述符依然被保留了,这是为了让父进程可以知道子进程的退出状态,因此进程结束后所占用资源的回收和进程描述符的释放是分开的。它的父进程可以调用wait()或waitpid()系统调用来取得子进程的终止状态,然后内核负责回收子进程残留的进程描述符等资源。需要注意这其实就是僵尸进程产生的原因,虽然子进程结束了,但是它的进程描述符依然被保留,没被释放。
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
int main(){
printf("before forkn");
pid_t pid=fork();
if(pid == -1){
printf("创建子进程失败");
}else if(pid == 0){
sleep(3);
printf("我是子进程,我的pid是:%d,父进程的pid是:%dn",getpid(),getppid());
}else{
printf("我是父进程,我的pid是:%d,子进程的pid是:%dn",getpid(),pid);
int status;
//作用:阻塞等待子进程结束、获取子进程的退出状态status、回收子进程残留资源
wait(&status);
printf("我是父进程,子进程已经被回收,我也要结束了n");
}
printf("after forkn");
}
最后
以上就是内向音响为你收集整理的fork子进程的全部内容,希望文章能够帮你解决fork子进程所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复