select函数与stdio混用的不良后果
2017-12-01 20:47
127 查看
原文地址: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流函数。
今天在看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流函数。
相关文章推荐
- select函数与stdio混用的不良后果 (转)
- select与stdio混合使用的不良后果
- 关于unix网络编程第六章select与stdio混用会产生错误的原因解释
- <iostream>和<stdio.h>一定不要混用
- 【java】 java 中stop方法终止线程的不良后果
- 突然断电或强制关机的不良后果
- 管理:制度化管理的5种不良后果
- MD和MT混用的后果
- 每天用电脑超过四小时的不良后果
- border标签造成的不良后果
- 19种不良生活 你占了几条
- 注意:以下文章只为各位DOTNET爱好者个人学习或研究使用,不得用于任何商业用途,否则后果自负
- 四六级阅读理解不良习惯
- 使大脑迟钝的9种不良习惯
- 误删了rundll32.exe文件导致的后果
- php bug? php的exit函数和apache的mod_include混用导致的问题
- 不良站点名单(续一) 推荐
- 我朋友被富婆包的后果(巨搞笑)
- Silverlight.1.0.R.C.发布后,让Visual Stdio Codename Orcas继续
- 发错短信的后果