Chinaunix首页 | 论坛 | 博客
  • 博客访问: 537577
  • 博文数量: 80
  • 博客积分: 1496
  • 博客等级: 上尉
  • 技术积分: 1292
  • 用 户 组: 普通用户
  • 注册时间: 2008-09-18 11:24
个人简介

IT码农一个~

文章分类

全部博文(80)

文章存档

2020年(3)

2019年(7)

2017年(1)

2016年(2)

2015年(2)

2014年(26)

2013年(26)

2012年(2)

2011年(1)

2010年(1)

2008年(9)

我的朋友

分类: LINUX

2013-12-26 14:19:02

原文地址:IO之标准C库buffer 作者:weizhulinux

在论述这个主题之前,先介绍一下标准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"在服务器领域从软件实现到硬件加速再到架构,真的是无处不在。


阅读(781) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~