epoll解读

来源:互联网 发布:数据库bms是什么意思 编辑:程序博客网 时间:2024/05/22 16:45

转自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、非阻塞忙轮询

    我们可以同时处理多个流了,把一个流从阻塞状态切换到非阻塞状态,读数据

  1. while true{
  2. for i in stream[];{
  3. if i has data
  4. read until unavaliable
  5. }
  6. }
    我们需要把所有流从头到位都问一遍,这样可以处理多个流了,如果所有流都没有数据,就白白浪费CPU了。
    为了避免CPU空转,引入代理(select,poll,epoll)
7、select、poll代理作用(无差别轮询)
   这个代理可以同时观察许多流的I/O事件,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有I/O事件时,就从阻塞状态中醒过来,于是轮询所有有流的线程,就把“忙”去掉了。
    但是还有一个问题,我们只是知道有I/O事件发生了,但是我们不知道是哪几个流有数据,我们只能无差别轮询所有的流,找出能读出数据或者写入数据的流,对他们进行操作。
  1. while true{
  2. select (streams[])
  3. for i in streams[]{
  4. if i has data
  5. read until unavaliable
  6. }
  7. }
8、说到这可以引入epoll了
    epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们,此时我们对这些流的操作都是有意义的,复杂度降低到O(1)
    讨论epoll细节之前,我们先把epoll的相关操作列出:
    epoll_create     创建一个epoll对象,一般epollfd = epoll_create()
    epoll_ctl            往epoll对象中增加、删除一个流的某一个事件
    比如:
    epoll_ctl(epollfd, EPOLL_CTL_ADD, socket, EPOLLIN);  注册缓冲区非空事件,即有数据流
    epoll_ctl(epollfd, EPOLL_CTL_DEL, socket, EPOLLOUT); 注册缓冲区非满事件,即流可以被写入
    epoll_wait(epollfd, EPOLL_CTL_WAIT,socket, EPOLLWAIT); 注册缓冲区等待事件
    (当对一个非阻塞的流的读写发生缓冲区满或者缓冲区空,write,read会返回-1,并设置errno=EAGAIN,而epoll只关心缓冲区非满和缓冲区非空事件)
一个eopll的代码大概如下:
  1. while true{
  2. active_stream[] = epoll_wait(epollfd)
  3. for i in active_stream[]{
  4. read or write till
  5. }
  6. }

0 0
原创粉丝点击