fork,vfork

转自  http://blog.csdn.net/todd911/article/details/14062103

1.fork函数

一个现有的进程可以调用fork函数创建一个新的子进程。

[cpp] view plaincopy

  1. #include <unitsd.h>
  2. pid_t fork(void);  //子进程返回0,父进程返回子进程ID,出错返回-1。

关于fork函数的常规用法这边不说了,下面说明下父子进程的文件共享。子进程是父进程的副本,例如,子进程获得父进程数

据空间、栈和堆的副本,这是子进程所拥有的副本,父子进程并不共享这些存储空间部分。由于在fork之后经常跟随着exec,

所以现在的很多实现并不执行一个父进程数据段、栈和堆的完全复制,作为替代,使用了写时复制(copy on write,COW)技术。

一个进程具有三个不同的打开文件,它们是标准输入,标准输出和标准出错,从fork返回时,我们有了下面的结构。

这种共享文件的方式使父子进程对同一文件使用了一个文件偏移量,如果父子进程都向标准输出进行写操作,如果父进程的标准输出

已经重定向,那么子进程写到标准输出时,它将更新与父进程共享的该文件的偏移量。比如当父进程等待子进程时,子进程写到标准

输出,而在子进程终止后,父进程也写到标准输出,并且其输出会添加在子进程所写数据之后,如果父子进程不共享同一文件偏移量,

这种形式的交互很难实现。

除了打开文件之外,父进程的很多其他属性也由子进程继承,包括:

  • 实际用户ID,实际组ID,有效用户ID,有效组ID。
  • 附加组ID。
  • 进程组ID。
  • session ID。
  • 控制终端。
  • 设置用户ID标志和设置组ID标志。
  • 当前工作目录。
  • 根目录。
  • 文件模式创建屏蔽字。
  • 信号屏蔽和安排。
  • 针对任意开打文件描述符的在执行时关闭(close-on-exec)标志。
  • 环境。
  • 连接的共享存储段。
  • 资源映射。
  • 资源限制。

父子进程的区别是:

  • fork返回值。
  • 进程ID不同。
  • 两个进程具有不同的父进程ID。
  • 子进程的tms_utime,tms_stime,tms_cutime以及tms_ustime均设置为0.
  • 父进程设置的文件锁不会被子进程继承。
  • 子进程的未处理alarm被清除。
  • 子进程的未处理信号设置为空集。

实践:

[cpp] view plaincopy

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. int g_a = 1;
  4. int main(void){
  5. int l_b = 1;
  6. pid_t p;
  7. if((p = fork()) < 0){
  8. perror("fork");
  9. }else if(p == 0){
  10. g_a++;
  11. l_b++;
  12. }
  13. printf("pid = %d,g_a = %d,l_b = %d\n", getpid(), g_a, l_b);
  14. return 0;
  15. }

运行结果:

[[email protected] apue]$ ./a.out
pid = 15869,g_a = 2,l_b = 2
pid = 15868,g_a = 1,l_b = 1
[[email protected] apue]$ ./a.out
pid = 15870,g_a = 1,l_b = 1
pid = 15871,g_a = 2,l_b = 2

从运行结果来看,子进程的变量进行了改变,而父进程中的变量没有,因为他们是不同的地址空间,不会相互影响。二是父子进程

到底哪个先运行是随机的。

2.vfork函数

vfork函数的调用序列和返回值与fork相同,但是两者的语义不同。

1.vfork和fork都是创建一个子进程,但是它并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用exec(或exit),

于是也就不会存访该地址空间,相反在子进程调用exec或exit之前,它在父进程的空间中运行。这种优化工作方式在某些unix的页

虚拟存储器实现中提高了效率。

2.vfork保证子进程先运行,在它调用exec或exit之后父进程才可能被调度运行。

实践:

[cpp] view plaincopy

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. int g_a = 1;
  5. int main(void){
  6. int l_b = 1;
  7. pid_t p;
  8. if((p = vfork()) < 0){
  9. perror("fork");
  10. }else if(p == 0){
  11. g_a++;
  12. l_b++;
  13. }
  14. printf("ppid = %d,pid = %d,g_a = %d,l_b = %d\n", getppid(), getpid(), g_a, l_b);
  15. return 0;
  16. }

运行结果:

[[email protected] apue]$ ./a.out
ppid = 16090,pid = 16091,g_a = 2,l_b = 2
ppid = 15773,pid = 16090,g_a = 2,l_b = 3908224
Segmentation fault

出现了段错误,因为父子程序在同一内存空间运行,子程序结束后肯定会释放相关的内存,此时父进程再去访问相关的内存,

则出现未知的段错误。将程序修改为如下:

[cpp] view plaincopy

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. int g_a = 1;
  5. int main(void){
  6. int l_b = 1;
  7. pid_t p;
  8. if((p = vfork()) < 0){
  9. perror("fork");
  10. }else if(p == 0){
  11. g_a++;
  12. l_b++;
  13. _exit(0);
  14. }
  15. printf("ppid = %d,pid = %d,g_a = %d,l_b = %d\n", getppid(), getpid(), g_a, l_b);
  16. return 0;
  17. }

运行结果:

ppid = 15773,pid = 16166,g_a = 2,l_b = 2

在子进程中修改的变量在父进程中打印出来也被进行了修改,说明父子进程使用的是同一块内存空间。

在子进程调用了_exit()函数,使子进程退出,但是不关闭标准的IO流等清理操作,如果使用了exit()函数,那么等到父进程执行时

可能会出现意料以外的结果。

下面是vfork的一段官方说明:

vfork()  differs from fork(2) in that the parent is suspended until the
       child terminates (either normally, by calling _exit(2), or  abnormally,
       after  delivery  of  a  fatal signal), or it makes a call to execve(2).
       Until that point, the child shares all memory with its parent,  includ‐
       ing  the stack.  The child must not return from the current function or
       call exit(3), but may call _exit(2).

时间: 2024-09-30 08:41:50

fork,vfork的相关文章

Linux中fork,vfork和clone详解(区别与联系)

fork,vfork,clone Unix标准的复制进程的系统调用时fork(即分叉),但是Linux,BSD等操作系统并不止实现这一个,确切的说linux实现了三个,fork,vfork,clone(确切说vfork创造出来的是轻量级进程,也叫线程,是共享资源的进程) 系统调用 描述 fork fork创造的子进程是父进程的完整副本,复制了父亲进程的资源,包括内存的内容task_struct内容 vfork vfork创建的子进程与父进程共享数据段,而且由vfork()创建的子进程将先于父进程

UNIX高级环境编程(9)进程控制(Process Control)- fork,vfork,僵尸进程,wait和waitpid

本章包含内容有: 创建新进程 程序执行(program execution) 进程终止(process termination) 进程的各种ID ? 1 进程标识符(Process Identifiers) 每个进程都有一个唯一的标识符,进程ID(process ID). 进程的ID是可重用的,如果一个进程被终止,那么它的进程ID会被系统回收,但是会延迟使用,防止该进程ID标识的新进程被误认为是以前的进程. 三个特殊ID的进程: Process ID 0:调度者进程,内核进程. Process

linux 进程创建clone、fork与vfork

目录: 1.clone.fork与vfork介绍 2.fork说明 3.vfork说明 4.clone说明5.fork,vfork,clone的区别 内容: 1.clone.fork与vfork介绍 Linux下的进程与线程相同点是都有进程控制块(PCB,具体的类是task_struct).区别在于一个有独立的进程资源,一个是共享的进程资源.除了内核线程是完全没有用户空间.进程资源包括进程的PCB.线程的系统堆栈.进程的用户空间.进程打开的设备(文件描述符集)等. Linux的用户进程不能直接被

clone(),fork()与vfork()的区别

Linux提供三种方式复制子进程:fork(),clone(),vfork(). 区别: fork()函数复制时将父进程的所以资源都通过复制数据结构进行了复制,然后传递给子进程,所以fork()函数不带参数: clone()函数则是将部分父进程的资源的数据结构进行复制,复制哪些资源是可选择的,这个可以通过参数设定,所以clone()函数带参数,没有复制的资源可以通过指针共享给子进程.Clone()函数的声明如下: int clone(int(*fn)(void *), void *child_s

fork和vfork,return和exit的理解

fork和vfork的差别: 1.fork是创建一个子进程,并把父进程的内存数据copy到子进程中. vfork是创建一个子进程,并和父进程的内存数据share一起. 2.vfork是这样的工作的: (1).保证子进程先执行. (2).当子进程调用exit()或exec()后,父进程往下执行. 3.fork后来采用的优化技术,这样,对于fork后并不是马上拷贝内存,而是只有你在需要改变的时候,才会从父进程中拷贝到子进程中,这样fork后立马执行exec的成本就非常小了.而vfork因为共享内存所

c语言exit和return区别,在fork和vfork中使用

c语言exit和return区别,在fork和vfork中使用 exit函数在头文件stdlib.h中. exit(0):正常运行程序并退出程序: exit(1):非正常运行导致退出程序: return():返回函数,若在main主函数中,则会退出函数并返回一值,可以写为return(0),或return 0. 1. return返回函数值,是关键字:exit是一个函数. 2. return是语言级别的,它表示了调用堆栈的返回:而exit是系统调用级别的,它表示了一个进程的结束. 3. retu

FILE、inode、fork和vfork

1.FILE文件指针 a>.指向进程用户区中的 FILE 结构的数据结构: b>.FILE 结构包括:I/O缓冲区.文件描述符(文件描述表的一个索引): c>.FILE 结构中含有文件描述符,可以使用 fopen 来获得文件指针,然后从文件指针获取文件描述符(文件描述符唯一,而文件指针不唯一,但指向的对象唯一): d>.FILE 结构函数可以看作是队 fd 直接操作的系统调用的封装,它的优点是带有I/O缓存,比 fd 具有跨平台性. 2.inode,软链接,硬链接 inode:记录

Linux之fork与vfork区别

创建一个新进程的方法只有由某个已存在的进程调用fork()或vfork() 1.fork()函数 返回值:成功:父进程:返回子进程的PID 子进程:返回0 失败:父进程返回-1 子进程是父进程的一个拷贝.即子进程从父进程得到数据段和堆.栈段的拷贝,这些需要分配新的内存(不是与父进程共享,而是单独分配内存):而对于只读的代码段,通常使用共享内存的方式访问. fork返回后,子进程和父进程都从调用fork函数的下一条语句开始执行. 由于子进程与父进程的运行是无关的,所以,父进程可先于子进程运行,子进

小程序分析(fork()和vfork())

分析下述程序,i<4时,最终创建了多少个进程. fork()是创建一个子进程. 分析:i=0时,父进程1创建了一个子进程1.(此时共2个进程) i=1时,父进程1继续创建子进程2,将子进程1又当成父进程,创建子进程3.(此时共6个进程) i=2时,将子进程当成父进程继续创建子进程.之前的父进程继续创建子进程.(此时共14个进程) i=3时,将子进程继续当成父进程继续创建子进程.之前的父进程继续创建子进程.(此时共30个进程) 注:可先计算由父进程创建的进程数,最后乘2即为总进程数. 扩展:当i<