【原创】《Linux高级程序设计》杨宗德著- 进程管理与程序开发 - fork和vfork函数
【原创】《Linux高级程序设计》杨宗德著- 进程管理与程序开发 - fork和vfork函数
1. 创建进程
fork函数调用成功后,其子进程会复制父进程的几乎所有信息(除PID等信息),主发复制父亲进程的代码段、数据段、BSS、堆、栈(关于进程结构参阅本书第3章)、打开的文件描述符(但共用同一个文件表项)。另外,子进程从父进程继承下列属性:实际用户/组号、有效用户/组号以及保留的用户/组号、进程组号、环境变量、对文件的执行时关闭标志、信号处理方式设置、信号掩码、当前工作目录、根目录、文件模式创建掩码、文件大小限制等信息。
代码实例:
/************************************************************************* > File Name: fork_basic.c > Author: Geng > Mail: genglut@163.com > Created Time: Thu 30 Oct 2014 10:19:00 PM CST ************************************************************************/ #include<stdio.h> #include<unistd.h> #include<sys/types.h> int main(int argc,char *argv[]) { pid_t pid; if((pid=fork())==-1) printf("fork error"); printf("Geng!\n"); return 0; }
运行结果:
$ ./fork_basic Geng! Geng!
从运行结果可以看出,fork函数后的代码在子进程中也被执行。实际上,其他代码也在子进程的代码段中,只是子进程执行位置为fork返回的位置,其之前的代码无法执行罢了。
2. 子进程对父进程文件流缓冲区的处理
在创建子进程时,子进程的用户空间将复制父进程的用户空间所有信息,显然,也包含流缓冲区的内容。如果流缓冲区中有临时信息,则同样复制到子进程的用户空间流缓冲区中。
示例代码如下:
/************************************************************************* > File Name: streamfork.c > Author: Geng > Mail: genglut@163.com > Created Time: Thu 30 Oct 2014 10:36:05 PM CST ************************************************************************/ #include <stdio.h> #include <unistd.h> #include <stdlib.h> int main(int argc,char *argv[]) { pid_t pid; printf("before fork,have enter\n"); printf("before fork,no enter:pid=%d\t",getpid()); pid=fork(); if(pid==0) printf("\nchild,after fork:pid=%d\n",getpid()); else printf("\nparent,after fork:pid=%d\n",getpid()); }
运行结果:
$ ./a.out before fork,have enter before fork,no enter:pid=5397 parent,after fork:pid=5397 before fork,no enter:pid=5397 child,after fork:pid=5398
从运行结果可以看出,如果流缓冲区中有临时信息,则同样复制到子进程的用户空间流缓冲区中。
3. 子进程对父进程打开的文件描述符的处理
fork函数创建子进程后,子进程将复制父进程的数据段、BSS段、代码段、堆空间、栈空间和文件描述符,而对于文件描述符关联的内核文件表项(即struct file结构),则是采用共享的方式。
示例代码如下:
/************************************************************************* > File Name: datah.c > Author: Geng > Mail: genglut@163.com > Created Time: Thu 30 Oct 2014 11:20:37 PM CST ************************************************************************/ #include <sys/types.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <stdlib.h> int main(int argc,char *argv[]) { pid_t pid; int fd; int i=1; int status; char *ch1="hello"; char *ch2="world"; char *ch3="IN"; if((fd=open("test.txt",O_RDWR|O_CREAT,0644))==-1) { perror("parent open"); exit(EXIT_FAILURE); } if(write(fd,ch1,strlen(ch1))==-1) { perror("parent write"); exit(EXIT_FAILURE); } if((pid=fork())==-1) { perror("fork"); exit(EXIT_FAILURE); } else if(pid==0) { i=2; printf("in child\n"); printf("i=%d\n",i); if(write(fd,ch2,strlen(ch2))==-1) perror("child write"); return 0; } else { sleep(1); printf("in parent\n"); printf("i=%d\n",i); if(write(fd,ch3,strlen(ch3))==-1) perror("parent,write"); wait(&status); return 0; } }
运行结果:
$ ./a.out in child i=2 in parent i=1 $ cat test.txt helloworldIN
通过文件test.txt可以看出,父子进程共同对同一文件操作,且写入数据不交叉覆盖,说明父子进程共享文件偏移,因此,共享文件表项。
对于变量i,在子进程中进行了第二次赋值(i=2),其结果为2,而父进程中i的值不变,显然,父子进程各自拥有这一变量的副本,互不影响。
4. vfork测试全局数据段与BSS段的使用策略
fork和vfork有一定的区别,fork是复制一个父进程的副本,拥有自己独立的代码段、数据段及堆栈空间。而vfork是共享父进程的代码及数据段。
示例程序:
#include<unistd.h> #include<error.h> #include<sys/types.h> #include<stdio.h> #include<stdlib.h> int glob=6; int main() { int var; pid_t pid; var=88; printf("in beginning:\tglob=%d\tvar=%d\n",glob,var); if((pid=vfork())<0) { perror("vfork"); exit(EXIT_FAILURE); } else if(pid==0) { printf("in child,modify the var:glob++,var++\n"); glob++; var++; printf("in child:\tglob=%d\tvar=%d\n",glob,var); _exit(0); } else { printf("in parent:\tglob=%d\tvar=%d\n",glob,var); return 0; } }
运行结果:
$ ./a.out in beginning: glob=6 var=88 in child,modify the var:glob++,var++ in child: glob=7 var=89 in parent: glob=7 var=89
由结果可以看出,父子进程共享数据空间,因此,打印的信息是一致的。
如果将上述代码中的vfork换成fork,其他代码不做修改,重新编译执行后,结果如下:
8$ ./a.out in beginning: glob=6 var=88 in parent: glob=6 var=88 in child,modify the var:glob++,var++ in child: glob=7 var=89
由上可知,父子进程打印的信息不一致。即父子进程各自拥有独立的地址空间。
5. 子函数调用vfork创建子进程
首先看运行结果:
$ ./a.out 1:child pid=7087,ppid=7086 3:child pid=7087,ppid=7086 2:parent pid=7086,ppid=3363 段错误 (核心已转储)
示例代码:
#include<stdio.h> #include<unistd.h> #include<stdlib.h> void test() { pid_t pid; pid=vfork(); if(pid==-1) { perror("vfork"); exit(EXIT_FAILURE); } else if(pid==0) { printf("1:child pid=%d,ppid=%d\n",getpid(),getppid()); return; } else printf("2:parent pid=%d,ppid=%d\n",getpid(),getppid()); } void fun() { int i; int buf[100]; for(i=0;i<100;i++) buf[i]=0; printf("3:child pid=%d,ppid=%d\n",getpid(),getppid()); } int main() { pid_t pid; test(); fun(); }
出现段错误的原因,见教材197页。
6. fork和vfork的区别
参考链接:
http://blog.csdn.net/buaalei/article/details/5348382
http://blog.sina.com.cn/s/blog_7673d4a5010103x7.html