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

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

标准I/O提供了三种类型的缓冲区:全缓存,行缓存和不带缓存,全缓存只有在缓冲区区满时才会主动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实现零拷贝。具体参见  linux 中的零拷贝技术 第1部分  第2部分

后台开发之IO缓冲区管理的更多相关文章

  1. java开发之IO流

    一直对IO流记不清楚,从别的地方转过来. 看下图: 流的概念和作用 学习Java IO,不得不提到的就是JavaIO流. 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两 ...

  2. Liferay7 BPM门户开发之10: 通用流程实现从Servlet到Portlet(Part1)

    开发目的: 实现通用流程自动化处理(即实现不需要hardcode代码的bpm统一处理后台,仅需要写少量前端html form代码和拖拽设计BPM定义) 既可独立运行或可依托于Liferay或依托其它门 ...

  3. Android混合开发之WebViewJavascriptBridge实现JS与java安全交互

    前言: 为了加快开发效率,目前公司一些功能使用H5开发,这里难免会用到Js与Java函数互相调用的问题,这个Android是提供了原生支持的,不过存在安全隐患,今天我们来学习一种安全方式来满足Js与j ...

  4. iOS开发之Socket通信实战--Request请求数据包编码模块

    实际上在iOS很多应用开发中,大部分用的网络通信都是http/https协议,除非有特殊的需求会用到Socket网络协议进行网络数 据传输,这时候在iOS客户端就需要很好的第三方CocoaAsyncS ...

  5. DarkStone - 跨平台移动应用开发之 Flex 的崛起

    我的好友Ds 发布一个flex的消息.我帮忙转发 DarkStone - 跨平台移动应用开发之 Flex 的崛起 (2013-08-20 22:28:32)     此文章由 周戈 (DarkSton ...

  6. 谷歌插件Image downloader开发之popup

    Image downloader的交互逻辑是这样的:用户点击Image downloader的图标,会向页面(content script,见上一篇文章:谷歌插件Image downloader开发之 ...

  7. 基于xmpp openfire smack开发之Android客户端开发[3]

    在上两篇文章中,我们依次介绍openfire部署以及smack常用API的使用,这一节中我们着力介绍如何基于asmack开发一个Android的客户端,本篇的重点在实践,讲解和原理环节,大家可以参考前 ...

  8. android开发之Intent.setFlags()_让Android点击通知栏信息后返回正在运行的程序

    android开发之Intent.setFlags()_让Android点击通知栏信息后返回正在运行的程序     在应用里使用了后台服务,并且在通知栏推送了消息,希望点击这个消息回到activity ...

  9. JavaEE开发之Spring中Bean的作用域、Init和Destroy方法以及Spring-EL表达式

    上篇博客我们聊了<JavaEE开发之Spring中的依赖注入以及AOP>,本篇博客我们就来聊一下Spring框架中的Bean的作用域以及Bean的Init和Destroy方法,然后在聊一下 ...

随机推荐

  1. java线程的实现

    一共有两种方法Thread类和Runnable接口,相对来讲,更趋向于用Runnable因为一个类可以实现多个接口,但是只能继承一个类,所以相对来说倾向用Runnable 第一种方法:用Thread其 ...

  2. Web.简单配置

    XML 元素不仅是大小写敏感的,而且它们还对出现在其他元素中的次序敏感.所有这些元素都是可选的.因此,可以省略掉某一元素,但不能把它放于不正确的位置. icon icon元素指出IDE和GUI工具用来 ...

  3. python基本数据类型——list

    一.创建列表: li = [] li = list() name_list = ['alex', 'seven', 'eric'] name_list = list(['alex', 'seven', ...

  4. <CentOS7>如何设置hostname

    在CentOS/RHEL 7中,有个叫hostnamectl的命令行工具,它允许你查看或修改与主机名相关的配置: ceph@client-node ~]$ hostnamectlstatus Stat ...

  5. Linux的kobject机制

    作者:Younger Liu 本作品采用知识共享署名-非商业性使用-相同方式共享 3.0 未本地化版本许可协议进行许可. sysfs文件系统下的每个目录对应于一个kobj,kset是kobj的封装,内 ...

  6. 基于CSS的个人网页

    前端时间做的CSS作业:基于CSS的个人网页 基于CSS的个人网页 效果图: 代码: <!DOCTYPE html> <html> <head> <meta ...

  7. SQL Server中关于基数估计如何计算预估行数的一些探讨

    关于SQL Server 2014中的基数估计,官方文档Optimizing Your Query Plans with the SQL Server 2014 Cardinality Estimat ...

  8. Oracle数据库时间类型悬疑案

    这次遇到的问题小Alan其实一年半前做证券行业项目就已经遇到过,但是一直没有去思考是什么原因导致的这样的悬疑案,悬疑案是什么呢?其实很简单,我想有不少童鞋都有用到Oracle数据库,情形是这样子的,这 ...

  9. Maven的简单搭建

    Maven这个个项目管理和构建自动化工具,越来越多的开发人员使用它来管理项目中的jar包.接下来将从下面几个方向介绍maven: (1)Maven简单介绍 (2)Maven安装与配置 (3)Maven ...

  10. 1018 Big Number

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission( ...