IO之标准C库buffer

标准C库和linux系统调用以及windows API之间的关系

在论述这个主题之前,先介绍一下标准C库和linux系统调用以及windows API之间的关系。
拿写文件来举个例子
linux下写文件用write()
windows下写文件用WriteFile()
这说明不同操作系统实现同样的系统功能的接口应该是不一样的。造成这种现状是操作系统发展的历史原因造成的,无法在操作系统的层面统一系统函数接口。同样功能的程序在linux上写一套,windows上又得写另外一套,毫无移植性可言。如果要开发一个既能在linux跑,又能在windows上跑的程序,开发成本飙升!
为了解决这个移植性的问题,标准C库利用了封装技术,扮演了一个重要的角色,统一了部分基本功能接口。
标准C规定的写文件的函数是fwrite(),就是不管在linux还是在windows上,各自都有一个标准C库,库函数封装的下层细节不一样,但是接口完全一样,提供的功能完全一样。
这是怎么做到的?猜一猜大致实现就知道了
在linux上,标准C接口fwrite()的实现伪代码

size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream){
   ...
   ...
   return write(stream->fd,buffer,count);
}

在windows上,标准C接口fwrite()的实现伪代码

size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream){
#define OUT 
   BOOL ret = false;
   OUT int optnum;
   ...
   ...
    ret = WriteFile(stream->filehandle, buffer, count, &optnum,...);
    if( ret == true)
       return optnum;
   else
       return -1;
}
 

内部实现不一致,没关系,接口一样就可以,不管在linux还是windows上,写文件都用fwrite(),分别在各自平台上编译就可以了。
标准C就是这样一个处于系统层面之上的应用层标准函数库,为了统一各个操作系统上的函数接口而生。

回到我们的主题----IO之应用层buffer

什么是应用层buffer?

回想一下我之前介绍的《IO之内核buffer"buffer cache"》,既然write()能把需要写文件的数据推送到一个内核buffer来偷工减料欺骗应用层(为了加速I/O),说“我已经写完文件并返回了”。那应用层的标准C库的fwrite()按道理也可以为了加速,在真正调用write()之前,把数据放到(FILE)stream->buffer中,等到多次调用fwrite(),直至(FILE)stream->buffer中积攒的数据量达到(FILE*)stream->bufferlen这么多的时候,一次性的把这些数据全部送入write()接口,写入内核,这是多么美妙啊。。。
实际上,标准C库就是这么做的!
把fwrite()的linux实现再细致一下
过程其实仍然很粗糙,为了突出buffer的重点,计算stream->buffer是否满,拷贝多少,填充多少这样的细节和主题无关的东西我略去了

size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream){
   ...
   if( stream->buffer满 ){
       write(stream->fd,stream->buffer,stream->bufferlen);
   } else{
       拷贝buffer内容至stream->buffer
   }
   ...
    return count;
   //过程很粗糙,为了突出buffer的重点,计算stream->buffer是否满,拷贝多少,填充多少这样的细节和主题无关的东西我略去了
}

fwrite()在windows平台的实现也基本上是这样的,也有buffer。
值得一说的是,fread()也有一个读cache来完成预读。

setvbuf()和setbuf()都是控制这个标准C库的buffer的。
还有fflush()是C库用于flush数据的函数。
以上三个函数,如果大家有兴趣,可以去看看linux上对应的man文档。
重点是要知道不仅系统的内核有buffer,应用层的C库同样也有buffer。这些buffer的唯一作用就是为了加速应用,不让应用老是卡在和磁盘交互上。
说个题外话,实际上对于磁盘、RAID卡、盘阵这样的外存介质而言,他们各自在硬件上也都有一层前端的buffer,有时也叫cache,用来缓冲读写加速。cache越多,价格越贵,性能越好。大型存储设备一般拥有多层cache,用的是昂贵的SSD。
需要分享的一点经验是,不管是标准C库的buffer也好,内核的"buffer cache"也罢,我们终究对它们的控制力度是有限的。我们在做服务器程序的时候,如果业务上涉及太大的I/O量,需要做服务整体加速的时候,我们一般自己在业务层做一层自己的"buffer",把业务数据buffer住,攒成以文件系统或者磁盘的block块单位的大块数据,然后集中写,然后集中写又有集中写的策略。。。
再引申一点内容,做高性能大流量的大站的架构,其中最重要几个架构角色之一就是cache。前端CDN、后端memcache、redis、mysql内部cache等等,都是cache的应用场景,可以说"buffer cache"在服务器领域从软件实现到硬件加速再到架构,真的是无处不在。

posted @ 2016-10-12 20:34  苏小北1024  阅读(750)  评论(0编辑  收藏  举报