Linux 缓冲区与fork()函数

Linux下fork()函数 Linux下的fork()函数是系统调用不是C语言内置的库函数,这里mark一下笔试面试中常出现的fork()试题。 程序一: #include "stdio.h" #include "sys/types.h" #include "unistd.h" int main() { int i = 0; for(i = 0; i < 2; i++) 阅读详情

缓冲区

从一个简单的实例开始,话不多说,直接上代码。

#include <stdio.h>
#include <unistd.h>

int main()
{
    printf("hello\n");  // 实例1
    // printf("hello");  // 实例2
    write(STDOUT_FILENO, "lijd\n", 5);
    printf("world\n");

    return 0;
}

实例1 运行结果截图:

实例2 运行结果截图:

从这个程序运行的表象,引出内核高速缓冲区和stdio缓冲的概念。程序内部为提高IO速度,减少CPU等待IO而浪费CPU资源。

如下图概括了stdio函数库和内核所采用的缓冲(针对输出文件),以及对各种缓冲类型的控制机制。从图中自上而下,首先是通过 stdio 库将用户数据传递到 stdio 缓冲区,该缓冲区位于用户态内存区。当缓冲区填满时,stdio 库会调用 write()系统调用,将数据传递到内核高速缓冲区(位于内核态内存区)。最终,内核发起磁盘操作,将数据传递到磁盘。

printf缓冲区刷新的条件:
1.进程结束。
2.遇到\n。
3.缓冲区满。
4.手动刷新缓冲区fflush(stdout)。
5.调用exit(0) (但是还可以调用_exit(0),不刷新缓冲区)。

由上图可知,利用fflush()函数刷新stdio缓冲区,实例代码如下。

#include <stdio.h>
#include <unistd.h>

int main()
{
    printf("hello");
    fflush(STDOUT_FILENO);
    write(STDOUT_FILENO, "lijd\n", 5);
    printf("world\n");

    return 0;
}

执行结果如下:

 综上所述:write()的输出结果先于printf()而出现,是因为 write()会将数据立即传给内核高速缓存,而 printf()的输出则需要等到满足一定条件时才刷新stdio缓冲区。

缓冲区与fork()函数

fork()的流程:fork()创建子进程时继承了父进程的数据段、代码段、栈段、堆,注意从父进程继承来的是虚拟地址空间,同时也复制了页表(没有复制物理块)。因此,此时父子进程拥有相同的虚拟地址,映射的物理内存也是一致的(独立的虚拟地址空间,共享父进程的物理内存)。当父子进程中有更改相应段的操作发生时,系统会给子进程分配物理空间。这就是写时拷贝。写时拷贝是以“页”为单位复制的,没发生写时拷贝时子进程虚拟内存中的指针指的是父进程的物理地址。

给一个简单的案例如下:

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

int main()
{
    int a = 0x1234;
    if(fork() == 0)
    {
	a = 0x5678;
	printf("son    : pid=%d, a=%x, &a=%x\n", getpid(), a, &a);
    }else{
	wait();    // 等待子进程执行完成返回
	printf("father : pid=%d, a=%x, &a=%x\n", getpid(), a, &a);
    }

    return 0;
}

执行结果如下:

上述案例执行的结果中&a地址打印的是a变量的虚拟地址,由此可以看出fork出的子进程的虚拟地址与父进程的虚拟地址值相同,开始指向同一物理地址,当子进程对变量进行修改时,发生了写时拷贝。

 分析如下案例代码:

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

int main()
{
    printf("hello world\n");
    write(STDOUT_FILENO, "lijd\n", 5);
	
    pid_t pid = fork(); 
    if(pid == -1)
    {
	perror("fork");
    }
    else if(pid == 0)
    {
	printf("son~~~\n");
    }
    else{
	wait();
	printf("father~~~\n");
    }
	
    return 0;
}

两种执行结果如下:

为什么会出现这种现象?首先要记住,是在进程的用户空间内存中维护 stdio 缓冲区的。因此,通过 fork()创建子进程时会复制这些缓冲区。当标准输出定向到终端时,因为缺省为行缓冲,所以会立即显示函数 printf()输出的包含换行符的字符串。不过,当标准输出重定向到文件时,由于缺省为块缓冲,所以在本例中,当调用 fork()时,printf()输出的字符串仍在父进程的 stdio 缓冲区中,并随子进程的创建而产生一份副本。父、子进程调用 exit()时会刷新各自的 stdio 缓冲区,从而导致重复的输出结果。

如何避免重复的输出结果?

1、作为针对stdio缓冲区问题的特定解决方案,可以在调用fork()之前使用函数fflush()来刷新stdio缓冲区。作为另一种选择,也可以使用 setvbuf()和 setbuf()来关闭stdio流的缓冲功能。

2、子进程可以调用_exit()而非exit(),以便不再刷新stdio缓冲区。这一技术例证了一个更为通用的原则:在创建子进程的应用中,典型情况下仅有一个进程(一般为父进程)应通过调用exit()终止,而其他进程应调用_exit()终止,从而确保只有一个进程调用退出处理程序并刷新 stdio缓冲区,这也算是众望所归吧。

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

int main()
{
    printf("hello world\n");
    write(STDOUT_FILENO, "lijd\n", 5);
	
    pid_t pid = fork(); 
    if(pid == -1)
    {
        perror("fork");
    }
    else if(pid == 0)
    {
	printf("son~~~\n");
	char bufr[512];
	if (setvbuf(STDOUT_FILENO, bufr, _IOFBF, 512) != 0)	//方法1
	{
	    perror("setvbuf");
	    }
	}
	else{
	    wait();
	    printf("father~~~\n");
	    _exit(0);		//方法2
	}
	
    return 0;
}

 执行结果如下图所示:

SIPVicious实战指南:从零开始掌握VoIP渗透测试(Kali Linux环境) 本文为SIPVicious实战指南,详细介绍了在Kali Linux环境下进行VoIP渗透测试的完整流程。从环境准备与工具安装开始,逐步讲解如何使用svmap扫描SIP服务器、svwar枚举PBX分机号,以及svcrack进行密码安全测试,并整合svreport生成报告。旨在帮助安全从业者从零开始,系统掌握这套经典开源工具,在授权范围内有效评估VoIP系统的安全性。 阅读详情

相关推荐

当迁移学习遇见室内定位:基于UJIIndoorLoc的跨场景适配实践

本文探讨了迁移学习在跨建筑室内定位中的应用,重点解析了UJIIndoorLoc数据集的特性及其在解决设备差异和环境变化中的关键作用。通过特征工程、迁移学习策略和小样本微调技术,显著提升了定位模型的跨场景适配能力,为实际部署提供了有效解决方案。

weixin_29038155的博客 394

fork与缓冲区问题

fork是unix下创建子进程的函数 pid_t fork(void); //创建一个进程,父进程返回值为子进程pid,子进程返回值为0 当我们运行如下代码时, #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<unistd.h> #include<sys/wait.h> #include<errno.h> #include<stdio.

tgkyrie的博客 482

Node.js内存溢出?一招搞定JavaScript heap out of memory错误(附永久配置方案)

本文深入解析Node.js内存溢出问题,提供从临时调整到永久配置的完整解决方案。重点介绍通过设置`max-old-space-size`参数和`NODE_OPTIONS`环境变量来扩展JavaScript堆内存限制,适用于Webpack、Vite等构建工具的大型项目场景,并涵盖Windows、Linux/macOS多平台配置方法。

cake8的博客 1055

fork()时缓冲区注意事项

fork()调用时,整个父进程空间会原模原样复制到子进程中,包括指令、变量值、程序调用栈、环境变量和缓冲区等等。 #include #include #include int main() {     int i;     for (i=0; i1; ++i) {         fork();         p

Simon的博客 768

Lama:基于傅立叶卷积的分辨率鲁棒性大掩模修复

为了缓解修复网络和损失函数都缺乏有效的感受野这个问题,提出了大掩模修复(LaMa)。LaMa基于一种新的修复网络架构,该架构使用快速傅立叶卷积(FFC),具有图像范围的感受野;高感受野感知损失;大型训练掩码,释放前两个组件的潜力。

qq_44926214的博客 1417

Ubuntu20.04安装NVIDIA显卡驱动避坑指南:从.run文件到完美运行

本文详细介绍了在Ubuntu20.04系统上通过.run文件安装NVIDIA显卡驱动的完整流程和避坑技巧。从禁用Nouveau驱动、选择合适显示管理器到处理常见安装问题,提供了一套经过验证的解决方案,帮助用户顺利完成驱动安装并优化性能。

weixin_30776273的博客 603

fork()子进程复制父进程的缓冲区的一个例子

#include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(void) { int i; for(i=0; i<2; i++){ fork(); printf("-"); } return 0; }如果你对fork()的机制比较熟悉的话,这个题并不难,输出应该是6个“-”

夜夜夜夜 1568

VTK的编译和部署,配合c++和visual studio2022,VTK开发环境的配置

在官网选择最新的版本下载之后进行解压,然后再里面创建build目录,方便后面使用cmake进行编译。

dz131lsq的博客 3223

C fllush的使用与介绍

fflush()的作用是用来刷新缓冲区,fflush(stdin)刷新标准输入缓冲区,把输入缓冲区里的东西丢弃; fflush(stdout)刷新标准输出缓冲区,把输出缓冲区里的东西强制打印到标准输出设备上。 fflush(stdin)不太常用,在有些编译器中是错误的用法,可以用以下方法替代:while(getchar()!=’/n’); fflush(stdout)在单进程程序中作用不大,但在多进程程序中很有用。程序的输出内容一般不会立即输出,而是在程序结束后再输出。fflush(stdout)会强制每次

Martin12111的博客 741

3、提升管壳式换热器能效:双螺旋盘管的应用研究

本研究探讨了在管壳式热交换器中应用双螺旋盘管以提升能效的可行性。通过实验对比双螺旋与单螺旋盘管的热传递效率,结果显示双螺旋盘管达到54.33%的效率,显著优于单螺旋的48.88%。双螺旋结构不仅增大了换热面积,还因产生剪切应力而减少结垢,延长运行周期。采用NTU方法评估热传递性能,验证了该设计在工业应用中的高效性与实用性,建议在制冷、空调、石油精炼等领域推广使用。

k8s6orchestrator的博客 107

uniapp:微信支付报错requestPayment:fail errors,uniAPP调用uni.requestPayment微信支付提示错误(安卓app、ios苹果app、微信小程序支付报错)

uniapp微信支付报错requestPayment:fail errors,uniApp APP端调起微信支付失败报错requestPayment:fail errors,uniapp微信小程序支付的坑 “errMsg“: “requestPayment:fail errors“,uni.requestPayment微信支付,UNI-APP支付报错“errMsg“: “requestPayment:fail errors“ app微信支付,uniapp 安卓app微信支付报错,uni.requestPay

✅ 精选推荐文章 3916

tensorRT的使用笔记!

几个重要概念 binding 描述engine的输入输出端口。其实就是对输入输出端口的tensor描述其大小等信息。我们用cudaMalloc()函数为engine申请输入输出buff的时候需要查询使用这些信息。

烂笔头而已 945

硅电容的工艺结构原理及选型参数总结

硅电容,也称为硅质电容或硅二极管电容,是一种特殊的电容器。它利用硅二极管的结构来实现电容的功能。本文对硅电容的工作原理,结构特点,选型参数及设计注意事项进行总结。

fydar的博客 2332

对于\n冲刷缓冲区,对Linux中fork()函数的理解

首先今天写了两道感觉一模一样的fork题目,都是输出_的,结果一个题目输出8个_,一个题目输出6个_,我看了许久,才发现一点点的不同,于是去Linux系统下自己编译了两个cpp文件,果然,现在贴上图:   第一个文件,输出的是hello!world people! hello!world people! ,第二个文件,输出的是hello!world people! world people! ...

qq_42058590的博客 461

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

深入Linux系统编程:缓冲区、进程管理(fork()写时拷贝、exec())、系统调用和库函数与信号处理全指南

本文摘要:文章系统讲解了Linux系统编程中的核心概念和技术。主要内容包括:1. 缓冲区机制与刷新方式(printf、fflush);2. 静态库(.a)与共享库(.so)的创建使用及区别;3. 进程管理(fork、exec、wait等系统调用),重点说明写时拷贝机制;4. 文件操作(open/read/write等系统调用);5. 信号处理(kill、signal等);6. 实现简单shell的流程。通过代码示例演示了进程创建替换、文件复制、信号处理等典型场景,并对比了系统调用与库函数的本质差异。

2503_92011100的博客 439

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

fork()函数的面试题

fork()函数的面试题今天我们分析一下fork()常出现的一些面(笔)试题,以备以后面试或者笔试被提及到,我们知道fork()这个函数是一种特殊的函数,它有两个返回值,一个是子进程的pid,一个是父进程的pid,那么它们的这些特别之处,到底会给它们带来什么样的变化呢,我们接下来开始分析。fork()函数的标准模式:pid_t pid = fork();代码一:面试题一:  int i =0;...

IT_model的博客 8240

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

一、Linux的复制与替换--printf函数输出问题、主函数参数介绍、fork进程

这里写目录标题3、复制进程 fork 3、复制进程 fork 总结: 复制从 fork 执行结束开始,父进程中 fork 的返回值为子进程(pid)的 id 号,子进程的返回值为 0。 在进程中打印的变量地址,都是逻辑地址,不是物理地址。 子进程会复制父进程的内存空间。 僵死进程: 子进程先于父进程结束,父进程没有获得子进程的退出码,子进程就变成僵死进程。 ...

li94263的博客 625

fork()|| fork()与 fork在for循环中的执行次数

(1)fork() || fork() 在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID; 在main这个主进程中,首先执行 fork() || fork(), 左边的fork()返回一个非零值,由于||的特性,前面的表达式为真时,后面的表达式不执行,故包含main的这个主进程创建了一个子进程; 由于子进程是复制父进程的,子进程与父进程要做同样的事情;而在子进程中, fork() ||fork()这条语句左边表达式的返回值是0, 所以||右边的表达式要执行,这时在子进程中又创

lbb20的博客 1935

linux:关于fork()的例题

下面这段代码输出几个a? int main() { fork()||fork(); printf("a\n"); exit(0); } 答案是3个,下面我们来一起分析一下: 父子进程的pid是题目的关键,pid=0,ppid!=0.或(||)的关系下第一个fork()为真,就无需判断第二个,两个fork()均为0时,结束。所以这段代码可以输出三个a

qq_40657299的博客 766

Linux下的 fork( ) 函数

      今天我们来谈一谈Linux下的fork()函数     fork()函数需要的头文件 #include&lt;unistd.h&gt;      linux中的fork函数是非常重要的,它的作用为在一个已有的进程中创建一个新进程,新进程称为当前进程的子进程,当前进程称为新进程的父进程。fork函数创建子进程后,【1】系统会给子进程分配一块新的空间和内核数据结构,【2】然后将父进程...

Amin_Luckdog的博客 1235

Linux文件系统详解——内存中打开的文件(缓冲区部分)

摘要:本文探讨了C语言缓冲区对程序输出的影响。当使用C标准库函数(如printf)时,数据会先存入用户缓冲区,而系统调用(如write)则直接写入内核。向显示器输出时采用行缓冲(遇'\n'刷新),向文件输出时采用全缓冲(需手动刷新或缓冲区满)。在fork()后,父子进程共享缓冲区内容,导致全缓冲模式下数据被重复写入。此外,exit()会刷新用户缓冲区,而_exit()则直接退出不刷新。这些机制解释了为何不同输出方式会呈现不同结果,缓冲区的存在是为了提高I/O效率。

2302_77620024的博客 6万+

Linux 指令——HCQ缓冲区

进程:是一种动态执行的过程,操作系统会为程序去分配所需的CPU时间,内存空间等,让程序的指令被CPU逐条运行。_exit():是系统调用,会跳过所有用户态的清理步骤,直接通知内核终止进程。僵死进程:子进程先于父进程结束,父进程没有获取子进程的退出码,子进程就会变成僵死进程。区别:exit()会先执行用户态的资源清理操作,在调度内核级的进程终止逻辑。程序:是静态的代码和数据,是待执行的指令集合,不占用CPU,内存等资源。返回值:1.父进程中,返回子进程的PID,用于识别的管理子进程。2.子进程中,返回0。

weixin_49844966的博客 513

Linux(静态动态库,缓冲区输出问题,fork()父子进程,逻辑物理地址,僵死进程,孤儿进程,主函数参数)

gcc -c 文件名.car命令动态库生成的可执行文件,再动态库被删除后,可执行文件不能执行(编译阶段仅记录可执行文件对动态库的依赖关系,不复制库代码,是运行时才开始链接的,所以必须依赖动态库)main(大)----静态库 main1-----动态库优点 不依赖外部文件,移植性强(复制可执行文件即可执行)优点 节省内存,只需要在磁盘上存一份即可,多个可执行文件共性同一份库文件(共享内存)

2402_85373917的博客 380

printf()缓冲区的问题

printf去哪了? #include <unistd.h> #include <sys/types.h> #include <stdio.h> #include <errno.h> int g_val = 6; char g_buf[]="A string write to stdout.\n"; int main (int argc, char **argv) { int var; pid_t pid; v

蟹油饼的博客 256

进程的创建fork()、消亡wait()

类型pid_t;;最多容纳三万多,16位有符号整型ps命令ps axfgetpid();getppid();注意理解关键字:duplicating,意味着拷贝、克隆、一模一样的完整的两份代码,执行的位置也一样;fork后的父子进程区别:init进程:1号,是所有进程的祖先进程 vfork();wait();waitpid ();4.exec函数5、用户权限和组权限6. 观摩课:解释器文件7、 system()8、进程会计9、进程时间10、...

weixin_46432495的博客 258

2020.12.8Linux学习笔记————进程 fork() 和 pid

Linux学习笔记————进程 fork() 和 pid fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值: 1)在父进程中,fork返回新创建子进程的进程ID; 2)在子进程中,fork返回0; 3)如果出现错误,fork返回一个负值; 创建新进程成功后,系统中出现两个基本完全相同的进程,这两个进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。此时,两个进程都从fork开始往下执行,只是pid不同。 有人可能疑惑为什么不是从#include处开始复制

CQ的学习笔记 413
上一篇: MySQL C++ 封装接口及事务测试验证
下一篇: Linux的线程控制
码农诗人
博客等级 码龄11年 987粉丝 84原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值