标准IO_缓冲区

目录

1.文件IO和标准IO

1.1 标准IO(Stardard IO)

1.2 文件IO(File IO)

1.3 主要区别

2.标准IO

2.1 标准IO缓冲区

2.2 标准IO缓冲区实现

2.2.1 FILE结构体

2.2.2 读缓冲区和写缓冲区


1.文件IO和标准IO

图 1-1 文件IO和标准IO架构图


1.1 标准IO(Stardard IO)

标准IO是通过C语言标准库提供的函数来进行IO操作的。例如,printf、scanf等函数。 标准IO使用缓冲区来提高IO性能,数据首先被读取到缓冲区中,然后再从缓冲区中读取或写入到目标位置(如终端或文件)。 

1.2 文件IO(File IO)

文件IO是直接通过操作系统提供的系统调用来进行IO操作的。例如,open、read、write等系统调用。 文件IO可以对文件进行读取和写入操作,通常用于处理文件的输入和输出。 文件IO可以使用不同的文件打开模式(如只读、只写、追加等),并可以对文件进行随机访问。

1.3 主要区别

  • 标准IO是基于文件IO之上的一个高级抽象层,它提供了更简单、更方便的接口,使得程序员能够更容易地进行IO操作。
  • 标准IO使用缓冲区,可以提高IO性能,减少与底层设备的交互次数,但也可能导致数据延迟输出或丢失。
  • 文件IO直接操作系统调用,没有缓冲区的概念,每次读写都会直接与磁盘进行交互,可以实现更精细的控制和操作文件。

2.标准IO

2.1 标准IO缓冲区

标准IO缓冲区是在标准IO操作中使用的一种缓冲机制,它可以提高IO性能。

在C语言中,标准IO库提供了一组函数(如printf、scanf、fopen、fwrite等)来进行标准IO操作。 标准IO缓冲区有三种类型:全缓冲、行缓冲和无缓冲,其类型是根据IO设备和缓冲区的设置而定。

  • 全缓冲: 当输出的数据量达到一定大小(通常是4096字节)或遇到文件结束符时,缓冲区才会被刷新,数据才会被写入到目标设备(如终端或文件)。 当输入的数据量达到一定大小或使用特定的输入函数(如fgets)时,缓冲区才会被填充。 当打开一个文件时,如果文件与终端设备无关,则默认为全缓冲模式。
  • 行缓冲: 当输出的数据中包含换行符\n时,缓冲区会被立即刷新,数据会被写入到目标设备。 当输入的数据中包含换行符\n时,缓冲区会被填充。 当打开一个文件时,如果文件与终端设备有关,则默认为行缓冲模式。
  • 无缓冲: 每次进行IO操作时,缓冲区会被立即刷新,数据会立即写入到目标设备。 当使用stderr作为输出流时,默认为无缓冲模式。

可以使用setvbuf函数来设置缓冲区的类型,或使用setbuf函数来手动设置缓冲区。

可以使用fflush函数来手动刷新缓冲区,确保数据被写入到目标设备。

需要注意的是,当程序正常终止时,标准IO缓冲区会自动被刷新。但是在异常终止或非正常关闭程序时,可能会导致部分数据丢失。因此,在关键的数据输出场景中,建议显式地调用fflush函数来刷新缓冲区。

2.2 标准IO缓冲区实现

2.2.1 FILE结构体

struct _IO_FILE
{
    int _flags;
    char *_IO_read_ptr;
    char *_IO_read_end;
    char *_IO_read_base;
    char *_IO_write_base;
    char *_IO_write_ptr;
    char *_IO_write_end;
    char *_IO_buf_base;
    char *_IO_buf_end;
    char *_IO_save_base;
    char *_IO_backup_base;
    char *_IO_save_end;
    struct _IO_marker *_markers;
    struct _IO_FILE *_chain;
    int _fileno;
    int _flags2;
    __off_t _old_offset;

    unsigned short _cur_column;
    signed char _vtable_offset;
    char _shortbuf[1];
    _IO_lock_t *_lock;
};

_flags:这是一个整型字段,用于表示文件流的状态和属性标志。它包含了多个位域,用于表示文件的打开模式、读写状态、错误标志等。

_IO_read_ptr 和 _IO_read_end:这两个字段用于支持文件的读取操作。_IO_read_ptr 表示读取操作的指针,指向下一个要读取的字符。_IO_read_end 表示读取操作的结束位置,当 _IO_read_ptr 指针达到 _IO_read_end 时,表示已经读取完了所有字符。

_IO_write_ptr 和 _IO_write_end:这两个字段用于支持文件的写入操作。_IO_write_ptr 表示写入操作的指针,指向下一个要写入的位置。_IO_write_end 表示写入操作的结束位置,当 _IO_write_ptr 指针达到 _IO_write_end 时,表示已经写入了所有字符。

_IO_buf_base 和 _IO_buf_end:这两个字段用于表示文件缓冲区的起始地址和结束地址。_IO_buf_base 表示缓冲区的起始地址,_IO_buf_end 表示缓冲区的结束地址。

_fileno:这是一个整型字段,表示文件的描述符。文件描述符是操作系统中用于标识打开文件的整数值。

_IO_save_base 和 _IO_backup_base:这两个字段用于支持文件流的回溯操作。_IO_save_base 表示回溯操作时要保存的起始位置,_IO_backup_base 表示回溯操作时要备份的起始位置。

_markers:这是一个指针类型的字段,用于支持文件流的标记操作。它指向一个结构体,包含了多个标记的信息,例如当前标记的位置和标记的类型。

_chain:这是一个指针类型的字段,用于表示文件流的链表关系。在一些特定场景下,多个文件流可以通过 _chain 字段形成链表结构,方便进行管理。

2.2.2 读缓冲区和写缓冲区

(1)读缓冲区

 图 2-1 读缓冲区工作原理

读缓冲区指的是_IO_buf_base至_IO_buf_end之间的内存区域,通常为4096字节。

_IO_read_base,_IO_read_ptr,_IO_read_end三个指针用于操作读缓冲区。

进行读文件操作(fread)时,如果缓冲区的数据不够fread读取,会通过read系统调用从内核空间读取默认4096字节至读缓冲区,再拷贝fread指定长度数据至指定内存。

如果缓冲区的数据足够fread读取,不会从内核空间拷贝数据至缓冲区,直接从缓冲区拷贝指定长度数据至指定内存。

读缓冲区的作用是批量从内核读取数据值读缓冲区,再多次分发给应用程序。

(2)写缓冲区

图 2-2 写缓冲区工作原理

写缓冲区指的是_IO_buf_base至_IO_buf_end之间的内存区域,通常为4096字节。注意读缓冲区和写缓冲区用的都是同样的指针变量,所以存在耦合关系,再进行读写交互操作时要确保正确的操作规范,才能保证数据不出错。

_IO_write_base,_IO_write_ptr,_IO_write_end三个指针用于操作读缓冲区。

进行写文件操作(fwrite)时,如果写缓冲区能够存储fwrite指定长度的数据,会先把数据写入写缓冲区,如果写缓冲区已存数据加上fwrite指定长度数据大于写缓冲区长度,会调用write系统调用,将写缓冲区数据写入到内核空间。

写缓冲区的作用是多次收集应用程序数据至写缓冲区,再批量写入内核。

猜你喜欢

转载自blog.csdn.net/weixin_28673511/article/details/131887996