(1) fork系统调用说明

fork系统调用用于从已存在进程中创建一个新进程,新进程称为子进程,而原进程称为父进程。fork调用一次,返回两次,这两个返回分别带回它们各自的返回值,其中在父进程中的返回值是子进程的进程号,而子进程中的返回值则返回 0。因此,可以通过返回值来判定该进程是父进程还是子进程。

使用fork函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间,包括进程上下文、进程堆栈、内存信息、打开的文件描述符、信号控制设定、进程优先级、进程组号、当前工作目录、根目录、资源限制、控制终端等,而子进程所独有的只有它的进程号、计时器等。因此可以看出,使用fork系统调用的代价是很大的,它复制了父进程中的数据段和堆栈段里的绝大部分内容,使得fork系统调用的执行速度并不很快。

fork的返回值这样设计是有原因的,fork在子进程中返回0,子进程仍可以调用getpid函数得到自己的进程ID,也可以调用getppid函数得到父进程的进程ID。在父进程中使用getpid函数可以得到自己的进程ID,然而要想得到子进程的进程ID,只有将fork的返回值记录下来,别无它法。

fork的另一个特性是所有由父进程打开的文件描述符都被复制到子进程中。父、子进程中相同编号的文件描述符在内核中指向同一个file结构体,也就是说,file结构体的引用计数要增加。

由于代码段(加载到内存的执行码)在内存中是只读的,所以父子进程可共用代码段,而数据段和堆栈段子进程则完全从父进程复制拷贝了一份。

2)父进程进行fork系统调用时完成的操作

假设id=fork(),父进程进行fork系统调用时,fork所做工作如下:

①    为新进程分配task_struct任务结构体内存空间。

②    把父进程task_struct任务结构体复制到子进程task_struct任务结构体。

③    为新进程在其内存上建立内核堆栈。

④    对子进程task_struct任务结构体中部分变量进行初始化设置。

⑤    把父进程的有关信息复制给子进程,建立共享关系。

⑥    把子进程加入到可运行队列中。

⑦    结束fork()函数,返回子进程ID值给父进程中栈段变量id。

⑧    当子进程开始运行时,操作系统返回0给子进程中栈段变量id。

(3)fork调用时所发生的事情

下面代码是fork函数调用模板,fork函数调用后常与if-else语句结合使用使父子进程执行不同的流程。假设下面代码执行时产生的是X进程,fork后产生子进程的是XX进程,XX进程的进程ID号为1000。

int pid ;

pid = fork();

if (pid < 0) {

perror("fork failed");

exit(1);

}

if (pid == 0) {

message = "This is the child/n";

调用fork前,内存中只有X进程,如图12-9所示,此时XX进程还没“出生”。

图12-9 fork前的内存

调用fork后,内存中不仅有X进程(父进程),还有XX进程(子进程)。fork的时候,系统几乎把父进程整个堆栈段(除代码段,代码段父子进程是共享的)复制给了子进程,复制完成后,X进程和XX进程是两个独立的进程,

如下图12-10所示,只不过X进程栈中变量id值此时为1000,而XX进程栈中变量id值为0。fork调用完成后,X进程由系统态回到用户态。此后,X进程和XX进程各自都需要从自己代码段指针指向的代码点继续往下执行,父进程X往下执行时判断id大于0,所以执行大于0的程序段,而子进程XX往下执行时判断id等于0,所以执行等于0的程序段。

图12-10 fork后的内存

4)fork 函数原型

所需头文件

#include <sys/types.h>   // 提供类型 pid_t 的定义

#include <unistd.h>

函数说明

建立一个新的进程

函数原型

pid_t fork(void)

函数返回值

0:返回给子进程

子进程的ID(大于0的整数):返回给父进程

-1:出错,返回给父进程,错误原因存于errno中

错误代码

EAGAIN:内存不足

ENOMEM:内存不足,无法配置核心所需的数据结构空间

(5)fork函数使用实例

fork.c源代码如下:

#include <sys/types.h>

#include <sys/wait.h>

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

int main(void)

{

pid_t pid;

char *message;

int n;

pid = fork();

if (pid < 0) {

perror("fork failed");

exit(-1);

}

if (pid == 0) {

message = "This is the child\n";

n = 3;

} else {

wait(0) ; /*阻塞等待子进程返回*/

message = "This is the parent\n";

n = 1;

}

for(; n > 0; n--) {

printf(message);

sleep(1);

}

return 0;

}

编译 gcc fork.c –o fork。

执行./fork,执行结果如下:

This is the child

This is the child

This is the child

This is the parent

读者可以把sleep(1)改成sleep(30),然后通过ps -ef|grep fork查看进程数。

(6)fork后程序处理的两种情形

一种为父进程希望复制自己,使父、子进程同时执行不同的代码段。这是网络并发服务端常见的模型,父进程等待客户端的服务请求,当这种请求到达时,父进程调用fork,让子进程处理此请求,父进程则继续等待下一个服务请求。

另一种为fork后通过exec执行另一个程序,在终端上执行命令属于这种情况,Shell进程fork后立即调用exec去执行执行命令。

(7)fork之后处理文件描述符有两种常见情况

父进程等待子进程完成。在这种情况下,父进程无需对其文件描述符做任何处理,当子进程终止后,它曾进行过读、写操作的任一共享描述符的文件位移量已做了相应更新。

父、子进程各自执行不同的程序段。在这种情况下,在fork之后,父、子进程各自关闭它们不需使用的文件描述符,并且不干扰对方使用的文件描述符。这种方式在并发网络服务器中经常使用到。

8)除了打开文件之外,很多父进程的其他性质也由子进程继承

Ÿ      实际用户ID、实际组ID、有效用户ID、有效组ID;

Ÿ      附加组ID;

Ÿ      进程组ID;

Ÿ      会话ID;

Ÿ      控制终端;

Ÿ      设置-用户-ID标志和设置-组-ID标志;

Ÿ      当前工作目录;

Ÿ      根目录;

Ÿ      文件权限屏蔽字;

Ÿ      信号屏蔽和排列;

Ÿ      打开的文件描述符;

Ÿ      环境变量;

Ÿ      连接的共享存储段;

Ÿ      数据段、代码段、堆段、.bss段;

Ÿ      资源限制。

(9)父、子进程之间有如下区别

Ÿ      fork的返回值;

Ÿ      进程ID;

Ÿ      不同的父进程ID;

Ÿ      子进程的tms_utime、tms_stime、tms_cutime以及tms_ustime设置为0;

Ÿ      父进程设置的锁,子进程不继承;

Ÿ      未处理的闹钟信号子进程将清除;

Ÿ      子进程的未决告警被清除;

Ÿ      子进程的未决信号集设置为空集。

(10)fork与vfork的区别

使用fork调用会为子进程复制父进程所拥有的资源(进程环境、栈堆等),而vfork设计时要求子进程立即调用exec,而不修改任何内存,vfork新建的子进程则是和父进程共享所有的资源,在子进程中对数据的修改也就是对父进程数据的修改,这一点一定要注意。

使用fork系统调用产生父子进程,在默认情况下无需人为干预,父子进程的执行顺序是由操作系统调度的,谁先执行并不确定。而对于vfork所生成的父子进程,父进程是在子进程调用了_exit或者exec后才会继续执行,不调用这两个函数父进程会等待(父进程由于没有收到子进程表示已执行的相关信号所以进行等待)。

vfork的出现是为了解决当初fork浪费用户空间内存的问题,因为在fork后,很有可能去执行exec函数重生,vfork设计思想就是取消fork造成堆栈的复制,使用vfork可以避免资源的浪费,但是也带了资源共享所产生的问题。

在Linux中,对fork进行了优化,调用时采用写时复制 (COW,copy on write)的方式,在系统调用fork生成子进程的时候,不马上为子进程复制父进程的资源,而是在遇到“写入”(对资源进行修改)操作时才复制资源。它使得一个普通的fork调用非常类似于vfork,但又避免了vfork的缺点,使得vfork变得没有必要。

fork系统调用(转载)的更多相关文章

  1. 一个fork()系统调用的问题

    转载:http://coolshell.cn/articles/7965.html 题目:请问下面的程序一共输出多少个“-”? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ...

  2. 《Linux内核分析》 week6作业-Linux内核fork()系统调用的创建过程

    一.进程控制块PCB-stack_struct 进程在操作系统中都有一个结构,用于表示这个进程.这就是进程控制块(PCB),在Linux中具体实现是task_struct数据结构,它主要记录了以下信息 ...

  3. 以python代码解释fork系统调用

    import os print('Process (%s) start...' % os.getpid()) # Only works on Unix/Linux/Mac: pid = os.fork ...

  4. fork 系统调用

    对自己知识储备的感觉就是过于肤浅,很多东西知其名后就不了了之 此系列博客将记录进程分析的学习过程,希望能够多些深度 提到进程,最容易的想到就是fork系统调用,比较好和快速的找到的fork的相关信息就 ...

  5. fork系统调用方式成为负担,需要淘汰

    微软研究人员发表论文称用于创建进程的 fork 系统调用方式已经很落后,并且对操作系统的研究与发展产生了极大的负面影响,需要淘汰,作者同时提出了替代方案.相信每位开发者都对操作系统中的 fork () ...

  6. fork()系统调用的理解

    系统调用fork()用于创建一个新进程.我们可以通过下面的代码来理解,最好是能自己敲一遍运行验证. ​#include<stdio.h> #include<stdlib.h> ...

  7. 用 set follow-fork-mode child即可。这是一个 gdb 命令,其目的是告诉 gdb 在目标应用调用fork之后接着调试子进程而不是父进程,因为在 Linux 中fork系统调用成功会返回两次,一次在父进程,一次在子进程

    GDB的那些奇淫技巧 evilpan 收录于 Security  2020-09-13  约 5433 字   预计阅读 11 分钟  709 次阅读  gdb也用了好几年了,虽然称不上骨灰级玩家,但 ...

  8. glibc中fork系统调用传参

    因为想跟踪下在新建进程时,如何处理新建进程的vruntime,所以跟踪了下fork. 以glic-2.17中ARM为例(unicore架构的没找到),实际上通过寄存器向系统调用传递的参数为: r7: ...

  9. linux fork()函数 转载~~~~

    转自  ::  http://blog.csdn.net/jason314/article/details/5640969  一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork ...

随机推荐

  1. (copy) How to remote desktop a Fedora 19 from a Windows 7

    source: http://forums.fedoraforum.org/showthread.php?t=293665 Try xrdp or freerdpInstall xrdp or fre ...

  2. ASP.NET Highcharts图表

    js 图表代码,如下: charts[i] = new Highcharts.Chart({ chart: { backgroundColor: { linearGradient: [0, 0, 50 ...

  3. boost 1.57.0安装

    一. PC编译安装boost boost是C++的准标准库,其有两种安装方法. 1. ubuntu下,通过sudo apt-get install libboost-all-dev. 2. 通过源码包 ...

  4. 第十篇 SQL Server安全行级安全

    本篇文章是SQL Server安全系列的第十篇,详细内容请参考原文. 不像一些其他industrial-strength数据库服务,SQL Server缺乏一个内置保护个别数据记录的机制,称为行级安全 ...

  5. 第一篇 SQL Server安全概述

    本篇文章是SQL Server安全系列的第一篇,详细内容请参考原文. Relational databases are used in an amazing variety of applicatio ...

  6. Redis "MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk"问题的解决

    异常详细信息 Exception in thread "main" redis.clients.jedis.exceptions.JedisDataException: MISCO ...

  7. js实现svg图形转存为图片下载

    我们知道canvas画布可以很方便的js原生支持转为图片格式并下载,但是svg矢量图形则并没有这方面原生的支持.研究过HighChart的svg图形的图片下载机制,其实现原理大体是浏览器端收集SVG代 ...

  8. SQL Server 2008 Express 安装或卸载时提示“重启计算机失败"的解决办法

    安装或卸载SQL Server 遇到错误提示:以前的某个程序安装已在安装计算机上创建挂起的文件操作.运行安装程序之前必须重新启动计算机.如下图: 解决办法: 1.在开始->运行中输入regedi ...

  9. 发现前端框架 bui-min.js

    http://www.builive.com/apps/default/main.html#menu/code http://www.builive.com/demo/grid-plugin.php# ...

  10. SQL 数据库备、还,附、分,数据查询,聚合函数

    认识数据库备份和事务日志备份 数据库备份与日志备份是数据库维护的日常工作,备份的目的是在于当数据库出现故障或者遭到破坏时可以根据备份的数据库及事务日志文件还原到最近的时间点将损失降到最低点. 数据库备 ...