上一篇文章对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总结
- 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) {
.....
// 初始化iowatcher
iowatcher_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_event
int 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 stdio
if (io->fd < 3) return -1;
#endif
hloop_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关联完成。
总结
- 代码根据编译选项(
宏
)选则某一种IO模型 - loop在初始化的时候将
loop和IO模型绑定
,默认创建custom event,即socket - 在fd进行读(
hio_read
)写(hio_write
)的时候将fd添加到IO模型
中 hloop_process_ios
在IO模型检测(epoll_wait
)已经准备就绪的IO事件,并且加入pendding
队列。
为了更一步了解io细节,下一步将对hio源码进行讲解分析。