`
lancelotwjq
  • 浏览: 53547 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

epoll与event_pool

 
阅读更多

  epoll is a scalable I/O event notification . 它在内核2.5.44首次引入, 用来取代POSIX select和poll系统调用。

    说到select和poll,也就是IO多路转接,在APUE 14.5节有详细介绍:

转自http://blog.csdn.net/delphiwcdj/article/details/8827659

------------------------------------------------

当从一个描述符读,然后又写到另一个描述符时,可以在下列形式的循环中使用阻塞I/O:

 

但是如果需要从多个描述符读,上面的方法是不行的,如果仍旧使用阻塞I/O,那么就可能长时间阻塞在一个描述符上,而另一个描述符虽有很多数据却不能得到及时处理。针对这个问题,有哪些处理方法呢?

    while ( (n = read(STDIN_FILENO, buf,BUFSIZ)) > 0 )  
    {  
                if( write(STDOUT_FILENO, buf, n) != n )  
                {  
                            printf("write error\n");  
                            return1;  
                }  
    }  

 

 

 

方法1:

 

用fork将一个进程变成两个进程,每个进程处理一条数据通路,即每个进程都执行阻塞read。

 

问题是:在进程终止时,使程序变得复杂。

 

 

 

方法2:

 

使用多线程单进程,这避免了终止的复杂性。

 

问题是:但却要求处理线程之间的同步,依然没有摆脱复杂性。

 

 

 

方法3:

 

使用单进程非阻塞I/O读数据,即轮询(polling)的方式。将多个描述符都设置为非阻塞的,对第一个描述符发一个read,如果该输入上有数 据,则读数据并处理它;如果无数据可读,则read立即返回。然后对第二个描述符做同样的处理。所有描述符处理完之后,等待若干秒然后再进行轮询。

 

问题是:浪费CPU时间,因为大多数时间实际是无数据可读的,但仍花费时间不断反复执行read系统调用,在多任务系统中应当避免使用这种方法。

 

 

 

方法4:

 

异步I/O(asynchronous I/O),其思想是:告知内核启动某个操作,并让内核在整个操作(包括将数据从内核复制到我们自己的缓冲区)完成后通知我们。即,异步I/O模型是由内核通知我们I/O操作合适完成。

 

问题是:并非所有系统都支持这种机制。

 

 

 

方法5:

 

使用I/O多路转接(I/Omultiplexing)。先构造一张有关描述符的列表,然后调用一个函数,直到这些描述符中的一个已准备好进行I /O时,该函数才返回,在返回时,此函数告诉进程哪些描述符已准备好可以进行I/O。poll、pselect和select这三个函数使我们能够执行I /O多路转接。即,我们使用select可以等待多个描述符就绪。

-----------------------------------

 

    在监控文件数量n很大的时候,select和poll的算法复杂度为O(n),而epoll的算法复杂度为O(1)。因而性能会提高很多。

    epoll只有三个API[1]

 

int epoll_create(int size);

Creates an epoll object and returns its file descriptor.

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

Controls (configures) which file descriptors are watched by this object, and for which events.

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

Waits for any of the registered events, until at least one occurs or the timeout elapses.

 

    Glusterfs使用event_pool封装了epoll。相关代码在libglusterfs/src/event-epoll.c

 

    主要有如下几个函数:

 

        .new              = event_pool_new_epoll,
        .event_register   = event_register_epoll,
        .event_select_on  = event_select_on_epoll,
        .event_unregister = event_unregister_epoll,
        .event_dispatch   = event_dispatch_epoll

 

static struct event_pool *
event_pool_new_epoll (int count)
{
        struct event_pool *event_pool = NULL;
        int                epfd = -1;

        //申请空间,event_pool封装了下epoll
        event_pool = GF_CALLOC (1, sizeof (*event_pool),
                                gf_common_mt_event_pool);

        event_pool->count = count;
        event_pool->reg = GF_CALLOC (event_pool->count,
                                     sizeof (*event_pool->reg),
                                     gf_common_mt_reg);
        //获得epoll的文件描述符
        epfd = epoll_create (count);

        event_pool->fd = epfd;
        event_pool->count = count;

        pthread_mutex_init (&event_pool->mutex, NULL);
        pthread_cond_init (&event_pool->cond, NULL);

out:
        return event_pool;
}


 

int
event_register_epoll (struct event_pool *event_pool, int fd,
                      event_handler_t handler,
                      void *data, int poll_in, int poll_out)
{
....
                //如果满了,就扩容1倍
                if (event_pool->count == event_pool->used) {
                        event_pool->count *= 2;

                        event_pool->reg = GF_REALLOC (event_pool->reg,
                                                      event_pool->count *
                                                      sizeof (*event_pool->reg));

                        if (!event_pool->reg) {
                                gf_log ("epoll", GF_LOG_ERROR,
                                        "event registry re-allocation failed");
                                goto unlock;
                        }
                }

                idx = event_pool->used;
                event_pool->used++;
                //向event_pool中加入一个注册信息
                event_pool->reg[idx].fd = fd;
                event_pool->reg[idx].events = EPOLLPRI;
                event_pool->reg[idx].handler = handler;
                event_pool->reg[idx].data = data;
....
                event_pool->changed = 1;

                epoll_event.events = event_pool->reg[idx].events;
                ev_data->fd = fd;
                ev_data->idx = idx;

                //注册到epoll
                ret = epoll_ctl (event_pool->fd, EPOLL_CTL_ADD, fd,
                                 &epoll_event);
 ....

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics