一个例子叫你理解缓冲输入与非缓冲输入,以及流的概念:论read与fget open与fopen的区别

来源:互联网 发布:红蜻蜓截图软件 编辑:程序博客网 时间:2024/05/16 11:39

首先讲一下基本概念

本文假设你已经基本了解标准IO库函数fopen fgets setbuf等和非缓冲IO库函数open write read lseek等。这里只就他们的区别加以阐述,并以最简单的程序实现他们之间差异的体现。让你理解流,缓冲,非缓冲的概念。

 

一、缓冲输入与非缓冲输入。

fopen,fgets等属于缓冲输入,open,write,read系列属于非缓冲。

 

APUE上的解释:

unbuffered IO是指在每个read和write都调用内核中的一个系统调用,他们不是ISO C的组成部分,但属于POSIX.1

标准IO库兼容各种系统,是带缓冲的输入输出。属于高级库。由ISO C标准定义,在linux上还做了一点扩展,实现了部分只支持linux的接口 比如 fdopen。

 

从网上得到的一些值得参考的解释:

       1. 缓冲文件系统
       缓冲文件系统的特点是:在内存开辟一个“缓冲区”,为程序中的每一个文件使用,当执行读文件的操作时,从磁盘文件将数据先读入内存“缓冲区”, 装满后再从内存“缓冲区”依此读入接收的变量。执行写文件的操作时,先将数据写入内存“缓冲区”,待内存“缓冲区”装满后再写入文件。由此可以看出,内存 “缓冲区”的大小,影响着实际操作外存的次数,内存“缓冲区”越大,则操作外存的次数就少,执行速度就快、效率高。一般来说,文件“缓冲区”的大小随机器 而定。

     非缓冲文件系统
缓冲文件系统是借助文件结构体指针来对文件进行管理,通过文件指针来对文件进行访问,既可以读写字符、字符串、格式化数据,也可以读写二进制数 据。非缓冲文件系统依赖于操作系统,通过操作系统的功能对文件进行读写,是系统级的输入输出,它不设文件结构体指针,只能读写二进制文件,但效率高、速度 快,由于ANSI标准不再包括非缓冲文件系统,因此建议大家最好不要选择它。open, close, read, write, getc, getchar, putc, putchar 等。
 (这是一个文件系统层面的缓冲概念)

    

2. open返回一个文件描述符,文件描述符是linux下的一个概念,linux下的一切设备都是以文件的形式操作.如网络套接字、硬件设备等。当然包括操作文件。
fopen是标准c函数。返回文件流而不是linux下文件句柄。

 

3. 设备文件不可以当成流式文件来用,只能用open
fopen是用来操纵正规文件的,并且设有缓冲的,跟open还是有一些区别

一般用fopen打开普通文件,用open打开设备文件

fopen是标准c里的,而open是linux的系统调用.
他们的层次不同.
fopen可移植,open不能。

 

4. fopen和open最主要的区别是fopen在用户态下就有了缓存,在进行read和write的时候减少了用户态和内核态的切换,而open则每次都需要进行内核态和用户态的切换;表现为,如果顺序访问文件,fopen系列的函数要比直接调用open系列快;如果随机访问文件open要比fopen快。

这一回答将fopen 与open的区别解释的非常到位


5.区别总结

open,fopen

前者属于低级IO,后者是高级IO。
前者返回一个文件描述符(用户程序区的),后者返回一个文件指针。
前者无缓冲,后者有缓冲。
前者与 read, write 等配合使用, 后者与 fread, fwrite等配合使用。
后者是在前者的基础上扩充而来的,在大多数情况下,用后者。


在linux下fopen函数系列的实现是对open系统调用系列的封装。

 

     以键盘输入为例,过程是这样的:  

    键盘中断 -> driver与硬件交互获取键值 -> driver将键值上报给kernel -> kernel 通过缓冲队列的形式(linux下应该是input子系统)管理得到的消息,并上报给应用层 ->  系统调用 读取到键值,存入文件或者显示到屏幕上。

二、fgets,read

由于fgets函数比较个性,还是要稍微介绍一下他的特殊之处:char *fgets(char *buf, int bufsize, FILE *stream);

1、他是操作文件流的函数 *FILE stream

2、每次最多能读取bufsize - 1个字符,然后在最后还会加上'\0'

3、每次读取最多一行,且换行符'\n'会被存入buff中,也就是说读取一行时 buffsize大于strlen + 2 (‘\n’‘\0’)

read函数很单纯哒,我给一个原型就好啦,剩下的自己去看manual吧

int read(int handle, void *buf, int nbyte);

1、操作文件描述符fd的函数

 

三、fflush,rewind,setbuf,lseek

前三个是操作流的,lseek是操作文件描述符的。

 

代码分析:(需要加载的头文件我都不写了,自己添加)

代码1:

int main (void)

{

char buff[10];

read(0,buff,5);//标准输入的文件描述符为0

write(1,buff,5);//  标准输出的文件描述符为1

exit(0);

}

输入: 12345678回车  //输入超过了read的size

输出:12345

usename@usergroup:~$678

678: command not found

解释:首先write是正常工作了的,buff内存储的是{1,2,3,4,5后面是空的}

但是在标准输入中还缓存着 678\n

程序结束后,缓存会被清空(冲洗 flush)678\n这几个字符就会在程序结束时强制输出到terminal中也就成了你输入了一个命令678,但这并不是命令。


改进代码1

int main (void)

{

char buff[10];

read(0,buff,5);//键盘输入的文件描述符为0

write(1,buff,5);//  显示屏输出的文件描述符为1

//fflush(stdin);

//rewind(stdin);

//setbuf(stdin,NULL);

char a = getchar();

putchar(a);

//read(0,buff,5); //特别注意如果这里再加上一个read 那么,在第一次输入后程序不会结束,会阻塞直到再次输入,这是因为标准输入流已经把kernal那里的东西全都读到ISO C层的buff内了,所以read这种不能从ISO层buff得到数据的非缓冲函数就无法获得数据了,只有阻塞直到新的数据输入。把这个小程序熟悉一下,你基本就明白流和文件描述符,缓冲与非缓冲输入的区别了。

exit(0);

}

输入: 12345678回车  //输入超过了read的size

输出:123456usename@usergroup:

分析被注释掉的fflush,rewind,setbuf都是可以清空缓存的函数,但如果此处使用,不会有任何作用,因为这几个函数是针对文件流的缓存,即构建在系统调用层之上的缓存,目的是提前预加载文件减少系统调用次数,内核实现的键盘输出缓存并不在他们可操作范围之内,read是没有缓存的,他是直接的系统调用。而getchar是有缓存的函数,调用getchar后ISO C层会以文件流的形式打开打开标准输入文件,接收标准输入传来的数据,存入文件流的缓存中,所以这时标准输入那边的缓存就空了。而ISO层的缓存是系统调用层之上的缓存,所以在程序结束时释放缓存是不会在终端打印字符的。当然如果是以清除缓存为目的显然getchar没有达到目的,应该写为while((a = getchar() != EOF) && a != '\n')

改进代码3:

了解了缓冲输入与非缓冲输入的区别后我们知道还可以把read改写成fgets这样一来setbuf就可以清除缓存了(当然这时不清除缓存也不会出现将多余字符输出到命令行中的情况了),但是fflush和rewind还是不能用,为什么呢?因为这里是linux。。。。如果是在windows下是可以的。这只是标准实现时的区别,不要在意这些细节。注意sefbuf(stdin,NULL)并不是清空缓存这么简单而是将stdin设置为无缓存模式。。。所以虽然有清除缓存的功能,但是其实并不是清除缓存函数。

int main (void)

{

char buff[10];

fgets(buff,5,stdin);//键盘输入的文件描述符为0

write(1,buff,5);//  显示屏输出的文件描述符为1

//fflush(stdin);

//rewind(stdin);

setbuf(stdin,NULL);

exit(0);

}

输入: 12345678回车  //输入超过了read的size

输出:1234usename@usergroup:

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 沁园净水机不制水指示灯不亮怎么办 太辣了辣得胃疼怎么办 出现连接问题或mmi码无效怎么办 存折丢了怎么办卡号也不记得了 车内皮子被烂苹果腐蚀有印怎么办 锅被腐蚀后变黑色应该怎么办 后厨炉灶里的炉芯进水了怎么办 小儿九个月老是流黄鼻子该怎么办 肉炖的老了不烂怎么办 吃了凉东西现在一直打嗝应该怎么办 喝了很多水还是觉得口渴怎么办 刚买的猪肝没洗直接炒了怎么办 四个多月的宝宝吃了脏东西怎么办 狗吃了脏东西拉稀呕吐怎么办 五个月宝宝怕吃药导致奶不喝怎么办 蒸锅锅盖吸住了怎么办锅比锅盖要大 豇豆没熟孕妇吃了中毒怎么办 孩子积食拉不出粑粑憋的直哭怎么办 2岁宝宝总是半夜拉粑粑怎么办 金毛拉很臭的稀粑粑怎么办 点餐系统登录后没有菜单怎么办? 环亚在线微交易亏了钱怎么办 钢管舞报了教练班觉得学不会怎么办 微信上聊天被外国人给骗了该怎么办 微信冒充朋友骗走我钱怎么办 凉皮调料水鸡精味精放多了怎么办 吃了地屈孕酮后月经不干不净怎么办 藕片用热水炒后变色了怎么办? 外汇延期收款忘了报告了怎么办 怀孕不小心吃了马生菜怎么办 高压锅的皮圈很容易坏是怎么办 华为应用市场账号密码忘记了怎么办 业主对我们提出批评意见时怎么办 向环保局投诉被公司发现了怎么办 在政务大厅上班被群众投诉怎么办 政府下发的文件通知不履行该怎么办 给私人老板开车不给工资怎么办 给个体老板开车不给工资怎么办 户口转走在人才市场的档案怎么办 外来媳妇转上海户口没有档案怎么办 公务员考试笔试差9分面试怎么办