使用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. vim 查找及替换

    #全文(%)查找(s)行首2个空格开头(/^ ), 替换(g)为无即删掉(//) :%s/^ //g #全文查找每行尾的2个空格,删除 :%s/ $//g

  2. FreeMarker 获取页面appplication、request、session

    使用Request里的Attribute值最简单的方法就是直接${AttributeName}或者安全一点:${AttributeName!"default Value"} 1.取 ...

  3. NKOJ1472 警卫安排

    P1472警卫安排   时间限制 : 10000 MS   空间限制 : 65536 KB 问题描述 一个重要的基地被分为n个连通的区域.出于某种神秘的原因,这些区域以一个区域为核心,呈一颗树形分布. ...

  4. Linux常用命令操作详解

    https://mp.weixin.qq.com/s/IR4yy7Q0mOA_XV16R21CdQ 一:Linux下tomcat服务的启动.关闭与错误跟踪 使用PuTTy远程连接到服务器以后,通常通过 ...

  5. 【JZOJ5071】【GDSOI2017第二轮模拟】奶酪 树形dp

    题面 CJY很喜欢吃奶酪,于是YJC弄到了一些奶酪,现在YJC决定和CJY分享奶酪. YJC弄到了n-1块奶酪,于是他把奶酪挂在了一棵n个结点的树上,每根树枝上挂一块奶酪,每块奶酪都有重量. YJC和 ...

  6. Appium 常用的API函数

    常用的API函数[转] http://blog.sina.com.cn/s/blog_68f262210102vzf9.html 获取信息类API (1)获取默认系统语言对应的Strings.xml文 ...

  7. JavaScript 生成32位UUID

    function uuid(){ var len=32; //32长度 var radix=16; //16进制 var chars='0123456789ABCDEFGHIJKLMNOPQRSTUV ...

  8. C++之正则表示,字符串是否为全字母或者全数字

    bool isLetter(std::string& inputtext){ tr1::regex reg("^[A-Za-z]+$"); bool bValid = tr ...

  9. Leetcode872.Leaf-Similar Trees叶子相似的树

    请考虑一颗二叉树上所有的叶子,这些叶子的值按从左到右的顺序排列形成一个 叶值序列 . 举个例子,如上图所示,给定一颗叶值序列为 (6, 7, 4, 9, 8) 的树. 如果有两颗二叉树的叶值序列是相同 ...

  10. PHP是解释型语言:边解析边运行

    计算机语言的发展史: 第一代:机器语言,全部都是01010二进制代码,计算机能够直接的识别,运行效率是最高的,但是难编,难记,难区分,可移植性差! 第二代:汇编语言,其实就是符号化的机器语言,增加了编 ...