上一篇文章学习了进程的基本概念,以及进程的状态,最后学习了Linux中是如何描述一个进程的。本节来学习Linux中进程是如何创建的,以及fork和vfork的区别。

在大学的时候操作系统课程中我们都学过如何去创建一个进程,是通过fork系统调用命令来创建的。

使用fork创建进程

如下是一个简单的通过fork系统调用来创建子进程的例子

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main()
{pid_t pid = fork();if(pid == -1){printf("create child process failed!\n");return -1;}else if(pid == 0){printf("This is child process!\n");}else{printf("This is parent process!\n");printf("parent process pid = %d\n",getpid());printf("child process pid = %d\n",pid);}getchar();return 0;
}

运行结果:

root@ubuntu:zhuxl$ ./a.out
This is parent process!
parent process pid = 91985
child process pid = 91986
This is child process!

先说几个关于fork的知识点:

  • fork返回值为-1, 代表创建子进程失败
  • fork返回值为0,代表子进程创建成功。返回值为0,这个分支就是子进程运行的逻辑
  • fork返回值大于0,这个分支是父进程的运行逻辑。并且返回值等于子进程的pid
  • 简单来说就是fork创建子进程成功后,父进程返回子进程的pid,子进程返回0.

从上面的运行结果来看,子进程的pid=91986, 父进程的pid=91985

ps -ef 命令可以看进程的
UID         PID   PPID  C STIME TTY          TIME CMD
root       91985  90968  0 00:02 pts/0    00:00:00 ./a.out
root       91986  91985  0 00:02 pts/0    00:00:00 ./a.out

Copy-on-Write(写时复制)

在linux早期设计中,当调用fork命令来创建子进程时,子进程会将父进程的所有资源做一次全部拷贝复制工作。这个拷贝复制的工作是相当耗时的,影响系统的整体性能。

当引入了COW机制之后,当fork用来创建子进程时,子进程只拷贝页表的内存,不再拷贝物理地址对应的真正数据了。

比如对应mm资源,使用fork创建子进程后,父子进程会指向同一片物理内存,当父子进程中随便一个去写这块内存时,就会发生分裂(fork),然后谁先写给谁分配一块新的物理页面。这就是COW的原理。我们用一个简单里的例子描述下:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int data=10;int main()
{pid_t pid = fork();if(pid == -1){printf("create child process failed!\n");return -1;}else if(pid == 0){printf("This is child process, data=%d!\n",data);data =90;printf("After child process modify data =%d\n",data);}else{printf("This is parent process=%d!\n",data);}  getchar();return 0;
}

执行结果:

root@ubuntu:zhuxl$ ./a.out
This is parent process=10!
This is child process, data=10!
After child process modify data =90

从运行结果可以看到,不论子进程如何去修改data的值,父进程永远看到的是自己的那一份。有人就可能会说上面的代码是父进程先运行的,父进程运行的时候子进程还没修改data=90。有兴趣的小伙伴可以自动动手修改代码尝试下。

当然了从man fork中也可以找到答案:

子进程虽然是和父进程做资源的拷贝,但是也有一些不同之处,这些在man fork中都有详解。

就比如pid,子进程和父进程都有一个unique的ID。相当于您和您的父亲,出生时是做了一次拷贝比如DNA,但是不一样的也是有的,比如面貌,身材。这都是fork之后的结果

The child process is an exact duplicate of the parent process except for the following points:*  The child has its own unique process ID, and this PID does not match the ID of any existing process group (setpgid(2)) or session.*  The child's parent process ID is the same as the parent's process ID.*  The child does not inherit its parent's memory locks (mlock(2), mlockall(2)).*  Process resource utilizations (getrusage(2)) and CPU time counters (times(2)) are reset to zero in the child.*  The child's set of pending signals is initially empty (sigpending(2)).等等

man fork中也提到了linux中fork是通过cow实现的,是通过复制父进程的page table了实现的。而且我们在现在调用fork命令是通过glibc封装的,其实真正的是调用clone的系统调用命令

Under Linux, fork() is implemented using copy-on-write pages, so the only penalty that it incurs is the time  and  memory  required  toduplicate the parent's page tables, and to create a unique task structure for the child.C library/kernel differencesSince  version  2.3.3,  rather  than invoking the kernel's fork() system call, the glibc fork() wrapper that is provided as part of theNPTL threading implementation invokes clone(2) with flags that provide the same effect as the traditional  system  call.   (A  call  tofork()  is  equivalent  to a call to clone(2) specifying flags as just SIGCHLD.)  The glibc wrapper invokes any fork handlers that havebeen established using pthread_atfork(3).

通过strace来看下fork最终调用的是clone系统调用

可以看到最终是通过clone系统调用,来实现fork子进程的。

通过vfork来创建子进程

上面我们学习了使用fork来创建子进程,接下来看下使用vfork来创建子进程,以及两者的区别。

还是通过例子来学习vfork

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int data=10;int main()
{pid_t pid = vfork();if(pid == -1){printf("create child process failed!\n");return -1;}else if(pid == 0){printf("This is child process, data=%d!\n",data);data =90;printf("After child process modify data =%d\n",data);exit(0);}else{printf("This is parent process=%d!\n",data);}getchar();return 0;
}

运行结果:

root@ubuntu:zhuxl$ ./a.out
This is child process, data=10!
After child process modify data =90
This is parent process=90!

从运行结果中可以看出,当子进程修改了data=90之后,父进程中打印data的值也是90。而且细心的朋友可以看出在相比fork的代码,在子进程中调用了exit函数、

总结下vfork的几个特点:

  • vfork父子进程的mm资源是共享的,当父子进程任意一个进行修改,另外一个进程都会看到修改后的值
  • vfork中子进行是永远先执行的,等待子进程退出父进程才可以执行
  • 以上就是fork和vfork的区别

再通过man vfork来详细看下vfork

Historic descriptionUnder Linux, fork(2) is implemented using copy-on-write pages, so the only penalty incurred by fork(2) is the time and memory  requiredto  duplicate  the  parent's  page tables, and to create a unique task structure for the child.  However, in the bad old days a fork(2)would require making a complete copy of the caller's data space, often needlessly, since usually immediately afterward  an  exec(3)  isdone.   Thus,  for greater efficiency, BSD introduced the vfork() system call, which did not fully copy the address space of the parentprocess, but borrowed the parent's memory and thread of control until a call to execve(2) or an exit occurred.  The parent process  wassuspended  while  the  child  was  using  its  resources.  The use of vfork() was tricky: for example, not modifying data in the parentprocess depended on knowing which variables were held in a register.

上面一大段介绍了vfork出现的背景。in the bad old days 就指的是fork子进程的时候需要全部拷贝父进程的地址空间数据,而且是没必要的,因为拷贝完毕后会立刻执行exec会去重新装载子进行的数据,导致前面的拷贝浪费了。

而vfork的出现使得子进程和父进程共享内存资源,但必须是子进程先运行父进程挂起等待子进程调用exit退出时,父进程才可以真正的运行。

Linux descriptionvfork(), just like fork(2), creates a child process of the calling process.  For details and return value and errors, see fork(2).vfork() is a special case of clone(2).  It is used to create new processes without copying the page tables of the parent  process.   Itmay be useful in performance-sensitive applications where a child is created which then immediately issues an execve(2).vfork()  differs from fork(2) in that the calling thread 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  withits  parent,  including the stack.  The child must not return from the current function or call exit(3) (which would have the effect ofcalling exit handlers established by the parent process and flushing the parent's stdio(3) buffers), but may call _exit(2).As with fork(2), the child process created by vfork() inherits copies of  various  of  the  caller's  process  attributes  (e.g.,  filedescriptors, signal dispositions, and current working directory); the vfork() call differs only in the treatment of the virtual addressspace, as described above.Signals sent to the parent arrive after the child releases the parent's memory (i.e., after the child terminates or calls execve(2)).

上面说了几点:

  • fork和vfork都是用来创建子进程的。
  • vfork和fork相比是不需要做page table拷贝的,也就是父子进程共享地址空间数据
  • vfork创建的子进程是必须先运行的
Linux notesFork  handlers  established  using  pthread_atfork(3)  are not called when a multithreaded program employing the NPTL threading librarycalls vfork().  Fork handlers are called in this case in a program using the LinuxThreads threading library.  (See  pthreads(7)  for  adescription of Linux threading libraries.)A call to vfork() is equivalent to calling clone(2) with flags specified as:CLONE_VM | CLONE_VFORK | SIGCHLD

Vfork相当于调用了clone系统调用,而且参数传递的是CLONE_VM | CIONE_VFORK ,这两个表示CLONE_VM意思是共享mm资源,CLONE_VFORK代表的意思是使用vfork来创建子进程。这两个标识在分析fork的内核源码的时候会用的上。

进程的创建fork vs vfork相关推荐

  1. 进程的创建-fork(python版)

    进程的创建-fork 1. 进程 VS 程序 编写完毕的代码,在没有运行的时候,称之为程序 正在运行着的代码,就成为进程 进程,除了包含代码以外,还有需要运行的环境等,所以和程序是有区别的 2. fo ...

  2. 小何讲进程: Linux进程控制编程 (fork、vfork)

    所谓进程控制,就是系统使用一些具有特定功能的程序段来创建进程.撤消进程以及完成进程在各种状态之间的转换, 从而达到多进程高效率并发执行和协调资源共享的目的.进程控制是进程管理和处理机管理的一个重要任务 ...

  3. 进程的创建-fork

    1.fork是linux/unix中专有的用来创建进程的系统调用. 2.在linux/unix系统中,提供fork()系统函数,非常特殊.普通的系统调用,调用一次就返回一次,而fork()调用一次,会 ...

  4. linux 进程(二) --- 进程的创建及相关api

    一.进程的创建fork()函数 由fork创建的新进程被称为子进程(child process).该函数被调用一次,但返回两次.两次返回的区别是子进程的返回值是0,而父进程的返回值则是 新子进程的进程 ...

  5. 实验二:进程的创建与可执行程序的加载

    学号:SA*****259 姓名:吕良  关键字:fork() exec() task_struct 进程地址空间 ELF文件格式 动态链接库 实验总结(应该说是回答实验问题,因为300-500字真的 ...

  6. 《Linux内核分析》 第六节 进程的描述和进程的创建

    <Linux内核分析> 第六节 进程的描述和进程的创建 20135307 张嘉琪 原创作品转载请注明出处 +<Linux内核分析>MOOC课程http://mooc.study ...

  7. 高级OS(五) - 进程的创建和调度

    高级OS(五) - 进程的创建和调度 一.题目 二.解答 1.进程从诞生到结束,是一个生命周期,在这个生命周期中,进程的创建至关重要,请分析进程的生命周期,并深入到内核源代码对fork进行分析** ( ...

  8. 进程控制——创建,终止

    目录 1.进程的创建 fork函数初识 fork函数返回值 写时拷贝 fork常规用法 2.进程终止 进程退出场景 进程常见退出方法 进程退出返回值的意义: 1.进程的创建 fork函数初识 在lin ...

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

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

  10. Linux进程的创建函数fork()及其fork内核实现解析

    进程的创建之fork() Linux系统下,进程可以调用fork函数来创建新的进程.调用进程为父进程,被创建的进程为子进程. fork函数的接口定义如下: #include <unistd.h& ...

最新文章

  1. 15万人调查发现:做博后越久,一辈子挣的钱越少
  2. 运维企业专题(11)RHCS高可用集群下MySql数据库与共享磁盘(单点写入、多点写入)的设置
  3. windows域中时间同步的解决方案
  4. QtUI设计:设置控件透明
  5. 互联网晚报 | 3月25日 星期五 |​ ​​私募大佬但斌疑似空仓;蔚来和小米汽车拟采用比亚迪电池...
  6. Linux基础(6)--文件IO操作
  7. mysql 分区 key 写法_MySQL KEY分区
  8. Gnome2.30 GDM圖片風格設置方式
  9. [Spring]04_最小化Spring XML配置
  10. 2019美赛A题—学习记录
  11. 万用表蜂鸣器档の响一声
  12. 树莓派官方显示屏亮度
  13. 哈哈哈……~好敷衍的第一篇博客标题~
  14. 【程序员高考】2018年全国统一高考程序员试卷
  15. html5 模仿语音聊天气泡,HTML5实现对话气泡动画方法
  16. 【MySQL】在线无锁无延迟DDL神器gh-ost
  17. cmd窗口最小化运行
  18. python实现mongodb数据导入到mysql
  19. 设计模式入门(王者荣耀之设计英雄篇)
  20. linux下区分各种SCSI磁盘类型

热门文章

  1. Eclipse中经常出现的问题解决方案
  2. Hibernate - Query简易
  3. 微擎系统 微信支付 get_brand_wcpay_request:fail
  4. 升级Xcode8后需要添加的一些权限
  5. 【转】VMware网络连接模式—桥接、NAT以及仅主机模式的详细介绍和区别
  6. 视频点播-上传视频状态异常
  7. 限流的简单使用及学习
  8. T-SQL数据类型的细微差别(四)
  9. web 开发安全性问题
  10. java web 开发需要用到的技术