libevent代码阅读(14)——select复用机制(一)

来源:互联网 发布:认识数据库管理 编辑:程序博客网 时间:2024/06/09 23:11

libevent实现几种io复用机制,本文将会重点介绍select和epoll模式。

selectops(eventop类型)对象是用于操作select复用机制的结构对象,它内部成员都是回调函数等,这些回调函数用于实现select复用机制,定义如下:

// select模式的io复用机制const struct eventop selectops = {// 名字"select",// 初始化select_init,// 添加事件select_add,// 移除事件select_del,// 事件等待和分发select_dispatch,// 删除select_dealloc,// 是否需要重新初始化0, /* doesn't need reinit. */EV_FEATURE_FDS,0,};

selectop结构则是存放select模式一些信息的结构,例如套接字集合等,定义如下:

// select数据结构struct selectop {// 在集合中最大的文件描述符的数值int event_fds;/* Highest fd in fd set */// 集合的大小int event_fdsz;int resize_out_sets;// 读集合,但是在调用select之前需要将其复制到event_readset_out中fd_set *event_readset_in;// 写集合fd_set *event_writeset_in;// 读集合,作为select的输入(select函数回修改这些集合,所以在调用之前需要备份好)fd_set *event_readset_out;// 写集合,作为select的输入(select函数回修改这些集合,所以在调用之前需要备份好)fd_set *event_writeset_out;};

初始化select模式:

/* * select初始化 */static void *select_init(struct event_base *base){struct selectop *sop;// 分配select对象if (!(sop = mm_calloc(1, sizeof(struct selectop))))return (NULL);// 分配select对象的套接字集合if (select_resize(sop, SELECT_ALLOC_SIZE(32 + 1))) {select_free_selectop(sop);return (NULL);}// 信号事件处理器初始化evsig_init(base);return (sop);}

添加事件:

/* * 添加事件 */static intselect_add(struct event_base *base, int fd, short old, short events, void *p){struct selectop *sop = base->evbase;(void) p;EVUTIL_ASSERT((events & EV_SIGNAL) == 0);check_selectop(sop);/* * Keep track of the highest fd, so that we can calculate the size * of the fd_sets for select(2) */// 如果当前记录的最大文件描述符小于fd,那么需要替换为fd// 即event_fds始终记录着集合中数值最大的套接字// 同时需要调整套接字集合大小if (sop->event_fds < fd) {int fdsz = sop->event_fdsz;if (fdsz < (int)sizeof(fd_mask))fdsz = (int)sizeof(fd_mask);/* In theory we should worry about overflow here.  In * reality, though, the highest fd on a unixy system will * not overflow here. XXXX */while (fdsz < (int) SELECT_ALLOC_SIZE(fd + 1))fdsz *= 2;if (fdsz != sop->event_fdsz) {if (select_resize(sop, fdsz)) {check_selectop(sop);return (-1);}}sop->event_fds = fd;}// 读事件if (events & EV_READ)FD_SET(fd, sop->event_readset_in);// 写事件if (events & EV_WRITE)FD_SET(fd, sop->event_writeset_in);check_selectop(sop);return (0);}

事件等待以及分发:

/* * 事件等待以及分发 */static intselect_dispatch(struct event_base *base, struct timeval *tv){int res=0, i, j, nfds;struct selectop *sop = base->evbase;check_selectop(sop);// 判断是否需要重新调整输出集合的大小(如果event_readset_in的大小大于event_readset_out,那么必须调整event_readset_out的大小)if (sop->resize_out_sets) {fd_set *readset_out=NULL, *writeset_out=NULL;size_t sz = sop->event_fdsz;if (!(readset_out = mm_realloc(sop->event_readset_out, sz)))return (-1);sop->event_readset_out = readset_out;if (!(writeset_out = mm_realloc(sop->event_writeset_out, sz))) {/* We don't free readset_out here, since it was * already successfully reallocated. The next time * we call select_dispatch, the realloc will be a * no-op. */return (-1);}sop->event_writeset_out = writeset_out;sop->resize_out_sets = 0;}// 将备份集合中的数据复制到将要被select处理的集合中// 这样被修改的始终都是event_readset_out和event_writeset_out// 而event_readset_in和event_writeset_in中总是存放这最新的文件描述符集合memcpy(sop->event_readset_out, sop->event_readset_in,       sop->event_fdsz);memcpy(sop->event_writeset_out, sop->event_writeset_in,       sop->event_fdsz);nfds = sop->event_fds+1;EVBASE_RELEASE_LOCK(base, th_base_lock);// 调用select函数等待事件发生res = select(nfds, sop->event_readset_out,    sop->event_writeset_out, NULL, tv);EVBASE_ACQUIRE_LOCK(base, th_base_lock);check_selectop(sop);if (res == -1) {if (errno != EINTR) {event_warn("select");return (-1);}return (0);}event_debug(("%s: select reports %d", __func__, res));check_selectop(sop);i = random() % nfds;// 遍历已经发生的事件for (j = 0; j < nfds; ++j) {if (++i >= nfds)i = 0;res = 0;// 读事件if (FD_ISSET(i, sop->event_readset_out))res |= EV_READ;// 写事件if (FD_ISSET(i, sop->event_writeset_out))res |= EV_WRITE;if (res == 0)continue;// 将事件插入到已激活事件队列中,以进行下一步的操作evmap_io_active(base, i, res);}check_selectop(sop);return (0);}


0 0
原创粉丝点击