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中改变echo输出颜色的实现方法

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

linux hostname设置全过程

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

Linux中压缩、网络传输与系统监控工具的使用完整指南

《Linux中压缩、网络传输与系统监控工具的使用完整指南》在Linux系统管理中,压缩与传输工具是数据备份和远程协作的桥梁,而系统监控工具则是保障服务器稳定运行的眼睛,下面小编就来和大家详细介绍一下它... 目录引言一、压缩与解压:数据存储与传输的优化核心1. zip/unzip:通用压缩格式的便捷操作2.

Linux中SSH服务配置的全面指南

《Linux中SSH服务配置的全面指南》作为网络安全工程师,SSH(SecureShell)服务的安全配置是我们日常工作中不可忽视的重要环节,本文将从基础配置到高级安全加固,全面解析SSH服务的各项参... 目录概述基础配置详解端口与监听设置主机密钥配置认证机制强化禁用密码认证禁止root直接登录实现双因素

在Linux终端中统计非二进制文件行数的实现方法

《在Linux终端中统计非二进制文件行数的实现方法》在Linux系统中,有时需要统计非二进制文件(如CSV、TXT文件)的行数,而不希望手动打开文件进行查看,例如,在处理大型日志文件、数据文件时,了解... 目录在linux终端中统计非二进制文件的行数技术背景实现步骤1. 使用wc命令2. 使用grep命令

Linux如何快速检查服务器的硬件配置和性能指标

《Linux如何快速检查服务器的硬件配置和性能指标》在运维和开发工作中,我们经常需要快速检查Linux服务器的硬件配置和性能指标,本文将以CentOS为例,介绍如何通过命令行快速获取这些关键信息,... 目录引言一、查询CPU核心数编程(几C?)1. 使用 nproc(最简单)2. 使用 lscpu(详细信

linux重启命令有哪些? 7个实用的Linux系统重启命令汇总

《linux重启命令有哪些?7个实用的Linux系统重启命令汇总》Linux系统提供了多种重启命令,常用的包括shutdown-r、reboot、init6等,不同命令适用于不同场景,本文将详细... 在管理和维护 linux 服务器时,完成系统更新、故障排查或日常维护后,重启系统往往是必不可少的步骤。本文

基于Linux的ffmpeg python的关键帧抽取

《基于Linux的ffmpegpython的关键帧抽取》本文主要介绍了基于Linux的ffmpegpython的关键帧抽取,实现以按帧或时间间隔抽取关键帧,文中通过示例代码介绍的非常详细,对大家的学... 目录1.FFmpeg的环境配置1) 创建一个虚拟环境envjavascript2) ffmpeg-py

Linux脚本(shell)的使用方式

《Linux脚本(shell)的使用方式》:本文主要介绍Linux脚本(shell)的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述语法详解数学运算表达式Shell变量变量分类环境变量Shell内部变量自定义变量:定义、赋值自定义变量:引用、修改、删

Linux链表操作方式

《Linux链表操作方式》:本文主要介绍Linux链表操作方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、链表基础概念与内核链表优势二、内核链表结构与宏解析三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势六、典型应用场景七、调试技巧与