Sylar C++高性能服务器学习记录11 【IO调度模块-知识储备篇】

早在19年5月就在某站上看到sylar的视频了,一直认为这是一个非常不错的视频,由于本人一直是自学编程,基础不扎实,也没有任何人的督促,没能坚持下去,每每想起倍感惋惜。恰逢互联网寒冬,在家无事,遂提笔再续前缘。

为了能更好的看懂sylar,本套笔记会分两步走,每个系统都会分为两篇博客。
分别是【知识储备篇】和【代码分析篇】
(ps:纯粹做笔记的形式给自己记录下,欢迎大家评论,不足之处请多多赐教)
QQ交流群:957100923


IO调度模块-知识储备篇

一、select

sockfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&addr, 0, sizeof (addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(2000);
addr.sin_addr.s_addr = INADDR_ANY;
bind(sockfd,(struct sockaddr*)&addr ,sizeof(addr));
listen (sockfd, 5); for (i=0;i<5;i++) {memset(&client, 0, sizeof (client));addrlen = sizeof(client);fds[i] = accept(sockfd,(struct sockaddr*)&client, &addrlen);if(fds[i] > max)max = fds[i];
}//-------------------------------------------------------------while(1){FD_ZERO(&rset);for (i = 0; i< 5; i++ ) {FD_SET(fds[i],&rset);}puts("round again");select(max+1, &rset, NULL, NULL, NULL);for(i=0;i<5;i++) {if (FD_ISSET(fds[i], &rset)){memset(buffer,0,MAXBUF);read(fds[i], buffer, MAXBUF);puts(buffer);}}	
}

我们对代码进行分析

FD_ZERO(&rset);
for (i = 0; i< 5; i++ ) {FD_SET(fds[i],&rset);
}

可以看到每次循环都需要重置每一个fd的状态,这里不是很合理

//我们循环创建5个文件句柄,存放到 fds 数组中
for (i=0;i<5;i++) {memset(&client, 0, sizeof (client));addrlen = sizeof(client);//通过这行代码我们会得到一个随机的文件句柄,默认是1024以内的fds[i] = accept(sockfd,(struct sockaddr*)&client, &addrlen);//由于select是使用bitmap来标记对应fd是否有消息,所以我们需要知道fd的最大值if(fds[i] > max)max = fds[i];
}

如果我们得到的fds是[3,9,7,2]
那么我们就需要计算出用来标记的bitmap最大尺寸就是max = 9+1
也就是 0000000000 这样10个位置

select(max+1, &rset, NULL, NULL, NULL);

当我们调用select方法时,bitmap就会做以下的转换:
0000000000
0011000101
其中1的位置表示有fd的存在也就是位置被坐了,0表示没有fd也就是位置空着
其中的rset会从用户态拷贝一份到内核态执行,存在一定的开销

//当句柄发生变化是需要遍历全部的句柄查看是哪个fd有数据
for(i=0;i<5;i++) {if (FD_ISSET(fds[i], &rset)){memset(buffer,0,MAXBUF);read(fds[i], buffer, MAXBUF);puts(buffer);}
}	

所以我们可以得出select有以下几个缺点:

  1. 监听有上限,默认是 1024个
  2. FDset不可重用,需要每次都循环重置
  3. 判断某个fd是否有信息需要遍历全部的fd
  4. select的底层实现上需要将rset由用户态拷贝到内核态执行,所以有拷贝操作的开销

二、poll

for (i=0;i<5;i++) {memset(&client, 0, sizeof (client));addrlen = sizeof(client);pollfds[i].fd = accept(sockfd,(struct sockaddr*)&client, &addrlen);pollfds[i].events = POLLIN;
}
sleep(1);//-------------------------------------------------------------------while(1){puts("round again");poll(pollfds, 5, 50000);for(i=0;i<5;i++) {if (pollfds[i].revents & POLLIN){pollfds[i].revents = 0;memset(buffer,0,MAXBUF);read(pollfds[i].fd, buffer, MAXBUF);puts(buffer);}}
}

以上代码我们可以看到,监听的fd没有长度限制
我们的fd被包装成了结构体,这样取消了rset用revents来代替,所以只需要重置revents就可以

struct pollfd {int fd;			//文件句柄short events;	//操作类型short revents;	//状态
};

所以虽然poll比起select优化了一些,但是还存在以下缺点:

  1. 判断某个fd是否有信息需要遍历全部的fd
  2. poll的底层实现上还是需要将信息从用户态拷贝到内核态,所以有一定的开销

三、epoll

struct epoll_event events[5];int epfd = epoll_create(10);......for (i=0;i<5;i++) {static struct epoll_event ev;memset(&client, 0, sizeof (client));addrlen = sizeof(client);ev.data.fd = accept(sockfd,(struct sockaddr*)&client, &addrlen);ev.events = EPOLLIN;epoll_ctl(epfd, EPOLL_CTL_ADD, ev.data.fd, &ev); }//--------------------------------------------------------------------  while(1){puts("round again");nfds = epoll_wait(epfd, events, 5, 10000);for(i=0;i<nfds;i++) {memset(buffer,0,MAXBUF);read(events[i].data.fd, buffer, MAXBUF);puts(buffer);}
}

epoll取消了标记位,那么如何告知用户哪些fd有消息呢?

nfds = epoll_wait(epfd, events, 5, 10000);

这里epoll_wait有返回值,返回的值表示有信息的fd的个数。
难道通过个数就能知道哪些fd有信息了吗?显然不行。
但是epoll_wait会将events进行排序,将有消息的排到前面,
这样一来只要知道个数就能知道哪些fd有消息了。
假如有以下5个fd:
[fd1,fd2,fd3,fd4,fd5]

如果这时候 fd2和fd5有消息来了就会变成:
[fd2,fd5,fd1,fd3,fd4]
并且告诉你有2个消息

那么你就可以直接取出数组中的前两个就行了。

而且epoll的底层实现上共用了events,无需从用户态切换到内核态,较少了不必要的开销。


参考资料: 【IO多路复用】强烈建议花30分钟看一看。


四、epoll的API介绍

1.方法列表
//头文件
#include <sys/epoll.h>  //创建一个epoll实例 
int epoll_create(int size);   //控制epoll上的事件
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);  //阻塞等待事件发生
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout); 
2.epoll_event
typedef union epoll_data {void *ptr;int fd;uint32_t u32;uint64_t u64;
} epoll_data_t;struct epoll_event {uint32_t events;	epoll_data_t data;
}

其中的events有以下几种:

EPOLLIN:可读事件,表示连接上有数据可读。
EPOLLOUT:可写事件,表示连接上可以写入数据。
EPOLLPRI:紧急事件,表示连接上有紧急数据可读。
EPOLLRDHUP:连接关闭事件,表示连接已关闭。
EPOLLERR:错误事件,表示连接上发生错误。
EPOLLHUP:挂起事件,表示连接被挂起。
3.epoll_create
//创建一个epoll fd,返回一个新的epoll文件描述符。
//参数size用于指定监听的文件描述符个数,但是在Linux 2.6.8之后的版本,该参数已经没有实际意义。传入一个大于0的值即可。
int epfd=epoll_create(1);
4.epoll_ctl
//用于向epoll实例中添加、修改或删除关注的文件描述符和对应事件
//epfd:epoll文件描述符,通过epoll_create函数创建获得。
//op:操作类型,可以是以下三种取值之一:
//EPOLL_CTL_ADD:将文件描述符添加到epoll实例中。
//EPOLL_CTL_MOD:修改已添加到epoll实例中的文件描述符的关注事件。
//EPOLL_CTL_DEL:从epoll实例中删除文件描述符。
//fd:要控制的文件描述符。
//event:指向epoll_event结构体的指针,用于指定要添加、修改或删除的事件。
//成功时返回0,表示操作成功。
//失败时返回-1,并设置errno错误码来指示具体错误原因。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
5.epoll_wait
//epfd:epoll文件描述符,通过epoll_create函数创建获得。
//events:用于接收事件的epoll_event结构体数组。
//maxevents:events数组的大小,表示最多可以接收多少个事件。
//timeout:超时时间,单位为毫秒,表示epoll_wait函数阻塞的最长时间。常用的取值有以下三种:
//-1:表示一直阻塞,直到有事件发生。
//0:表示立即返回,不管有没有事件发生。
//> 0:表示等待指定的时间(以毫秒为单位),如果在指定时间内没有事件发生,则返回。
//成功时返回接收到的事件的数量。如果超时时间为0并且没有事件发生,则返回0。
//失败时返回-1,并设置errno错误码来指示具体错误原因。
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
6.工作模式

水平触发(Level Triggered, LT)模式(默认)
有事件就一直不断通知

  1. 当被监控的文件描述符上的状态发生变化时,epoll会不断通知应用程序,直到应用程序处理完事件并返回。
  2. 如果应用程序没有处理完事件,而文件描述符上的状态再次发生变化,epoll会再次通知应用程序。
  3. 应用程序可以使用阻塞或非阻塞I/O来处理事件。
  4. 水平触发模式适合处理低并发的I/O场景。
int epfd=epoll_create(1);
struct epoll_event even;
even.events=EPOLLIN;  //用水平触发模式来检测
even.data.fd=lfd;

边缘触发(Edge Triggered, ET)模式
有事件只通知一次,后续一次处理没解决完的内容需要程序员自己解决

  1. 仅当被监控的文件描述符上的状态发生变化时,epoll才会通知应用程序。
  2. 当文件描述符上有数据可读或可写时,epoll会立即通知应用程序,并且保证应用程序能够全部读取或写入数据,直到读写缓冲区为空。
  3. 应用程序需要使用非阻塞I/O来处理事件,以避免阻塞其他文件描述符的事件通知。
  4. 边缘触发模式适合处理高并发的网络通信场景。
int epfd = epoll_create(1);
struct epoll_event even;
even.events = EPOLLIN | EPOLLET; //使用边缘触发模式检测
even.data.fd = lfd;

五、总结

epoll用法很简单,了解了他的概念和使用方法就可以了,具体的内部实现我希望不要过分深入,毕竟我们要学的东西很多。

【最后求关注、点赞、转发】
QQ交流群:957100923

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/9392.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何利用SSL证书让IP实现HTTPS安全访问

在互联网日益发展的今天&#xff0c;数据安全与隐私保护成为了用户和企业共同关注的焦点。HTTPS&#xff08;超文本传输安全协议&#xff09;作为一种广泛采用的安全通信协议&#xff0c;通过加密数据传输&#xff0c;为网站访问提供了安全保障。然而&#xff0c;要实现HTTPS访…

uniapp百度地图聚合

// loadBMap.js ak 百度key export default function loadBMap(ak) {return new Promise((resolve, reject) > {//聚合API依赖基础库,因此先加载基础库再加载聚合APIasyncLoadBaiduJs(ak).then(() > {// 调用加载第三方组件js公共方法加载其他资源库// 加载聚合API// Ma…

Oracle数据库如何插入平方(²)立方(³)字符

第一步&#xff1a;创建数据表&#xff0c;字段一定要是NVARCHAR2类型的 第二步&#xff0c;插入数据用 unistr(1\00b3) 形式的写法 00b3 代表m&#xff0c;00b2代表㎡ SELECT * FROM TESTABC; UPDATE TESTABC set NAME1unistr(1\00b3); UPDATE TESTABC set NAME2unistr(2\00b2…

微信小程序里的地理导航:百度地图API与路径规划实战

微信小程序里的地理导航&#xff1a;百度地图API与路径规划实战 引言&#xff1a;百度地图API与微信小程序的融合准备工作&#xff1a;申请密钥与引入SDK1. 注册百度地图开放平台账号2. 引入百度地图SDK 展示地图&#xff1a;初次接触百度地图组件地图组件基础定位当前用户位置…

【OceanBase诊断调优】—— checksum error ret=-4103 问题排查

适用版本 OceanBase 数据库所有版本。 什么是 checksum data checksum&#xff1a;一个 SSTable 中所有宏块内存二进制计算出来的 checksum 值。反映了宏块中的数据和数据分布情况。如果宏块中数据一致但是数据分布不一致&#xff0c;计算出来的 checksum 也不相等。 column…

【LLM多模态】MiniGPT4模型结构和训练流程

note 图生文应用场景&#xff1a;比如电商领域根据产品图像生成产品描述、娱乐领域中根据电影海报生成电影介绍等MiniGPT-4将预训练的大语言模型和视觉编码器参数同时冻结&#xff0c;只需要单独训练线性投影层&#xff0c;使视觉特征和语言模型对齐。MiniGPT4的视觉编码器&am…

centos 8.5 Node v20.12.2 npm 安装及环境配置 配置淘宝最新镜像地址

1下载&#xff1a;Node.js — Download Node.js 2文件上传到服务器 rootlocalhost software]# tar xvf node-v20.12.2-linux-x64.tar.xz [rootlocalhost software]# mv node-v20.12.2-linux-x64/ /usr/local/node [rootlocalhost software]# vim /etc/profile export PA…

VSCode:设置搜索时的排除目录

VSCode搜索时默认会搜索目录下所有文件 $ tree . ├── a.c ├── m.c └── x └── b.c //a.c #include <stdio.h> #include <string.h>int main() {char s[] "hello\n";fprintf(stdout, s, strlen(s));return 0; } //m.c #include <stdio…

【LSTM】LSTM网络及参数学习笔记

图1 LSTM模型结构可视化 [6]. 图2 LSTM cell结构说明 图3 LSTM cell和num_units说明 [4]. 图4 LSTM的网络结构 1. LSTM 是对一个LSTM层的抽象&#xff0c;可以看成是由多个LSTM cell组成&#xff0c;是包含时间步的一个网络 2. LSTM cell 图2是LSTM在时间步上的结构&#xf…

【硬件开发】原型设计对于成功产品开发重要性及常见阶段

电子产品的设计与开发始于原型制作阶段。这些原型虽可能与最终产品极为相似&#xff0c;但总带有实验性质&#xff0c;因为电子原型的制作过程包括对新概念、新思想及新解决方案的测试。虽然存在出错的风险&#xff0c;跳过这一阶段可能会导致不必要的开支。不擅长电子硬件设计…

PELCO-D相机云台控制协议

pelco pelco D云台控制协议 参考手册 PELCO-D协议手册PELCO-D命令列表 PELCO-D格式 Pelco-D是由7个十六进制字节组成&#xff08;除非另有说明&#xff0c;本页中使用的所有字节数据均为十六进制格式&#xff09;。 Byte1Byte2Byte3Byte4Byte5Byte6Byte7Sync同步字节控制…

packageKit学习(一)

最近在学习packagekit&#xff0c;本系列主要讲述&#xff0c;如何使用packageKit接口。 1. 导入依赖 在使用packageKit 之前需要导入一些依赖和安装一些包&#xff0c;不然会报错&#xff0c;以下以报错信息讲解&#xff1a; cmakelist demo/updatesystemdemo/mainwindow.cpp…

element-ui 图片(图片压缩)与文件上传设置【添加请求头信息】

1.图片上传 <template><div><el-upload class"avatar-uploader" :action"upload /Api/upload" :show-file-list"false" :on-success"upSuccess":before-upload"beforeAvatarUpload" :on-exceed"handl…

2024年想要开一家抖音小店,需要多少钱?一篇详解!

大家好&#xff0c;我是电商糖果 随着抖音卖货的持续火爆&#xff0c;抖音小店也成了电商行业讨论度最大的项目之一。 不少朋友都想知道&#xff0c;如果今年开抖音小店大概需要多少钱。 糖果做小店的时间也比较长&#xff0c;也经营了多家小店。 对于开一家抖音小店需要多…

MADbench2

MADbench2 MADbench2是一款用于测试大规模并行架构的I/O、通信和计算子系统在真实科学应用压力下的综合性能的工具。 MADbench2 基于 MADspec 代码&#xff0c;该代码根据天空的噪声像素化图及其像素-像素噪声相关矩阵计算宇宙微波背景辐射的最大似然角功率谱。MADbench2 保留…

多规格产品应该如何设置呢?

今天一用户从供应商手中拿到产品价目表&#xff0c;但是设置起来蒙圈了&#xff0c;接下来我们就一起设置一下吧&#xff5e; 一、产品价格表 我们通过供应商手中拿到产品价目表是这个样子的&#xff1a; 我们可以看到此产品的销售客价根据不同地区导致的价格不同&#xff0…

ABAP小技巧汇总(自用)

1.TIMESTAMP搜索帮助 PARAMETERS:p_begin TYPE ty_screen-date_begiu MATCHCODE OBJECT cpe_timestamp, "开始时间戳p_end TYPE ty_screen-date_end MATCHCODE OBJECT cpe_timestamp. "结束时间戳 效果&#xff1a;

Git笔记-常用指令

Git笔记-常用指令 一、概述二、仓库管理二、缓存区操作1. 添加文件到缓存区2. 取消缓存文件3. 忽略列表 三、日志状态信息四、分支操作五、六、 一、概述 这里记录一些git常用的指令。 二、仓库管理 # 本地仓库初始化 git init# 克隆仓库 git clone git_url # git clone ht…

深入剖析Java的“幽灵之手“:NullPointerException的原因、解决与创意思考

1. 原因分析 java.lang.NullPointerException&#xff08;简称NPE&#xff09;是Java程序员在编程过程中经常会遇到的“幽灵之手”&#xff0c;它在毫无预警的情况下出现&#xff0c;让程序崩溃。NPE的根源在于尝试访问或修改一个null对象的成员或方法。以下是NPE出现的几个常…

怎么选择适合Selenium使用的网络代理

Selenium可以让你使用所有主流浏览器&#xff0c;访问你想测试的任何网站或服务。这种多功能性使 Selenium 不仅在测试方面不可或缺。例如&#xff0c;你可以将 Selenium 与 Python 结合使用&#xff0c;对网站进行搜刮。当然&#xff0c;为了不被拦截&#xff0c;你需要一个代…