当前位置: 首页 > news >正文

linux中fork()函数的小问题

问题描述:分析下列代码,分别能产生多少a 

//  1
for(int i=0; i<3; i++){
printf("a\n");
fork();
}// 2
for(int i=0; i<3; i++){
fork();
printf("a\n");
}// 3
for(int i=0; i<3; i++){
fork();
printf("a");
}
fflush(stdout);// 4
for(int i=0; i<3; i++){
printf("a");
fork();
}

分析:

【问题1】第0次循环:此时只有p0主进程,直接输出a,然后执行fork();产生一个子进程p1;现在有一个a

        第1次循环:此时有两个进程p0,p1;都输出一个a ,共两个a;然后执行fork();分别产生p2,p3,两个子进程;目前终端输出三个a,有四个进程p0,p1,p2,p3;

        第2次循环:此时四个进程各输出一个 a ;随后产生四个进程,程序结束;终端上一共有1+2+4 = 7 个a。

【问题2】第0次循环:此时只有p0主进程,执行fork(),生成子进程p1,两个进程各自在终端输出一个a,此时终端共两个a;

        第1次循环:此时有p0,p1进程,执行fork(),生成子进程p2,p3,共四个进程,各自输出一个a,此时终端共有 2+4 = 6 个a;

        第2次循环:此时有p0,p1,p2,p3进程,执行fork(),生成四个子进程,共八个子进程,各自在终端输出8个子进程,此时共有 2+ 4+8 = 14个a;

        但是在终端上出现如下情况,原因为各进程间结束时间不一致,当主进程结束时,部分子进程还未结束,会继续向终端输出a;

 此时只要在for循环后加sleep();等待进程全结束即可得到规整的输出:

 void test2(){12     printf("[test2] 预测有 14个\n");13     for(int i=0;i<3;i++){14        fork();15         printf("[test2] : a\n");16     }17 18     sleep(1);                                                                               19 }   

 【问题3】(注:本题以及问题4涉及缓存相关知识)

        第0次循环:主进程p0执行fork()后产生子进程p1,随后执行满缓存输出,此时将a输出当进程p0、p1的缓冲区中,并不直接输出到终端(后两次循环也是一样);

        第1次循环:p0,p1执行fork()后产生p2,p3子进程,随后执行printf(),将a输出到各进程的缓冲区中,此时p0,p1,p2,p3中的缓冲区都存在两个但还未输出;

        第2次循环:同理第1次循环,会产生四个子进程,共八个进程,随后的printf()在各自的进程缓存空间中输出a,当进程结束时,标准输出会刷新缓存,将每个进程中的三个a输出到终端;程序结束后,终端上会显示 3*8 = 24个 a;

 若出现输出在命令行之后的情况,原因同问题2,各个进程结束时间不一致导致,使用sleep()就可以解决。

【4】

        第0次循环:主进程p0执行printf()后并未直接输出,而是将a输出到p0的缓存中。随后执行fork(),产生的子进程p1会复制p0的用户态地址空间;这里有个小知识点fork()采用写时复制,在随后对共享页中i++时候才真正复制p0的栈堆数据等,此时才将p0中的缓存复制一份,这是区别与问题3的;问题3是在各自缓存中加了一个a,问题4是在父进程缓存中先加了一个a,随后复制到各子进程(后两次不作过多赘述,fork()写时复制策略放在最后);此时p0,p1缓存中各有一个a;

        第1次循环:p0,p1执行printf(),将a输出到各进程的缓冲区中,缓存中各自有两个a,执行fork(),产生p2,p3,此时p0,p1,p2,p3中的缓冲区都存在两个a但还未输出;

        第2次循环:同理上次循环,会产生四个子进程,共八个进程,并且在fork()前的printf()在各自的进程缓存空间中输出a,各个进程缓存中存在3个a,当进程结束时,标准输出会刷新缓存,将每个进程中的三个a输出到终端;程序结束后,终端上会显示 3*8 = 24个 a;

 

写时复制

为了减少数据复制的开销, 优化内存管理, fork采用是写时复制(Copy-On-Write,简称COW)的策略

  1. 在fork()执行时,操作系统并不立即复制父进程的整个内存空间给子进程。操作系统使父进程和子进程暂时共享相同的物理内存页。
  2. 这些共享的页面在内存中被标记为只读。如果父进程或子进程尝试写入这些共享的页面(以页为单位),操作系统会为发起写操作的进程(父进程或子进程)分配一个新的物理内存页, 并复制数据到这个页。

写时复制机制确保只有在必要时才复制数据页,这极大地减少了内存使用和提高了效率。

 

相关文章:

  • 多模态RAG与LlamaIndex——1.deepresearch调研
  • SpringBoot的单体和分布式的任务架构
  • 项目三 - 任务6:回文日期判断
  • C 语言_常见排序算法全解析
  • 嵌入式软件--stm32 DAY7 I2C通讯上
  • jenkins流水线常规配置教程!
  • aardio - 将文本生成CSS格式显示
  • 汽车功能安全--TC3xx MBIST设计要点
  • 2025高质量数据集实践指南
  • 22.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--单体转微服务--增加公共代码
  • 裸金属服务器 VS 传统物理机
  • c++STL-list的使用和迭代器
  • 基于Session实现短信登录全流程详解
  • Windows下安装mysql8.0
  • 【Tauri2】044 移动端Android的初步配置
  • React Native告别图标体积大手动更换慢的噩梦:让图标更新像修改文字一样简单
  • STM32 HAL驱动程序 内部Flash
  • 基于深度学习的工业OCR数字识别系统架构解析
  • Linux的文件查找与压缩
  • BGP实验练习2
  • 国务院关税税则委员会公布公告调整对原产于美国的进口商品加征关税措施
  • 硅料收储挺价“小作文”发酵光伏板块罕见大涨,知情人士:确实在谈
  • 牟海松任国家信访局副局长
  • 深一度|在亚马尔的天才面前,姆巴佩戴上“帽子”又如何
  • 何立峰:中方坚定支持多边主义和自由贸易,支持世贸组织在全球经济治理中发挥更大作用
  • 人民时评:透过上海车展读懂三组密码