加入收藏 | 设为首页 | 会员中心 | 我要投稿 衡阳站长网 (https://www.0734zz.cn/)- 数据集成、设备管理、备份、数据加密、智能搜索!
当前位置: 首页 > 创业 > 经验 > 正文

浅谈带缓冲I/O 和不带缓冲I/O的区别与联系

发布时间:2020-12-31 20:29:42 所属栏目:经验 来源:网络整理
导读:这里搜集从网上看到的一些言论,自认为还是比较靠谱的,有些不靠谱的根据自己的理解进行了修正。 首先要明白不带缓冲的概念: 所谓不带缓冲,并不是指内核不提供缓冲,而是只单纯的系统调用,不是函数库的调用 。系统内核对磁盘的读写都会提供一个块缓冲(在

程序中用open和write打开创建并把“hello world“写入文件test.txt,相应用fopen和fwrite操作文件test2.txt。程序执行到open和fopen之后,sleep 15秒,这时用ls查看生成了文件没,这时用open打开的test.txt出现了,用fopen打开的的test2.txt也出现了;当程序执行完write和 fwrite之后,在15秒睡眠期间,用cat查看test.txt,其内容是“hello,world”;但是此时用cat查看test2.txt,其内容为空。睡眠结束后,执行了close(fd),此时再用cat查看test2.txt,发现其内容也有了:“hello,world”。该例子证明了open和write是不带缓冲的,即程序一执行其io操作也立即执行,不会停留在系统提供的缓冲里,不需等到close操作完才执行。与之相比的fopen和fwrite则是带缓冲的,(一般)要等到fclose操作完后才会执行。

相关的源码示例如下:

#include <unistd.h>
#include <iostream>
#include <fcntl.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>

using namespace std;

int main(){
int fd;
FILE *file;
char *s="hello,worldn";
if((fd=open("test.txt",O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))==-1){
cout<<"Error open file"<<endl;
return -1;
}
if((file=fopen("test2.txt","w"))==NULL){
cout<<"Error Open File."<<endl;
return -1;
}
cout<<"File has been Opened."<<endl;
sleep(15);
if(write(fd,s,strlen(s))<strlen(s)){
cout<<"Write Error"<<endl;

return -1;
}
if(fwrite(s,sizeof(char),strlen(s),file)<strlen(s)){
cout<<"Write Error in 2."<<endl;

return -1;
}
cout<<"After write"<<endl;

sleep(15);
cout<<"After sleep."<<endl;

close(fd);
return 0;
}

以 ssize_t write(int filedes,const void *buff,size_t nbytes)和size_t fwrite(const void *ptr,size_t size,size_t nobj,FILE *fp)来讲讲自己对unix系统下带缓存的I/O和不带缓存的I/O的区别。

首先要清楚一个概念,所谓的带缓存并不是指上面两个函数的buff参数。

当将数据写到文件上时,内核先将该数据写到缓存,如果该缓存未满,则并不将其排入输出队列,直到缓存写满或者内核再次需要重新使用此缓存时才将其排入输入队列,待其到达队首,再进行实际的I/O操作,也就是此时才把数据真正写到磁盘,这种技术叫延迟写。

现在假设内核所设的缓存是100个字节,如果你使用write,且buff的size为10,当你要把9个同样的buff写到文件时,你需要调用9次write,也就是9次系统调用,此时也并没有写到硬盘,如果想立即写到硬盘,调用fsync,可以进行实际的I/O操作。

标准I/O,也就是带缓存的I/O采用 FILE*,FILE实际上包含了为管理流所需要的所有信息:实际I/O的文件描述符,指向流缓存的指针(标准I/O缓存,由malloc分配,又称为用户态进程空间的缓存,区别于内核所设的缓存),缓存长度,当前在缓存中的字节数,出错标志等,假设流缓存的长度为50字节,把以上的数据写到文件,则只需要2次系统调用(fwrite调用write系统调用),因为先把数据写到流缓存,当其满以后或者调用fflush时才填入内核缓存,所以进行了2次的系统调用write。

fflush将流所有未写的数据送入(刷新)到内核(内核缓冲区),fsync将所有内核缓冲区的数据写到文件(磁盘)。至于究竟写到了文件中还是内核缓冲区中对于进程来说是没有差别 的,如果进程A和进程B打开同一文件,进程A写到内核I/O缓冲区中的数据从进程B也能读到,因为内核空间是进程共享的,
而c标准库的I/O缓冲区则不具有这一特性,因为进程的用户空间是完全独立的.(个人觉得这句话非常重要)

不带缓存的read和write是相对于 fread/fwrite等流函数来说明的,因为fread和fwrite是用户函数(3),所以他们会在用户层进行一次数据的缓存,而read/write是系统调用(2)所以他们在用户层是没有缓存的,所以称read和write是无缓存的IO,其实对于内核来说还是进行了缓存,不过用户层看不到罢了。

上面的内容介绍了库缓冲机制,其中也提到了内核缓冲区这个概念,到底内核缓冲存在的价值是很么呢:

为什么总是需要将数据由内核缓冲区换到用户缓冲区或者相反呢?

答:用户进程是运行在用户空间的,不能直接操作内核缓冲区的数据。 用户进程进行系统调用的时候,会由用户态切换到内核态,待内核处理完之后再返回用户态

应用缓冲技术能很明显的提高系统效率。内核与外围设备的数据交换,内核与用户空间的数据交换都是比较费时的,使用缓冲区就是为了优化这些费时的操作。其实核心到用户空间的操作本身是不buffer的,是由I/O库用buffer来优化了这个操作。比如read本来从内核读取数据时是比较费时的,所以一次取出一块,以避免多次陷入内核。

应用内核缓冲区的 主要思想就是一次读入大量的数据放在缓冲区,需要的时候从缓冲区取得数据。

管理员模式和用户模式之间的切换需要消耗时间,但相比之下,磁盘的I/O操作消耗的时间更多,为了提高效率,内核也使用缓冲区技术来提高对磁盘的访问速度。磁盘是数据块 的集合,内核会对磁盘上的数据块做缓冲。内核将磁盘上的数据块复制到内核缓冲区中,当一个用户空间中的进程要从磁盘上读数据时,内核一般不直接读磁盘,而 是将内核缓冲区中的数据复制到进程的缓冲区中。当进程所要求的数据块不在内核缓冲区时,内核会把相应的数据块加入到请求队列,然后把该进程挂起,接着为其 他进程服务。一段时间之后(其实很短的时间),内核把相应的数据块从磁盘读到内核缓冲区,然后再把数据复制到进程的缓冲区中,最后唤醒被挂起的进程。

注:理解内核缓冲区技术的原理有助于更好的掌握系统调用read&write,read把数据从内核缓冲区复制到进程缓冲区,write把数据从进程缓冲区复制到内核缓冲区,它们不等价于数据在内核缓冲区和磁盘之间的交换。

(编辑:衡阳站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读