使用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. Mac Eclipse上Android SDK manager闪退的问题!!

    最近想自学一下Android,也没啥人指导,安装的过程中就花了一整天....安装完ADT,安装完SDK,所有步骤都照着网上来,可是一打开SDK manager就闪退!网上所有方法都找了,可是几乎全是w ...

  2. flask的基本操作

    常用的SQLAlchemy字段类型 # coding:utf-8 from flask import Flask from flask_sqlalchemy import SQLAlchemy app ...

  3. java图形验证码生成工具类及web页面校验验证码

    最近做验证码,参考网上案例,发现有不少问题,特意进行了修改和完善. 验证码生成器: import javax.imageio.ImageIO; import java.awt.*; import ja ...

  4. 学习线程池源码--ThreadPoolExecutor

    1 创建ThreadPoolExecutor ThreadPollExecutor有四个构造函数,但本质上都是调用这一个构造函数. public ThreadPoolExecutor(int core ...

  5. jsp必填项加红色星号

    <th><font color=red>*</font>文字:</th>

  6. 设置Linux系统的空闲等待时间TMOUT的方法和Linux反空闲设置的两种方法

    为了增强linux系统的安全性,我们需要在用户输入空闲一段时间后自动断开,这个操作可以由设置TMOUT值来实现.将以下字段加入到/etc/profile 中即可(对所有用户生效). export TM ...

  7. Vue--使用watch、computed、filter方法来监控

    watch与computed.filter: watch:监控已有属性,一旦属性发生了改变就去自动调用对应的方法 computed:监控已有的属性,一旦属性的依赖发生了改变,就去自动调用对应的方法 f ...

  8. webpack学习之—— 模块热替换(Hot Module Replacement)

    模块热替换(HMR - Hot Module Replacement)功能会在应用程序运行过程中替换.添加或删除模块,而无需重新加载整个页面.主要是通过以下几种方式,来显著加快开发速度: 保留在完全重 ...

  9. Spring_Hibernate

    Spring与Hiberante整合 通过hibernate的学习,我们知道,hibernate主要在hibernate.cfg.xml配置文件中 接下来我们看一下hibernate的一个配置文件 h ...

  10. js的动态tab导航

    html部分 <div class="container"> <h3 class="page-header">tab切换</h3& ...