关于unix网络编程第六章select与stdio混用会产生错误的原因解释
来源:互联网 发布:知乎周刊和知乎一样吗 编辑:程序博客网 时间:2024/05/19 21:15
unix网络编程第六章 I/O复用 select和poll函数第五节批量输入里提到了select和stdio混用会产生错误的后果。
看到一篇博文对这个现象的原理做了详细的解释,特地转发一下。原文地址: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流函数。
- 关于unix网络编程第六章select与stdio混用会产生错误的原因解释
- select函数与stdio混用的不良后果
- Unix 网络编程 select 与 epool 函数的区别
- UNIX网络编程卷一:第六章 I/O 复用 select, poll
- 《UNIX网络编程卷1》读书笔记--第六章I/O复用:select和poll函数
- 什么是运行期包与设计期包 (同时解释了产生 DesignIntf 错误的原因)
- unix网络编程-第六章-小结
- unix网络编程-第六章-小结
- unix 网络编程 select 讲解
- UNIX网络编程——epoll 系列函数简介、与select、poll 的区别
- unix网络编程之socket:epoll 系列函数简介、与select、poll 的区别
- 关于Unix网络编程
- 关于ORA-01000错误产生的原因和解决办法
- UNIX网络编程笔记 第六章 IO复用
- unix网络编程之利用select 实现群发的功能
- Linux/Unix网络编程 epoll和select/poll的对比
- 关于startService 与 bindservice的混用
- 段错误产生的原因
- JDK中设计模式
- 关于fastjson混淆打包后javaBean报空指针异常的解决方案
- 冒泡排序及其优化
- Caused by: java.lang.RuntimeException: Unable to instantiate org.apache.hadoop.hive.metastore.HiveMe
- arm开发板 libusb_open打开失败问题
- 关于unix网络编程第六章select与stdio混用会产生错误的原因解释
- excle 文件上传
- jquery实现拖拽、复制、放置
- Oracle.DataAccess.dll 下载 dll之家
- Cookie工具类
- [leetcode]15. 3Sum
- AlertDialog显示错误 Unable to add window token null is not for an application
- XRecyclerView下拉刷新和上拉加载
- 150盏亮着的电灯,各有一个拉线开关控制,编号为1~150 将编号为1~150倍数的灯,依次拉一下 问:拉完后亮着的灯数为几盏? 哪盏灯被拉的最多?