上一篇文章对hloop源码大概逻辑和思想进行了一个简单的分析,其中主要涉及三类(timer>io>idles)事件处理。下面将对hloop_process_ios事件做一下简单的分析。

int hloop_process_ios(hloop_t* loop, int timeout) {// That is to call IO multiplexing function such as select, poll, epoll, etc.//iowatcher_poll_events主要针对不同模型下的IO事件处理,代码将根据环境选择其一int nevents = iowatcher_poll_events(loop, timeout);if (nevents < 0) {hlogd("poll_events error=%d", -nevents);}return nevents < 0 ? 0 : nevents;}


下面将以epoll模型为例,对IO进行一个简单的分析。

iowatcher_poll_events总结

  1. epoll_wait检测io事件,如果存在则加入pendding队列,如果不存在则直接返回
int iowatcher_poll_events(hloop_t* loop, int timeout) {epoll_ctx_t* epoll_ctx = (epoll_ctx_t*)loop->iowatcher;if (epoll_ctx == NULL)return 0;if (epoll_ctx->events.size == 0) return 0;//等待io事件,如果存在事件则加入pendding队列,单线程模型int nepoll = epoll_wait(epoll_ctx->epfd, epoll_ctx->events.ptr, epoll_ctx->events.size, timeout);if (nepoll < 0) {if (errno == EINTR) {return 0;}perror("epoll");return nepoll;}if (nepoll == 0) return 0;int nevents = 0;for (int i = 0; i < epoll_ctx->events.size; ++i) {struct epoll_event* ee = epoll_ctx->events.ptr + i;int fd = ee->data.fd;uint32_t revents = ee->events;if (revents) {++nevents;hio_t* io = loop->ios.ptr[fd];if (io) {if (revents & (EPOLLIN | EPOLLHUP | EPOLLERR)) {io->revents |= HV_READ;}if (revents & (EPOLLOUT | EPOLLHUP | EPOLLERR)) {io->revents |= HV_WRITE;}//加入pendding队列EVENT_PENDING(io);}}if (nevents == nepoll) break;}return nevents;}

此时看看epoll.c源码接口

int iowatcher_init(hloop_t* loop);int iowatcher_cleanup(hloop_t* loop);int iowatcher_add_event(hloop_t* loop, int fd, int events);int iowatcher_del_event(hloop_t* loop, int fd, int events);int iowatcher_poll_events(hloop_t* loop, int timeout);

发现模型接口很简单也很统一:初始化模型,加入事件,检测事件,删除事件,清理模型

1. io模型何时与loop绑定的呢?loop初始化即绑定或者在add_event的时候检测绑定
void hloop_init(hloop_t* loop) {..... // 初始化iowatcheriowatcher_init(loop);......}int iowatcher_add_event(hloop_t* loop, int fd, int events) {if (loop->iowatcher == NULL) {iowatcher_init(loop);}......}
2. fd何时与IO模型绑定的呢?io异步读写的时候进行io模型绑定
//read的时候添加io,int hio_read (hio_t* io) {if (io->closed) {hloge("hio_read called but fd[%d] already closed!", io->fd);return -1;}hio_add(io, hio_handle_events, HV_READ);if (io->readbuf.tail > io->readbuf.head &&io->unpack_setting == NULL &&io->read_flags == 0) {hio_read_remain(io);}return 0;}//将IO和模型绑定iowatcher_add_eventint hio_add(hio_t* io, hio_cb cb, int events) {printd("hio_add fd=%d io->events=%d events=%d\n", io->fd, io->events, events);#ifdef OS_WIN// Windows iowatcher not work on stdioif (io->fd < 3) return -1;#endifhloop_t* loop = io->loop;if (!io->active) {EVENT_ADD(loop, io, cb);loop->nios++;}if (!io->ready) {hio_ready(io);}if (cb) {io->cb = (hevent_cb)cb;}if (!(io->events & events)) {iowatcher_add_event(loop, io->fd, events);io->events |= events;}return 0;}

至此io和loop关联完成。

总结

  1. 代码根据编译选项()选则某一种IO模型
  2. loop在初始化的时候将loop和IO模型绑定,默认创建custom event,即socket
  3. 在fd进行读(hio_read )写(hio_write)的时候将fd添加到IO模型
  4. hloop_process_ios 在IO模型检测(epoll_wait)已经准备就绪的IO事件,并且加入pendding队列。

为了更一步了解io细节,下一步将对hio源码进行讲解分析。