进程创建fork()和vfork()
2018-03-10 16:04
134 查看
一个现有的进程可以通过两种方式创建一个新的进程,下面详细介绍两种
描述:
返回值:
在父进程中返回子进程 的pid,子进程中返回0。可以这样理解,对于父进程来说它可以有多个子进程,所以父进程需要知道子进程的 pid , 而对于子进程来说,它只有一个父亲,而且其父进程 ppid 已经记录在它的
fork失败原因:
进程当前系统进程数量达到“上限”;
系统内存内存不足;
操作系统不支持 fork。
用法实例:
运行结果:
从代码中看,我们让父进程睡眠三秒(三秒并不保证子进程能执行完),以保证让子进程先执行,3 秒后父进程开始执行,打印结果如我们所料。
下面看看系统调用 fork 的原理。
在执行
一般情况下,被创建的子进程会调用进程替换函数
父子进程除了共享代码段之外,它们也共享数据段。父子进程的数据段通过页表映射到同一块物理内存中,当有一方企图修改数据段时,便以写时拷贝的方式 —— 先拷贝一份要修改的数据,再通过页表映射到新拷贝的内存处,这样一方对数据的修改就不会影响另一方。如下实例:
运行结果:
可以看到,父子进程对数据的修改并不会影响到对方。如下图:
除此之外,
让子进程睡眠
可以看到,子进程运行完了,父进程才运行,而且子进程中对变量
——完!
【作者:果冻:http://blog.csdn.net/jelly_9】
fork()、
vfork()。
fork()
函数原型( man 手册):#include <unistd.h> pid_t fork(void);
描述:
fork()以当前的进程为副本创建一个新的进程,新创建的进程被称为子进程, 当前的进程被称为父进程,父进程和子进程运行在各自的地址空间。
返回值:
在父进程中返回子进程 的pid,子进程中返回0。可以这样理解,对于父进程来说它可以有多个子进程,所以父进程需要知道子进程的 pid , 而对于子进程来说,它只有一个父亲,而且其父进程 ppid 已经记录在它的
PCB中了,所以就没必要再返回父进程 pid 了。而fork失败的话返回-1。
fork失败原因:
进程当前系统进程数量达到“上限”;
系统内存内存不足;
操作系统不支持 fork。
用法实例:
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <stdlib.h> int main() { pid_t ret = fork(); if(ret < 0){ perror("fork"); return 1; } else if(ret > 0){//father sleep(3); printf("father start!\n"); printf("pid : %d\n", getpid()); printf("ret(child’s pid) : %d\n", ret); printf("father end!\n"); wait(NULL); }else{//child printf("child start!\n"); printf("pid: %d\n", getpid()); printf("ppid: %d\n", getppid()); printf("ret: %d\n", ret); printf("child end!\n"); } printf("%d come here\n", getpid()); return 0; }
运行结果:
从代码中看,我们让父进程睡眠三秒(三秒并不保证子进程能执行完),以保证让子进程先执行,3 秒后父进程开始执行,打印结果如我们所料。
下面看看系统调用 fork 的原理。
在执行
fork函数后,内核将拷贝一份父进程的副本作为子进程。子进程获得父进程的数据空间,堆和栈、文件描述符表(这通常在网络服务中使用,即父子进程各自关闭自己不需要的文件描述,这样就不会对对方造成影响)…的副本,但是父子进程共享正文段,即代码段。所以
fork之后,父子进程仍然执行的是同一份代码,但我们根据返回值得不同,可以让它们执行不同的逻辑,如上图所示,在
fork之后父子进程各自执行灰色部分代码,从上面执行结果中也可以看出 (注意!这只是让它们执行不同的逻辑,本质上执行的还是同一份代码)
一般情况下,被创建的子进程会调用进程替换函数
exec,所以
fork之后子进程一般就不去执行和父进程同样的代码。
父子进程除了共享代码段之外,它们也共享数据段。父子进程的数据段通过页表映射到同一块物理内存中,当有一方企图修改数据段时,便以写时拷贝的方式 —— 先拷贝一份要修改的数据,再通过页表映射到新拷贝的内存处,这样一方对数据的修改就不会影响另一方。如下实例:
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <stdlib.h> int g_val = 0; int main() { int i = 10; printf("g_val: %d,i: %d\n", g_val, i); pid_t ret = fork(); if(ret < 0){ perror("fork"); return 1; } else if(ret > 0){//father sleep(3); printf("father start!\n"); g_val = 1; i = 11; printf("g_val: %d,i: %d\n", g_val, i); printf("father end!\n"); wait(NULL); }else{//child printf("child start!\n"); g_val = 2; i = 12; printf("g_val: %d,i: %d\n", g_val, i); printf("child end!\n"); } printf("%d come here\n", getpid()); return 0; }
运行结果:
可以看到,父子进程对数据的修改并不会影响到对方。如下图:
vfork()
vfork函数也用来创建一个进程,但不同于
fork的是
vfork出的子进程不会获得一份父进程的副本,而是直接在父进程的地址空间内运行,所以子进程对数据的修改会影响到父进程内。这一点将在下面验证。这么做的原因是:创建的子进程会立即调用
exec进程函数替换,所以子进程不会使用父进程的地址空间。
除此之外,
vfork函数会保证子进程先运行,直到子进程调用
exit或
exec函数后,父进程才会被调度执行,此间,父进程一直被挂起等待子进程的运行结束。如下代码:
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <stdlib.h> int main() { int i = 0; pid_t ret = vfork(); if(ret < 0){ perror("fork"); return 1; } else if(ret > 0){//father printf("father start!\n"); printf("%p: %d\n", &i, i); printf("father end\n"); }else{//child printf("child start!\n"); ++i; printf("%p: %d\n", &i, i); sleep(5); printf("child end\n"); exit(0); } return 0; }
让子进程睡眠
5秒是为了验证子进程先执行的问题,而变量
i则是为了验证子进程在父进程的地址空间内运行。运行结果如下:
可以看到,子进程运行完了,父进程才运行,而且子进程中对变量
i的修改影响到了父进程。
——完!
【作者:果冻:http://blog.csdn.net/jelly_9】
相关文章推荐
- linux进程介绍之fork vs vfork(转)
- vfork和clone--红灯和围墙
- 进一步了解vfork()函数
- linux之fork,vfork创建子进程
- c语言exit和return区别,在fork和vfork中使用
- c语言exit和return区别,在fork和vfork中使用(转的,觉得对我有帮助)
- 函数fork与vfork的区别与联系
- fork与vfork
- vfork()的若干问题
- linux进程地址空间(1) fork/clone/vfork详解(1)
- 调用vfork 段错误
- fork()与vfork()
- android ndk vfork 缺陷
- 4进程原语:fork()函数,getpid()函数和getppid()函数,getuid()函数,getgid()函数,vfork()
- 用return和exit结束fork和vfork创建的子进程的思考
- linux中fork()和vfork()函数的使用和区别
- [学习笔记]Vfork深入理解
- fork() 、vfork()、clone()
- vfork 挂掉的一个问题
- fork与vfork的区别[转载学习]