9. C++通过epoll+fork的方式实现高性能网络服务器
epoll+fork 实现高性能网络服务器
一般在服务器上,CPU是多核的,上述epoll实现方式只使用了其中的一个核,造成了资源的大量浪费。因此我们可以将epoll和fork结合来实现更高性能的网络服务器。
创建子进程函数–fork( )
要了解线程我们先来了解fork()函数:fork() 函数的功能是在当前的进程创建一个子进程;在多核时代,CPU管理多个进程,一个单核CPU同一时间只能运行一个进程,比如8 核的 CPU 只能同时运行 8 个进程。但是一个进程中可以有多个线程并行运行。
线程
为什么要有线程
首先线程不是一开始就被提出来的技术概念!!而是由历史的发展而来的,也就是说我们现在研究的是线程的动机是什么!
打个比喻就是一个引用程序要做很多工作!如web浏览器,又要显示图片,文字,视频的!
假如这三个动作是顺序执行的,也就是说,一个网页显示完图片再显示文字,再显示视频,那么很明显这对用户来说是体验非常不好的,这样对cpu的利用也不高!
那么此时,就引入了进程的概念!我们希望这些三个动作,也就是文字,图片,视频能够“同时”的显示在网页上,那么就是说这三个程序需要并发或者并行(能并行那是因为有多个cpu)执行,此时,我们的网页就可以”同时“显示这三个内容!因为并发的进程是走走停停,交替执行,这个速度很快,快到我们人认为是同时进行的!此时,我们把这些能够同时执行的任务成为”执行流“,也就是说,在进程的概念中,执行流就是进程!,这里又文字,图片,视频三个执行流!很明显我们知道进程的创建和切换,也就是说并发执行是很耗时耗费资源的!
所以我们又提出了线程的概念,也就是说我们能否在一个进程中,执行这三执行流,其实可以的!
线程就是在一个进程中的一个执行流!有线程的概念我们就可以在一个进程执行这三个任务,不需要创建多个进程,并且进行进程切换!我们的线程在一个进程中,可以并发或者并行的执行!这样就大大减少了资源开销!
从内存块的角度理解线程
- 比如一个单线程的进程,其实他就等价于一个进程中的任务!和进程区别不大!这个线程(执行流)共享进程的代码段,数据段,打开文件的信息等内容!同时进程的栈空间也是线程的栈空间!
- 假如有多线程的进程,比如三个线程:说明:这个进程中有三个执行流,这个三个会有三个不同的空间,但是都属于一个进程中,它们有自己的栈空间,能够单独的执行自己的任务!但是这三个线程共享一个进程中的代码段,数据段,打开文件的信息等。
- 共享带来的好处就是访问这些共享资源的代价低,存储资源节省!不再需要进程那样又要多一份空间存储资源!
线程就是cpu调度的单位了,而进程就是资源分配的单位了,因为即使一个进程只有一个线程,真正执行的还是进程中的线程!
多线程模型
M:1模型
也就是多个用户线程对一个内核线程!
这种模型的好处就是,对于用户来说,它看的多个线程在并行执行!
在实际来说,多个线程占用一个内核线程,这个意思就是,用户线程中有一个线程占用了cpu资源,那么其他的用户线程就不可以执行,只能进入等待状态了!
1:1模型
一个用户线程对于一个内核线程,假如内核线程和用户线程数量不匹配的话,那么就会开多内核线程和用户线程匹配起来
好处就是多个线程真正意义上实现了并发或并行执行;
缺点就是:内核开销很大!
epoll+fork代码
这个代码就是每次fork一个进程,然后在每个线程里面可以用epoll申请多个进程来进行监听。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <iostream>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
//端口
#define PORT 8888
#define MESSAGE_LEN 1024
#define MAX_EVENTS 20
#define TIMEOUT 500
#define MAX_PROCESS 4int main(int argc,char* argv[]){int ret=-1;int on=1;int backlog=10;//缓冲区大小int socket_fd,accept_fd;struct sockaddr_in localaddr,remoteaddr;char in_buff[MESSAGE_LEN]={0,};int epoll_fd;struct epoll_event ev,events[MAX_EVENTS];//epoll中event的结构体int event_number;int flags = 1;pid_t pid=-1;socket_fd=socket(AF_INET,SOCK_STREAM,0);if(socket_fd==-1){std::cout<<"Failed to create socket!"<<std::endl;exit(-1);}//创建了socket之后我们要设置成异步的flags = fcntl(socket_fd,F_GETFL,0);//然后设置成非阻塞fcntl(socket_fd,F_SETFL,flags | O_NONBLOCK);ret=setsockopt(socket_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));if(ret==-1){std::cout<<"Failed to set socket options!"<<std::endl;}localaddr.sin_family=AF_INET;//地址族localaddr.sin_port=htons(PORT);//端口号localaddr.sin_addr.s_addr=INADDR_ANY;//这个就是0bzero(&(localaddr.sin_zero), 8);ret= bind(socket_fd,(struct sockaddr *)&localaddr,sizeof(struct sockaddr));//绑定if(ret==-1){//绑定失败std::cout<<"Failed to bind addr!"<<std::endl;exit(-1);}ret = listen(socket_fd,backlog);//第二个是缓冲区大小,因为同一时间只能处理一个,其他都放在缓冲区if(ret==-1){std::cout<<"failed to listen socket!"<<std::endl;exit(-1);}for(int i=0;i<MAX_PROCESS;i++){//这个一般是cup数*2+1if(pid!=0){//pid==0代表着子进程,第一次等于-1,就是父进程,然后fork一个子进程pid=fork();//父进程,fork出来一个子进程}}if(pid==0){//创建epoll,再每个进程下面都可以创建epoll,每个进程自己使用自己的epollepoll_fd = epoll_create(256);//先将侦听的socket_fd添加进去,然后再将与数据通讯的客户端的socket_fd添加进去ev.events=EPOLLIN;//对于侦听的这个事件来说就是输入,就是in,这个一般不变成边缘触发,为了保证所有来的都能连上ev.data.fd=socket_fd;//这个就是文件描述符socketepoll_ctl(epoll_fd,EPOLL_CTL_ADD,socket_fd,&ev);while(1){//等待连接event_number = epoll_wait(epoll_fd,events,MAX_EVENTS,TIMEOUT);//发生事件的个数for(int i=0;i<event_number;i++){//有多少个文件描述符发生事件了if(events[i].data.fd==socket_fd){//如果这个是侦听的socket发生事件了,那么说明是来了新的连接std::cout<<"listen event..."<<std::endl;socklen_t addr_len=sizeof(struct sockaddr);accept_fd = accept(socket_fd,(struct sockaddr *)&remoteaddr,&addr_len);//设置成非阻塞//创建了socket之后我们要设置成异步的flags = fcntl(accept_fd,F_GETFL,0);//然后设置成非阻塞fcntl(accept_fd,F_SETFL,flags | O_NONBLOCK);ev.events=EPOLLIN | EPOLLET;//|上边缘触发ev.data.fd=accept_fd;epoll_ctl(epoll_fd,EPOLL_CTL_ADD,accept_fd,&ev);//将accept_fd添加到epoll中去}else if(events[i].events&EPOLLIN){//这里只介绍读的do{memset(in_buff, 0, sizeof(in_buff));//接收消息ret = recv(events[i].data.fd,(void *)in_buff,MESSAGE_LEN,0);if(ret==0){close(events[i].data.fd);}if(ret==MESSAGE_LEN){//缓冲区满了std::cout<<"maybe have data..."<<std::endl;}}while(ret<-1&&errno==EINTR);if(ret<0){switch(errno){case EAGAIN:break;dafault:break;}}if(ret>0){//打印信息std::cout<<"receive messaage:"<<in_buff<<std::endl;//返回消息send(events[i].data.fd,(void*)in_buff,MESSAGE_LEN,0);}}}}}else{//pid!=0,父进程do{//这时候父进程等待所有的子进程完成pid=waitpid(-1,NULL,0);}while(pid!=-1);}std::cout<<"quit servet...\n"<<std::endl;close(socket_fd);return 0;
}
异步事件的惊群现象
参考文献https://blog.csdn.net/m0_46606290/article/details/120939528
相关文章:

9. C++通过epoll+fork的方式实现高性能网络服务器
epollfork 实现高性能网络服务器 一般在服务器上,CPU是多核的,上述epoll实现方式只使用了其中的一个核,造成了资源的大量浪费。因此我们可以将epoll和fork结合来实现更高性能的网络服务器。 创建子进程函数–fork( ) 要了解线程我们先来了解…...

【Mac】XMind for mac(XMind思维导图)v24.04.10311软件介绍和安装教程
软件介绍 XMind for Mac是一款功能强大的思维导图软件。它具有以下主要特点: 1.多样化的思维导图功能:XMind for Mac提供了丰富的思维导图编辑功能,用户可以创建各种类型的思维导图,包括组织结构图、逻辑图、时间轴图等…...

使用 Django ORM 进行数据库操作
文章目录 创建Django项目和应用定义模型查询数据更新和删除数据总结与进阶聚合和注解跨模型查询原始SQL查询 Django是一个流行的Web应用程序框架,它提供了一个强大且易于使用的对象关系映射(ORM)工具,用于与数据库进行交互。在本文…...

行为型设计模式之模板模式
文章目录 概述原理结构图实现 小结 概述 模板方法模式(template method pattern)原始定义是:在操作中定义算法的框架,将一些步骤推迟到子类中。模板方法让子类在不改变算法结构的情况下重新定义算法的某些步骤。 模板方法中的算法可以理解为广义上的业…...

大泽动力车载柴油发电机的特点和优势有哪些
大泽动力车载柴油发电机具有一系列显著的特点和优势,以下是对其的详细介绍: 低噪音性能:大泽动力车载柴油发电机具备明显的低噪音性能,其噪音限值在距离机组7米处测得为70dB(A),这为用户提供了一个相对安静的工作环境…...

基于 IP 的 DDOS 攻击实验
一、介绍 基于IP的分布式拒绝服务(Distributed Denial of Service, DDoS)攻击是一种利用大量受控设备(通常是僵尸网络)向目标系统发送大量请求或数据包,以耗尽目标系统的资源,导致其无法正常提供服务的攻击…...

GPT-4o如何重塑AI未来!
如何评价GPT-4o? 简介:最近,GPT-4o横空出世。对GPT-4o这一人工智能技术进行评价,包括版本间的对比分析、GPT-4o的技术能力以及个人感受等。 GPT-4o似乎是一个针对GPT-4模型进行优化的版本,它在性能、准确性、资源效率以及安全和…...
window本地域名映射修改
位置 C:\Windows\System32\drivers\etc 文件名 hosts 修改方法 复制一份到桌面 修改桌面的文件 # 前面为ip 后面为域名,域名-》ip的映射 127.0.0.1 link.com最后将修改后的文件保存,复制到C:\Windows\System32\drivers\etc替换...
【退役之重学】为什么要加入多级缓存
一、为什么 加入多级缓存是为了提高数据访问的效率和性能 二、怎么做 在多级访问系统中,数据首先会被存储在速度最快的 L1 缓存中,如果数据在 L1 缓存中未命中,则会继续在 L2 缓存 和 L3 缓存中查找,如果在所有缓存中都未命中&…...

Redis常用命令大全
目录 1、五大数据类型的基本命令 1.1 字符串 1.2 列表 1.3 哈希 1.4 集合 1.5 有序集合 2、与key相关 2.1 查看redis数据的类型 2.2 查看当前redis库中的所有key命令 3、除了五大数据类型外常见命令 3.1 键操作 3.2 服务器操作 3.3 连接操作 3.4 发布/订阅 3.5 事…...

HttpSecurity 是如何组装过滤器链的
有小伙伴们问到这个问题,简单写篇文章和大伙聊一下。 一 SecurityFilterChain 首先大伙都知道,Spring Security 里边的一堆功能都是通过 Filter 来实现的,无论是认证、RememberMe Login、会话管理、CSRF 处理等等,各种功能都是通…...

STM32 入门教程(江科大教材)#笔记2
3-4按键控制LED /** LED.c**/ #include "stm32f10x.h" // Device headervoid LED_Init(void) {/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_I…...

python zip()函数(将多个可迭代对象的元素配对,创建一个元组的迭代器)zip_longest()
文章目录 Python zip() 函数深入解析基本用法函数原型基础示例 处理不同长度的迭代器高级用法多个迭代器使用 zip() 与 dict()解压序列 注意事项内存效率:zip() 返回的是一个迭代器,这意味着直到迭代发生前,元素不会被消耗。这使得 zip() 特别…...
React.forwardRef 使用
React.forwardRef 是一个React提供的高阶组件函数,用于向函数组件传递ref。在函数组件中无法直接访问ref,如果需要在函数组件中操作子组件的DOM元素或组件实例,就可以使用React.forwardRef来转发ref给子组件。 当使用React.forwardRef包裹一…...
C# 中的值类型与引用类型:内存大小解析
在 C# 中,类型可以被归类为值类型或引用类型,它们在内存中的存储和管理方式不同。了解这些差异对于优化程序性能和资源管理至关重要。 值类型 (Value Types) 值类型包括所有内置的数值类型(如 int, double 等)、char 类型、bool…...
object对象列表使用sorted函数按照对象的某个字段排序
在Python中,如果你想要根据列表中对象的某个属性(比如create_time)来进行逆序排序,你可以使用sorted()函数并指定一个key参数。key参数应该是一个函数,该函数接受一个列表元素并返回一个用于排序的值。 假设你的objec…...

【再探】设计模式—中介者模式、观察者模式及模板方法模式
中介者模式让多对多的复杂引用关系变成一对多,同时能通过中间类来封装多个类中的行为,观察者模式在目标状态更新时能自动通知给订阅者,模版方法模式则是控制方法的执行顺序,子类在不改变算法的结构基础上可以扩展功能实现。 1 中…...

vue中使用svg图像
一 、svg图像是什么 SVG(可缩放矢量图形)是一种图像格式,它以XML文档的形式存在,用以描述图像中的形状、线条、文本和颜色等元素。由于其基于矢量的特性,SVG图像在放大或改变尺寸时能够保持图形质量不受影响。这种格式…...

Deconfounding Duration Bias in Watch-time Prediction for Video Recommendation
Abstract 观看时间预测仍然是通过视频推荐加强用户粘性的关键因素。然而,观看时间的预测不仅取决于用户与视频的匹配,而且经常被视频本身的持续时间所误导。为了提高观看时间,推荐总是偏向于长时间的视频。在这种不平衡的数据上训练的模型面…...
python多进程
python多进程的使用有两种方式: multiprocessingconcurrent的使用方式 multiprocessing的使用方式 定义线程池的数量开始处理,结果回调 下面以多进程下载图像为例: import multiprocessing import requests from io import BytesIO from…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...

ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
在树莓派上添加音频输入设备的几种方法
在树莓派上添加音频输入设备可以通过以下步骤完成,具体方法取决于设备类型(如USB麦克风、3.5mm接口麦克风或HDMI音频输入)。以下是详细指南: 1. 连接音频输入设备 USB麦克风/声卡:直接插入树莓派的USB接口。3.5mm麦克…...

Unity中的transform.up
2025年6月8日,周日下午 在Unity中,transform.up是Transform组件的一个属性,表示游戏对象在世界空间中的“上”方向(Y轴正方向),且会随对象旋转动态变化。以下是关键点解析: 基本定义 transfor…...
【WebSocket】SpringBoot项目中使用WebSocket
1. 导入坐标 如果springboot父工程没有加入websocket的起步依赖,添加它的坐标的时候需要带上版本号。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dep…...
命令行关闭Windows防火墙
命令行关闭Windows防火墙 引言一、防火墙:被低估的"智能安检员"二、优先尝试!90%问题无需关闭防火墙方案1:程序白名单(解决软件误拦截)方案2:开放特定端口(解决网游/开发端口不通)三、命令行极速关闭方案方法一:PowerShell(推荐Win10/11)方法二:CMD命令…...