转自http://my.oschina.net/dclink/blog/287198
1、流的概念
一个流可以是文件,socket,pipe等可以进行I/O操作的内核对象
不管是文件还是套接字还是管道,都可以看作是流
2、I/O操作
通过read,我们可以从流中读入数据,通过write,我们可以往流写入数据。
现在假设一个情况,我们要从流中读数据,但是流中还没有数据(典型的例子是clientsocket要从serversocket中读取返回数据,但是服务器还没有把数据返回),怎么办呢?这时候就产生了阻塞。
3、阻塞
阻塞:你去餐厅点饭,但是饭没好,需要你等待,还不能走,一直等。。。。
非阻塞盲轮询:你急着要吃饭,你一分钟去问下老板,饭好了没?然后一直问。。。
为了解决阻塞是如何进行的,需要讨论缓冲区,以及内核缓冲区
4、缓冲区及内核缓冲区
缓冲区:引入进来为了减少频繁的I/O操作而引起频繁的系统调用,当你操作一个流时,更多是以缓冲区为单位进行操作,这是相对于用户而言,对于内核而言,也需要缓冲区。
假设有一个管道,进程A为管道写入方,B为管道读出方
(1)缓冲区非空
假设一开始内核缓冲区是空的,B作为读取方,被阻塞了,然后A往管道内写数据,这时候内核缓冲区由空变为非空,内核就产生一个事件告诉B该醒了,来读数据了,这个事件为“缓冲区非空”
(2)缓冲区满
但是“缓冲区非空”事件通知B后,B还没有读出数据,且内核许诺了不能把写入管道中的数据丢掉这个时候,A写入的数据会滞留在内核缓冲区中,如果内核也缓冲区满了,B扔未开始读数据,最终内核会被填满,这时候产生一个I/O事件,告诉进程A,你该等待阻塞了,我们把这个事件定义为“缓冲区满”。
(3)缓冲区非空
假设最后B终于来读数据了,于是内核的缓冲区空了出来,这时候内核会告诉A,内核缓冲区有空位了,你可以从长眠中醒来了,继续写数据,我们把这个事件叫做“缓冲区非满”。
(4)缓冲区空
当事件通知了A,但是A没有数据写入了,而B继续读出数据,知道内核缓冲区空了,这时候内核就告诉B,需要阻塞了,此时为“缓冲区空”
这四个I/O事件,缓冲区空,缓冲区满,缓冲区非空,缓冲区非满,是阻塞同步的根本
5、阻塞I/O产生由来
由于一个线程只能处理一个流的I/O事件,所以就产生了阻塞I/O。
如果想要处理多个流,要么多进程,要么多线程,但是两种方法效率都不高
6、非阻塞忙轮询
我们可以同时处理多个流了,把一个流从阻塞状态切换到非阻塞状态,读数据
while true{
for i in stream[];{
if i has data
read until unavaliable
}
}
while true{
select (streams[])
for i in streams[]{
if i has data
read until unavaliable
}
}
while true{
active_stream[] = epoll_wait(epollfd)
for i in active_stream[]{
read or write till
}
}