👦个人主页:Weraphael
✍🏻作者简介:目前正在学习c++和算法
✈️专栏:Linux
🐋 希望大家多多支持,咱一起进步!😁
如果文章有啥瑕疵,希望大佬指点一二
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注😍
目录
- 前言
- 一、FILE结构体设计
- 二、fopen函数
- 二、fwrite函数
- 三、fflush函数
- 四、fclose函数
- 五、总结及相关代码
前言
在C语言中,FILE
结构体一定封装了诸如文件描述符等字段,使得C语言文件操作的库函数可以很好的调用系统调用,因此程序员可以更加方便地使用高级接口来完成任务,而无需深入了解底层系统调用的实现细节。
这篇博客将带领大家深刻理解C语言文件操作函数底层是如何封装系统调用接口,以及C语言用户级缓冲区的现象。
注:本篇博客不是为了造一个更好的轮子,而是重在理解!!!
一、FILE结构体设计
(以上是库封装的FILE
相关字段)
我们知道,C语言的文件操作函数底层必定会调用系统调用接口,而在往期博客中我们知道,文件相关的系统调用接口都是由文件描述符来定位文件的,因此,FILE
结构体必定封装了文件描述符。
而我们这篇博客还要实现缓冲区现象,因此,FILE
结构体还会封装维护缓冲区的相关字段。
_fileno
:文件描述符。outbuffer
:输出缓冲区。需要注意的是,用户级缓冲区通常是通过动态内存分配函数(如malloc
或new
)在堆区分配的,大小是不固定的。out_pos
:当前缓冲区字符的个数。
二、fopen函数
C语言中文件打开操作fopen
底层调用了系统调用接口open
open
函数的更多详细用法请查看此篇博客:点击跳转
// fopen函数模型
FILE *fopen(const char *path, const char *mode);
// path: 文件的路径
// mode: 文件的打开方式
文件打开的方式mode
有很多种,大家可以通过man
手册查询,这里我重点实现以下常见的三种:
-
"w"
: 以只写的方式打开方式。文件不存在会自动创建,并且每打开一次都会将文件内容清空再写入。 -
"a"
: 以追加的方式打开文件。文件不存在会自动创建,不会对文件原有的内容做清空,而是追加写入。 -
"r"
: 以只读的方式打开文件,文件不存在会报错。
此外,当使用fopen
函数打开文件,对于普通文件,默认情况下会使用全缓冲来刷新缓冲区,即直到缓冲区满了或者遇到'\n'
才将缓冲区中的内容写入磁盘。
二、fwrite函数
C语言中文件写入操作fwrite
底层也调用了系统调用接口write
write
函数的更多详细用法请查看此篇博客:点击跳转
// fwrite函数模型
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
-
ptr
:指向要写入的数据块的指针。通常使用void*
类型的指针,可以传递任何类型的数据块。需要注意的是,ptr
指向的数据必须与size
和nmemb
参数相对应,即size * nmemb
表示要写入的总字节数。 -
size
:要写入每个数据块的字节数。 -
nmemb
:要写入的数据块的数量。表示要写入多少个数据块。 -
stream
:指向要写入的文件的指针。需要使用标准库函数fopen()
成功打开文件后,将返回的文件指针作为参数传递给fwrite()
函数。 -
以
nmemb
值充当返回值。
用户调用fwrite
时,并不会直接将内容直接写入文件中,而是将数据写入到用户级缓冲区,然后通过一定条件,再将缓冲区的内容写入到文件中。详细步骤如下:
- 判断当前用户级缓冲区是否被填满。如果满了,先对缓冲区刷新,再进行后续操作。
- 如果遇到
'\n'
,就将'\n'
之间的字符全部刷新。 - 若不满足条件继续将字符往缓冲区里塞。
三、fflush函数
当用户调用fflush
函数时,不管缓冲区是否满了还是什么,直接刷新。因此,fflush
函数一定封装了系统调用接口write
// fflush函数原型
int fflush(FILE *stream);
四、fclose函数
fclose()
函数是C语言标准库中用于关闭文件流的函数。它的作用是将缓冲区中剩余的数据写入到文件中,并释放系统资源。因此它的底层必定会调用fflush
函数以及系统调用close()
int close(int fd);
五、总结及相关代码
-
数据到达文件一共要执行3次拷贝,第一次是拷贝到用户级缓冲区、第二次是拷贝到系统级缓冲区、最后一次则是真正写入文件中(第二次到第三次是由操作系统帮我们完成的)。
-
在模拟实现
fwrite
时,我们将一个字符一个字符拷贝到缓冲区,不满足条件,如缓冲区没有满或者没有遇到'\n'
就继续拷贝至缓冲区,这不是变相减少了调用系统调用的次数,从而提高IO
效率!
本篇博客相关代码:点击跳转