epoll_create 创建的 文件描述符和其他文件描述符一样,是被fork出的子进程继承的,那也就是子进程可以使用这个epoll fd添加感兴趣的io(epoll_ctl),然后是可以影响到父进程的epoll_wait。比如,子进程中注册了一个io写事件后,因为某种原因挂起来了,导致父进程的epoll_wait频繁返回,CPU占用率飙升。看下下面的演示代码:
void DoWrite(int epollfd) { 5 int fd = socket(AF_LOCAL, SOCK_STREAM, 0); 6 if (fd < 0) { 7 perror("socket "); 8 exit(-1); 9 } 10 struct sockaddr_un peer_addr; 11 memset(&peer_addr, 0, sizeof(peer_addr)); 12 peer_addr.sun_family = AF_LOCAL; 13 const char *ipc_path = "/home/longzhiri/my_code/nettest/localnettest.ipc"; 14 strncpy(peer_addr.sun_path, ipc_path, sizeof(peer_addr.sun_path)-1); 15 if (connect(fd, (struct sockaddr *)&peer_addr, SUN_LEN(&peer_addr))<0){ 16 perror("connect "); 17 exit(-1); 18 } 19 struct epoll_event ev, events[10]; 20 ev.events = EPOLLOUT; 21 ev.data.fd = fd; 22 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) < 0) { 23 perror("epoll_ctl"); 24 exit(-1); 25 } 26 27 sleep(10); 28 epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &ev); 29 printf("child process exit\n"); 30 } 31 32 int main(int argc, char *argv[]) { 33 int epollfd = epoll_create(10); 34 if (epollfd < 0) { 35 perror("epoll_create"); 36 return -1; 37 } 38 39 struct epoll_event events[10]; 40 int pid = fork(); 41 if (pid < 0) { 42 perror("fork"); 43 return -1; 44 } else if (pid > 0) { 45 for (;;) { 46 int nfds = epoll_wait(epollfd, events, 10, -1); 47 if (nfds < 0) { 48 perror("epoll_wait 1"); 49 return -1; 50 } 51 printf("wake up +++\n"); 52 } 53 } else { 54 DoWrite(epollfd); 55 } 56 return 0; 57 } 真实环境看起来很难犯这种错误,但其实在使用第三方库的时候,因为隐藏不少逻辑,犯错的概率就高了。比如在使用libevent的时候,fork出的子进程想要复用父进程的event_base是必需调用event_reinit的,但是不了解这个问题的人就会直接拿父进程的event_base来使用。看下event_reinit的代码,底层使用了epoll的重新epoll_create出一个epollfd的,而不是重用父进程的,也就是没有调用event_reinit,注册的读写事件,其实都是在父进程中那个epoll_wait那里返回。转载于:https://www.cnblogs.com/persistentsnail/p/3390839.html