`
gqh36gqh
  • 浏览: 16054 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

linux文件缓冲区

 
阅读更多

linux文件缓冲区
2011年01月27日
  一:缓冲区机制
  根据应用程序对文件的访问方式,即是否存在缓冲区,对文件的访问可以分为带缓冲区的操作和非缓冲区的文件操作:
  缓冲区文件操作:高级文件系统,将在用户空间中自动为正在使用的文件开辟内存缓冲区。
  非缓冲区文件系统:低级文件系统,如果需要,只能由用户在自己的程序中为每个文件设定缓冲区。
  如果采用非缓冲的文件访问方式,每次对该文件进行一次读写操作时,都需要使用读写文件系统掉用来处理该操作,因此,如果用户需要访问某个磁盘文件,则每访问一次都要执行一次系统调用,执行一次系统调用将涉及到CPU状态的切换,即从用户空间切换到内核空间,实现上下文的切换,这将损耗一定的CPU时间,频繁的磁盘访问对程序的执行效率造成很大的影响。
  ANSI标准C库函数建立在底层系统调用之上,即C函数库文件访问函数的实现中使用了低级文件I/O系统调用,ANSI标准C库中的文件处理函数为了减少使用系统调用的次数,提高效率,根据应用的不同,采用缓冲区机制,这样,在对磁盘文件进行读操作时,可以一次从文件中读出大量的数据到缓冲区中,以后对这部风的访问就不需要在使用系统调用了,即只需要少量的CPU状态切换。在对文件进行写操作时,可以先将内容存在缓冲区,待文件写满后,或者确实需要更新时在调用系统调用将文件一次写入到磁盘中。
  二:缓冲区类型
  标准 I/O 提供了 3 种类型的缓冲区。
  1,全缓冲区:这种缓冲区要求填满整个缓冲区后才进行 I/O 系统调用操作。对于磁盘
  文件通常使用全缓冲区访问。第一次执行 I/O 操作时,ANSI 标准的文件管理函数通过调用
  malloc 函数获得需使用的缓冲区。默认大小为 8192。
  //come from /usr/include/stdio.h
  /* Default buffer size. */
  #ifndef BUFSIZ
  # define BUFSIZ _IO_BUFSIZ //BUFSIZ 全局宏定义
  #endif
  //come from /usr/include/libio.h
  #define _IO_BUFSIZ _G_BUFSIZ
  //come from /usr/include/_g_config.h
  #define _G_BUFSIZ 8192 //真实大小
  刷新(flush)操作即标准 I/O 缓冲区的写操作。它可以由系统自动完成(当填满一个缓
  冲区时)或者调用函数 fflush()或 flush()实现。
  2,行缓冲区:在这种情况下,当在输入和输出中遇到换行符时,标准 I/O 库执行 I/O
  系统调用操作。当流涉及一个终端时(例如标准输入和标准输出),使用行缓冲区。因为标准I/O 库收集的每行的缓冲区长度是固定的,只要填满了缓冲区,即使还没有遇到换行符,也将执行 I/O 系统调用操作。默认行缓冲区大小为 128 字节。
  3,无缓冲区:标准 I/O 库不对字符进行缓存。如果用标准 I/O 函数写若干字符到不带
  缓冲区的流中,则相当于用 write 系统调用函数将这些字符写至相关联的打开文件。标准出错流 stderr 通常是不带缓冲区的,这使得出错信息能够尽快地显示出来。
  对于标准输入输出设备,ANSI C 要求具有以下缓冲区特征:
  l 标准输入和标准输出设备:当且仅当不涉及交互作用设备时,标准输入流和标准输
  出流才是全缓冲区的。
  l 标准错误输出设备:标准出错决不会是全缓冲区的。
  对于任何一个给定的流,可以调用 setbuf()和 setvbuf()函数更改其缓冲区类型。
  1, 名称:: setbuf 功能: 更改文件流的缓冲区位置 头文件: #inlcude  函数原形: void setbuf (FILE * restrict  stream, char * restrict  buf)  参数: stream 要操作的流对象
  buf 指定的缓冲区 返回值: 若成功则返回0,若出错则为非0 此函数第一个参数为要操作的流对象,第二个参数 buf 必须指向一个长度为 BUFSIZ 的
  缓冲区。如果将 buf 设置为 NULL,则关闭缓冲区。
  如果执行成功,将返回 0,否则返回非 0 值。
  2, 名称:: setvbuf 功能: 更改文件流的缓冲区位置 头文件: #inlcude  函数原形: int setvbuf (FILE * restrict  stream, char *restrict  buf, int modes,size_t  n)  参数: stream 要操作的流对象
  buf 指定的缓冲区
  mode 缓冲区类型
  n  buf的大小 返回值: 若成功则返回0,若出错则为非0 /* Make STREAM use buffering mode MODE. If BUF is not NULL, use N bytes of it for buffering;else allocate an internal buffer N bytes long. */
  此函数第一个参数为要操作的流对象;第二个参数 buf 必须指向一个长度为 BUFSIZ 的
  缓冲区;第三个参数为缓冲区类型,分别定义如下:
  //come from /usr/include/stdio.h
  /* The possibilities for the third argument to 'setvbuf'. */
  #define _IOFBF 0 /* Fully buffered. */ //全缓冲
  #define _IOLBF 1 /* Line buffered. */ //行缓冲
  #define _IONBF 2 /* No buffering. */ //无缓冲
  第四个参数
  为该 buf 的大小。如果指定一个不带缓冲区的流,则忽略 buf 和 size 参数。
  如果指定全缓冲区或行缓冲区,则 buf 和 size 可选择地指定一个缓冲区及其长度。如果指定该流是带缓冲区的,而 buf 是 NULL,则标准 I/O 库将自动为该流分配适当长度的缓冲适当长度指的是由文件属性数据结构(struct stat)的成员 st_blksize 所指定的值,如果系统不能为该流决定此值(例如若此流涉及一个设备或一个管道),则分配长度为 BUFSIZ 的缓冲区。
  此函数如果执行成功,将返回 0,否则返回非 0 值。
  以下是一个修改 buf 大小写文件的实例程序。其源代码如下:
  int main( int argc , char ** argv ) { int i; FILE * fp; char msg1[]="hello,wolrd\n"; char msg2[] = "hello\nworld"; char buf[128]; /*打开(或者创建)一个文件,然后使用 setbuf 设置为 nobuf 情况,并检查关闭前流的情况*/ if(( fp = fopen("no_buf1.txt","w")) == NULL) { perror("file open failure!"); return(-1); } setbuf(fp,NULL); //设置为无 buf fwrite( msg1 , 7 , 1 , fp ); //写内容 printf("test setbuf(no buf)!check no_buf1.txt\n"); //查看 buf 情况 printf("press enter to continue!\n"); getchar(); fclose(fp); //关闭流,因此将回写 buf(如果有 buf 的话) /打开(或者创建)一个文件,然后使用 setvbuf 设置为 nobuf 情况,并检查关闭前流的情况*/ if(( fp = fopen("no_buf2.txt","w")) == NULL) { perror("file open failure!"); return(-1); } setvbuf( fp , NULL, _IONBF , 0 ); //设置为无 buf fwrite( msg1 , 7, 1 , fp ); //写内容 printf("test setvbuf(no buf)!check no_buf2.txt\nbecause line buf, only before enter data write\n"); printf("press enter to continue!\n"); getchar(); fclose(fp); //关闭流,因此将回写 buf(如果有 buf 的话) /*打开(或者创建)一个文件,然后使用 setvbuf 设置为行 buf 情况,并检查关闭前流的情况*/ if(( fp = fopen("l_buf.txt","w")) == NULL) { perror("file open failure!"); return(-1); } setvbuf( fp , buf , _IOLBF , sizeof(buf) );//设置为行 buf fwrite( msg2 , sizeof(msg2) , 1 , fp ); //写内容 printf("test setvbuf(line buf)!check l_buf.txt, because line buf , only data before enter send to file\n"); printf("press enter to continue!\n"); getchar(); fclose(fp); //关闭流,因此将回写 buf //打开(或者创建)一个文件,然后使用 setvbuf 设置为全 buf 情况,并检查关闭前流的情况 if(( fp = fopen("f_buf.txt","w")) == NULL) { perror("file open failure!"); return(-1); } setvbuf( fp , buf , _IOFBF , sizeof(buf) ); for(i = 0 ; i { fputs( msg1 , fp ); } printf("test setbuf(full buf)!check f_buf.txt\n"); printf("press enter to continue!\n"); getchar(); fclose(fp); //关闭流,因此将回写 buf }    其编译过程及运行结果如下:
  [root@localhost linux_app]# ./setbuf_example
  test setbuf(no buf)!check no_buf1.txt//在按回车键前需要先查看当前目录下的 no_buf1.txt 内容
  press enter to continue!
  test setvbuf(no buf)!check no_buf2.txt//在按回车键前需要先查看当前目录下的 no_buf2.txt 内容
  because line buf, only before enter data write
  press enter to continue!
  test setvbuf(line buf)!check l_buf.txt, because line buf ,only data before enter send to file
  press enter to continue! //在按回车键前需要先查看当前目录下的 l_buf.txt 内容
  test setbuf(full buf)!check f_buf.txt
  press enter to continue! //在按回车键前需要先查看当前目录下的 f_buf.txt 内容
  以下内容是在过程中查看各文件内容信息:
  [root@localhost linux_app]# cat no_buf1.txt
  hello,w //写入的 7 个字符全部写入到文件中
  [root@localhost linux_app]# cat no_buf2.txt
  hello,w //写入的 7 个字符全部写入到文件中
  [root@localhost linux_app]# cat l_buf.txt
  hello //只有回车前的字符写入
  [root@localhost linux_app]# cat f_buf.txt //没有任何内容写入
  运行过程完成后:
  [root@localhost linux_app]# cat no_buf1.txt
  hello,w //与原来内容一样
  [root@localhost linux_app]# cat no_buf2.txt
  hello,w //与原来内容一样
  [root@localhost linux_app]# cat l_buf.txt
  hello
  world //因为调用了 fclose()函数,刷新了缓冲区,将 world 写入
  [root@localhost linux_app]# cat f_buf.txt
  hello
  world //因为调用了 fclose()函数,刷新了缓冲区,将 hello\nworld 都写入
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics