#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
#include <unistd.h>
#include <semaphore.h>
#include <sys/shm.h>
#define IP "127.0.0.1"
#define PORT 8888
#define PROCESS_NUM 4
#define MAXEVENTS 64 static int
create_and_bind ()
{
int fd = socket (PF_INET, SOCK_STREAM, );
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
inet_pton (AF_INET, IP, &serveraddr.sin_addr);
serveraddr.sin_port = htons (PORT);
bind (fd, (struct sockaddr *) &serveraddr, sizeof (serveraddr));
return fd;
} static int
make_socket_non_blocking (int sfd)
{
int flags, s;
flags = fcntl (sfd, F_GETFL, );
if (flags == -)
{
perror ("fcntl");
return -;
}
flags |= O_NONBLOCK;
s = fcntl (sfd, F_SETFL, flags);
if (s == -)
{
perror ("fcntl");
return -;
}
return ;
} void
worker (int sfd, int efd, struct epoll_event *events, int k, sem_t * sem)
{
/* The event loop */
struct epoll_event event;
// struct epoll_event *events;
efd = epoll_create (MAXEVENTS);
if (efd == -)
{
perror ("epoll_create");
abort ();
}
int epoll_lock = ;
while ()
{
int n, i;
int s;
event.data.fd = sfd;
event.events = EPOLLIN;
if ( == sem_trywait (sem))
{
//拿到锁的进程将listen 描述符加入epoll
if (!epoll_lock)
{
fprintf (stderr, "%d >>>get lock\n", k);
s = epoll_ctl (efd, EPOLL_CTL_ADD, sfd, &event);
if (s == -)
{
perror ("epoll_ctl");
abort ();
}
epoll_lock = ;
}
}
else
{
fprintf (stderr, "%d not lock\n", k);
//没有拿到锁的进程 将lisfd 从epoll 中去掉
if (epoll_lock)
{
fprintf (stderr, "worker %d return from epoll_wait!\n", k);
if (- == epoll_ctl (efd, EPOLL_CTL_DEL, sfd, &event))
{
if (errno == ENOENT)
{
fprintf (stderr, "EPOLL_CTL_DEL\n");
}
}
epoll_lock = ;
}
}
//epoll_ctl (efd, EPOLL_CTL_ADD, sfd, &event);
// fprintf(stderr, "ok\n");
//不能设置为-1 为了能让拿不到锁的进程再次拿到锁
n = epoll_wait (efd, events, MAXEVENTS, );
for (i = ; i < n; i++)
{
if (sfd == events[i].data.fd)
{
/* We have a notification on the listening socket, which means one or more incoming connections. */
struct sockaddr in_addr;
socklen_t in_len;
int infd;
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
in_len = sizeof in_addr;
while ((infd = accept (sfd, &in_addr, &in_len)) > )
{
fprintf(stderr, "get one\n");
close (infd);
}
}
}
if (epoll_lock)
{
//这里将锁释放
sem_post (sem);
epoll_lock = ;
epoll_ctl (efd, EPOLL_CTL_DEL, sfd, &event);
}
}
} int
main (int argc, char *argv[])
{
int shmid;
sem_t *acctl;
//建立共享内存
shmid = shmget (IPC_PRIVATE, sizeof (sem_t), );
acctl = (sem_t *) shmat (shmid, , );
//进程间信号量初始化 要用到上面的共享内存
sem_init (acctl, , );
int sfd, s;
int efd;
// struct epoll_event event;
// struct epoll_event *events;
sfd = create_and_bind ();
if (sfd == -)
{
abort ();
}
s = make_socket_non_blocking (sfd);
if (s == -)
{
abort ();
}
s = listen (sfd, SOMAXCONN);
if (s == -)
{
perror ("listen");
abort ();
}
efd = ;
int k;
for (k = ; k < PROCESS_NUM; k++)
{
printf ("Create worker %d\n", k + );
int pid = fork ();
if (pid == )
{
struct epoll_event *events;
events = calloc (MAXEVENTS, sizeof (struct epoll_event));
worker (sfd, efd, events, k, acctl);
break;
}
}
int status;
wait (&status);
close (sfd);
return EXIT_SUCCESS;
}
/*
* 这里处理惊群 用到了进程的锁(信号量, 共享内存), 根据试验的结果多个进程时accept接收客户端连接的效率并没有提高太多
* 但是处理其他可读可写(非监听描述符)时, 要比单个进程要快很多。
*/

  在早期的kernel中, 多线程或多进程调用accept就会出现如下情况, 当前多个进程阻塞在accept中, 此时有客户端连接时, 内核就会通知阻塞在accept的所有进程, 这时就会造成惊群现象, 也就是所有accept都会返回 但是只有一个能拿到有效的文件描述符, 其他进程最后都会返回无效描述符。但在linux kernel 版本2.6 以上时, accept惊群的问题已经解决, 大致方案就是选一个阻塞在accept的进程返回。

  但是在IO复用中, select/poll/epoll 还是存在这种现象,其原因就是这些阻塞函数造成了以上同样的问题。这里就给出了类似Nginx的解决方案, 给监听描述符竞争枷锁, 保证只有一个进程处理监听描述符。 这里还可以控制锁的频率,如果一个进程接受连接的数量达到一定数量就不再申请锁。  这里要注意的是epoll_create的位置要在fork之后的子进程中, 这是因为若在父进程中create 那么fork之后子进程保留这个描述符副本,epoll_create其实是内核中建立的文件系统 保存在内核中, 那么其子进程都会共用这个文件系统, 那么多任务的epoll_ctl就会出现问题。子进程中建立各自的epoll fd 就可以避免这种情况。

epoll 惊群处理的更多相关文章

  1. accept与epoll惊群 转载

    今天打开 OneNote,发现里面躺着一篇很久以前写的笔记,现在将它贴出来. 1. 什么叫惊群现象 首先,我们看看维基百科对惊群的定义: The thundering herd problem occ ...

  2. epoll惊群原因分析

    考虑如下情况(实际一般不会做,这里只是举个例子): 在主线程中创建一个socket.绑定到本地端口并监听 在主线程中创建一个epoll实例(epoll_create(2)) 将监听socket添加到e ...

  3. 源码剖析Linux epoll实现机制及Linux上惊群

    转载:https://blog.csdn.net/tgxallen/article/details/78086360 看源码是对一个技术认识最直接且最有效的方式了,之前用Linux Epoll做过一个 ...

  4. Linux惊群效应详解

    Linux惊群效应详解(最详细的了吧)   linux惊群效应 详细的介绍什么是惊群,惊群在线程和进程中的具体表现,惊群的系统消耗和惊群的处理方法. 1.惊群效应是什么?        惊群效应也有人 ...

  5. nginx&http 第三章 惊群

    惊群:概念就不解释了. 直接说正题:惊群问题一般出现在那些web服务器上,Linux系统有个经典的accept惊群问题,这个问题现在已经在内核曾经得以解决,具体来讲就是当有新的连接进入到accept队 ...

  6. NGINX怎样处理惊群的

    写在前面 写NGINX系列的随笔,一来总结学到的东西,二来记录下疑惑的地方,在接下来的学习过程中去解决疑惑. 也希望同样对NGINX感兴趣的朋友能够解答我的疑惑,或者共同探讨研究. 整个NGINX系列 ...

  7. “惊群”,看看nginx是怎么解决它的

    在说nginx前,先来看看什么是“惊群”?简单说来,多线程/多进程(linux下线程进程也没多大区别)等待同一个socket事件,当这个事件发生时,这些线程/进程被同时唤醒,就是惊群.可以想见,效率很 ...

  8. Linux网络编程“惊群”问题总结

    1.前言 我从事Linux系统下网络开发将近4年了,经常还是遇到一些问题,只是知其然而不知其所以然,有时候和其他人交流,搞得非常尴尬.如今计算机都是多核了,网络编程框架也逐步丰富多了,我所知道的有多进 ...

  9. Nginx学习之一-惊群现象

    惊群问题(thundering herd)的产生 在建立连接的时候,Nginx处于充分发挥多核CPU架构性能的考虑,使用了多个worker子进程监听相同端口的设计,这样多个子进程在accept建立新连 ...

随机推荐

  1. Arduino上传数据至贝壳物联并与FPGA进行交互

    本篇实现Arduino与FPGA交互,当然也没有什么新的协议,还是基于串口通讯,现在学一个串口通信基本上可以驱动大多数模块了,而且和各种单片机无缝数据交互,Arduino由于其强大的库函数支持,在实现 ...

  2. mysql密码更改

    1.用户修改密码: 方法一:mysqladmin -u用户 -p密码 password '新密码' mysqladmin -uroot -pdefault password 'zhouli.cn' 方 ...

  3. Spring、SpringMVC、SpringData + JPA 整合详解

    原创播客,如需转载请注明出处.原文地址:http://www.cnblogs.com/crawl/p/7759874.html ------------------------------------ ...

  4. Bluetooth A2DP --Audio payload type

    数据结构: 字段解释: payload type: 0x60(96), dynamic type type 定义: https://www.iana.org/assignments/rtp-param ...

  5. Java基础笔记14

    1.反射. Class:反射类 任何一个类都有一个Class反射类.(影子) java.lang.reflect.*; Field:字段类 Method:方法类类 Constructor:构造方法类. ...

  6. lua中的require机制

    lua中的require机制 为了方便代码管理,通常会把lua代码分成不同的模块,然后在通过require函数把它们加载进来.现在看看lua的require的处理流程.1.require机制相关的数据 ...

  7. 1031: [JSOI2007]字符加密Cipher

    1031: [JSOI2007]字符加密Cipher Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 7338  Solved: 3182[Submit ...

  8. Windows解决anaconda下双python版本安装TensorFlow

    首先,就是双版本anaconda的安装: 以前安装好的是python2.7版本,而TensorFlow的安装仅支持3.5版本的.但是自己本来的2.7版本又不想遗弃.所以安装双版本的: 在anacond ...

  9. Luogu 2296 寻找道路

    https://www.luogu.org/problemnew/show/2296 题目描述 在有向图G 中,每条边的长度均为1 ,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以 ...

  10. 介绍一款好用 mongodb 可视化工具

    最近想自己搭建一个个人博客,所以学了下mongodb,mongodb是用命令行输入的,有些人可能不太习惯,我自己找了下mongodb的一些可视化工具,一开始安装的是mongoVUE,mongoVUE页 ...