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. cf1088E Ehab and a component choosing problem (树形dp)

    题意(考试时看错了对着样例wa了好久..):从树上选k个连通块,使得权值的平均值最大的基础上,选的块数最多 如果不考虑块数最多的限制,肯定是只选一个权值最大的块是最好的 然后只要看这个权值最大的块有多 ...

  2. sublime text3 replace和反向引用

    实用小技巧,主要用于替换爬虫请求头,节省时间. chrome原信息显示: UserID: sds UserPass: sdsd codeKey: 350753 code: 277 B1: 提 subl ...

  3. mybatis 二级缓存

    Mybatis读取缓存次序: 先从二级缓存中获取数据,如果有直接获取,如果没有进行下一步: 从一级缓存中取数据,有直接获取,如果没有进行下一步: 到数据库中进行查询,并保存到一级缓存中: 当sqlSe ...

  4. height、clientHeight、offsetHeight、scrollHeight、height()、 innerHeight()、outerHeight()等的区别

    1.height height是css属性,这个属性定义元素内容区的高度,在内容区外面可以增加内边距.边框和外边距. 当  box-sizing: content-box 时,高度应用到元素的内容框. ...

  5. A1126. Eulerian Path

    In graph theory, an Eulerian path is a path in a graph which visits every edge exactly once. Similar ...

  6. 关于MySQL锁的详解

    有2种 1.表锁 2.行锁 支持 .innodb支持行锁,表级锁 .myisam只支持表级锁 innodb实现了下面2种标准的行级锁 .共享锁 S LOCK 允许事务读一行数据 .排他锁 X LOCK ...

  7. C++ template一些体悟(1)

    #include <iostream> using namespace std; template<typename T> class testClass { public: ...

  8. Unity 摄像机跟随

    方式一:将摄像机直接拖到游戏对象的下面: 方式二:脚本实现 using System.Collections; using System.Collections.Generic; using Unit ...

  9. PHP操作cookie

    1.当只有一个参数的时候,默认是删除,响应报文里面设置了一个过去的时间 setcookie('key2'); 2.当有两个参数的时候,是设置cookie setcookie('key','value1 ...

  10. tcping 与 telnet命令粗略使用

        使用tcping命令,在网上下载tcping文件,放入c盘的system32目录下,即可使用 使用tcping命令用来ping某个端口,能通的话,说明从外部到端口是没有问题的 使用telnet ...