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

相关文章

Linux进程CPU绑定优化与实践过程

《Linux进程CPU绑定优化与实践过程》Linux支持进程绑定至特定CPU核心,通过sched_setaffinity系统调用和taskset工具实现,优化缓存效率与上下文切换,提升多核计算性能,适... 目录1. 多核处理器及并行计算概念1.1 多核处理器架构概述1.2 并行计算的含义及重要性1.3 并

Linux线程之线程的创建、属性、回收、退出、取消方式

《Linux线程之线程的创建、属性、回收、退出、取消方式》文章总结了线程管理核心知识:线程号唯一、创建方式、属性设置(如分离状态与栈大小)、回收机制(join/detach)、退出方法(返回/pthr... 目录1. 线程号2. 线程的创建3. 线程属性4. 线程的回收5. 线程的退出6. 线程的取消7.

Linux下进程的CPU配置与线程绑定过程

《Linux下进程的CPU配置与线程绑定过程》本文介绍Linux系统中基于进程和线程的CPU配置方法,通过taskset命令和pthread库调整亲和力,将进程/线程绑定到特定CPU核心以优化资源分配... 目录1 基于进程的CPU配置1.1 对CPU亲和力的配置1.2 绑定进程到指定CPU核上运行2 基于

golang程序打包成脚本部署到Linux系统方式

《golang程序打包成脚本部署到Linux系统方式》Golang程序通过本地编译(设置GOOS为linux生成无后缀二进制文件),上传至Linux服务器后赋权执行,使用nohup命令实现后台运行,完... 目录本地编译golang程序上传Golang二进制文件到linux服务器总结本地编译Golang程序

Linux下删除乱码文件和目录的实现方式

《Linux下删除乱码文件和目录的实现方式》:本文主要介绍Linux下删除乱码文件和目录的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux下删除乱码文件和目录方法1方法2总结Linux下删除乱码文件和目录方法1使用ls -i命令找到文件或目录

Linux在线解压jar包的实现方式

《Linux在线解压jar包的实现方式》:本文主要介绍Linux在线解压jar包的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux在线解压jar包解压 jar包的步骤总结Linux在线解压jar包在 Centos 中解压 jar 包可以使用 u

linux解压缩 xxx.jar文件进行内部操作过程

《linux解压缩xxx.jar文件进行内部操作过程》:本文主要介绍linux解压缩xxx.jar文件进行内部操作,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、解压文件二、压缩文件总结一、解压文件1、把 xxx.jar 文件放在服务器上,并进入当前目录#

Linux系统性能检测命令详解

《Linux系统性能检测命令详解》本文介绍了Linux系统常用的监控命令(如top、vmstat、iostat、htop等)及其参数功能,涵盖进程状态、内存使用、磁盘I/O、系统负载等多维度资源监控,... 目录toppsuptimevmstatIOStatiotopslabtophtopdstatnmon

在Linux中改变echo输出颜色的实现方法

《在Linux中改变echo输出颜色的实现方法》在Linux系统的命令行环境下,为了使输出信息更加清晰、突出,便于用户快速识别和区分不同类型的信息,常常需要改变echo命令的输出颜色,所以本文给大家介... 目python录在linux中改变echo输出颜色的方法技术背景实现步骤使用ANSI转义码使用tpu

linux hostname设置全过程

《linuxhostname设置全过程》:本文主要介绍linuxhostname设置全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录查询hostname设置步骤其它相关点hostid/etc/hostsEDChina编程A工具license破解注意事项总结以RHE