• 学派吧-由商祺云独家赞助-https://www.sq9.cn。

IO缓冲区管理的实例详解教程分析-linux教程

unix 学派小编 8个月前 (01-17) 323次浏览 已收录 0个评论 扫描二维码

如果您有服务器咨询问题、购买问题、可以联系我们客服 7271895 690624
商祺云-阿里代理、景安代理、西部代理

Linux 系统 IO 中 write 原型为 ssize_t write(int filedes, const void * buff, size_t nbytes) ;

当调用 write 写数据的时候,调用完成后 write 直接返回,但是磁盘是个慢速设备,操作系统会将数据保存在内核中的缓冲区中,并负责异步地将数据写至磁盘。当然如果此时系统宕机了则会丢失数据。write 是系统调用,每次调用都会陷入内核,所以选取一个合适的块长度 buffsize,并尽量减少它的调用可以优化效率。在 ANSI C 的标准 IO 中我们调用 printf/fprintf/fputs 等会以流的方式进行处理,我们只需要写入流中,而不用像 write 一样选择一个 buffsize,因为标准 IO 库帮我们处理了很多细节,例如缓冲区分配,以优化长度执行 IO 等。这样的话就会减少 wirte/read 系统调用的数量,提高效率。但是与此同时会引入另外一个问题:数据拷贝,例如当使用函数 fgets 和 fputs 时,通常需要经过两次缓冲区:一次是标准 IO 缓冲区,还有一次是调用 read 和 write 的内核缓冲区。但是总的来说使用标准 IO 相对于系统 IO 来说接口简单,且效率相当。

标准 IO 提供了三种类型的缓冲区:全缓存,行缓存和不带缓存,全缓存只有在缓冲区满时才会主动 flush,通常用在对一个磁盘文件 IO。行缓存在缓冲区中遇到换行符就会 flush,还有一种情况是需要从标准输入输出得到输入数据时也会 flush 缓冲区,行缓存一般用在交互的终端中。不带缓存则相当于直接 write 系统调用输出,标准出错流 stderr 通常是不带缓存的,这就使得出错信息可以尽快显示出来。除了默认的 flush 条件外,显式调用 fflush 函数和程序正常终止时也会 flush 缓冲区。我们可以使用 setbuf/setvbuf 来更改默认的缓冲区长度,参见 APUE 5.4 节。

在使用标准 IO 的程序中,当我们将一个标准输出重新定向到一个文件时,会将行缓存变为全缓存,在某些情况下可能会导致一些非预期错误,比如调用 printf(“*****\n”)时,当以交互方式运行该程序时,会正常输出。但是当将标准输出重新定向到一个文件时,缓冲区区变为全缓存,printf 就不会正常输出,该行数据仍在缓冲区中。如果此时再 fork 一个子进程,数据空间被复制到子进程中时,该缓冲区数据也被复制到子进程中。接着在子进程中如果输出则会刷新之前在缓冲区的内容,产生一些非预期的输出。

在网络编程中,应该直接使用系统 IO,标准 IO 为提升性能而引入缓冲机制增加了网络应用程序的复杂性。并且,某种意义上说标准 IO 流是全双工的,能同时执行输入和输出,然而对流的限制和对套接字的限制,有时候会互相冲突。(参见 CSAPP P611)

某些高级的网络库中(比如说 muduo 库)在使用系统 IO 的基础上会创建自己的缓冲区,帮助用户屏蔽系统 IO 的某些不便,例如调用 write 发送大量数据的时候,发送缓冲区满时需要应用层等待,read 接收数据的时候粘包和数据接受的缓慢。当增加应用层缓冲区后,由网络库处理这些实现细节,简化用户操作。

Linux 还提供了零拷贝技术来减少内存拷贝,进而提升效率,我们知道利用 read/write 从磁盘发送数据到网卡会经过四次拷贝操作:当应用程序需要访问某块数据的时候,操作系统内核会先检查这块数据是不是因为前一次对相同文件的访问而已经被存放在操作系统内核地址空间的缓冲区内,如果在内核缓冲区中找不到这块数据,Linux 操作系统内核会先将这块数据从磁盘读出来放到操作系统内核的缓冲区里。如果这个数据读取操作是由 DMA 完成的,那么在 DMA 进行数据读取的这一过程中,CPU 只需要进行缓冲区管理,以及创建和处理 DMA ,除此之外,CPU 不需要再做更多的事情,DMA 执行完数据读取操作之后,会通知操作系统做进一步的处理。Linux 操作系统会根据 read 系统调用指定的应用程序地址空间的地址,把这块数据存放到请求这块数据的应用程序的地址空间中去,待用户对数据完成操作后,操作系统需要将数据再一次从用户应用程序地址空间的缓冲区拷贝到与网络堆栈相关的内核缓冲区中去,这个过程也是需要占用 CPU 的。数据拷贝操作结束以后,数据会被打包,然后发送到网络接口卡上去。从上面的描述可以看出,在这种传统的数据传输过程中,数据至少发生了四次拷贝操作,即便是使用了 DMA 来进行与硬件的通讯,CPU 仍然需要访问数据两次。

(ps:记得之前看过一个面试题说是 printf 输出过程经过几次缓冲区,现在大家明白了吧!)

使用零拷贝技术可以避免数据在系统内核地址空间的缓冲区和用户应用程序地址空间的缓冲区进行拷贝。有时候,应用程序在数据传输的过程中不需要对数据进行访问,传输的数据可以不用复制到用户应用区,直接通过内核发送到网卡就可以,这样可以提高性能,而此时就需要零拷贝技术。linux 下可以用 mmap,sendfile,splice 实现零拷贝。

以上就是 IO 缓冲区管理的实例详解的详细内容,更多请关注学派吧其它相关文章!


学派吧 , 版权所有丨如未注明 , 均为原创丨
喜欢 (0)
[sqwwnet]
分享 (0)
学派小编
关于作者:
君子爱财,取之有道!踏实做人,诚信做事! 欲全天道,先全人道,人道不能全,天道远

您必须 登录 才能发表评论!