fork 与vfork
来源:互联网 发布:ue json格式化 编辑:程序博客网 时间:2024/05/29 06:55
-----------------
缓冲区被复制引发的问题
其中的示例代码我跑了一下,没有得到文中的结果。
分析:printf("before fork!!\n");应该为printf("before fork!!");
遇到\n就会马上输出了。
-------------------------
一、fork系统调用
1、函数的声明:
- #include <unistd.h>
- pid_t fork(void);
2、返回值:
fork函数调用一次,将会返回两次(返回给主进程为新创建的子进程的进程ID,返回给子进程的是0)。当进程创建失败时候,fork返回值为-1。
- 因为父进程种可能有多个子进程,但没有一个函数可以获得所有子进程的进程ID,所以我们通过fork调用时候将新创建的进程的进程ID返回给主进程。
- 由于应用程序中创建的进程的进程ID不可能为0,且我们知道进程ID是一个非负值,所以为了在后续代码中区分当前进程是主进程还是新创建的子进程,我们需要通过返回值来区分,因此fork将在子进程中返回0;
3、函数详细说明
1、子进程是父进程的副本
子进程通过fork创建成功以后。子进程和父进程都将继续执行fork调用之后l的指令,子进程是父进程的副本(子进程获得父进程的数据空间:堆、栈副本)。这里我们特别要注意的是,子进程获得的是父进程的副本,而不是父子进程共享数据空间。但父子进程将会共享正文段。如下程序所示:
- #include <unistd.h>
- #include <stdio.h>
- int main()
- {
- int a = 9;
- pid_t pid = -1;
- if((pid = fork()) < 0)
- {
- //如果进程ID小于零则报错,退出
- printf("fork error!!\n");
- return -1;
- }
- else if(pid == 0)
- {
- //我们在子进程中将会获得主进程中自动变量a的一个副本,我们在子进程中a++操作将操作的是子进程中的副本而不是父进程中的自动变量a的值。 因此父进程中的变量a的值将不会改变。
- a++;
- }
- return 0;
- }
当我们在调用fork之前如果使用了标准I/O(带缓冲的I/O),则在使用fork之前最好刷新(或冲洗)标准输出流。因为在fork创建子进程后,系统会为子进程复制父进程数据空间以及标准输出缓冲区。而如果不刷新标准输出缓冲区,则调用fork时候,之前通过标准输出流输出的数据还在缓冲区中,则子进程也就拥有了一份缓冲区的副本。如下程序所示:
- #include <stdio.h>
- #include <unistd.h>
- int main()
- {
- pid_t pid = -1;
- //printf 带缓冲的标准输出,当输出以后,数据将仍就存留在缓冲区中。随后,子进程会复制缓冲区中的数据。所以在子进程中将会再次输出: before fork
- printf("before fork!!\n");
- if((pid = fork()) < 0)
- {
- //如果进程ID小于零则报错,退出
- printf("fork error!!\n");
- return -1;
- }
- else if(pid == 0)
- {
- //子进程中我们什么都没有做
- }
- return 0;
- }
- before fork!!
- before fork!!
- before fork!!
如果我们重定向了父进程的标准输出,那么子进程的标准输出也会被重定向。即:fork会将父进程中所有打开的文件描述符都复制到子进程中。
4、文件共享
从上面所述,我们已经了解到调用fork后,系统会将父进程中所有打开的文件描述符复制到子进程中。父进程与子进程将会共享所打开的文件表项,则父进程与子进程共享同一个文件偏移量。如果我们要满足子进程与父进程之间文件访问的同步,则在fork调用之后处理文件描述符有两种常见的情况:
- 父进程等待子进程完成。
- 父子进程各自执行不同的程序段。即,虽然子进程会复制父进程的文件描述符,但可以在子进程程序段开始关闭这些打开但不再需要的文件描述符,则可以避免父子进程之间的互相影响。
5、fork的两种用法:
- 一个父进程希望自己被复制,使得父子进程同时执行不同的程序段,这在网络程序设计中是常见的。例如:父进程一直等待客户端的连接请求。当请求到达时候,父进程调用fork,使子进程处理刚到达的请求,而父进程继续等待下一个请求的到达。
- 一个进程要执行一个不同的程序,这对shell是常见的。子进程从fork返回后立即调用exec。
6、另外的一点话:
某些操作系统讲fork与exec合并在一起,并称其为spawn。UNIX将其分开,可以使在fork和 exec之间做一些其他的必要的操作。如,重定向标准输出、设置用户ID等。另外有些时候,在我们调用fork之后没有必要执行exec。所以,Unix系统中并没有将fork与exec合并(UNIX在高级实现时候,选项组中确实包括了spawn接口,但是该接口并不打算代替fork和exec)。
二、vfork函数
1、函数声明:
- #include <sys/types.h>
- #include <unistd.h>
- pid_t vfork(void);
2、返回值:
vfork函数的返回值与fork函数返回值相同,参见fork函数返回值部分的说明。3、函数详细说明:
1、创建进程的目的
vfork函数用于创建一个新进程,新进程的目的是执行(exec)一个新程序。
2、与fork函数一样都创建一个子进程。但它并不将父进程的地址空间完全复制到子进程中。因为子进程会立即调用exec函数,于是也就没有必要保存父进程的地址空间了。
3、vfork函数创建的子进程中,在调用exec之前,子进程是在父进程空间中执行的。因此,子进程会与父进程共享堆、栈,子进程中改变父进程中的变量会引起父进程中变量值的修改,如下:
- #include <stdio.h>
- #include <unistd.h>
- int main()
- {
- int a = 9;
- pid_t pid = -1;
- if((pid = vfork()) < 0)
- {
- //如果进程ID小于零则报错,退出
- printf("vfork error!!\n");
- return -1;
- }
- else if(pid == 0)
- {
- //我们在子进程中修改a的值,会改变父进程中自动变量a的值
- a++;
- printf("child process: a = %d ppid = %d\n",a,getppid());
- _exit(0);
- }
- else
- {
- //我们在父进程中打印变量a的值。
- printf("parent process: a = %d child_pid = %d\n",a,pid);
- }
- return 0;
- }
- child process: a = 10 ppid = 2893
- parent process: a = 10 child_pid = 2894
4、vfork与fork之间的另外一个区别:
前面我们已经提到vfork与fork之间的一个区别(是否会复制父进程的地址空间)。vfork与fork之间的另外一个区别是,vfork会保证子进程首先运行,在调用exec或者exit之后,父进程才可能被调度运行。如果在这些函数(exec、exit)之前有依赖于父进程的进一步动作,则会导致死锁。
5、子进程中exit的使用
- fork与vfork
- fork与vfork
- fork() 与 vfork()比较
- fork与vfork 函数
- vfork()与fork()区别
- fork与vfork
- vfork与fork区别
- fork与vfork
- fork 与 vfork
- fork与vfork
- fork()与vfork()
- fork与vfork
- vfork与fork
- fork 与vfork
- fork 与 vfork区别
- APUE--fork与vfork
- fork与vfork
- fork与vfork浅析
- 58同城 2013研发一面面试(含参考答案)
- oj1104
- sql语句实现多列前缀匹配求比值
- 国内各地图API坐标系统比较
- soap
- fork 与vfork
- shell的字符串和数字的转化(数字自动做字符串处理,变量名做字符串输出用单引号)
- Linux--top命令的使用
- [转载]从零开始学习OpenGL ES之一 – 基本概念
- OCP-1Z0-052-V8.02-138题
- ASP.NET MVC 入门4、Controller与Action
- OpenGL学习总结
- 12306——(一)火车余票查询API
- 开博