操作系统fork函数解析

fork和pause等系统调用的实现原理(Linux0.11内核) 点击打开链接 阅读详情

fork的基本知识:

    函数原型:pid_t fork( void);

          返回值: 若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1


    一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程(child process)。fork函数被调用一次但返回两次。两次返回的唯一区别是子进程中返回0值,而父进程中返回子进程ID。



注意要点:

    1、 子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。

        此处先简要介绍下COW(Copy-on-write)机制,大致原理如下:

        在复制一个对象的时候并不是真正的把原先的对象复制到内存的另外一个位置上,而是在新对象的内存映射表中设置一个指针,指向源对象的位置,并把那块内存的Copy-On-Write位设置为1.这样,在对新的对象执行读操作的时候,内存数据不发生任何变动,直接执行读操作;
        而在对新的对象执行写操作时,将真正的对象复制到新的内存地址中,并修改新对象的内存映射表指向这个新的位置,并在新的内存位置上执行写操作。
       

         linux内核下fork使用COW机制工作原理:

        进程0(父进程)创建进程1(子进程)后,进程0和进程1同时使用着共享代码区内相同的代码和数据内存页面, 只是执行代码不在一处,因此他们也同时使用着相同的用户堆栈区。在为进程1(子进程)复制其父进程(进程0)的页目录和页表项时,进程0的640KB页表项的属性没有改动过(仍然可读写),但是进程1的640KB对应的页表项却被设置成只读。因此当进程1(子进程)开始执行时,对用户堆栈的入栈操作将导致页面写保护异常,从而使得内核的内存管理程序为进程1在主内存区中分配一内存页面,并把进程0中的页面内容复制到新的页面上。从此时开始,进程1开始有自己独立的内存页面,
        由于此时的内存页面在主内存区,因此进程1中继续创建新的子进程时也可以采用COW技术。

        内核调度进程运行时次序是随机的,进程0创建进程1后,可能先于进程1修改共享区,进程0是可读写的,在未分开前,进程1是只读的,由于两个进程共享内存空间,
        为了不出现冲突问题,就必须要求进程0在进程1执行堆栈操作(进程1的堆栈操作会导致页面保护异常,从而使得进程1在主内存区得到新的用户页面区,此时进程1和进程0才算是真正独立,
        如前面所述)之前禁止使用用户堆栈区。所以进程0在执行了fork(创建了进程1)之后的pause使用内嵌的方式,保证进程0(主进程)不会弄乱堆栈。

        fork()后立即执行exec(),地址空间就无需被复制了,一个进程一旦调用exec类函数,它本身就“死亡”了,系统把代码段替换成新的程序的代码,废弃原有的 数据段和堆栈段,并为新程序分配新的数据段与堆栈段,唯一留下的,就是进程号,也就是说,对系统而言,还是同一个进程,不过已经是另一个程序了。
       
        fork()的实际开销就是复制父进程的页表以及给子进程创建一个进程描述符。在一般情况下,进程创建后都为马上运行一个可执行的文件,采用COW这种优化,

        可以避免拷贝大量根本就不会被使用的数据(地址空间里常常包含数十兆的数据)。由于Unix强调进程快速执行的能力,所以这个优化是很重要的。


        2、在linux中存在缓冲区的问题。当写printf函数,但是没有换行的话,它是不会输出的,而是先将要输出的内容存放再缓冲区中,当碰到换行时,将缓冲区中的内容再一起输出。

       所以fork后子进程会复制父进程的缓冲区,因此也将待输出内容复制到自己“与父进程独立的缓冲区中”,因此当子进程执行遇到换行,就将缓冲区中的内容全都输出来。

代码1:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(){
    int i;
    pid_t pid;
    for(i = 0; i < 2; ++i){
        printf("-");
        pid = fork();
    }
    return 0;
}
输出:--------
8个中划线


分析:
    原因分析:如果不清楚fork的深拷贝机制,估计看到上述代码的第一感觉是:死循环,永远fork下去。一定要仔细理解上面的注意要点1。在整个执行周期内,一共会产生4个进程,由于是遍历所以主进程会产生两个子进程(假设i=0时fork出的子进程记为:first,i=1时fork出的子进程记为:second),first进程在遍历时会再产生一个自己的子进程,记录为:third,而second进程,i的值已经为1,不会再继续执行下去,其它进程同理,所以此处不会形成死循环。

主进程会输出2个中划线(总计:2个)
first进程会输出1个自己的中划线,考虑到注意要点2,还会输出从主进程缓冲区拷贝过来的1个中划线(总计:2个)
second进程没有自己的中划线输出,考虑到注意要点2,会输出从主进程缓冲区拷贝过来的2个中划线(总计:2个)
third进程没有自己的中划线输出,考虑到注意要点2,会输出从first进程缓冲区拷贝过来的2个中划线  (总计:2个)

所以一共会输出8个,怎么确定会产生4个进程呢,只要把进程的id号给打印出来,就非常清晰明了了,具体代码参考代码5


代码2:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(){
    int i;
    pid_t pid;
    for(i = 0; i < 2; ++i){
        pid = fork();
        printf("-");
    }
    return 0;
}

输出:--------
8个中划线

解析:虽然同代码1一样,也是输出8个中划线,但是内部运行机制相差很远,具体原因分析参考代码1原因分析

主进程会输出2个中划线(总计:2个)
first进程会输出2个自己的中划线,无主进程缓冲区拷贝输出(总计:2个)
second进程会输出1个自己的中划线,考虑到注意要点2,会输出从主进程缓冲区拷贝过来的1个中划线(总计:2个)
third进程会输出1个自己的中划线,考虑到注意要点2,会输出从first进程缓冲区拷贝过来的1个中划线  (总计:2个)


代码3:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(){
    int i;
    pid_t pid;
    for(i = 0; i < 2; ++i){
        printf("-\n");
        pid = fork();
    }
    return 0;
}

输出:
-
-
-
3个中划线

解析:具体原因分析参考代码1原因分析

主进程会输出2个中划线(总计:2个)
first进程会输出1个自己的中划线,无主进程程缓冲区拷贝输出(总计:1个)
second进程没有自己的中划线输出,无主进程缓冲区拷贝输出(总计:0个)
third进程会没有自己的中划线输出,无first进程缓冲区拷贝输出(总计:0个)


代码4:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(){
    int i;
    pid_t pid;
    for(i = 0; i < 2; ++i){
        pid = fork();
        printf("-\n");
    }
    return 0;
}

输出:
-
-
-
-
-
-
6个中划线

解析:具体原因分析参考代码1原因分析

主进程会输出2个中划线(总计:2个)
first进程会输出2个自己的中划线,无主进程缓冲区拷贝输出(总计:2个)
second进程会输出1个自己的中划线,无主进程缓冲区拷贝输出(总计:1个)
third进程会输出1个自己的中划线,无first进程缓冲区拷贝输出(总计:1个)


代码5:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(){
    int i;
    pid_t pid;
    for(i = 0; i < 2; ++i){
        pid = fork();
        if(pid == 0)
            printf("child process:%d", getpid());
        else if(pid > 0)
            printf("parent process:%d", getpid());
        else
            printf("error");
    }
    printf("\n");
    return 0;
}

输出:
parent process:5141parent process:5141
child process:5142parent process:5142
child process:5142child process:5144


操作系统(Linux)---fork()函数解析与进程 编制一段程序,使用系统调用fork( )创建两个子进程,这样在此程序运行时,在系统中就有一个父进程和两个子进程在活动。每一个进程在屏幕上显示一个字符,其中父进程显示字符A,子进程分别显示字符 B和字符C。试观察、记录并分析屏幕上进程调度的情况。 阅读详情

相关推荐

Unity PSD分层导入插件ps2d原理与实战指南

PSD作为UI设计主流源文件,其图层结构、混合模式、文字矢量属性等核心语义在传统引擎导入中普遍丢失,导致设计还原度低、多语言适配难、运行时不可控。ps2d通过深度解析PSD二进制规范,将图层映射为GameObject树、文字转为TextMeshPro可编辑组件、形状生成矢量Mesh、蒙版与图层样式编译为定制Shader,实现设计意图的精准程序化表达。该方案显著提升UI开发效率,支撑高保真还原、动态修改、分辨率自适应及运行时热重载等工程需求,是Unity中连接设计师与程序员的关键工作流中枢。

weixin_32943857的博客 348

fork()函数详解

目录 1.基本了解: 2.fork函数的了解: 3.僵死进程: 1.基本了解: 一个进程,包括代码、数据和分配给进程的资源。fork 函数会新生成一个进程,调用 fork 函数的进程为父进程,新生成的进程为子进程。在父进程中返回子进程的 pid,在子进程中返回 0,失败返回-1。 为什么两个进程的fpid不同呢,这与fork函数的特性有关。fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值: 1)在父进程中,fork返回新创建子进程的进程...

weixin_57133901的博客 4万+

【已解决】如何在服务器中下载huggingface模型,解决huggingface无法连接

在服务器上使用huggingface模型时,如果直接指定模型名称,用AutoTokenizer.from_pretrained(“model_name”),可能会由于网络原因会报错 Failed to connect to huggingface.co port 443 after 75018 ms: Operation time out因此我们需要下载模型到服务器上,得到模型本地的路径model_dir,再通过AutoTokenizer.from_pretrained(model_dir)方式来使用。

a61022706的博客 4万+

计算机系统基础学习 技术日志二(fork函数)

fork函数综述 一个已有的进程通过调用fork函数来创建一个新进程。新创建的进程称为子进程(child),而原来的进程则称为父进程(parent)。 子进程一个获得父进程代码、数据段、堆、栈等资源的副本。子进程获得与父进程的虚拟地址空间相同的副本,但是父子进程间不共享这些存储空间,子进程有了独立的地址空间。 父子进程最大的区别在于PID(进程标识符)的不同。 子进程被创建时,系统中有两个活动的...

qq_43919787的博客 336

Shopee App加密算法逆向实战:从libshpssdk.so到x-sap-ri的生成机制

本文详细解析了Shopee App核心加密参数x-sap-ri的生成机制。通过逆向分析libshpssdk.so动态库,揭示了其采用9套算法动态切换的复杂签名逻辑,并分享了从抓包定位、Frida动态调试到Unidbg模拟执行的全过程实战经验,为移动应用安全研究提供了完整的技术路径。

weixin_29323551的博客 1327

DFT实战:如何用Synopsys工具搞定多时钟域Scan Chain设计(附避坑指南)

本文详细解析了使用Synopsys DFT Compiler工具链实现多时钟域Scan Chain设计的全流程,包括时钟混合策略、跨时钟域平衡技巧和实战脚本剖析。通过量产验证的工程实践方案,帮助DFT工程师有效解决复杂SoC设计中的Scan Chain挑战,提升测试效率和质量。

mm9012的博客 1033

linux 用户态栈 fork,再谈关于任务0中fork和pause的内嵌问题

该楼层疑似违规已被系统折叠隐藏此楼查看此楼再谈关于任务0中fork和pause的内嵌问题chaser 2008-7-30 12:521。任务0中使用内嵌,仅仅是避免任务1的用户态堆栈中没有无用的多余信息。2。即使在任务0中fork和pause都不使用内嵌,对任务1也没有任何影响。因为任务1的用户态堆栈与任务0的没有任何关系。3。对main.c中开头的一段注释“我们需要下面这些内嵌语句--从内核空...

weixin_33656211的博客 246

香橙派5玩转SATA SSD:一个被忽略的overlays配置项,让你的Ubuntu系统飞起来

本文详细介绍了如何在香橙派5上通过配置`overlays=ssd-sata`启用SATA SSD作为系统启动盘,大幅提升Ubuntu系统的性能。文章深入解析了设备树覆盖的工作原理,提供了完整的配置流程和性能优化建议,帮助开发者充分发挥香橙派5的硬件潜力。

weixin_30787531的博客 219

fork()函数浅析

fork()是UNIX或类UNIX中的分叉函数,fork函数将运行着的程序分成2个(几乎)完全一样的进程,每个进程都启动一个从代码的同一位置开始执行的线程。这两个进程中的线程继续执行,就像是两个用户同时启动了该应用程序的两个副本。 其函数原型为: pid_t fork( void); 当调用fork()时,将执行以下动作: 向系统申请一个新PID 创建子进程,复制父进程的PCB,获得父进程...

Atom-L的博客 5038

仿人型机械臂逆运动学避坑指南:当末端三关节平行时如何处理奇异点

本文深入探讨了6R仿人型机械臂在末三轴平行结构下的逆运动学求解策略,重点分析了运动学逆解中的奇异点问题及其处理方法。通过MATLAB实现展示了D-H参数建模、几何解析法求解流程以及奇异点规避策略,为工业机器人开发提供了实用的数值算法方案和工程优化技巧。

sss66的博客 882

钢铁雄心4存档修改全攻略:从基础设置到高级玩法(附国家代码表)

本文详细解析《钢铁雄心4》存档修改技巧,从基础设置到高级玩法全覆盖。包括存档文件位置、格式转换、国家实力调整、国际关系修改等实用内容,并附赠国家代码表。掌握存档修改技能,解锁游戏全部潜力,实现精细参数控制,超越控制台指令的局限。

weixin_29233857的博客 1431

操作系统---fork函数解析与例题详解

fork的基本知识 函数原型:pid_t fork( void);    返回值: 若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1 一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程(child process)。fork函数被调用一次但返回两次。两次返回的唯一区别是子进程中返回0值,而父进程中返回子进程ID。...

IT_xiaoye的博客 4042

C#跨平台制图新纪元:Mapsui,一库在手,地图我有

深入学习Mapsui的渲染机制,定制属于你的地图样式,无论是颜色、图标还是文字,都可以随心所欲。

java专栏 1455

关于操作系统fork()函数详解

Hello!fork( ) 对于操作系统的fork()函数,我也是在学操作系统时第一次接触,做实验时对这个函数的功能也是很懵,后期经过大量的学习、实验才讲这个函数拿下。 fork基础 我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客: 全新的界面设计 ,将会带来全新的写作体验; 在创作中心设置你喜爱的代码高亮样式,Mark你 功能快捷键 撤销:Ctrl/Command + Z 重做:Ctrl/Command + Y

北芒的博客 4118

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

当fork遇上for循环的问题分析 & fork函数

文章目录Q1:以下代码会输出几个'-'?Q2:以下代码会输出几个'-'?fork函数 Q1:以下代码会输出几个’-’? 首先看个面试题:以下代码会输出几个’-’? #include <iostream> #include <unistd.h> int main() { for (int i = 0; i < 2; i++) { fork(); printf("-\n"); } return 0; }

Donald Su 2562

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

fork函数介绍

文章目录1 fork函数介绍 1 fork函数介绍 fork函数: 头文件: #include <unistd.h> 函数原型: pid_t fork(void); 返回值: 成功 :0或其他正整数 失败:-1 fork函数特性: 执行fork函数之后,fork函数会返回两次 在旧进程中返回时,返回值为子进程的pid 在新进程返回时,返回值为0 fork函数要点总结: 在执行fork函数之前,操作系统只有一个进程,fork函数之前的代码只会被执行一次。 在执行fork函数之后,操作

木子皿--啥都不会的菜鸟 1438

操作系统 fork与exec

前言 学习操作系统,首先便要学到process概念。process是什么?Process – a program in execution。七十年代UNIX最先提出多进程的构想,之后该构想便广泛用于linux与unix操作系统中。fork()函数给程序猿们提供了简便的多进程编程方式。 fork()creates new process exec() used after a fork to...

qq_39747794的博客 2816

为什么fork调用会返回两次

fork大家可能都比较熟悉,调用一次返回2次,返回pid>0为父进程,pid=0为子进程。一直对fork方法如何返回两次有些疑惑,函数调用一次只会返回一个只,fork怎么会返回2次?之前查过原因,时间久了有些模糊了,今天上网又查了下,其实大家都说的“返回两次”在表达上时不清楚的。fork实际上并不是执行两次,它依然返回了一次,准确地说是一次多一点,只是OS对fork进行的操作使得我们看起来它返回了两次

wangww631的专栏 8915

fork()函数的精辟分析

####################################### 首先声明这篇文章是从一个大牛博客里看到的整理而来 ####################################### #include

582

linux系统如何使用fork函数创建子进程

大家都知道linux是多进程的系统。可是,在linux中,进程是如何创建并运行的呢?在linux系统中创建进程有两种方式:一是由操作系统创建,二是由父进程创建进程(通常为子进程)。系统调用函数fork()是创建一个新进程的唯一方式,fork()函数是Linux系统中一个比较特殊的函数,其一次调用会有两个返回值。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程。

不积跬步,无以至千里! 2万+

Linux进程——fork函数

view plain #include    #include    /*  功能:复制进程  参数:无  返回值:  成功:  父进程:返回子进程id                  子进程:返回0           失败:  返回-1  */   pid_t fork(void);   由fork创建的新进程被称为子进程(child proce

菜鸟天堂的专栏 667

关于fork()函数的一点思考

关于fork()函数的一点思考 最近看操作系统的进程部分,对fork()函数的有些疑惑,于是查书本,看博客,花了一晚上时间研究了一下fork()的执行过程,终于有了一些收获。 fork()对初学者来说还是比较容易混淆的,特别是当多个fork连着用时。要想弄清楚fork的执行过程,首先要明白fork的重要特性。 fork()是一个创建进程的函数,新创建的子进程相当于父进程的一份拷贝,之前的

shhchen的博客 486

fork函数的用法总结

fork函数的概念: 在Unix操作系统中,fork函数源于。其作用是创建一个子进程。 其函数原型为 pid_t   fork( void );     · 若调用成功,则会产生一个子进程。因此会返回两个值,子进程内返回0,父进程内返回子进程的ID;     · 若调用失败,则父进程返回-1。 遇到fork最好要画出进程的树状关系图。 父子进程的空间关系:     子进程是父进程

LUCKYOJ 2479

linux fork函数浅析

print?#include #include /* 功能:复制进程 参数:无 返回值: 成功: 父进程:返回子进程id 子进程:返回0 失败: 返回-1 */ pid_t fork(void); 由fork创建的新进程被称为子进程(child process)。该函数被调用一次,但返回两次。两次返回的区别是子

gucheng789的专栏 558

Fork函数的解析(一)

Fork函数的运用:父进程通过调用Fork函数来创建一个新的子进程。 那么,什么是进程呢?进程的经典定义就是一个执行中程序的实例,即指程序的一次运行过程。程序是指按某种方式组合形成的代码和数据集合,代码即是机器指令序列,因而程序是一个静态的概念。不同于程序,进程是具有独立功能的一个程序关于某个数据集合的一次运行活动,因而进程具有动态的含义。 1.同一个程序处理不同的数据就是不同的进程。 2....

weixin_44688476的博客 797
上一篇: 南京理工大学第八届程序设计大赛
下一篇: 2016"百度之星" - 资格赛(Astar Round1)(hdu5685(线段树、乘法逆元),hdu5686(大数),hdu5687(字典树),hdu5688)
yphacker
博客等级 码龄12年 199粉丝 540原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值