使用epoll时,如果在调用epoll_create之后,调用了fork创建子进程,那么父子进程虽然有各自epoll实例的副本,但是在内核中,它们引用的是同一个实例。子进程向自己的epoll实例添加、修改和删除文件描述符时,是可以影响到父进程的epoll_wait的。所以会发生意想不到的问题,分情况看一下:

1:向子进程中的epoll实例添加描述符,描述符事件触发后,也会影响到父进程的epoll实例,代码如下:

#define MAXEVENTS 20

int listenfd;
struct epoll_event events[MAXEVENTS]; int epfd = epoll_create(MAXEVENTS); if((pid = fork()) < 0) return; if(pid == 0)
{
listenfd = socketfd();
struct epoll_event lisevent;
lisevent.events = EPOLLIN;
lisevent.data.fd = listenfd; res = epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &lisevent);
} while(1)
{
res = epoll_wait(epfd, events, MAXEVENTS, -1); for(i = 0; i < res; i++)
{
connectfd = accept(events[i].data.fd, (struct sockaddr *)&clientaddr, (socklen_t *)&addrlen);
if(connectfd < 0)
{
perror("accept error");
continue;
}
printf("connect from %s\n", inet_ntop(AF_INET, &(clientaddr.sin_addr), addrbuf, 20));
close(connectfd);
}
}

上述代码中,在fork之前创建epoll实例,然后在子进程中,创建监听socket,并且加入到epoll实例中。父子进程同时在epoll实例上调用epoll_wait等待连接的到来。如果此时客户端建链,则打印如下:

accept error: Bad file descriptor
accept error: Bad file descriptor
……
accept error: Bad file descriptor
connect from 127.0.0.1

也就是说,连接到来时,尽管是在子进程中创建的监听套接字,加入到子进程中的epoll实例中。但是父子进程中epoll实例都会收到触发的事件,二者的epoll_wait都会停止阻塞,开始调用accept。

父进程调用accept失败,打印出Bad file descriptor错误,是因为在父进程中,根本没有监听套接字。所以,只要子进程没有调用accept成功,则该连接事件就会一直触发,从而父进程一直打印accept错误信息,直到子进程调用accept成功,打印出connect from 127.0.0.1。

2:在fork之前,创建epoll实例、监听套接字listenfd,并将listenfd加入到epoll实例中。然后父子进程一起等待事件的触发,代码如下:

#define MAXEVENTS 20

int listenfd;
struct epoll_event events[MAXEVENTS]; int epfd = epoll_create(MAXEVENTS); listenfd = socketfd();
struct epoll_event lisevent;
lisevent.events = EPOLLIN;
lisevent.data.fd = listenfd; res = epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &lisevent); if((pid = fork()) < 0) return; while(1)
{
res = epoll_wait(epfd, events, MAXEVENTS, -1); for(i = 0; i < res; i++)
{
printf("[%s]before accept\n", pid?"father":"child");
connectfd = accept(events[i].data.fd, (struct sockaddr *)&clientaddr, (socklen_t *)&addrlen);
printf("[%s]after accept\n", pid?"father":"child"); if(connectfd < 0)
{
perror("accept error");
continue;
}
printf("connect from %s\n", inet_ntop(AF_INET, &(clientaddr.sin_addr), addrbuf, 20));
close(connectfd);
}
}

上述代码在fork之前创建好epoll实例和监听套接字,然后调用fork,父子进程在各自的epoll实例上等待事件的发生,如果此时到来了一个客户端连接,则打印如下:

[father]before accept
[father]after accept
connect from 127.0.0.1
[child]before accept

可见,到来的连接触发的事件,会同时通告给被父子进程的epoll实例。父进程调用accept得到该连接,而子进程调用accept时,连接已经被取走了,所以子进程中的accept阻塞。

总结:在fork之前创建的epoll实例,尽管分别处于父子进程各自的空间中,但是它们在底层引用的同一个内核结构。所以,当事件发生时,会同时通告给父子进程中的epoll实例。这其实算是epoll设计上的一个缺陷,应该避免在fork之前创建epoll实例,或者在fork之后,关闭原epoll实例,重新创建本进程的epoll实例。这一点在libev的文档中有所提及:

The biggest issue is fork races, however - if a program forks then both parent and child process have to recreate the epoll set, which can take considerable time (one syscall per file descriptor) and is of course hard
to detect.

参考:http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod

epoll与fork的更多相关文章

  1. epoll在fork子进程中的问题

    epoll_create 创建的 文件描述符和其他文件描述符一样,是被fork出的子进程继承的,那也就是子进程可以使用这个epoll fd添加感兴趣的io(epoll_ctl),然后是可以影响到父进程 ...

  2. C语言可以开发哪些项目?

    C语言是我们大多数人的编程入门语言,对其也再熟悉不过了,不过很多初学者在学习的过程中难免会出现迷茫,比如:不知道C语言可以开发哪些项目,可以应用在哪些实际的开发中--,这些迷茫也导致了我们在学习的过程 ...

  3. 大型分布式C++框架《四:netio之请求包中转站 上》

    本来一篇文章就该搞定的.结果要分上下篇了.主要是最近颈椎很不舒服.同时还在做秒杀的需求也挺忙的. 现在不能久坐.看代码的时间变少了.然后还买了两本治疗颈椎的书.在学着,不过感觉没啥用.突然心里好害怕. ...

  4. C语言可以开发哪些项目?(转)

    原文地址:https://www.cnblogs.com/shiyanlou/p/6098661.html 知乎:https://www.zhihu.com/question/20564904 C语言 ...

  5. 17个C语言可以做的小案例项目

    C语言是我们大多数人的编程入门语言,对其也再熟悉不过了,不过很多初学者在学习的过程中难免会出现迷茫,比如:不知道C语言可以开发哪些项目,可以应用在哪些实际的开发中……,这些迷茫也导致了我们在学习的过程 ...

  6. C 实现有追求的线程池 后续

    引言 -_- 还是老套路开局 很久以前写过一个有追求的线程池 -> C 实现有追求的线程池 探究 讲述的是一种思路, 并且实现了. 可以一用. 最近在详细搞simplec 框架. 准备发布个正式 ...

  7. 用C语言开发的19个经典项目,你会第几个?

    前言本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:实验楼 C语言是我们大多数人的编程入门语言,对其也再熟悉不过了,不过很多 ...

  8. swoole在线聊天学习笔记

    <?php $http=); $http->on('request',function(swoole_http_request $request,swoole_http_response ...

  9. (转载) Linux IO模式及 select、poll、epoll详解

    注:本文是对众多博客的学习和总结,可能存在理解错误.请带着怀疑的眼光,同时如果有错误希望能指出. 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案 ...

随机推荐

  1. Asp.Net Core2.0在linux下发布

    一.在linux上新建mvc项目发布 可以参考:https://segmentfault.com/a/1190000012428781 也可以看微软官方文档. 大致步骤如下: 1.在linux下安装. ...

  2. Mac OS 安装 独立的asio库

    先安装boost,见前文,然后上官网下载不带boost的asio,版本为:asio-1.12.2 cd到下载的库目录,配置 ./configure --with-boost="boost的安 ...

  3. POJ1991 NOI1999棋盘分割

    棋盘分割 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 15581   Accepted: 5534 Description ...

  4. 【CODEVS】2618 核电站问题

    2618 核电站问题 时间限制: 1 s 空间限制: 32000 KB 题目等级 : 黄金 Gold 题解 查看运行结果 题目描述 Description 一个核电站有N个放核物质的坑,坑排列在一条直 ...

  5. 【CodeVS】2822 爱在心中 [2017年6月计划 强连通分量03]

    2822 爱在心中 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond         题目描述 Description “每个人都拥有一个梦,即使彼此不相同,能够 ...

  6. ue4同c#通信时的中文乱码问题

    转:https://blog.csdn.net/XIAOZHI0999/article/details/80785570 本文讨论C#同ue4进行通信,出现的中文乱码情况,其他语言类似. 本文分两种情 ...

  7. itextsharp 1.0

    1 效果图 2.代码 引用组件: using iTextSharp.text;using iTextSharp.text.pdf;using System;using System.Data;usin ...

  8. leetcode 235-290 easy

    235. Lowest Common Ancestor of a Binary Search Tree 公共的祖先必定大于左点小于右点,否则不断递归到合适. class Solution { publ ...

  9. Ajax--art-template + 调用天气接口

    一.实现原理: <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...

  10. CSS中各种居中方法

    CSS中各种居中方法,本文回顾一下,便于后续的使用. 水平居中方法 1.行内元素居中 行内元素居中是只针对行内元素的,比如文本(text).图片(img).按钮等行内元素,可通过给父元素设置 text ...