- FILE 指针
- 标准输入、标准输出和标准错误
- 检查或复位状态
- I/O 缓冲
- 控制文件 I/O 内核缓冲的标志
- 直接 I/O:绕过内核缓冲
- stdio 缓冲
FILE 指针
FILE 是一个结构体数据类型,它包含了标准 I/O 库函数为管理文件所需要的所有信息,包括用于实际I/O 的文件描述符、指向文件缓冲区的指针、缓冲区的长度、当前缓冲区中的字节数以及出错标志等。FILE数据结构定义在标准 I/O 库函数头文件 stdio.h 中。
typedef struct _iobuf {
char *_ptr; // 当前读写位置
int _cnt; // 当前缓冲区剩余字节数
char *_base; // 缓冲区基址
int _flag; // 标志位
int _file; // 文件描述符
int _charbuf; // 字符缓冲区标志
int _bufsiz; // 缓冲区大小
char *_tmpbuf; // 临时缓冲区
int _status; // 文件状态
} FILE;
标准输入、标准输出和标准错误
/* Standard streams. */
extern struct _IO_FILE *stdin; /* Standard input stream. */
extern struct _IO_FILE *stdout; /* Standard output stream. */
extern struct _IO_FILE *stderr; /* Standard error output stream. */
/* C89/C99 say they're macros. Make them happy. */
#define stdin stdin
#define stdout stdout
#define stderr stderr
检查或复位状态
调用 fread()读取数据时,如果返回值小于参数 nmemb 所指定的值,表示发生了错误或者已经到了文件末尾(文件结束 end-of-file),但 fread()无法具体确定是哪一种情况;在这种情况下,可以通过判断错误标志或 end-of-file 标志来确定具体的情况。
I/O 缓冲
1 文件 I/O 的内核缓冲
read()和 write()系统调用在进行文件读写操作的时候并不会直接访问磁盘设备,而是仅仅在用户空间缓冲区和内核缓冲区(kernel buffer cache)之间复制数据。与此同理,对于读文件而言亦是如此。
2 刷新文件 I/O 的内核缓冲区
联系到一个实际的使用场景,当我们在 Ubuntu 系统下拷贝文件到 U 盘时,文件拷贝完成之后,通常在拔掉 U 盘之前,需要执行 sync 命令进行同步操作,这个同步操作其实就是将文件 I/O 内核缓冲区中的数据更新到 U 盘硬件设备,所以如果在没有执行 sync 命令时拔掉 U 盘,很可能就会导致拷贝到 U 盘中的文件遭到破坏!
1. 内容数据(Content Data)
定义:内容数据是指文件或数据对象中实际存储的信息,通常是用户生成或使用的数据。它包含了文件的主要内容。
示例:
文本文件中的文本内容。
图像文件中的像素数据。
数据库中的实际记录或条目。
特点:
直接影响用户的操作和应用的功能。
可能会频繁变化,例如文本文件中的内容或数据库中的记录。
2. 元数据(Metadata)
定义:元数据是描述内容数据的信息。它提供了关于数据的结构、管理和使用的背景信息,而不是数据本身。
示例:
文件的创建时间、修改时间和访问时间。
文件的大小、类型和权限(例如,读、写权限)。
数据库中表的列名、数据类型和约束条件。
特点:
主要用于管理、组织和检索内容数据。
通常较少变化,通常在文件或数据对象创建时设置,并在某些情况下更新(例如,文件的修改时间)。
控制文件 I/O 内核缓冲的标志
1、O_DSYNC 标志
在调用 open()函数时,指定 O_DSYNC 标志,其效果类似于在每个 write()调用之后调用fdatasync()函数进行数据同步。譬如:
fd = open(filepath, O_WRONLY | O_DSYNC);
2、O_SYNC 标志
在调用 open()函数时,指定 O_SYNC 标志,使得每个 write()调用都会自动将文件内容数据和元数据刷新到磁盘设备中,其效果类似于在每个 write()调用之后调用 fsync()函数进行数据同步,譬如:
fd = open(filepath, O_WRONLY | O_SYNC);
直接 I/O:绕过内核缓冲
从 Linux 内核 2.4 版本开始,Linux 允许应用程序在执行文件 I/O 操作时绕过内核缓冲区,从用户空间直接将数据传递到文件或磁盘设备,把这种操作也称为直接 I/O(direct I/O)或裸 I/O(raw I/O)。
直接 I/O 的对齐限制
⚫ 应用程序中用于存放数据的缓冲区,其内存起始地址必须以块大小的整数倍进行对齐;
⚫ 写文件时,文件的位置偏移量必须是块大小的整数倍;
⚫ 写入到文件的数据大小必须是块大小的整数倍。
stdio 缓冲
文件 I/O 内核缓冲,这是由内核维护的缓冲区,而标准 I/O 所维护的 stdio 缓冲是用户空间的缓冲区,当应用程序中通过标准 I/O 操作磁盘文件时,为了减少调用系统调用的次数,标准 I/O 函数会将用户写入或读取文件的数据缓存在 stdio 缓冲区,然后再一次性将 stdio 缓冲区中缓存的数据通过调用系统调用 I/O(文件 I/O)写入到文件 I/O 内核缓冲区或者拷贝到应用程序的 buf 中。
stdio 缓冲类型
- 无缓冲(Unbuffered)
定义:每次 I/O 操作都直接与操作系统进行交互,没有使用任何缓冲区。 - 行缓冲(Line Buffered)
定义:数据在缓冲区中以行的形式存储,当遇到换行符 (\n) 时,缓冲区的数据会被写入到目标设备。 - 全缓冲(Fully Buffered)
定义:数据在缓冲区中以块的形式存储,只有当缓冲区满时,才会将数据写入目标设备。
刷新 stdio 缓冲区
无论我们采取何种缓冲模式,在任何时候都可以使用库函数 fflush()来强制刷新(将输出到 stdio 缓冲区
中的数据写入到内核缓冲区)
关闭文件时刷新 stdio 缓冲区
程序退出时刷新 stdio 缓冲区。但是,与程序退出方式有关,如果使用 exit()、return 或像上述示例代码一样不显式调用相关函数或执行 return 语句来结束程序,这些情况下程序终止时会自动刷新 stdio 缓冲区;如果使用_exit 或_Exit()终止程序则不会刷新