这篇文件讲解下上面的几篇文章中欠下的几个函数解释。在看这个之前,请移步这篇文章这里说明了libevent库在编译的时候已经决定了所使用的I/O复用机制,系统中提供了五种机制:在这里指选择一种来说明 epoll
在even-internal.h文件中有一个结构体,
38 struct eventop {
39 const char *name;
40 void *(*init)(struct event_base *);
41 int (*add)(void *, struct event *);
42 int (*del)(void *, struct event *);
43 int (*dispatch)(struct event_base *, void *, struct timeva l *);
44 void (*dealloc)(struct event_base *, void *);
45 /* set if we need to reinitialize the event base */
46 int need_reinit;
47 };
这个结构体中的每个字段,都是函数指针,函数指针其实就是C语言中的多态,上面的这五个字段在编译的时候指向某个系统调用中的五个函数。也就是event_base中的evsel指针。在epoll机制中,首先封装了struct event :
59 struct evepoll {
60 struct event *evread;
61 struct event *evwrite;
62 };
接下来又是一个结构体:
64 struct epollop {
65 struct evepoll *fds;
66 int nfds;
67 struct epoll_event *events;
68 int nevents;
69 int epfd;
70 };
然后是五个函数,就是event中需要的五个函数
72 static void *epoll_init (struct event_base *);
73 static int epoll_add (void *, struct event *);
74 static int epoll_del (void *, struct event *);
75 static int epoll_dispatch (struct event_base *, void *, struct timeva l *);
76 static void epoll_dealloc (struct event_base *, void *);
然后初始化一个结构体, 这个结构体的名字就是epollops
78 const struct eventop epollops = {
79 "epoll",
80 epoll_init,
81 epoll_add,
82 epoll_del,
83 epoll_dispatch,
84 epoll_dealloc,
85 1 /* need reinit */
86 };
看在具体的代码中怎么使用上述的内容的:
107 static void *
108 epoll_init(struct event_base *base)
109 {
110 int epfd, nfiles = NEVENT;
111 struct rlimit rl;
112 struct epollop *epollop;
113
114 /* Disable epollueue when this environment variable is set */
115 if (getenv("EVENT_NOEPOLL"))
116 return (NULL);
117
118 if (getrlimit(RLIMIT_NOFILE, &rl) == 0 &&
119 rl.rlim_cur != RLIM_INFINITY) {
120 /*
121 * Solaris is somewhat retarded - it's important to drop
122 * backwards compatibility when making changes. So, don't
123 * dare to put rl.rlim_cur here.
124 */
125 nfiles = rl.rlim_cur - 1;
126 }
127
128 /* Initalize the kernel queue */
129
130 if ((epfd = epoll_create(nfiles)) == -1) {
131 if (errno != ENOSYS)
132 event_warn("epoll_create");
133 return (NULL);
134 }
135
136 FD_CLOSEONEXEC(epfd);
137
138 if (!(epollop = calloc(1, sizeof(struct epollop))))
139 return (NULL);
140
141 epollop->epfd = epfd;
142
143 /* Initalize fields */
144 epollop->events = malloc(nfiles * sizeof(struct epoll_event));
145 if (epollop->events == NULL) {
146 free(epollop);
147 return (NULL);
148 }
149 epollop->nevents = nfiles;
150
151 epollop->fds = calloc(nfiles, sizeof(struct evepoll));
152 if (epollop->fds == NULL) {
153 free(epollop->events);
154 free(epollop);
155 return (NULL);
156 }
157 epollop->nfds = nfiles;
158
159 evsignal_init(base);
160
161 return (epollop);
162 }
在这篇文章中使用了init,其实如果系统在编译中使用的epol机制,最终调用的就是上面的这个函数。
在130行调用了系统调用event_create,生成的32000个套接字描述符,从143行开始初始化,也就是32000个描述符,每个描述符都是一个struct epoll_event,在这个函数体中最终生成的一些列东西都是返回一个struct epollop,也就是上面其中一个结构体的封装。函数最后的返回值也是epollop.下面来看看这个函数式怎么初始化struct epollops的。在130行event_create返回值的epfd最终赋值给了epollop->epfd,而epollop->fds在151行是动态申请可空间,大小也是nfiles=NEVENT = 32000,epollop中的nevents和nfds都是NEVENT.这里暂时不讨论关于信号的任何内容。
这个函数也就是相当于初始化了一个struct epollop.