UNIX环境高级编程---标准I/O库(一)【原】


UNIX环境高级编程---标准I/O库

在前面《UNIX环境高级编程----文件描述符浅析》一文中所讲的I/O函数都是针对文件描述符。而对于标准I/O库,它们的操作都是围绕流来进行的。当用标准I/O库打开或创建一个文件时,我们已经使一个流与文件相结合。

一、流和FILE对象

当打开一个流时,标准I/O函数fopen返回一个指向FILE对象的指针。该对象通常是一个结构,它包含了I/O库为管理该流所需要的所有信息:用于实际I / O的文件描述符,指向流缓存的指针,缓存的长度,当前在缓存中的字符数,出错标志等等。

应用程序没有必要检验FILE对象。为了引用一个流,需将FILE指针作为参数传递给每个标准I/O函数。在《UNIX环境高级编程》一书中,我们称指向FILE对象的指针(类型为FILE*)为文件指针。

二、标准I/O库的缓存

标准I/O提供缓存的目的是尽可能少的使用read和write调用量,从而加速对文件的读和写操作。但是不幸的是标准I/O库最让人迷惑的恰好也是它的缓存。为了详细说明缓存的机制,必须先了解下为什么有了这个缓存就能提供文件的操作效率。

用户程序调用标准I/O库函数读写文件,而这些库函数要通过系统调用把读写请求传给内核,最终由内核驱动磁盘或设备完成I/O操作。标准I/O库为每个打开的文件分配一个I/O缓冲区以加速读写操作,通过文件的FILE结构体可以找到这个缓冲区,用户调用读写函数大多数时候都在I/O缓冲区中读写,只有少数时候需要把读写请求传给内核。以fgetc/fputc为例,当用户程序第一次调用fgetc读一个字节时,fgetc函数可能通过系统调用进入内核读1K字节到I/O缓冲区中,然后返回I/O缓冲区中的第一个字节给用户,把读写位置指向I/O缓冲区中的第二个字符,以后用户再调fgetc,就直接从I/O缓冲区中读取,而不需要进内核了,当用户把这1K字节都读完之后,再次调用fgetc时,fgetc函数会再次进入内核读1K字节到I/O缓冲区中。标准I/O库之所以会从内核预读一些数据放在I/O缓冲区中,是希望用户程序随后要用到这些数据,标准I/O库的I/O缓冲区也在用户空间,直接从用户空间读取数据比进内核读数据要快得多。另一方面,用户程序调用fputc通常只是写到I/O缓冲区中,这样fputc函数可以很快地返回,如果I/O缓冲区写满了,fputc就通过系统调用把I/O缓冲区中的数据传给内核,内核最终把数据写回磁盘。有时候用户程序希望把I/O缓冲区中的数据立刻传给内核,让内核写回设备,这称为Flush操作,对应的库函数是fflush,fclose函数在关闭文件之前也会做Flush操作。

下图一以fgets/fputs示意了I/O缓冲区的作用,使用fgets/fputs函数时在用户程序中也需要分配缓冲区(图中的buf1和buf2),注意区分用户程序的缓冲区和C标准库的I/O缓冲区。

图一 I/O缓存区

标准I/O库提供了三种类型的缓存:

1) 全缓存:如果缓冲区写满了就写回内核。常规文件通常是全缓冲的。

2) 行缓存:如果用户程序写的数据中有换行符就把这一行写回内核,或者如果缓冲区写满了就写回内核。标准输入和标准输出对应终端设备时通常是行缓冲的。

行缓存有两个限制:

第一个是行缓存区的缓存长度是固定,系统一般默认为1K,所以只要行缓存区满了,即使没有写一个新换行符,系统也会执行I/O操作;关于这一点,可以从下面的例子看出来。

第二个是任何时候只要通过标准输入输出库要求从( a )一个不带缓存的流,或者( b )一个行缓存的流(它预先要求从内核得到数据)得到输入数据,那么就会造成刷新所有行缓存输出流。

Example 01.c

#include<stdio.h>

int main()

{

printf("Hello World");

Whlie(1);

return 0;

}

编译执行时会发现终端什么都没有输出。如果把whlie(1)去掉,就会在终端打印出Hello World。

Example 02.c

#include<stdio.h>

int main()

{

printf("Hello World\n");

Whlie(1);

return 0;

}

编译执行时会发现终端印出Hello World。

Example 03.c

#include<stdio.h>

int main()

{

printf("Hello World ...Hello World");//...代表1024-11*2个字节

Whlie(1);

return 0;

}

编译执行时会发现终端打印出Hello World ...Hello World。以上三个例子足以说明行缓存类型的缓存区长度是固定的。写入缓存区的数据为换行符或长度超过缓存区长度时系统会执行I/O操作。

3) 不带缓存:用户程序每次调库函数做写操作都要通过系统调用写回内核。标准错误输出通常是无缓冲的,这样用户程序产生的错误信息可以尽快输出到设备。

对于任何一个流,如果我们不喜欢这些系统默认,可以通过调用下面两个函数中一个来更改缓存类型

----------------------------------------------------------------------------------------------------------------------

void setbuf(FILE *fp, char *buf) ;

int setvbuf(FILE *fp, char *buf, int mode, size_t size) ;

返回:若成功则为0,若出错则为非0

----------------------------------------------------------------------------------------------------------------------

下图二是setbuf和setvbuf函数各选项说明,可以明显看出函数setvbuf功能更强大一些。

图二 setbuf和setvbuf函数各选项说明

http://hi.baidu.com/uu_dou/item/14da243b12c22c48023edcfb
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值