通过与 Jira 对比,让您更全面了解 PingCode

  • 首页
  • 需求与产品管理
  • 项目管理
  • 测试与缺陷管理
  • 知识管理
  • 效能度量
        • 更多产品

          客户为中心的产品管理工具

          专业的软件研发项目管理工具

          简单易用的团队知识库管理

          可量化的研发效能度量工具

          测试用例维护与计划执行

          以团队为中心的协作沟通

          研发工作流自动化工具

          账号认证与安全管理工具

          Why PingCode
          为什么选择 PingCode ?

          6000+企业信赖之选,为研发团队降本增效

        • 行业解决方案
          先进制造(即将上线)
        • 解决方案1
        • 解决方案2
  • Jira替代方案

25人以下免费

目录

linux系统下C标准库IO缓存区和内核缓存区的区别

linux系统下C标准库IO缓存区和内核缓存区的区别:它们的作用范围不同,C标准库IO缓存区一般用于减少用户空间与内核空间之间的数据传输次数,而内核缓存区则用于减少IO操作对磁盘设备的访问次数,从而提高IO性能。

一、linux系统下C标准库IO缓存区和内核缓存区的区别

在Linux系统下,C标准库提供了许多用于IO的函数,如fread、fwrite、fscanf、fprintf等。这些函数使用缓冲区来提高IO性能,其中缓冲区可以分为两种类型:C标准库IO缓存区和内核缓存区。C标准库IO缓存区是在用户空间中的一段内存,用于暂存IO操作的数据,当缓冲区满或者执行fflush等函数时,才会将数据写入到内核缓存区中。此外,C标准库IO缓存区也包括了读取文件时的缓存区,可以减少读取文件时的IO次数,提高读取性能。

C标准库IO缓存区和内核缓存区都可以提高IO性能,但是它们的作用范围不同。C标准库IO缓存区一般用于减少用户空间与内核空间之间的数据传输次数,而内核缓存区则用于减少IO操作对磁盘设备的访问次数,从而提高IO性能。

内核缓存区是内核中的一段内存,用于与硬件设备进行交互的数据缓存区。当程序调用标准库函数向文件写入数据时,数据会首先被复制到C标准库IO缓存区(如果启用了缓冲区),再被C标准库函数调用write()系统调用将数据从用户空间复制到内核缓存区。内核缓存区的大小通常比C标准库IO缓存区大,能够容纳更多的数据,因此它可以减少对磁盘设备的IO操作,提高IO性能。

二、C标准库的I/O缓冲区

UNIX的传统是Everything is a file,键盘、显示器、串口、磁盘等设备在/dev 目录下都有一个特殊的设备文件与之对应,这些设备文件也可以像普通文件(保存在磁盘上的文件)一样打开、读、写和关闭,使用的函数接口是相同的。用户程序调用C标准I/O库函数读写普通文件或设备,而这些库函数要通过系统调用把读写请求传给内核,最终由内核驱动磁盘或设备完成I/O操作。C标准库为每个打开的文件分配一个I/O缓冲区以加速读写操作,通过文件的FILE 结构体可以找到这个缓冲区,用户调用读写函数大多数时候都在I/O缓冲区中读写,只有少数时候需要把读写请求传给内核。

以fgetc / fputc 为例,当用户程序名列前茅次调用fgetc读一个字节时,fgetc 函数可能通过系统调用进入内核读1K字节到I/O缓冲区中,然后返回I/O缓冲区中的名列前茅个字节给用户,把读写位置指 向I/O缓冲区中的第二个字符,以后用户再调fgetc,就直接从I/O缓冲区中读取,而不需要进内核了,当用户把这1K字节都读完之后,再次调用fgetc 时,fgetc 函数会再次进入内核读1K字节到I/O缓冲区中。在这个场景中用户程序、C标准库和内核之间的关系就像在“Memory Hierarchy”中 CPU、Cache和内存之间的关系一样,C标准库之所以会从内核预读一些数据放在I/O缓冲区中,是希望用户程序随后要用到这些数据,C标准库的I/O缓冲区也在用户空间,直接从用户空间读取数据比进内核读数据要快得多。

另一方面,用户程序调用fputc通常只是写到I/O缓冲区中,这样fputc 函数可以很快地返回,如果I/O缓冲区写满了,fputc 就通过系统调用把I/O缓冲区中的数据传给内核,内核最终把数据写回磁盘或设备。有时候用户程序希望把I/O缓冲区中的数据立刻传给内核,让内核写回设备或磁盘,这称为Flush操作,对应的库函数是fflush,fclose函数在关闭文件之前也会做Flush操作。

我们知道main 函数被启动代码这样调用:exit(main(argc, argv))。main 函数return时启动代码会调用exit,exit 函数首先关闭所有尚未关闭的FILE *指针(关闭之前要做Flush操作),然后通过_exit 系统调用进入内核退出当前进程。C标准库的I/O缓冲区有三种类型:全缓冲、行缓冲和无缓冲。当用户程序调用库函数做写操作时, 不同类型的缓冲区具有不同特性。

  • 全缓冲:如果缓冲区写满了就写回内核。常规文件通常是全缓冲的。
  • 行缓冲:如果用户程序写的数据中有换行符就把这一行写回内核,或者如果缓冲区写满了就写回内 核。标准输入和标准输出对应终端设备时通常是行缓冲的。
  • 无缓冲:用户程序每次调库函数做写操作都要通过系统调用写回内核。标准错误输出通常是无缓冲的,这样用户程序产生的错误信息可以尽快输出到设备。

除了写满缓冲区、写入换行符之外,行缓冲还有两种情况会自动做Flush操作。如果用户程序调用库函数从无缓冲的文件中读取,或者从行缓冲的文件中读取,并且这次读操作会引发系统调用从内核读取数据,如果用户程序不想完全依赖于自动的Flush操作,可以调fflush函数手动做Flush操作。

#include <stdio.h>
int fflush(FILE *stream);
//返回值:成功返回0,出错返回EOF并设置error

fflush函数用于确保数据写回了内核,以免进程异常终止时丢失数据,如fflush(stdout);作为一个特例,调用fflush(NULL)可以对所有打开文件的I/O缓冲区做Flush操作。

三、内核缓存区

当一个用户进程要从磁盘读取数据时,内核一般不直接读磁盘,而是将内核缓冲区中的数据复制到进程缓冲区中。但若是内核缓冲区中没有数据,内核会把对数据块的请求,加入到请求队列,然后把进程挂起,为其它进程提供服务。

等到数据已经读取到内核缓冲区时,把内核缓冲区中的数据读取到用户进程中,才会通知进程,当然不同的io模型,在调度和使用内核缓冲区的方式上有所不同。你可以认为,read是把数据从内核缓冲区复制到进程缓冲区。write是把进程缓冲区复制到内核缓冲区。当然,write并不一定导致内核的写动作,比如os可能会把内核缓冲区的数据积累到一定量后,再一次写入。这也就是为什么断电有时会导致数据丢失。所以说内核缓冲区,是为了在OS级别,提高磁盘IO效率,优化磁盘写操作。

延伸阅读1:Linux系统的特点

  • 完全免费
  • 完全兼容POSIX 1.0标准
  • 多用户、多任务
  • 良好的界面
  • 丰富的网络功能
  • 可靠的安全、稳定性能
  • 支持多种平台
相关文章