linux惊群效应

2024-02-24 13:10
文章标签 linux 效应 惊群

本文主要是介绍linux惊群效应,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Linux惊群效应

  • 1.什么是惊群效应
  • 2.惊群效应有什么影响
  • 3.常见惊群情况
    • 1. accept惊群
    • 2. epoll惊群
      • 1 是在fork之前创建epollfd,所有进程共用一个epoll;
      • 2 是在fork之后创建epollfd,每个进程独用一个epoll
    • 3. nginx惊群
    • 4. 线程池惊群
  • 3.accept惊群效应验证
  • 4.epoll惊群效应验证

1.什么是惊群效应

定义:多进程(多线程)在同时阻塞等待同一个事件的时候(休眠状态),如果等待的这个事件发生,那么他就会唤醒等待的所有进程(或者线程),但是最终却只可能有一个进程(线程)获得这个时间的“控制权”,对该事件进行处理,而其他进程(线程)获取“控制权”失败,只能重新进入休眠状态,这种现象和性能浪费就叫做惊群

简单地说:就是扔一块食物,所有鸽子来抢,但最终只一个鸽子抢到了食物。

语义分析:食物只有一块,最终只有一个鸽子抢到,但是惊动了所有鸽子,每个鸽子都跑过来,消耗了每个鸽子的能量。(这个很符合达尔文的进化论,物种之间的竞争,适者生存。)

2.惊群效应有什么影响

  • 惊醒所有进程/线程,导致n-1个进程/线程做了无效的调度,上下文切换,cpu瞬时增高
  • 多个进程/线程争抢资源,所以涉及到同步问题,需对资源进行加锁保护,加解锁加大系统CPU开销

3.常见惊群情况

惊群的几种情况
在高并发(多线程/多进程/多连接)中,会产生惊群的情况有:

1. accept惊群

在内核2.6及之后,解决了惊群,在内核中增加了一个互斥等待变量。一个互斥等待的行为与睡眠基本类似,主要的不同点在于:
1)当一个等待队列入口有 WQ_FLAG_EXCLUSEVE 标志置位, 它被添加到等待队列的尾部. 没有这个标志的入口项, 相反, 添加到开始.
2)当 wake_up 被在一个等待队列上调用时, 它在唤醒第一个有 WQ_FLAG_EXCLUSIVE 标志的进程后停止。
对于互斥等待的行为,比如如对一个listen后的socket描述符,多线程阻塞accept时,系统内核只会唤醒所有正在等待此时间的队列 的第一个,队列中的其他人则继续等待下一次事件的发生,这样就避免的多个线程同时监听同一个socket描述符时的惊群问题。

2. epoll惊群

1 是在fork之前创建epollfd,所有进程共用一个epoll;

  • 主进程创建listenfd, 创建epollfd

  • 主进程fork多个子进程

  • 每个子进程把listenfd,加到epollfd中

    当一个连接进来时,会触发epoll惊群,多个子进程的epoll同时会触发
    linux内核通过和accept加锁方式解决

2 是在fork之后创建epollfd,每个进程独用一个epoll

  • 主进程创建listendfd

  • 主进程创建多个子进程

  • 每个子进程创建自已的epollfd

  • 每个子进程把listenfd加入到epollfd中

  • 当一个连接进来时,会触发epoll惊群,多个子进程epoll同时会触发

分析:
因为每个子进程的epoll是不同的epoll, 虽然listenfd是同一个,但新连接过来时, accept会触发惊群,但内核不知道该发给哪个监听进程,因为不是同一个epoll。所以这种惊群内核并没有处理。惊群还是会出现。

3. nginx惊群

4. 线程池惊群

3.accept惊群效应验证

demo验证

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>#include "epoll.hpp"#define PROCESS_NUMBER 10void test_accept()
{int fd = socket(PF_INET,SOCK_STREAM,0);struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_addr.s_addr = htonl(INADDR_ANY);addr.sin_port = htons(8080);bind(fd, (struct sockaddr*)&addr, sizeof(addr));listen(fd,1024);int pid = -1;int connfd = 0;char sendbuf[1024] = {0};for(int i = 0 ; i < PROCESS_NUMBER; i++){pid = fork();if(pid == 0){while(true){connfd = accept(fd,(struct sockaddr*)NULL,NULL);if(connfd < 0){printf("process %d accept a connection failed: %s\n", getpid(), strerror(errno));close(connfd);}else{snprintf(sendbuf,sizeof(sendbuf),"accept event, pid = %d", getpid());send(connfd,sendbuf,strlen(sendbuf)+1, 0);printf("process %d accept success\n", getpid());close(connfd);}}}}wait(0);
}int main()
{test_accept();return 0;
}

结果
在这里插入图片描述
在这里插入图片描述
只有一个进程被触发
其实在linux2.6版本以后,linux内核已经解决了accept()函数的“惊群”现象,大概的处理方式就是,当内核接收到一个客户连接后,只会唤醒等待队列上的第一个进程(线程),所以如果服务器采用accept阻塞调用方式,在最新的linux系统中已经没有“惊群效应”了

4.epoll惊群效应验证

编写demo验证

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/wait.h>#define PROCESS_NUM 3
#define MAXEVENTS 64int create_bind_socket(int port)
{int sock = socket(AF_INET,SOCK_STREAM, 0);struct sockaddr_in serveraddr;serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(port);serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);bind(sock,(struct sockaddr*)&serveraddr,sizeof(serveraddr));return sock;
}int make_nonblocking(int fd)
{int val = fcntl(fd, F_GETFL);val |= O_NONBLOCK;if(fcntl(fd,F_SETFL,val) < 0){perror("fcntl set error");return -1;}return 0;
}void epoll_demo()
{struct epoll_event event;struct epoll_event* events;int sock_fd = -1;int epoll_fd = -1;if((sock_fd = create_bind_socket(8080)) < 0){perror("socket and bind fail");exit(1);}listen(sock_fd,1024);epoll_fd = epoll_create(MAXEVENTS);event.data.fd = sock_fd;event.events = EPOLLIN;epoll_ctl(epoll_fd,EPOLL_CTL_ADD,sock_fd,&event);events = (epoll_event*)calloc(MAXEVENTS, sizeof(event));for(int i = 0 ; i < PROCESS_NUM; ++i){int pid = fork();if(pid == 0){while(true){int num = epoll_wait(epoll_fd, events, MAXEVENTS, -1);printf("process %d return epoll_wait\n", getpid());sleep(2);for(int j = 0; j < num; ++j){if((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN))){printf("process %d epoll error\n", getpid());close(events[i].data.fd);continue;}else if(sock_fd == events[i].data.fd){struct sockaddr in_addr;socklen_t in_len = sizeof(in_addr);if(accept(sock_fd, &in_addr, &in_len) < 0){printf("process %d accept faild\n", getpid());}else{printf("process %d accept successful\n", getpid());}}}}}}wait(0);free(events);close(sock_fd);
}

结果
在这里插入图片描述
telnet 发现3个子进程都被触发了响应

accpet linux内核修改了只能被一个进程调用成功,但epoll不一样,他监听的文件描述符,除了可能后续被accept调用外,还有可能是其他网络IO事件的,而其他IO事件是否只能由一个进程处理,是不一定的,内核不能保证这一点,这是一个由用户决定的事情,例如可能一个文件会由多个进程来读写。所以,对epoll的惊群,内核则不予处理

这篇关于linux惊群效应的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/742203

相关文章

kali linux 无法登录root的问题及解决方法

《kalilinux无法登录root的问题及解决方法》:本文主要介绍kalilinux无法登录root的问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,... 目录kali linux 无法登录root1、问题描述1.1、本地登录root1.2、ssh远程登录root2、

Linux ls命令操作详解

《Linuxls命令操作详解》通过ls命令,我们可以查看指定目录下的文件和子目录,并结合不同的选项获取详细的文件信息,如权限、大小、修改时间等,:本文主要介绍Linuxls命令详解,需要的朋友可... 目录1. 命令简介2. 命令的基本语法和用法2.1 语法格式2.2 使用示例2.2.1 列出当前目录下的文

Linux中的计划任务(crontab)使用方式

《Linux中的计划任务(crontab)使用方式》:本文主要介绍Linux中的计划任务(crontab)使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、前言1、linux的起源与发展2、什么是计划任务(crontab)二、crontab基础1、cro

Linux换行符的使用方法详解

《Linux换行符的使用方法详解》本文介绍了Linux中常用的换行符LF及其在文件中的表示,展示了如何使用sed命令替换换行符,并列举了与换行符处理相关的Linux命令,通过代码讲解的非常详细,需要的... 目录简介检测文件中的换行符使用 cat -A 查看换行符使用 od -c 检查字符换行符格式转换将

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

Linux系统中卸载与安装JDK的详细教程

《Linux系统中卸载与安装JDK的详细教程》本文详细介绍了如何在Linux系统中通过Xshell和Xftp工具连接与传输文件,然后进行JDK的安装与卸载,安装步骤包括连接Linux、传输JDK安装包... 目录1、卸载1.1 linux删除自带的JDK1.2 Linux上卸载自己安装的JDK2、安装2.1

Linux卸载自带jdk并安装新jdk版本的图文教程

《Linux卸载自带jdk并安装新jdk版本的图文教程》在Linux系统中,有时需要卸载预装的OpenJDK并安装特定版本的JDK,例如JDK1.8,所以本文给大家详细介绍了Linux卸载自带jdk并... 目录Ⅰ、卸载自带jdkⅡ、安装新版jdkⅠ、卸载自带jdk1、输入命令查看旧jdkrpm -qa

Linux samba共享慢的原因及解决方案

《Linuxsamba共享慢的原因及解决方案》:本文主要介绍Linuxsamba共享慢的原因及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux samba共享慢原因及解决问题表现原因解决办法总结Linandroidux samba共享慢原因及解决

新特性抢先看! Ubuntu 25.04 Beta 发布:Linux 6.14 内核

《新特性抢先看!Ubuntu25.04Beta发布:Linux6.14内核》Canonical公司近日发布了Ubuntu25.04Beta版,这一版本被赋予了一个活泼的代号——“Plu... Canonical 昨日(3 月 27 日)放出了 Beta 版 Ubuntu 25.04 系统镜像,代号“Pluc

Linux安装MySQL的教程

《Linux安装MySQL的教程》:本文主要介绍Linux安装MySQL的教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux安装mysql1.Mysql官网2.我的存放路径3.解压mysql文件到当前目录4.重命名一下5.创建mysql用户组和用户并修