这篇文件讲解下上面的几篇文章中欠下的几个函数解释。在看这个之前,请移步这篇文章这里说明了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 timeval *); 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 timeval *); 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.
libevent源码分析--epoll中的几个函数 epoll_init epoll_add epoll_dispatch