22.1 fork 和 vfork 函数

22.1.1 函数说明

 #include <unistd.h>
#include <sys/types.h>
pid_t fork( void);
  • 函数说明:

    • 一个现有进程可以调用fork函数创建一个新进程。
    • 由fork创建的新进程被称为子进程(child process)。
    • fork函数被调用一次但返回两次。
    • 两次返回的唯一区别是子进程中返回 0 值而父进程中返回子进程 ID。
    • 子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。
    • 注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间。
    • UNIX将复制父进程的地址空间内容给子进程,因此,子进程有了独立的地址空间。
    • 在不同的UNIX (Like)系统下,我们无法确定fork之后是子进程先运行还是父进程先运行,这依赖于系统的实现。
    • 所以在移植代码的时候我们不应该对此作出任何的假设。
  • 函数返回值:
    • 子进程中为 0,父进程中为子进程 ID,出错为 -1
  • 子进程的继承属性
    • 用户信息和权限、目录信息、信号信息、环境、共享存储段、资源限制、堆、栈和数据段,共享代码段。  
  • 子进程特有属性
    • 进程 ID、锁信息、运行时间、未决信号  
  • 操作文件时的内核结构变化
    • 子进程只继承父进程的文件描述表,不继承但共享文件表项和 i-node。
    • 父进程创建一个子进程后,文件表项中的引用计数器加1变成2,当父进程作 close 操作后,计数器减 1,子进程还是可以使用文件表项,只有当计数器为 0 时,才会释放文件表项。 
 #include <unistd.h>
pid_t vfork(void);
  • 函数说明

    • vfork()会产生一个新的子进程,其子进程会复制父进程的数据与堆栈空间,并继承父进程的用户代码,组代码,环境变量、已打开的文件代码、工作目录和资源限制等。
    • Linux 使用 copy-on-write(COW)技术,只有当其中一进程试图修改欲复制的空间时才会做真正的复制动作,由于这些继承的信息是复制而来,并非指相同的内存空间,因此子进程对这些变量的修改和父进程并不会同步。
    • 此外,子进程不会继承父进程的文件锁定和未处理的信号。
    • 注意,Linux不保证子进程会比父进程先执行或晚执行,因此编写程序时要留意死锁或竞争条件的发生。
  • 返回值
    • 如果 vfork()成功则在父进程中会返回新建立的子进程代码(PID),而在新建立的子进程中则返回 0。
    • 如果 vfork 失败则直接返回-1,失败原因存于errno中。
  • 错误代码
    • EAGAIN 内存不足。
    • ENOMEM 内存不足,无法配置核心所需的数据结构空间。

  两个函数的附加说明:

  • fork 创建的新进程被称为子进程,该函数被调用一次,但返回两次。两次返回的区别是:在子进程中的返回值是 0,而在父进程中的返回值则是新子进程的进程 ID。
  • 创建子进程,父子进程哪个先运行根据系统调度且子进程会复制父进程的内存空间
  • vfork 创建的子进程会先运行,但不会复制父进程的内存空间

22.1.2 例子

22.1.2.1 查看父子进程的运行

  process_fork.c

 #include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h> int main(void)
{
printf("pid: %d", getpid()); pid_t pid;
pid = fork();//创建子进程
//在 fork 后,会运行两个进程(父进程和子进程)
if(pid < ) {
perror("fork error");
} else if(pid > ) {
//父进程(在父进程中返回的是子进程的 pid)
//父进程执行的代码
printf("I am parent process pid is %d, ppid is %d, fork return is %d\n",
getpid(), getppid(), pid);
} else {
//子进程(在子进程中 fork 返回的是0)
//子进程执行的代码
printf("I am child process pid is %d, ppid is %d, fork return is %d\n",
getpid(), getppid(), pid);
} //这里的代码是父子进程都要执行的代码
printf("pid: %d\n", getpid());
sleep(); return ;
}

  

  可以看见 父子进程的 pid 是不同的,父进程中通过fork 获得是子进程的ID号,子进程的父进程是102839,fork 后返回的是0

  子进程的ID 和 父进程的ID 在公共代码区都打印了出来。

22.1.2.2 两进程循环启动后,各自输出内容

 /* 父子进程交替运行 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> int main(void)
{
printf("current pid : %d\n", getpid()); pid_t pid = fork();
if(pid < ) {
perror("fork error");
} else if(pid > ) {
//父进程执行
int i;
for(i = ; i < ; i++) {
printf("This is parent process pid is : %d\n", getpid());
sleep();
}
} else {
//子进程
int i;
for(i = ; i < ; i++) {
printf("This is child process pid is : %d\n", getpid());
sleep();
}
} return ;
}

  编译运行:

  

  虽然设置了交替运行,但是不是真的交替运行,这个与系统调度来决定的。

22.1.2.3  子进程内存的复制

  

  案例:

 #include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h> int g_val = ;//全局变量,存放在数据段 int main(void)
{
int a_val = ;//局部变量,调用的时候存放在栈中
static int s_val = ;//静态变量,存放在数据段
printf("pid: %d", getpid()); pid_t pid;
pid = fork();//创建子进程
//在 fork 后,会运行两个进程(父进程和子进程)
if(pid < ) {
perror("fork error");
} else if(pid > ) {
//父进程(在父进程中返回的是子进程的 pid)
//父进程执行的代码
g_val = ;
a_val = ;
s_val = ; printf("I am parent process pid is %d, ppid is %d, fork return is %d\n",
getpid(), getppid(), pid);
printf("g_val: %p, a_val: %p, s_val: %p\n", &g_val, &a_val, &s_val);
} else {
//子进程(在子进程中 fork 返回的是0)
//子进程执行的代码
g_val = ;
a_val = ;
s_val = ;
printf("I am child process pid is %d, ppid is %d, fork return is %d\n",
getpid(), getppid(), pid);
printf("g_val: %p, a_val: %p, s_val: %p\n", &g_val, &a_val, &s_val);
} //这里的代码是父子进程都要执行的代码
printf("pid: %d, g_val: %d, a_val: %d, s_val: %d\n", getpid(), g_val, a_val, s_val);
sleep(); return ;
}

  可以看见变量所在的虚拟地址都是一样的,但是输出的值却不一样。

  值不一样是因为,物理空间不一样,子进程和父进程指向的物理空间不一样。数据段、堆和栈都由各自的物理空间。虚拟内存存放的数据最终都会存放在物理地址上。最终修改的也是物理内存中的内容。程序中最终输出的也是物理地址的内容。

二十二、Linux 进程与信号---进程创建的更多相关文章

  1. Linux学习之CentOS(二十六)--Linux磁盘管理:LVM逻辑卷的创建及使用

    在上一篇随笔里面 Linux学习之CentOS(二十五)--Linux磁盘管理:LVM逻辑卷基本概念及LVM的工作原理,详细的讲解了Linux的动态磁盘管理LVM逻辑卷的基本概念以及LVM的工作原理, ...

  2. JAVA基础知识总结:一到二十二全部总结

    >一: 一.软件开发的常识 1.什么是软件? 一系列按照特定顺序组织起来的计算机数据或者指令 常见的软件: 系统软件:Windows\Mac OS \Linux 应用软件:QQ,一系列的播放器( ...

  3. 智课雅思词汇---二十二、-al即是名词性后缀又是形容词后缀

    智课雅思词汇---二十二.-al即是名词性后缀又是形容词后缀 一.总结 一句话总结: 后缀:-al ②[名词后缀] 1.构成抽象名词,表示行为.状况.事情 refusal 拒绝 proposal 提议 ...

  4. [分享] IT天空的二十二条军规

    Una 发表于 2014-9-19 20:25:06 https://www.itsk.com/thread-335975-1-1.html IT天空的二十二条军规 第一条.你不是什么都会,也不是什么 ...

  5. Bootstrap <基础二十二>超大屏幕(Jumbotron)

    Bootstrap 支持的另一个特性,超大屏幕(Jumbotron).顾名思义该组件可以增加标题的大小,并为登陆页面内容添加更多的外边距(margin).使用超大屏幕(Jumbotron)的步骤如下: ...

  6. Web 前端开发精华文章推荐(HTML5、CSS3、jQuery)【系列二十二】

    <Web 前端开发精华文章推荐>2014年第一期(总第二十二期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各类能够提升网站用户体验的优秀 jQuery 插件,展示前沿的 HTML ...

  7. 二十二、OGNL的一些其他操作

    二十二.OGNL的一些其他操作 投影 ?判断满足条件 动作类代码: ^ $   public class Demo2Action extends ActionSupport {     public ...

  8. WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[中篇]

    原文:WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[中篇] 在[上篇]中,我们分别站在消息交换和编程的角度介绍了SOAP Fault和FaultException异常.在服务执行过 ...

  9. VMware vSphere 服务器虚拟化之二十二桌面虚拟化之创建View Composer链接克隆的虚拟桌面池

    VMware vSphere 服务器虚拟化之二十二桌面虚拟化之创建View Composer链接克隆的虚拟桌面池 在上一节我们创建了完整克隆的自动专有桌面池,在创建过程比较缓慢,这次我们将学习创建Vi ...

  10. Bootstrap入门(二十二)组件16:列表组

    Bootstrap入门(二十二)组件16:列表组 列表组是灵活又强大的组件,不仅能用于显示一组简单的元素,还能用于复杂的定制的内容. 1.默认样式列表组 2.加入徽章 3.链接 4.禁用的列表组 5. ...

随机推荐

  1. Linux基本命令总结(一)

    java开发的服务器一般都是linux系统,因此把有关命令小结一下: 1,cd [目录名] 进入相应的目录下. cd / 进入系统的根目录 cd .. 或者 cd .. // 退入当前位置的上级目录 ...

  2. 解决在vue中axios请求超时的问题

    查看更多精彩内容请访问我的新博客:https://www.cssge.com/ 自从使用Vue2之后,就使用官方推荐的axios的插件来调用API,在使用过程中,如果服务器或者网络不稳定掉包了, 你们 ...

  3. [bzoj1692][队列变换]

    题目链接 思路 首先肯定想到贪心,从队尾和队首取更小的那个. 但是如果遇到队尾和队首一样大的情况呢,总不能再挨个往前比较.所以就把整个字符串倒过来再挂到现在字符串的后面,也就是把当前字符串对称过去.然 ...

  4. js 判断所选时间(或者当前时间)是否在某一时间段的实现代码

    var time_range = function (beginTime, endTime, nowTime) { var strb = beginTime.split (":") ...

  5. Django 路由

    创建好项目后在项目文件下的urls.py为设置路由 Django 有两种路由方式一种的精确路由 另一个为模糊路由 """mysite URL Configuration ...

  6. 汇编 gdb调试

    as -g --32 -o hello.o hello.s ld -m elf_i386 -o hello hello.o gdb hello

  7. vue-cli+webpack在生成的项目中使用bootstrap的方法

    在一个html页面中加入bootstrap是很方便,就是一般的将css和js文件通过Link和Script标签就行.那么在一个用vue-vli生成的前端项目中如何加入?因为框架不一样了,略微要适应一下 ...

  8. JS实现clone()方法,对五种主要数据类型进行值复制

    Object.Array.Boolean.Number.String 分为三种情况:普通变量,Array,Object 递归调用

  9. charles使用:iOS11的手机用charles抓包https

    参考:https://www.jianshu.com/p/235bc6c3ca77 因为ios11经常抓不了包,以前一直没管,今天实在是不行,,,,搞了一下.OK了 步骤: 1.下载并安装charle ...

  10. noi.openjudge 1.13.15

    http://noi.openjudge.cn/ch0113/15/ 总时间限制:  1000ms 内存限制:  65536kB 描述 输入一个长度为N的整数序列 (不多于128个整数),每个整数的范 ...