建设一个网站的好处/每日精选12条新闻
序言
在编程的世界里,文件操作是不可或缺的一部分。无论是数据的持久化存储、日志记录,还是简单的文本编辑,文件都扮演着至关重要的角色。然而,当我们通过编程语言如 C、Java
等轻松地进行文件读写时,背后隐藏的复杂机制和底层细节往往被我们所忽略。
本文将带着大家理解文件操作在底层的样子,原来文件不仅仅是被简单地读入或写入内存中。
1. 系统调用接口 🔁 用户编程接口
操作系统(简称 OS
)是计算机系统的核心软件,它管理计算机的硬件和软件资源,为上层应用程序提供一个稳定、统一的运行环境。
如果将你的电脑比作财产的话,那么操作系统就是你的管家,而且还是一个强势的管家。
1.1 系统调用接口
1. 系统调用接口的概念
当你想要访问你的计算机的软硬件资源时,必须符合操作系统的规定,而系统调用就是操作系统为用户空间程序提供的一种服务接口。
2. 为什么要存在系统调用接口
我的电脑我想干嘛就干嘛呀! 😤 为什么操作系统管的这么严呀,还要按照他的要求来使用,到底谁是主人呀!
操作系统的存在就是避免用户不合法的行为(比如:错误的修改数据,不正确的使用)导致争整个系统的崩溃!所以正是想要用户得到一个良好的运行环境,才约束用户的行为。
3. 系统调用接口的作用
系统调用是计算机操作系统中非常核心的概念,它的作用包含但不限于如下内容:
- 硬件保护与隔离:系统调用作为用户程序和硬件设备之间的中介,确保用户程序不能直接访问硬件,从而保护硬件资源免受非法访问和破坏。
- 资源管理与分配:操作系统通过系统调用来管理CPU时间、内存、文件和设备等系统资源,确保资源的公平分配和有效利用。
- 实现操作系统功能:系统调用是操作系统实现各种功能(如进程管理、内存管理、文件系统、网络通信等)的基础。
1.2 用户编程接口
1. 用户编程接口的概念
用户操作接口接口(API
)是操作系统或库函数提供给程序员的接口,在 C
语言环境中,API
通常以库函数的形式出现,这些函数封装了系统调用的细节,为程序员提供了更为简便、易用的编程接口。
2. 为什么要存在用户编程接口
API
提供了一种对系统调用抽象和封装。通过将复杂的系统细节隐藏起来,API
只暴露用户需要的功能和接口,使得用户(包括程序员)可以更容易地理解和使用这些功能。这种抽象和封装降低了直接与系统底层交互的难度和复杂性。
3. 编程接口的跨平台性
除了简化用户对系统调用的使用,编程接口还具有一个非常重要的性质,那就是 跨平台性
。
系统调用接口对应不同的系统如 Linux, Windows,Mac等
,实现的细节肯定是不一致的。所以你在 Linux
系统下使用了系统调用的程序,在 Mac
下就不一定能运行。
但是 API
通过特定的方法(如条件编译配置)可以实现在不同的平台下都能正常运行。
2. 利用系统调用接口进行文件操作🐧
在使用 C
语言进行文件操作时,我们利用 fopen
函数以特定的方式打开一个文件,利用 fread
读取文件或者是利用 fwrite
写文件,最后利用 fclose
函数关闭文件。
这都是将系统接口进行封装过后的 API
,今天我们尝试直接使用系统接口,这更加接近底层,更好的帮助我们引出后续的内容。😀
2.1 open
函数
系统提供的 open
函数有两个版本:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
下面多出的 mode
参数用于指定新创建文件的权限
,所以我们使用下面那个的版本。我们看看他整体的参数:
pathname
:指向要打开的文件的路径名flags
: 用于指定打开文件的方式以及其他选项(如是否创建文件)。这些选项可以通过位或操作组合起来。mode
: 指定新创建文件的权限 。- 返回值为一个
int
,叫做文件描述符(fd
)
我们在这里详细介绍 flags
参数 以及 mode
参数:
flags
参数 — 位图
某些函数需要我们传递标志位,该函数根据标志位执行特定的功能,就比如:
1 #include <iostream>2 using namespace std;3 4 5 int Func(int num, int flag1, int flag2, int flag3){6 if(flag1 == 1){7 num = num + 1;8 }9 if(flag2 == 1){10 num = num - 1;11 }12 if(flag3 == 1){13 num = num * 2;14 }15 16 return num;17 }18 19 20 int main(){21 int num = 10;22 int flag1 = 1, flag2 = 0, flag3 = 1;23 24 num = Func(num, flag1, flag2, flag3);25 cout << "num = " << num << endl; 26 27 return 0;28 }
我们根据 3 个标志位质的不同执行不同的逻辑,但现在只是 3 个标志位,如果是 10 个,20 个,那我们也设置相同数量的形参吗,这太麻烦,也太浪费空间了。
那怎么办呢?位图
。一个 int
变量在该环境下是 32 位,是否可以表示为 32 种状态呢?为了简单,就拿 8 位举例:
#define FLAG1 1 // 0000 0001
#define FLAG2 2 // 0000 0010
#define FLAG3 4 // 0000 0100
不同的状态对应的位置我就取 1 ,如果这 3 个状态我都想要呢?那就利用 |
或对他们进行运算:
FLAG1 | FLAG2 | FLAG3 // 0000 0111
可以看到,这就代表这三个状态我都表示,并且不同的状态之间是相互不干扰的。
根据这个方法,我们更新上述代码:
1 #include <iostream>2 using namespace std;3 4 #define FLAG1 1 // 0000 00015 #define FLAG2 2 // 0000 00106 #define FLAG3 4 // 0000 01007 8 // 利用 & 操作,判断是否选取该状态9 int Func(int num, int flags){10 if(flags & FLAG1){11 num = num + 1;12 }13 if(flags & FLAG2){14 num = num - 1;15 }16 if(flags & FLAG3){ 17 num = num * 2;18 }19 20 return num;21 }22 23 24 int main(){25 int num = 10;26 27 num = Func(num, FLAG1 | FLAG3);28 cout << "num = " << num << endl;29 30 return 0;31 }
flags
参数就采用了位图的方式,我将列举我们常用的选项:
O_RDONLY
:以只读方式打开文件。O_WRONLY
:以只写方式打开文件。O_RDWR
:以读写方式打开文件。O_CREAT
:如果文件不存在,则创建它。需要 mode 参数来指定新文件的权限。O_TRUNC
:如果文件已存在且为只写或读写模式打开,则清空文件内容。O_APPEND
:以追加方式打开文件,数据会被写入到文件尾。
mode
参数 — 文件权限
关于文件权限的知识在这里就不展开细说了,这样的话篇幅太长了,也不易消化😖。我专门有一篇文章 👉点击查看 详细地介绍了文件权限相关的知识,大家有兴趣可以看一下哈。
前置知识结束了,现在我们可以进入正题了,我们以写的方式创建一个新的文件:
16 // 写方式打开文件,并且文件不存在就创建一个,文件的权限是 rw-rw-r--17 int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);18 if(fd == -1) perror("open");
注意:这里就可以看出系统调用接口和编程接口的区别,在 C语言中的 fopen 函数,当文件不存在时会为我们自动创建一个文件,而系统调用接口就需要我们指定。
2.2 write
函数
write
函数的参数就稍显简单一些:
ssize_t write(int fd, const void *buf, size_t count);
fd
: 要写入数据的文件描述符。buf
:指向要写入数据的缓冲区的指针。count
:要写入文件的字节数。- 返回值:写入错位时返回 -1
我们就直接写入内容吧:
20 // 写入指定信息21 const char* msg = "Its a test!!!\n"; 22 ssize_t ret = write(fd, msg, strlen(msg));23 if(ret == -1) perror("write");
2.3 运行结果
系统按照我们的需求创建了一个指定权限的文件:
并且为我们写入了相应的内容:
3. 提出几个为什么
我们需要根据现有的现象来发现问题,不断地问自己为什么,这才能提升自己。
3.1 fd
文件描述符是什么?
我们通过接受 open
函数返回的 fd
文件描述符,并将 fd
传入到 write
函数的参数中,就可以向指定文件写入内容,这是为什么呢?怎么就可以根据一个 fd
的整数向指定文件写入内容呢?
3.2 open
函数的返回值?
我们先看一段代码:
21 int main(){22 int fd1 = open("log1.txt", O_WRONLY | O_CREAT, 0666);23 printf("fd1: %d.\n", fd1);24 25 int fd2 = open("log2.txt", O_WRONLY | O_CREAT, 0666);26 printf("fd2: %d.\n", fd2);27 28 int fd3 = open("log3.txt", O_WRONLY | O_CREAT, 0666);29 printf("fd3: %d.\n", fd3);30 31 int fd4 = open("log4.txt", O_WRONLY | O_CREAT, 0666);32 printf("fd4: %d.\n", fd4); 33 return 0;34 }
这段代码的输出结果是:
fd1: 3.
fd2: 4.
fd3: 5.
fd4: 6.
我们知道 open
函数的返回值是 fd
,但为什么他们的值是连续的?并且唯独缺少 0, 1, 2
,这是巧合吗?如果一次运行时这样,有理由怀疑是巧合。但是,我们多次运行还是这样,那就肯定不是了。
4. struct file
结构体
当一个文件没有被任何进程调度时,该文件就静静的躺在你的磁盘中。但是当文件被调度时,就会被送往内存中,所以内存中的文件就只是文件的内容吗?肯定不是!同一时间,内存中肯定存在着大量需要操作的文件,操作系统需要高效的管理文件那怎么办呢?
使用 结构体 + 双链表
的结构,类似于管理进程的结构 ,在这篇文章,有较详细的说明👉点击查看。
4.1 struct file
中的内容
该结构体中包含了文件的基本信息,包括:
f_op
:指向一个file_operations
结构体的指针,该结构体包含了指向文件操作函数的指针,如read、write、open、release
等。f_count
:表示有多少进程或文件描述符引用了这个文件。当f_count
降到 0 时,文件将被关闭。f_flags
:包含文件的标志,如O_RDONLY、O_WRONLY、O_RDWR
等,表示文件的打开模式。f_mode
:表示文件的访问模式(只读、只写、读写)。f_pos
:当前的文件偏移量,表示下一次读写操作将从哪个位置开始。f_owner
:包含文件的所有者信息,用于权限检查。- …
4.2 相关的内核级别的缓存区
还有一个空间叫做 缓冲区
尽管不直接受到该结构体的管理,但是和结构体紧密相关:
可以看出该 缓存
就是一块在内存中的空间,那有啥用呢?用处可大了!
当用户请求读取文件时,内核会首先检查缓冲区中是否已经缓存了所需的数据
。如果是,则直接从缓冲区中读取数据,避免了磁盘访问的延迟
。如果不是,内核会从磁盘读取数据到缓冲区中,并更新缓冲区的内容。
当用户更新文件内容时,内核并不会直接将该内容立马更新到磁盘上,而是先放在缓冲区等到累积到一定的量,在一次写入磁盘,减少 I/O
操作。
所以对于内存这种告诉设备来说,内核级缓冲区通过减少对磁盘等低速设备的直接访问次数,显著提高了文件I/O操作的性能。
4.3 理解 Linux
一切皆文件
相信大家肯定听过一句话,Linux 中一切皆文件。
但是对于我们的键盘,鼠标,显示屏来说,他们确实是实实在在的硬件呀!我该怎么把它看作文件呢?
属性 + 方法
对于任何的硬件设备都离不开两个概念 属性 + 方法
,但是本章节我们主要关注该结构体的 方法
。Linux
将一切的对象都视作一个文件,那么就拿键盘举例吧,请问键盘这个文件有什么方法呢?无非就是 读方法
或者是 写方法
!键盘你能读我理解,写方法
又是什么呢?键盘确实没有写方法,该方法置空不就好啦!
所以我们可以这么看待硬件:
对于操作系统来说,对于普通文件的管理使用的是一个 struct file
,里面包含文件的基本属性以及读写方法等,对于硬件我也可以一同看待呀!也还不是硬件的基本属性以及读写方法:
所以说当操作系统调用一个文件的时候,才不关心该文件本质是硬件还是啥,我该使用读方法就使用读方法,改写就使用写方法,反正所有文件的操作函数接口是一致的。
这就是使用 struct
封装的好处,尽管底层千差万别,但是上层的调用都是一致的!
5. fd
含义以及文件描述符表
上面说到,操作系统会将系统调度的所有文件的 struct
利用双链表的形式管理起来。我们的一个进程可能同一时间调度多个文件,又该如何管理呢?
5.1 文件描述符表
进程会将自己所控制的所有文件的 struct
结构体的指针放在一个文件描述表中:(注:该表中包含其他信息,但在本文中不关注。
)
该表包含一个结构体指针数组,每一个存储的元素就是你所控制文件结构体的指针,该表可以在进程的 PCB
(在 Linux
下叫做 struct_task
)中找到。
5.2 fd
的含义
到这里就不难理解 fd
的含义了,他为你想要操作文件的结构体指针在结构体指针数组里面的下标。
依靠他,就能找到想要操作文件的位置,环环相扣!
5.3 fd
的分配方式
那进程新加入文件描述符表的文件,怎么分配 fd
呢?会从上到下遍历该表,哪个位置是空闲的就放到哪个位置。
6. 解答疑惑
有了一定的知识基础之后,我们尝试解决在 3
中提出的问题。
6.1 fd
文件描述符是什么?
进程新使用的文件会将该文件的结构体指针放入进程描述符表的结构体指针数组中,该 fd
就是指针放入结构体指针数组的下标位置。有点绕口哈😇
6.2 open
函数的返回值?
为什么 open
函数的返回值缺少 0, 1, 2
呢?因为这几个文件描述符已经被标准输入(stdin
)、标准输出(stdout
)和标准错误输出(stderr
)在进程启动时占用。
0
通常被分配给标准输入键盘
(stdin
)1
通常被分配给标准输出显示器
(stdout
)2
通常被分配给标准错误输出显示器
(stderr
)
怎么证明呢?
证明 1:
14 int main(){15 16 const char* msg = "Its a test!!!\n";17 ssize_t ret = write(1, msg, strlen(msg)); 18 19 return 0;20 }
我们直接向 1
对应的文件写入信息,运行,成功在显示器打印:
Its a test!!!
证明 2:
13 int main(){14 15 umask(0);16 17 close(1);// 关闭显示器文件18 19 // 写方式打开文件,并且文件不存在就创建一个,文件的权限是 rw-rw-r--20 int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);21 if(fd == -1) perror("open");22 printf("fd = %d.\n", fd); 23 24 25 return 0; 26 }
在这里我们关闭 1 对应的文件,之后我们打开一个文件,并打印。
最后我们发现,并没有输出任何内容到屏幕上,反而内容在 log.txt
文件上?
大家思考一下这是为什么?
7. 总结
在这篇文章中介绍了部分文件系统的内容,希望大家有所收获!
相关文章:

文件系统 --- 文件结构体,文件fd以及文件描述符表
序言 在编程的世界里,文件操作是不可或缺的一部分。无论是数据的持久化存储、日志记录,还是简单的文本编辑,文件都扮演着至关重要的角色。然而,当我们通过编程语言如 C、Java 等轻松地进行文件读写时,背后隐藏的复杂机…...

【第三节】python中的函数
目录 一、函数的定义 二、函数的调用 三、函数的参数 3.1 可变与不可变对象 3.2 函数参数传递 3.3 参数类型 四、匿名函数 五、函数的return语句 六、作用域 七、python的模块化 八、 main 函数 一、函数的定义 函数是经过精心组织、可重复使用的代码片段࿰…...

“论云原生架构及其应用”写作框架软考高级论文系统架构设计师论文
论文真题 近年来,随着数字化转型不断深入,科技创新与业务发展不断融合,各行各业正在从大工业时代的固化范式进化成面向创新型组织与灵活型业务的崭新模式。在这一背景下,以容器和微服务架构为代表的云原生技术作为云计算服务的新…...

深度剖析Google黑科技RB-Modulation:告别繁琐训练,拥抱无限创意生成和风格迁移!
给定单个参考图像,RB-Modulation提供了一个无需训练的即插即用解决方案,用于(a)风格化和(b)具有各种提示的内容样式组合,同时保持样本多样性和提示对齐。例如,给定参考样式图像(例如“熔化的黄金3d渲染样式”)和内容图像(例如(a)“狗”),RB-Modulation方法可以坚持所需的提…...

react native 和 flutter 区别
React Native 和 Flutter 都是用于构建跨平台移动应用的优秀框架,各有其优点和适用场景。 1. React Native 1.1 优点 | 基于 JavaScript 生态:对于熟悉 JavaScript 和 React 的开发者来说,学习成本相对较低,能够利用大量现有的 …...

ITSS服务经理/ITSS服务工程师,招投标需要准备吗?
信息技术服务标准(ITSS)是中国首套完整的信息技术服务标准体系,全面规定了IT服务产品及其组成要素的标准化实施,旨在提供可信赖的IT服务。 在国际竞争日益激烈的背景下,推动国内标准的国际化已成为广泛共识࿰…...

eleven接口、多态
能够写出接口的定义格式 public interface 接口名 { public static final 数据类型 名称 数据值; //抽象方法: 必须使用实现类对象调用 void method(); //默认方法: 必须使用实现类对象调用 public default void show() {...} …...

重磅惊喜!OpenAI突然上线GPT-4o超长输出模型!「Her」高级语音模式已开放测试
在最近的大模型战争中,OpenAI似乎很难维持霸主地位。虽然没有具体的数据统计,但Claude3.5出现后,只是看网友们的评论,就能感觉到OpenAI订阅用户的流失: Claude3.5比GPT-4o好用,为什么我们不去订阅Claude呢&…...

解决问题 CUDA error: CUBLAS_STATUS_INVALID_VALUE when calling `cublasGemmEx
遇到问题如下: Traceback (most recent call last):File "run_warmup_a.py", line 431, in <module>main()File "run_warmup_a.py", line 142, in mainreturn main_worker(args, logger)File "run_warmup_a.py", line 207, in…...
【Python实战因果推断】67_图因果模型2
目录 Are Consultants Worth It? Crash Course in Graphical Models Chains Are Consultants Worth It? 为了展示有向无环图(DAG)的力量,让我们考虑一个更有趣但处理因素并未随机化的情况。假设你是某公司的经理,正在考虑是否聘请顶级咨询顾问。你…...

RK3588+MIPI+GMSL+AI摄像机:自动车载4/8通道GMSL采集/边缘计算盒解决方案
RK3588作为目前市面能买到的最强国产SOC,有强大的硬件配置。在智能汽车飞速发展,对图像数据矿场要求越来越多的环境下,如何高效采集数据,或者运行AI应用,成为刚需。 推出的4/8通道GMSL采集/边缘计算盒产品满足这些需求…...

智云-一个抓取web流量的轻量级蜜罐
智云-一个抓取web流量的轻量级蜜罐 安装环境要求 apache php7.4 mysql8 github地址 https://github.com/xiaoxiaoranxxx/POT-ZHIYUN 系统演示...

面向对象程序设计之sort排序
目录 java 升序 降序 c# 升序 倒序 小结 敲过排序算法的都会的,Sort排序与compareTo的改写。 java 升序 一般自带的sort方法就是升序的。 Arrays.sort(arr);//传入要排序的数组,默认升序 Collections.sort(list);//传入要排序的集合类&am…...

ARM学习(29)NXP 双coreMCU MCXN94学习
笔者来介绍一下NXP 双core板子 ,新系列的mcxn94 1、MCX 新系列介绍 恩智浦 MCU 系列产品包括 Kinetis 、LPC 系列,以及 i.MX RT 系列,现在又推出新系列产品 MCX 产品,包括四个系列,目前已经发布产品的是 MCX N 系列。…...

视频剪辑免费素材哪里能找到?
在创作视频时,素材的选择至关重要。为了让您的项目更具吸引力和专业性,我整理了8个剪辑必备素材网站,它们提供了丰富多样的资源,从高清视频到优质音乐,应有尽有。让我们一起探索这些资源丰富、质量上乘的平台ÿ…...

多线程为什么是你必需要掌握的知识
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、多线程是什么?二、多线程学习的必要性1.提升代码性能2.更优秀的软件设计和架构3.更好的工作机会 总结 前言 相信每一位开发者,都应…...

转转上门履约服务拆分库表迁移实践
文章目录 1 背景2 数据迁移方案2.1 方案一:双写新旧库2.2 方案二:灰度开关切换新旧库 3 迁移细节3.1 业务代码改造3.2 数据同步3.3 数据一致性校验 4 总结5 参考资料 1 背景 随着业务不断发展,一个服务中部分功能模块适合沉淀下来作为通用的…...

upload-labs 1-19关 攻略 附带项目下载地址 小白也能看会
本文章提供的工具、教程、学习路线等均为原创或互联网收集,旨在提高网络安全技术水平为目的,只做技术研究,谨遵守国家相关法律法规,请勿用于违法用途,如有侵权请联系小编处理。 环境准备: 1.靶场搭建 下…...

如何设置SQL Server的端口:详细步骤指南
如何设置SQL Server的端口:详细步骤指南 在SQL Server中,配置端口是确保数据库服务能够正确通信的重要步骤。无论是为了提高安全性还是满足特定的网络配置需求,正确设置SQL Server的端口都是必要的。本文将详细介绍如何设置SQL Server的端口…...

昇思25天学习打卡营第16天|Diffusion扩散模型,DCGAN生成漫画头像
Diffusion扩散模型 关于扩散模型(Diffusion Models)有很多种理解,本文的介绍是基于denoising diffusion probabilistic model (DDPM),DDPM已经在(无)条件图像/音频/视频生成领域取得…...

【吊打面试官系列-Dubbo面试题】Dubbo SPI 和 Java SPI 区别?
大家好,我是锋哥。今天分享关于 【Dubbo SPI 和 Java SPI 区别?】面试题,希望对大家有帮助; Dubbo SPI 和 Java SPI 区别? JDK SPI JDK 标准的 SPI 会一次性加载所有的扩展实现,如果有的扩展吃实话很耗时&…...

7.31 Day13 网络散记(http,https...)
http固定对应80端口 https固定对应443端口...

LumaLabs 用例和应用分析
介绍 LumaLabs AI 是一家尖端技术公司,通过创新使用人工智能 (AI) 和神经渲染技术,彻底改变了 3D 内容创作领域。本报告深入探讨了 LumaLabs AI 的各种用例和应用,重点介绍了其在不同行业中的能力、优势和潜在影响。 LumaLabs AI 概述 LumaL…...

leetcode88.合并两个有序数组(简单题!)
思路:合并两个数组,再进行排序(利用快速排序) class Solution(object):def quicksort(self, num, i, j):if i>j: # 跳出循环的条件要出来return left iright jtemp num[i]while left < right:while left < right and…...

鸿蒙(HarmonyOS)DatePicker+TimePicker时间选择控件
一、操作环境 操作系统: Windows 11 专业版、IDE:DevEco Studio 3.1.1 Release、SDK:HarmonyOS 3.1.0(API 9) 二、效果图 可实现两种选择方式,可带时分选择,也可不带,使用更加方便。 三、代码 SelectedDateDialog…...

2024年和2025年CFA FRM CAIA ESG自己整理的资料
本人金融女一枚,CFA FRM CAIA ESG已过,研究生学历,职位投资经理。从事金融快5年了,月薪30000,周未双休五险一金。工作很充实也很累,每天失眠,思考了很久,还是决定离职了,…...

AMD第二季度财报:数据中心产品销售激增,接近总收入一半
#### 财报亮点 7月30日,AMD公布了截至6月29日的第二季度财务业绩,利润超过了华尔街的预期。根据TechNews的报道,最值得注意的是,AMD现在近一半的销售额来自于数据中心产品,而非传统的PC芯片、游戏主机或是工业与汽车嵌…...

ThreadLocal详解及ThreadLocal源码分析
提示:ThreadLocal详解、ThreadLocal与synchronized的区别、ThreadLocal的优势、ThreadLocal的内部结构、ThreadLocalMap源码分析、ThreadLocal导致内存泄漏的原因、要避免内存泄漏可以用哪些方式、ThreadLocal怎么解决Hash冲突问题、避免共享的设计模式、ThreadLoca…...

FastGPT、Dify、Coze产品功能对比分析
在当前的人工智能领域,模型接入、应用发布、应用构建、知识库和工作流编排等功能是衡量一个AI平台综合能力的重要指标。本文将对FastGPT、Dify和Coze这三款产品的功能进行详细对比分析,以帮助用户更好地了解它。 订阅模式及市场概况 在订阅模式及市场概…...

【Linux】缓冲区的理解
目录 一、实验现象二、初步认知缓冲区2.1 缓冲区的刷新策略2.2 缓冲区在哪里 三、缓冲区模拟实现四、再次全面理解缓冲区4.1 用户强制刷新缓冲区(fflush/fsync) 一、实验现象 我们先来看一个现象: 在显示器中打印内容时,fprintf先打印出来,w…...