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. poj 1611 The Suspects(并查集输出集合个数)

    Description Severe acute respiratory syndrome (SARS), an atypical pneumonia of unknown aetiology, wa ...

  2. js 获取当前时间 年月日

    var datetime = new Date(); var year = datetime.getFullYear(); var month = datetime.getMonth() + 1 &l ...

  3. Go 语言和 Scala 语言对比

    我在Google写过Go(自己的业余时间),也在LinkedIn写过Scala.两者都是具有一流的并发特性的现代语言. 下面的回答是基于我编写大规模的软件的经验得出. Go是一种开发模式严格固定,并且 ...

  4. (转)C++ 值传递、指针传递、引用传递详解

    一直以来对函数的值传递引用传递理解很模糊,这篇文章可以说是给自己扫盲了. 值传递:实参不会发生改变,是因为形参传递的是不是实参的源地址(形参和实参地址不一样).不影响实参 指针传递:本质也是值传递,只 ...

  5. 第二十九篇-Fragment动态用法

    效果图: 上节学习了静态添加Fragment的方法,这节学习动态添加方法. 主页面 layout.xml Fragment页面 layout2.xml 实现功能,当点击主页面的button时,将Fra ...

  6. Echarts CPU监控 (折线仪表盘,图例混搭)

    https://blog.csdn.net/mengxiangfeiyang/article/details/44802939 CPU page   <script type="tex ...

  7. 关于ComponentName组件名称的使用

    ComponentName,顾名思义,就是组件名称,通过调用Intent中的setComponent方法,我们可以打开另外一个应用中的Activity或者服务. 实例化一个ComponentName需 ...

  8. okhttp 内网可以有,但外网访问数据返不回来,代码一样

    :1.问题点在于 下图红框里写成 text/html了,需要改成application/json,造成的问题有:unexpected end of stream  这个是406错误:加上日志之后okh ...

  9. position:fixed固定定位的用法

    一.position:fixed:固定定位 1.实现某个元素在可视窗口的居中位置显示 1)给自身设置宽高: 2)给自身加position:fixed: 3)用margin向左移动自身宽度的一半,向上移 ...

  10. 114. Flatten Binary Tree to Linked List(M)

    . Flatten Binary Tree to Linked List Given a binary tree, flatten it to a linked list in-place. For ...