Linux 标准I/O编程 流缓冲 文件流

本文深入探讨C语言中的底层文件I/O和标准I/O编程,解释了流与文件描述符的区别及联系,详细分析了流缓冲的作用及其实现原理,强调了使用流进行输入输出的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天学完底层文件i/o和标准i/o编程

底层i/o操作基于文件描述符。

标准i/o操作基于流缓冲

C语言中流的概念

一句话,流(steam)表示任意输入源或任意输出的目的地。很多程序是通过一个或多个流进行读入和输出的。这些流可能存储在不同的介质(如硬盘,CD,DVD,闪存等等),也可能是不存储文件的设备(打印机,网络套接字)。头文件<stdio.h>中定义了处理流的函数。(注意不仅仅只有表示文件的流)

C程序中对流的操作是通过FILE *实现的。所以说这种数据类型表示的就是一个流。一个流对象保存了和文件(也可能不是文件,但是Unix下几乎“一切”都是文件)连接的情况以及缓冲的状态,还有文件位置定位符的状态。每一个流还有一个文件末位指示器和错误指示器,可以通过ferror和feof来监测。(参见EOF and Errors.)

注意不要试图创建自己的FILE *, 让函数库去实现。

流与文件描述符区别与联系

一句话,流是文件描述符的抽象,一般使用文件描述符是系统层次的调用

当向一个文件读入或者输出时,既可以选择流,也可以选择使用文件描述符。文件描述符是int类型的,而流是用FILE *来表示的。

文件描述符提供了一个原始、低层次的输入输出接口。文件描述符和流都可以表示一个连接,可以是和设备的(例如终端),或者管道,或者一个和另一个进程的套接字,或者就是一个正常的文件(normal file)。但是,如果你想要对特殊设备进行特定的操作,你必须使用文件描述符。另外,如果你的程序需要以特殊模式进行输入输出(例如nonblocking, polled input, 参见File Status Flags),也必须使用文件描述符。

而流提供了一个基于原始的文件描述符的高层次接口。流接口对于所有类型的文件的操作大多都是类似的,唯一的区别就是缓冲的策略(参见下面的流缓冲)。

使用流的主要优势是操作流的函数比文件描述符多得多,而且更加强大方便。文件描述符仅仅提供了一个单一的函数用来传输字符块,但是流接口提供了很多格式化的输入输出(例如printf和scanf)和一些字符函数以及列读入输出函数。

因为流是基于文件描述符的,所以实际上你可以“拆解”一个流得到对应的文件描述符然后进行低层次的操作。相反地,你也可以先用文件描述符和一个文件建立连接,然后建立一个链接这个文件描述符的流对象。

通常情况下,你都应该使用流来进行输入输出,这样不仅方便强大,而且可以保证程序的移植性:你可以在任何一个遵守ISO C标准的机器上使用流,但是在一个非GNU机器上你可能无法使用文件描述符。

缓冲为什么存在?

标准I/o库提供缓冲的目的是尽可能减少使用read和write调用的次数

正如上面谈到的,缓冲区可能导致输出延迟,那么它为什么存在呢?

在系统底层调用这个层次,数据是用write+文件描述符写入的,这种方法将数据写入到文件描述符对应的一个字节缓冲中。

大多数语言有着非常快的函数调用,在C/C++这种语言中调用一个函数可能只需要几个cpu周期,时间开销几乎可以忽略不计(只有在近端的情况下才会使用inline.)。然而,一个系统调用时间开销是非常可观的。在Linux上的一个系统调用可能会花费几千个cpu周期并掺杂着上下文转化.所以系统调用比用户空间里的函数调用花的时间多得多。

缓冲存在的主要目的就是为用户空间函数抵消调用系统函数的开销。当函数做很多写入操作时这非常重要——否则系统调用的时间会占程序运行时间的主要部分。

让我们考虑一下当你使用 grep在一个文件或者stdin中搜索字段。假设你在nginx日志中找一个IP地址,而在nginx日志里一行大概有100个字符。如果不使用缓冲的话,就意味着只要grep遇到了想要的ip地址,它就会调用write().,而这会接连不断的发生,并且每次写入的字符大概都在100字节。如果使用默认缓冲4096字节的话,grep会等到缓冲区满以后再调用write()清除缓冲,这大概会读入40行才发生一次(译者注:缓冲策略是写入流/文件的充分条件,不是必要的。缓冲区存在的意义就是使用“Stream-level I/O”时从缓冲区进行异步块写入/读出,这样可以在设备堵塞或者大量的写入操作的时候加快效率。如果一次只写入少量数据,内核一看没有堵塞,“干脆”就把缓冲区的内容写入了,反正放着也是放着。满缓冲在gnu library c 中定义为以任意大小的块写入流。),所以会减少大概40倍在系统调用上花的时间。不错!

如果grep程序向标准输出写入大量的数据的话,你可能都不会注意到缓冲区造成的延迟。并且如果grep只是找一个简单的字段,通常它在输出上花的时间会比寻找该字段画的时间更多。但是假如grep匹配字段发生的非常缓慢,例如每十秒钟才发生一次,那么我们可能要等400秒才能得到一个输出!(即便在开始的时候已经匹配到几个字段)

 

从这边网页得到启发:https://www.cnblogs.com/liqiuhao/p/7676125.html

设计标准i/o编程就是基于流缓冲:

用用户空间函数抵消调用系统函数的开销,先读写到缓冲区,根据你设置的缓冲区满了,然后写/读

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值