代码

#include <unistd.h>
#include <sys/types.h>
#include <iostream>
#include <errno.h>int main()
{pid_t pid;pid = fork();if (pid < 0)std::cout << "error in fork! errno = " << errno << std::endl;else if (pid == 0)std::cout << "I am the child process, my process id is " << getpid() << std::endl;elsestd::cout << "I am the parent process, my process id is " << getpid() << std::endl;return 0;
}

结果

I am the parent process, my process id is 24275
I am the child process, my process id is 24276

对于 fork() 函数,在父进程中返回子进程的 PID( > 0 ),在子进程中返回 0 。

1、那么 fork() 函数是如何实现在不同的进程中返回不同的结果呢?

答案:

当你的程序执行到下面的语句:

int pid = fork(); 

操作系统创建一个新的进程(子进程),并且在进程表中相应为它建立一个新的表项。新进程和原有进程的可执行程序是同一个程序。上下文和数据,绝大部分就是 原进程(父进程)的拷贝,但它们是两个相互独立的进程!此时程序寄存器 pc,在父、子进程的上下文中都声称,这个进程目前执行到 fork 调用即将返回(此时子进程不占有CPU,子进程的 pc 不是真正保存在寄存器中,而是作为进程上下文保存在进程表中的对应表项内)。问题是怎么返回,在父子进程中就分道扬镳了呢?

父进程继续执行,操作系统对 fork 的实现,使这个调用在父进程中返回刚刚创建的子进程的 PID(一个正整数),所以下面的 if 语句中 pid < 0 , pid == 0 的两个分支都不会执行。所以输出 “I am the parent process...”。

子进程在之后的某个时候得到调度,它的上下文被换入,占据 CPU,操作系统对 fork 的实现,使得子进程中 fork 调用返回0。所以在这个进程(注意这不是父进程了哦,虽然是同一个程序,但是这是同一个程序的另外一次执行,在操作系统中这次执行是由另外一个进程表示的,从执行的角度说和父进程相互独立)中 pid = 0 。这个进程继续执行的过程中,if 语句中 pid < 0 不满足,但是 pid == 0 是 true。所以输出 “I am the child process...”。

为什么看上去程序中互斥的两个分支都被执行了?在一个程序的一次执行中,这当然是不可能的;但是你看到的两行输出是来自两个进程,这两个进程来自同一个程序的两次执行。

fork 之后,操作系统会复制一个与父进程完全相同的子进程,虽说是父子关系,但是在操作系统看来,他们更像兄弟关系,这 2 个进程共享代码空间,但是数据 空间是互相独立的,子进程数据空间中的内容是父进程的完整拷贝,指令指针也完全相同,但只有一点不同,如果 fork 成功,子进程中 fork 的返回值是 0 , 父进程中 fork 的返回值是子进程的进程号,如果 fork 不成功,父进程会返回错误。

可以这样想象,2 个进程一直同时运行,而且步调一致,在 fork 之后,他们分别作不同的工作,也就是分岔了。这也是 fork为什么叫 fork 的原因。

在程序段里用了 fork() 之后程序出了分岔,派生出了两个进程。具体哪个先运行就看该系统的调度算法了。


2、fork() 执行过程。

看这一句:pid=fork() 。

当执行这一句时,当前进程进入 fork() 运行,此时,fork() 内会用一段嵌入式汇编进行系统调用:int 0x80(具体代码可参见内核版本 0.11 的 unistd.h 文件的 133 行 _syscall0 函数)。

这时进入内核根据此前写入 eax 的系统调用功能号 便会运行 sys_fork 系统调用。

接着,sys_fork 中首先会调用 C 函数 find_empty_process 产生一个新的进程,然后会调用 C 函数 copy_process 将父进程的内容复制给子进程,但是子进程 tss 中的 eax 值赋值为 0(这也是为什么子进程中返回 0 的原因)。当赋值完成后, copy_process 会返回新进程(该子进程)的 pid,这个值会被保存到 eax 中。

这时子进程就产生了,此时子进程与父进程拥有相同的代码空间,程序指针寄存器 eip 指向相同的下一条指令地址,当 fork 正常返回调用其的父进程后,因为 eax 中的值是新创建的子进程号。所以,fork()返回子进程号,执行 else(pid > 0)。

当产生进程切换运行子进程时,首先会恢复子进程的运行环境即装入子进程的 tss 任务状态段,其中的 eax 值( copy_process 中置为 0 )也会被装入 eax 寄存器。所以,当子进程运行时,fork 返回的是 0 执行 if( pid == 0 )。


3、fork() 和 vfork()

二者的功能都是创建一个新的进程,但是二者的区别还是比较大的,主要部分如下:

(1)前者创建完进程之后,父进程由于占用了CPU的时间片,所以一般父进程先返回。当然了,如果父子进程都准备好了要返回了,但是此时 CPU 开始轮询了,那么最后谁先返回就不确定了。总的来说,父子进程谁先执行是不确定的。对于后者,能够保证子进程执行 exec 和 exit 之前,父进程处于休眠状态,即:子进程先执行。

(2)前者在创建子进程时,子进程会复制掉父进程的数据段、堆、栈(代码段共享)(Copy on Write)。后者的子进程在执行 exec 和 exit 之前,会与父进程共用父进程的虚拟地址空间。

(SAW:Game Over!)

Linux fork() 和 vfork()相关推荐

  1. Linux fork与vfork的区别

    vfork用于创建一个新进程,而该新进程的目的是exec一个新进程,vfork和fork一样都创建一个子进程,但是它并不将父进程的地址空间完全复制到子进程中,不会复制页表.因为子进程会立即调用exec ...

  2. OS / Linux / clone、fork、vfork 与 pthread_create 创建线程有何不同

    进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合,这些资源在Linux中被抽象成各种数据对象:进程控制块.虚存空间.文件系统,文件I/O.信号处理函数.所以创建一个进程的过程就是这些数 ...

  3. linux如何在C程序中使用exit,c语言exit和return区别,在fork和vfork中使用

    exit函数在头文件stdlib.h中. 简述: exit(0):正常运行程序并退出程序: exit(1):非正常运行导致退出程序: return():返回函数,若在main主函数中,则会退出函数并返 ...

  4. linux内核-系统调用fork、vfork与clone

    前面已经简要地介绍过fork与clone二者的作用于区别.这里先来看一下二者在程序设计接口上的不同: pid_t fork(void); int clone(int (*fn)(void *), vo ...

  5. 进程创建函数fork()和vfork()

    Linux下使用fork()创建一个新的进程,该函数不需要参数,返回值是一个进程id.对于不同的对象,分别是:新建的子进程id(返回给父进程),0(返回给创建的子进程)或者-1(子进程创建失败,返回给 ...

  6. Linux fork()一个进程内核态的变化

    [前言]用户态的变化,耳熟能详不在赘述.现在支持读时共享,写时复制. 一.内核态的变化 1.fork一个子进程代码 #include <stdio.h> #include <stdl ...

  7. fork和vfork,return和exit的理解

    fork和vfork的差别: 1.fork是创建一个子进程,并把父进程的内存数据copy到子进程中. vfork是创建一个子进程,并和父进程的内存数据share一起. 2.vfork是这样的工作的: ...

  8. 细究fork()和vfork()

    在linux系统下,对于程序执行的过程中,我们都会想到进程.而创建进程通常使用fork函数,当然还有vfork函数. 今天在这,我将分享一下我的学习心得:关于fork()和vfork() fork() ...

  9. linux 内核 fork,Linux fork()一个进程内核态的变化

    [前言]用户态的变化,耳熟能详不在赘述.现在支持读时共享,写时复制. 一.内核态的变化 1.fork一个子进程代码 #include #include#include int main(int arg ...

最新文章

  1. shap_value
  2. 高数之差分方程---定义
  3. Spring OXM-XStream注解
  4. LABLEME UPDATE DAMOD
  5. 2016年印度公有云服务市场将达13亿美元
  6. MATLAB自定义画布大小
  7. 用 GDI 操作 EMF 文件[5]: GetEnhMetaFileDescription - 获取 EMF 文件的说明文本
  8. 数仓无损压缩算法:gzip算法
  9. Elasticsearch-2.3.x填坑之路
  10. mongodbVUE基本操作(转)
  11. python编程教学视频-【科研资源03】最全Python编程全套系统视频学习教程
  12. Cache之直接映射
  13. 你想知道的JPype全在这里∞
  14. 为什么快捷指令无法将媒体转换为文本_小红书去水印快捷指令重磅更新,连视频封面图都可以下载了...
  15. 【NLP】千呼万唤始出来——GPT-3终于开源!
  16. python安装失败未指定_win7 64 位安装 python,提示: 0x80240017-未指定的错误
  17. mysql 分区表 外键_【MySQL】表分区
  18. prometheus对接influxdb
  19. Revit二次开发_获取视图样式替换
  20. https改成http(轮播图)

热门文章

  1. 数据库入门基本操作1
  2. 在Linux里设置环境变量的方法(export PATH)--待修改
  3. Oracle数据文件的备份与恢复
  4. 华清远见java考试题_Java面试题,你能答对几个?
  5. HTTP 和 HTTPS
  6. 内核函数 系统调用 系统命令 库函数
  7. ECMAScript 2021(ES12)新特性简介
  8. Go的GAPATH详解
  9. Command命令设计模式
  10. 【软考-软件设计师】计算机指令系统