出自:http://www.cppblog.com/mysileng/archive/2013/01/15/197284.html

今天在看UNP6.5节,学习到了select与stdio混用的后果。特此进程实验一番。再实验之前需明确一下几点:
1.stdio流的i/o函数 与 系统i/o函数不同。stdio流函数在用户空间和内核都有缓冲,系统i/o函数只在内核有缓冲,用户空间没有。

2.stdio流的i/o函数缓冲机制:在面对文件时候用的是全缓冲,面对设备的时候用的行缓冲。(等下试验用的是键盘和屏幕),所以实验用的stdio函数采用行行缓冲。

3.select函数对于某一个描述符是否准备好可读可写,是对内核缓冲区中的数据是否达到某一个最低标准,而不是用户缓冲区。也就是说select函数不知道用户缓冲区的存在。

首先写了一个系统i/o函数 简单的select函数程序:

     程序给select函数只设置的键盘的描述符。也就是说如果键盘的描述符准备好了就不再阻塞。但是这里有一个问题,解除阻塞后,我们最多只从内核缓冲区读3个字节,这个时候就会有两个情况:
(1)内核空间本来存储的数据就小于等于3个字节,全被读走。那下次再次调用select函数,应该肯定会阻塞的,因为键盘输入的内核缓冲区已经没有数据了。
情况如下(内核空间只有3个字节:1 2 \n):

(2)如果内核空间的数据多余3个字节,但是因为最多只能读3个字节,就必将导致内核中有数据读不完。那么下次再遇到select函数的时候是否会阻塞呢?
情况见下:

     当我们输入5个字符时候(1 2 3 4 \n),第一次read掉3个字符,内核空间还剩下2个字符,然后再次碰到select函数,默认情况下如果键盘内核空间字符数大于1,select是不会阻塞键盘描述符的。结果也印证了,又read了2个字节,并没有堵塞。
      综上所述select是可以看见内核空间的缓冲区的。那到底能不能看见用户空间缓冲区呢?我们换成stdio流的i/o函数继续实验。

--------------------------------------------------------------------
stdio流的i/o函数使用select函数的程序如下:

程序用stdio流的getc函数从键盘读数据,运行结果如下:

   我们输入5个字符(1 2 3 4 \n),结果只输出了1个字符,然后就阻塞了。我们分析一下,首先输入5个字符,这5个字符被放入用户缓冲区,因为最后一个是换行符并且stdio面对设备使用行缓冲机制,所以这5个字符马上接着被从用户缓冲区刷入内核缓冲区。然后调用select函数,select函数发现内核空间中有数据,于是不阻塞返回。接着getc函数从用户空间输出缓冲区取一个字符,因为用户空间输出缓冲区没有数据,于是把内核空间的数据调入一行给用户空间输出缓冲区,然后getc返回。接着又碰上select函数,因为内核缓冲空间的数据已经被放入用户空间输出缓冲区了,所以内核缓冲没有数据,那么select认为键盘没有准备好,所以阻塞。虽然阻塞了,但需要注意的时候这个时候,用户空间是有4个字符数据的,被select函数无视了。

     接下来假设我们再输入2个字符(1 \n),将会发生什么呢?

    输出一对东西,这是怎么回事,我们继续分析。当输入2个字符(1 \n)的时候,内核空间缓冲没有数据,用户空间输出缓冲有4个字符。2个字符根据上一段同样原理,被刷入内核空间缓冲区。select函数被调用,发现有2个字符,于是不阻塞返回。getc函数从用户输出缓冲取出一个字符,打印stardard... --2然后返回。再次循环,调用select函数。关键来了,这里跟上次不一样了。这个时候内核空间的缓冲中还有上次遗留的2个字符,所以依然不阻塞返回,调用getc函数继续打印。。。这里的关键是,getc函数。getc函数在用户输出缓冲中有数据的时候,不会把内核空间缓冲中的数据移入用户空间的输出缓冲,使得内核空间缓冲一直留有数据。这将会持续到用户空间输出缓冲的数据被取完为止。所以上述奇怪的打印结果就可以解释的了。
综上所述,select函数确实是看不见用户空间缓冲的寻在的。

所以如果在使用select函数的时候,要谨慎使用stdio流函数。

最新文章

  1. echo同时输出到多个文件中
  2. Monkeyrunner小脚本关于camera的使用
  3. Spark:一个高效的分布式计算系统
  4. C# 查处出现次数最多的元素
  5. .net System.Net.Mail 之用SmtpClient发送邮件 Demo
  6. [Design Pattern] Adapter Pattern 简单案例
  7. 基于visual Studio2013解决面试题之0308Fibonacci数列
  8. apk反编译方式
  9. String的split()方法可以将字符串按照特定的分隔符拆分成字符串数组
  10. Java框架之spring框架的优点,为什么要学习spring框架
  11. 图解HTTP第六章
  12. 约瑟夫环简介,问题以及java实现
  13. Idea设置行注释不显示在行首
  14. MongoDB 副本集 pymongo使用
  15. Gravitational Teleport简单使用
  16. iOS.Library.Architecture
  17. ORACLE创建用户,表空间,并且导出数据,导出表
  18. Maven变量及常见插件配置详解(转)
  19. spring data redis的配置类RedisConfig
  20. window.location属性用法及解决一个window.location.search为什么为空的问题

热门文章

  1. iOS应用架构谈-part2 view层的组织和调用方案
  2. 【转】如何在VC下检测当前存在的串口及串口热拔插
  3. NOIP2018 - 暑期博客整理
  4. unix cc编译过程
  5. python面试题解析(数据库和缓存)
  6. csrf 攻击及防御
  7. Leetcode 419.甲板上的战舰
  8. AtCoder Grand Contest 020
  9. 九度oj 题目1370:数组中出现次数超过一半的数字
  10. iOS学习笔记42-Swift(二)函数和闭包