首先看看add函数实在event_add函数中调用的,肯定是evsel->add的方式调用的。
259 static int
260 epoll_add(void *arg, struct event *ev)
261 {
262 struct epollop *epollop = arg;
263 struct epoll_event epev = {0, {0}};
264 struct evepoll *evep;
265 int fd, op, events;
266
267 if (ev->ev_events & EV_SIGNAL)
268 return (evsignal_add(ev));
269
270 fd = ev->ev_fd;
271 if (fd >= epollop->nfds) {
272 /* Extent the file descriptor array as necessary */
273 if (epoll_recalc(ev->ev_base, epollop, fd) == -1)
274 return (-1);
275 }
276 evep = &epollop->fds[fd];
277 op = EPOLL_CTL_ADD;
278 events = 0;
279 if (evep->evread != NULL) {
280 events |= EPOLLIN;
281 op = EPOLL_CTL_MOD;
282 }
283 if (evep->evwrite != NULL) {
284 events |= EPOLLOUT;
285 op = EPOLL_CTL_MOD;
286 }
287
288 if (ev->ev_events & EV_READ)
289 events |= EPOLLIN;
290 if (ev->ev_events & EV_WRITE)
291 events |= EPOLLOUT;
292
293 epev.data.fd = fd;
294 epev.events = events;
295 if (epoll_ctl(epollop->epfd, op, ev->ev_fd, &epev) == -1)
296 return (-1);
297
298 /* Update events responsible */
299 if (ev->ev_events & EV_READ)
300 evep->evread = ev;
301 if (ev->ev_events & EV_WRITE)
302 evep->evwrite = ev;
303
304 return (0);
305 }
这个时候传递的是两个参数,第一个参数是void*,但是在event_add中使用的是evsel->add(evbase,ev);这个时候就明白了evbase的用处,同时再回忆一下evsel,是一个struct eventop,而这里的epoll_add中将第一个参数转化为struct epollop,而struct epollop就是上篇文章中主要介绍的那个,就是epoll_init主要初始化的一个结构体,咱们看看在event_init()中调用evsel->init返回的东西是什么,返回的即使void*,正好,也就是说,一种I/O复用机制对应一个这样的结构体,在select对应的就是struct selectop结构体。其实这也没有什么课奇怪的!
event_add就是将struct event类型的ev转化为epoll机制认识的struct epoll_event ,并将其存放到struct epollop,这么说,在上层框架中封装了具体实现,且看且分析。267行判断ev的类型,如果是EV_SIGNAL,直接将这个ev添加到信号队列中。
271判断ev的描述符,不行的话重新申请空间。注意276行,在struct epollop结构体中有一个字段fds,这个就是指向了nfds个struct evepoll,而struct evepoll都是可读可写的struct event.
从276到292都是为了一个struct epoll_event封装做准备,这里的变量名是epev。
根据fds取出evepoll,也就是276行,赋值给evep,再根据函数第二个参数组合 到293行组合好了一个struct epoll_event epev。然后添加到epfd中。
然后299行到结束更新这个evep的相关内容。其实epoll_del和add很相似,这里不多加累述,下面分析另一个重要的函数epoll_dispatch()