epoll在fork子进程中的问题

it2022-06-27  85

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


最新回复(0)