目录
前言
一、回顾C语言中的文件操作
二、认识文件缓冲区
三、Linux系统提供的文件接口
四、文件描述符fd简介
Linux下C语言文件接口简单模拟实现
前言
每个编程语言都有自己的文件操作方法,在不同的操作系统下相同的语言有相同的文件操作方法。这是如何实现的呢?
本文将通过讲述语言级文件接口与系统级文件接口的关系来回答这个问题。
一、回顾C语言中的文件操作
我们以C语言为例,我们先来简单回顾一下C语言中的文件操作。
文件打开关闭
//打开⽂件
FILE * fopen ( const char * filename, const char * mode );
//关闭⽂件
int fclose ( FILE * stream );
mode表示文件的打开模式,下面都是文件的打开模式:
文件访问 模式字符串 | 含义 | 解释 | 若文件已存在的动作 | 若文件不存在的动作 |
---|---|---|---|---|
"r" | 读 | 打开文件以读取 | 从头读 | 打开失败 |
"w" | 写 | 创建文件以写入 | 销毁内容 | 创建新文件 |
"a" | 后附 | 后附到文件 | 写到结尾 | 创建新文件 |
"r+" | 读扩展 | 打开文件以读/写 | 从头读 | 错误 |
"w+" | 写扩展 | 创建文件以读/写 | 销毁内容 | 创建新文件 |
"a+" | 后附扩展 | 打开文件以读/写 | 写到结尾 | 创建新文件 |
文件访问模式标签 "b" 可以可选地指定以二进制模式打开文件。 |
文件的读写
函数名 | 功能 | 适用于 |
---|---|---|
fgetc | 字符输入函数 | 所有输入流 |
fputc | 字符输出函数 | 所有输出流 |
fgets | 文本行输入函数 | 所有输入流 |
fputs | 文本行输出函数 | 所有输出流 |
fscanf | 格式化输入函数 | 所有输入流 |
fprintf | 格式化输出函数 | 所有输出流 |
fread | ⼆进制输入 | 文件 |
fwrite | ⼆进制输出 | 文件 |
二、认识文件缓冲区
在Linux下运行以下两段代码:
#include <stdio.h>
#include <unistd.h>int main()
{int cnt = 5;while (cnt--){printf("test!!!!, %d ", cnt);sleep(1);}return 0;
}
#include <stdio.h>
#include <unistd.h>int main()
{int cnt = 5;while (cnt--){printf("test!!!!, %d\n", cnt);sleep(1);}return 0;
}
我们会发现,第一段代码在5秒之后所有内容一次性将所有内容刷新到显示器上;第二段代码每一秒在显示器上刷新一条内容。
为什么会这样呢?
因为编程语言的文件结构体中为我们提供了一个语言级的缓冲区,这个缓冲区可以让CPU一次性向硬件中写入较多的数据,减少写入次数,提高运行效率。
我们可以通过fflush函数将缓冲区内数据立即刷新到文件中。
三、Linux系统提供的文件接口
文件打开
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
pathname: 要打开或创建的目标文件
flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。
参数:
O_RDONLY: 只读打开
O_WRONLY: 只写打开
O_RDWR : 读,写打开
这三个常量,必须指定一个且只能指定一个
O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限 O_APPEND: 追加写
返回值:
成功:新打开的文件描述符
失败:-1
文件关闭
#include <unistd.h>int close(int fd);
关闭文件描述符fd对应的文件;
返回值:
成功:0
失败:-1
文件读取
#include <unistd.h>ssize_t read(int fd, void *buf, size_t count);
从文件描述符fd对应的文件中读取至多count字节个字符到buf中;
返回值:
成功:读取的字符个数
失败:-1
文件写入
#include <unistd.h>ssize_t write(int fd, const void *buf, size_t count);
从buf中写入至多count字节个字符到文件描述符fd对应的文件中;
返回值:
成功:写入的字符个数
失败:-1
四、文件描述符fd简介
fd
是 File descriptor
的缩写,中文名叫做:文件描述符。文件描述符是一个非负整数,本质上是一个索引值。
想要真正理解fd是什么,就要先知道Linux进程是如何管理文件的。
每一个进程被创建时,都会建立一个files_struct结构体来管理打开的文件,结构体源码如下:
/** Open file table structure*/
struct files_struct {// 读相关字段atomic_t count;bool resize_in_progress;wait_queue_head_t resize_wait;// 打开的文件管理结构struct fdtable __rcu *fdt;struct fdtable fdtab;// 写相关字段unsigned int next_fd;unsigned long close_on_exec_init[1];unsigned long open_fds_init[1];unsigned long full_fds_bits_init[1];struct file * fd_array[NR_OPEN_DEFAULT];
};
files_struct
这个结构体我们说是用来管理所有打开的文件的。怎么管理?本质上就是数组管理的方式,所有打开的文件结构都在一个数组里。这可能会让你疑惑,数组在那里?有两个地方:
struct file * fd_array[NR_OPEN_DEFAULT]
是一个静态数组,随着files_struct
结构体分配出来的,在 64 位系统上,静态数组大小为 64;struct fdtable
也是个数组管理结构,只不过这个是一个动态数组,数组边界是用字段描述的;
所以fd我们可以理解为Linux进程文件管理结构体中的数组对应的下标;
Linux下C语言文件接口简单模拟实现
7.mystdio · 梁羽赫/Code_in_linux - 码云 - 开源中国 (gitee.com)https://gitee.com/yuhe-liang/code_in_linux/tree/master/7.mystdio