文档章节

unix环境-文件操作: 带缓冲I/O 和 不带缓冲I/O详解

follitude
 follitude
发布于 2016/09/22 08:36
字数 1728
阅读 57
收藏 0

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

首先要明白不带缓冲的概念:所谓不带缓冲,并不是指内核不提供缓冲,而是只单纯的系统调用,不是函数库的调用。系统内核对磁盘的读写都会提供一个块缓冲,当用write函数对其写数据时,直接调用系统调用,将数据写入到块缓冲进行排队,当块缓冲达到一定的量时,才会把数据写入磁盘。因此所谓的不带缓冲的I/O是指进程不提供缓冲功能。每调用一次write或read函数,直接系统调用。
而带缓冲的I/O是指进程对输入输出流进行了改进,提供了一个流缓冲,当用fwrite函数往磁盘写数据时,先把数据写入流缓冲区中,当达到一定条件,比如流缓冲区满了,或刷新流缓冲,这时候才会把数据一次送往内核提供的块缓冲,再经块缓冲写入磁盘。
因此,带缓冲的I/O在往磁盘写入相同的数据量时,会比不带缓冲的I/O调用系统调用的次数要少。

 

举例一:

讨论关于open,write等基本系统IO的带缓冲与不带缓冲的差别

“术语不带缓冲指的是每个read和write都调用了内核中的一个系统调用。所有的磁盘I/O都要经过内核的块缓冲(也称内核的缓冲区-高速缓存),唯一例外的是对原始磁盘设备的I/O。既然read或write的数据都要被内核缓冲,那么术语“不带缓冲的I/O“指的是在用户的进程中对这两个函数不会自动缓冲,每调用一次read或write就要进行一次系统调用。“--------摘自<unix环境编程>

      带缓存的文件操作是标准C库的实现,第一次调用带缓存的文件操作函数时标准库会自动分配内存并且读出一段固定大小的内容存储在缓存中(用户态进程空间的缓存)。所以以后每次的读写操作并不是针对硬盘上的文件直接进行的,而是针对内存中的缓存的(用户态进程空间的缓存)。不带缓存的文件操作通常都是系统调用, 更加低级,直接从硬盘中读取和写入文件(站在用户角度是直接从硬盘读写;在内核角度,不是直接从磁盘读写,因为系统内核默认有个块缓冲),由于IO瓶颈的原因,速度并不如意,而且原子操作需要程序员自己保证,但使用得当的话效率并不差。另外标准库中的带缓存文件IO 是调用不带缓存IO实现的(即fwrite的实现调用了write等)。

程序示例:

程序中用open和write打开创建并把“hello world“写入文件test1.txt,相应用fopen和fwrite操作文件test2.txt。程序执行到open和fopen之后,sleep15秒,这时用ls查看生成了文件没,这时用open打开的test1.txt出现了,但是fopen的test2.txt没有;当程序执行完write和 fwrite之后,fopen的test2.txt仍然没有出现(还是用ls查看),再用cat看test1.txt,可以看到 “helloworld”;最后再关闭test1.txt和test2.txt,这时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,world\n";
 if((fd=open("test1.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;
}

举例二:
用两个函数来讲讲unix系统下带缓存的I/O和不带缓存的I/O的区别。

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)

注意:所谓的带缓存并不是指上面两个函数的buff参数

    内核I/O,当将数据写到文件上时,内核先将该数据写到缓存,如果该缓存未满,则并不将其排入输出队列,直到缓存写满或者内核再次需要重新使用此缓存时才将其排入输入队列,待其到达队首,在进行实际的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字节,把50字节的数据写到文件,则需要2次系统调用(fwrite调用write系统调用),因为先把数据写到流缓存,当其满以后或者调用 fflush时才填入内核缓存,所以进行了2次的系统调用write。

    fflush将流所有未写的数据送入(刷新)到内核(内核缓冲区),fsync将所有内核缓冲区的数据写到文件(磁盘)。

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

详情请见:http://blog.sina.com.cn/s/blog_4a92ce12010004ub.html

本文转载自:http://blog.csdn.net/lmh12506/article/details/6803847

follitude
粉丝 6
博文 118
码字总数 4956
作品 0
浦东
私信 提问
带缓冲I/O 和 不带缓冲I/O详解

链接: http://blog.csdn.net/lmh12506/article/details/6803847 以下是我对这两者的理解: 首先要明白不带缓冲的概念:所谓不带缓冲,并不是指内核不提供缓冲,而是只单纯的系统调用,不是函...

huang19830104
2018/06/28
0
0
OS X下UNIX环境高级编程(第三版)学习日志-第一章ChapterI,输入和输出

文件描述符 通常是一个小的非负整数,也就是说0,1等 标准输入,标准输出和标准错误 standard input,standard output,standard error,在不做特殊处理的情况下,3个描述符都链接到终端,也就...

AlexTuan
2015/11/24
127
0
APUE: Standard I/O Library

流和FIFE对象 标准I/O文件流可以处理单字节和宽字节. 函数fwide用于设置流的方向: #include <stdio.h> #include <wchar.h> int fwide(FILE *fp, int mode); returns: 宽字节流则返回正数, 单......

fzyz_sb
2014/10/24
22
0
第15章 进程间通行 15.4 协同进程

(1)什么是协同进程? 当一个过滤程序既产生某个过滤程序的输入,又读取该过滤程序的输出时,它就变成了协同进程。 (2) 协同进程通常在shell的后台运行,其标准输入和标准输出通过管道连接...

fxdhdu
2015/10/17
55
0
转载-C语言FILE类型与标准I/O流

<cstdio> (stdio.h) - C++ Reference http://www.cplusplus.com/reference/cstdio/ 标准IO以及文件IO。 标准IO:标准I/O是ANSI C建立的一个标准I/O模型,是一个标准函数包和stdio.h头文...

zray4u
2016/07/15
47
0

没有更多内容

加载失败,请刷新页面

加载更多

图的拓扑排序(动图)

概述 对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。通常,...

大洼X
21分钟前
5
0
CentOS7安装Redis和搭建Redis高可用集群

一、CentOS7下Redis安装 1.查看linux内核版本 此步骤可以省略,一般CentOS7内核版本都应该满足,因为搭建Redis高可用集群是Redis3以上才支持的,而Redis3需要linux内核高于3.10版本,所有才有...

liddblog
23分钟前
4
0
活动泄漏了最初添加的窗口

这是什么错误,为什么会发生? 05-17 18:24:57.069: ERROR/WindowManager(18850): Activity com.mypkg.myP has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@44c4......

技术盛宴
23分钟前
4
0
IT兄弟连 HTML5教程 DIV+CSS的兼容性问题

使用DIV+CSS布局网页其实是很容易的事情,但各种浏览器之间的不兼容性问题,加大了页面布局的难度,给程序员带来很多不便,于是需要花费更多的时间在调试各种浏览器的兼容性上。因为部分CSS...

老码农的一亩三分地
26分钟前
4
0
商家发货

商家发货 商家发货 打开后台-订单-订单列表 1.点击筛选出待发货的订单可进行批量操作。批量发货、批量打印快递单、批量打印发货单。 2.点击发货时,弹出框显示待发货的商品,发货方式可选需要...

Geek-Chic
27分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部