Chinaunix首页 | 论坛 | 博客
  • 博客访问: 660282
  • 博文数量: 171
  • 博客积分: 2246
  • 博客等级: 大尉
  • 技术积分: 1574
  • 用 户 组: 普通用户
  • 注册时间: 2012-05-31 11:45
文章分类

全部博文(171)

文章存档

2018年(3)

2017年(4)

2015年(1)

2014年(20)

2013年(57)

2012年(86)

分类: LINUX

2013-09-26 16:37:15

缓冲区

作用: 在实际编程中,I/O速度取决于显示器、键盘、硬盘等I/O设备的性能,而这些设备比起CPU和内存是比较
            慢的。因此系统采用缓冲区
的方式来减少I/O的读写,以便提高系统性能。

I/O的缓冲区的种类:1、无缓冲;2、行缓冲;3、全缓冲。

一、行缓冲:

看一个例子:

  1. #include   
  2.   
  3. int main(void)  
  4. {  
  5.     printf("123\n456");  
  6.     while(1){}  
  7.     return 0;  
  8. }  

结果:

  1. 123  
  2.    
这段代码只输出了“123\n”而没有输出“456”。原因是标准I/O:stdin,stdout是行缓冲。

行缓冲的特性是:C标准输出先写到行缓冲区里,当遇到下列四种情况才一次性把行缓冲区的数据写到I/O设备里去:

1、遇到\n字符;

2、行缓冲区(linux默认大小为1024字节)被填满后。

3、调用冲洗缓冲区的函数:fflush等。

4、进程返回、调用exit退出、文件流关闭等。

上例中printf的作用是把"123\n456" 依次写入缓冲区,由于中途遇到\n,因此立即会把缓冲区里的所有数据——"123\n"写入I/O设备(此处是屏幕),接着的是把“456”写到缓冲区 里。“456”之后没有“\n”、缓冲区又没有满、程序死循环在while(1)里没有结束,因此“456”将永远不会输出到I/O设备里。

属于行缓冲的I/O设备通常是需要交互的I/O:键盘(默认的stdin),屏幕(默认的stdout)等。

二、全缓冲

还是上面的代码(假设生成的执行程序名为“a.ex”,并且目录下有个“b.txt”的文本),如果这样启动:

  1. $ a.ex > b.txt  
那么程序将不会输出任何字符到b.txt中。

因为对于文件(文件是在硬盘上的),硬盘I/O属于全缓冲。

缓冲的特性是:C标准输出先写到缓冲区里,当遇到下列三种情况才一次性把缓冲区的数据写到I/O设备里去:

1、缓冲区被填满后。

2、调用冲洗缓冲区的函数:fflush等。

3、进程返回、调用exit退出、文件流关闭等。

上例中启动a.ex程序时,用重定向符号“>”将stdout重定向到b.txt里。由于b.txt是文件,属于硬盘I/O,所以满足全缓冲的写入条件。

属于全缓冲的I/O设备有:硬盘等。

三、无缓冲

  1. #include   
  2. #include   
  3. int main(void)  
  4. {  
  5.     write(STDOUT_FILENO,"123\n456",7);  
  6.     while(1){}  
  7.     return 0;  
  8. }  
结果是:
  1. 123  
  2. 456  
没错,无缓冲会直接将字符串写入I/O设备里去。

write写文件或者屏幕等设备都是无缓冲的;stderr无论重定向到哪里都是无缓冲的。


四、最后补充一些注意:

  1. #include   
  2. #include   
  3. int main(void)  
  4. {  
  5.     printf("123\n456");  
  6.     _exit(0);  
  7. }  
结果是:
  1. 123  
  2.    
因为unix系统中_exit 函数并不冲洗缓冲区。

关于行缓冲

转载请注明出处:http://blog.csdn.net/footman_/article/details/7163666

       标准I/O库是带缓冲的,而stdin和stdout默认都是行缓冲。行缓冲只有在遇到行结束符’\n’时才会进行一次实际的I/O操作(当然,如果缓冲区被填满时,即使没有遇到’\n’也会进行实际的I/O操作)。

         下面看一个例子:

  1. #include   
  2. #include   
  3. #include   
  4.   
  5. void main()  
  6. {  
  7.  pid_t pid;  
  8.  char buf[] = "hello wrold!\n";  
  9.    
  10.  write( STDOUT_FILENO, buf, sizeof(buf)-1 );  
  11.   
  12.  if((pid = fork()) < 0)  
  13.  {  
  14.    printf("fork error\n");  
  15.  }  
  16.  else if(pid == 0)  
  17.  {  
  18.    printf("child\n");  
  19.  }  
  20.  else  
  21.  {  
  22.    printf("parent\n");  
  23.  }  
  24.  exit(0);  
  25. }  

输出结果如下,由于write在fork前,所以只会被执行一次。

将write( STDOUT_FILENO,buf, sizeof(buf)-1 );修改为printf("%s",buf);执行结果也一样。

但如果去除char buf[] ="hello world!\n";中的’\n’,执行write( STDOUT_FILENO, buf, sizeof(buf)-1 );呢?

执行结果如下,“hello world!”还是只执行了一次,只不过没有换行而已。

此时将write改为printf("%s",buf),执行后就会出现不同的结果:

从结果中看出,为什么printf("%s",buf);执行了两次,不是说子进程只会执行fork后的代码吗?

         原因就出在那个’\n’上,由于wirte函数是不带缓冲的,fork之前调用wirte数据就写到标准输出。但是标准I/O库是带缓冲的,且如果标准输出连接到终端设备则缓冲是行缓冲,也就是说当遇到行尾标志’\n’时才会进行一次实际的I/O操作。

         所以fork之前执行printf,由于没有’\n’,所以数据仍在缓冲区中,调用fork后,内核将父进程数据空间复制到子进程中,且缓冲区也被复制到 子进程中,此时的父子进程各有一个带行内容的标准I/O缓冲区,进程终止时,最终会冲洗缓冲区中的数据。

阅读(737) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~