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磁盘分区、格式化和挂载方式

《Linux磁盘分区、格式化和挂载方式》本文详细介绍了Linux系统中磁盘分区、格式化和挂载的基本操作步骤和命令,包括MBR和GPT分区表的区别、fdisk和gdisk命令的使用、常见的文件系统格式以... 目录一、磁盘分区表分类二、fdisk命令创建分区1、交互式的命令2、分区主分区3、创建扩展分区,然后

Linux中chmod权限设置方式

《Linux中chmod权限设置方式》本文介绍了Linux系统中文件和目录权限的设置方法,包括chmod、chown和chgrp命令的使用,以及权限模式和符号模式的详细说明,通过这些命令,用户可以灵活... 目录设置基本权限命令:chmod1、权限介绍2、chmod命令常见用法和示例3、文件权限详解4、ch

Linux内核之内核裁剪详解

《Linux内核之内核裁剪详解》Linux内核裁剪是通过移除不必要的功能和模块,调整配置参数来优化内核,以满足特定需求,裁剪的方法包括使用配置选项、模块化设计和优化配置参数,图形裁剪工具如makeme... 目录简介一、 裁剪的原因二、裁剪的方法三、图形裁剪工具四、操作说明五、make menuconfig

Linux使用nohup命令在后台运行脚本

《Linux使用nohup命令在后台运行脚本》在Linux或类Unix系统中,后台运行脚本是一项非常实用的技能,尤其适用于需要长时间运行的任务或服务,本文我们来看看如何使用nohup命令在后台... 目录nohup 命令简介基本用法输出重定向& 符号的作用后台进程的特点注意事项实际应用场景长时间运行的任务服

什么是cron? Linux系统下Cron定时任务使用指南

《什么是cron?Linux系统下Cron定时任务使用指南》在日常的Linux系统管理和维护中,定时执行任务是非常常见的需求,你可能需要每天执行备份任务、清理系统日志或运行特定的脚本,而不想每天... 在管理 linux 服务器的过程中,总有一些任务需要我们定期或重复执行。就比如备份任务,通常会选在服务器资

Linux限制ip访问的解决方案

《Linux限制ip访问的解决方案》为了修复安全扫描中发现的漏洞,我们需要对某些服务设置访问限制,具体来说,就是要确保只有指定的内部IP地址能够访问这些服务,所以本文给大家介绍了Linux限制ip访问... 目录背景:解决方案:使用Firewalld防火墙规则验证方法深度了解防火墙逻辑应用场景与扩展背景:

Linux下MySQL8.0.26安装教程

《Linux下MySQL8.0.26安装教程》文章详细介绍了如何在Linux系统上安装和配置MySQL,包括下载、解压、安装依赖、启动服务、获取默认密码、设置密码、支持远程登录以及创建表,感兴趣的朋友... 目录1.找到官网下载位置1.访问mysql存档2.下载社区版3.百度网盘中2.linux安装配置1.

Linux使用粘滞位 (t-bit)共享文件的方法教程

《Linux使用粘滞位(t-bit)共享文件的方法教程》在Linux系统中,共享文件是日常管理和协作中的常见任务,而粘滞位(StickyBit或t-bit)是实现共享目录安全性的重要工具之一,本文将... 目录文件共享的常见场景基础概念linux 文件权限粘滞位 (Sticky Bit)设置共享目录并配置粘

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip