关于IO多路复用

先说总结

IO 多路复用的概念可以从网络 IO 的阻塞模型谈起。早期网络编程通常依赖阻塞的 read 函数读取数据,这会导致线程被阻塞,无法处理其他任务。为避免线程阻塞,常使用多线程来处理新的客户端连接。然而,随着客户端连接数的增加,多线程切换带来的操作系统开销逐渐成为瓶颈,难以支撑高并发的场景。为了解决这个问题,人们开始尝试单线程 + 非阻塞 read 函数的方式。

单线程轮询 + 非阻塞 read 的方案,确实能在一定程度上避免多线程带来的资源消耗与阻塞问题,但频繁的轮询操作同样会浪费系统资源,造成大量无效操作。为此,操作系统提供了 select 和 poll 函数,使程序可以传入一批文件描述符,并获取每个文件描述符的就绪状态。利用这种批量处理的方式,应用程序只需一次系统调用就能获得就绪状态。然而,select 和 poll 函数底层仍依赖遍历的方式来刷新就绪状态,带来一些限制。

具体而言,select 函数存在一些问题,比如较高的内存占用、内部实现为遍历、返回结果仍需用户逐个检查获取就绪状态等。poll 虽然解决了 select 的文件描述符数量限制问题,但与 select 的内部机制依然相似。

相较而言,epoll 函数在设计上采用了异步事件通知机制来获取就绪状态,并且在内核中保留了文件描述符的副本以避免重复分配内存。最终,epoll 返回所有就绪状态的文件描述符,从而成为 IO 多路复用的最佳方案。

综上,IO 多路复用是一种使用单个线程管理所有客户端网络事件的技术。服务器端通过一个线程调用 epoll 获取网络连接的就绪状态,然后利用多线程来处理这些已就绪的连接,从而避免了“一连接一线程”的模式,变为“一有效连接一线程”,极大提升了系统的并发效率。

以下是操作系统实现的细节&源码

网卡 – 内核缓冲区 – 用户缓冲区

# 打开一个网络通信端口,文件描述符
listenfd = socket();
# 绑定
bind(listenfd);
# 监听
listen(listenfd);
while(1){# 阻塞等待客户端建立连接connfd = accept(listenfd);# 阻塞读数据,等待客户端发送的数据就绪(数据经网卡拷贝到了内核缓冲区)int n = read(connfd,buf);# 拿到数据后的具体业务处理dosomeThing(buf);# 关闭连接,循环等待下一个连接close(connfd);
}

阻塞read函数,数据未到达(数据到达网卡并拷贝到了内核缓冲区)时阻塞

同步阻塞模型、多线程网络模型

当客户端连接后,将后续步骤丢给一个新的线程处理,即为多线程网络模型,也是同步阻塞模型

# 打开一个网络通信端口,文件描述符
listenfd = socket();
# 绑定
bind(listenfd);
# 监听
listen(listenfd);
while(1){# 阻塞等待客户端建立连接connfd = accept(listenfd);# 创建一个新的线程处理,然后立刻返回,继续下一个循环pthread_create(workNewThread);
}void workNewThread(){# 阻塞读数据,等待客户端发送的数据就绪(数据经网卡拷贝到了内核缓冲区)int n = read(connfd,buf);# 拿到数据后的具体业务处理dosomeThing(buf);# 关闭连接,循环等待下一个连接close(connfd);
}

非阻塞read函数

非阻塞read函数,当数据未就绪时,返回-1,当数据到达时(数据到达网卡,并且从网卡拷贝到内核缓冲区),阻塞读取数据(数据从内核缓冲区拷贝到用户缓冲区)

# 打开一个网络通信端口,文件描述符
listenfd = socket();
# 绑定
bind(listenfd);
# 监听
listen(listenfd);
while(1){# 阻塞等待客户端建立连接connfd = accept(listenfd);# 将连接放到数据中 addConnfdToArray(connfd);
}void loopHandle(){# 循环数据,依次取出连接,调用非阻塞readwhile(1){connfd = array[index];# 阻塞读数据,等待客户端发送的数据就绪(数据经网卡拷贝到了内核缓冲区)int n = read(connfd,buf);if(n != -1){# 拿到数据后的具体业务处理dosomeThing(buf);# 关闭连接,循环等待下一个连接close(connfd);}}
}

每次遍历遇到read返回-1时,仍然是一次浪费资源的资源调用,在while循环里做系统调用,跟分布式项目里循环做rpc调用一样,是不划算的


select函数

将一批文件描述符通过一次系统调用传给内核,由内核层遍历

int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);
// nfds:监控的文件描述符集里最大文件描述符加1
// readfds:监控有读数据到达文件描述符集合,传入传出参数
// writefds:监控写数据到达文件描述符集合,传入传出参数
// exceptfds:监控异常发生达文件描述符集合, 传入传出参数
// timeout:定时阻塞监控时间,3种情况
//  1.NULL,永远等下去
//  2.设置timeval,等待固定时间
//  3.设置timeval里时间均为0,检查描述字后立即返回,轮询

服务端处理流程

  1. 首先一个线程不断接受客户端连接,将socket文件描述符放到一个list里
  2. 另一个线程不再自己遍历,而是调用select,将这批文件描述符交给操作系统去遍历
  3. select 返回后,用户遍历list,已准备就绪的文件描述符会做上标识,用户自行判断处理

select 函数

  1. select 调用需要传入fd数组,需要拷贝一份到内核,高并发场景下这样的拷贝消耗的资源是惊人的
  2. select 在内核层仍然是通过遍历的方式检查文件描述符的就绪状态,是个同步过程,只不过无系统调用切换上下文的开销
  3. select 仅仅返回可读文件描述符的个数,具体哪个可读还是要用户自己遍历

这种方式,既做到了一个线程处理多个客户端连接(文件描述符),又减少了系统调用的开销(多个文件描述符只有一次 select 的系统调用 + n 次就绪状态的文件描述符的 read 系统调用)


poll 函数

poll 也是操作系统提供的系统调用函数

int poll(struct pollfd *fds, nfds_tnfds, int timeout);struct pollfd {intfd; /*文件描述符*/shortevents; /*监控的事件*/shortrevents; /*监控事件中满足条件返回的事件*/
};

它和select的区别,去掉了select 只能监听1024个文件描述符的限制


epoll 函数

epoll 函数解决了select 和 poll 的一些问题

  1. 内核中保存一份文件描述符集合,无需用户每次都重新传入,只需要告诉内核修改的部分即可
  2. 内核不再通过轮询的方式找到就绪的文件描述符,而是通过异步IO事件唤醒
  3. 内核仅会将有IO事件的文件描述符返回给用户,用户也无需遍历整个文件描述符集合

具体操作系统提供了三个函数

# 创建一个epoll句柄
int epoll_create(int size);# 向内核添加、修改或删除要监控的文件描述符
int epoll(int epfd,int op,int fd,struct epoll_event *event);# 类似发起了select调用
int epoll_wait(int epfd, struct epoll_event *events, int max events, int timeout);

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

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

相关文章

面试记录(1)

java中的抽象类和接口的区别: 相同点 (1) 都可以被继承 (2) 都不能被实例化 (3) 都可以包含方法声明 (4) 派生类必须实现未实现的方法 不同点 1.关键字不同: ​ ① 继承抽象类的关键字是extends,而实现接口的关键字是implements;…

【rust实战】rust博客系统3_项目目录结构及文件目录引入

项目中如何文件目录分层 blog Cargo.toml --依赖项 src main.rs --主文件 handlers --处理用户请求的函数 user_handler.rs mod.rs models --定义用户模型 user.rs mod.rs routes --定义路由 user_ro…

构建您自己的 RAG 应用程序:使用 Ollama、Python 和 ChromaDB 在本地设置 LLM 的分步指南

在数据隐私至关重要的时代,建立自己的本地语言模型 (LLM) 为公司和个人都提供了至关重要的解决方案。本教程旨在指导您完成使用 Ollama、Python 3 和 ChromaDB 创建自定义聊天机器人的过程,所有这些机器人都托管在您的系统本地。以…

聊聊Web3D 发展趋势

随着 Web 技术的不断演进,Web3D 正逐渐成为各行业数字化的重要方向。Web3D 是指在网页中展示 3D 内容的技术集合。近年来,由于 WebGL、WebGPU 等技术的发展,3D 内容已经能够直接在浏览器中渲染,为用户提供更加沉浸、互动的体验。以…

同一个页面击穿element样式后,会影响同样组件的使用

问题:同一个页面里,我用deep击穿第一个dialog后,怎么不影响第二个dialog。 解决:使用更具体的选择器 给新的对话框一个特定的类名或者ID,然后为这个类名或ID下的 .el-dialog 使用 :deep() 选择器。这样,样式…

电科金仓(人大金仓)更新授权文件(致命错误: XX000: License file expired.)

问题:电科金仓(人大金仓)数据库链接异常,重启失败,查看日志如下: 致命错误: XX000: License file expired. 位置: PostmasterMain, postmaster.c:725 解决方法: 一、下载授权文件 根据安装版本在官网下载授权文件(电科金仓-成为世界卓越的数据库产品与服务提供商)…

阿里云ECS访问GitHub解决方案

使用阿里云 ECS 访问 Github 和拉取代码时,速度非常慢,等于不可用。 本解决方案适用于墙内所有云服务器。 修改系统hosts方式 阻碍 GitHub 访问的一般手段是 DNS 污染,可以通过修改hosts的方式暂时缓解。 访问 ipaddress.com,获取github.co…

uniapp的video视频属性打包app后层级过高

问题:在使用uniapp开发APP时,使用video标签显示视频发现H5可以正常展示,但是打包到APP后,它的层级过高,把底部导航都盖住了。 官网说明:uni-app官网 官网给了cover-view组件或plus.nativeObj.view、subNVue…

考研资料分享系统的设计与实现(lw+演示+源码+运行)

摘 要 互联网发展至今,无论是其理论还是技术都已经成熟,而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播,搭配信息管理工具可以很好地为人们提供服务。针对高校教师成果信息管理混乱,出错率高,信息安全…

[perl] 数组与哈希

数组变量以 符号开始,元素放在括号内 简单举例如下 #!/usr/bin/perl names ("a1", "a2", "a3");print "\$names[0] $names[0]\n"; print "size: ",scalar names,"\n";$new_names shift(names); …

项目符合行业安全标准的必要步骤与实用建议

要保障项目符合行业安全标准,关键在于建立全面的安全管理体系、定期进行风险评估、持续培训员工,以及确保合规性文件和审核流程完整。例如,通过建立合规文件和审核流程,可以系统性地跟踪项目的安全实践和合规性,使安全…

小米15和小米15 Pro区别没那么大,但也得看准再下手

小米15和小米15 Pro区别大总结 接下来,我们将从关键差别等多个方面来分析两个机型的具体区别(Ps:只聊不一样的,没提到就是一样的): 关键差别 • 屏幕素质:小米15采用的是6.36英寸1.5K&#xf…

【科研绘图】3DMAX管状图表生成插件TubeChart使用方法

3DMAX管状图表生成插件TubeChart,一款用于制作3D管状图表的工具。可以自定义切片的数量以及随机或指定切片颜色。 【版本要求】 3dMax 2008及更高版本 【安装方法】 TubeChart插件无需安装,使用时直接拖动插件脚本文件到3dMax视口中打开即可&#xff0…

【Python爬虫实战】络爬虫完整指南:从TCP/IP协议到爬虫实践

网络爬虫完整指南:从TCP/IP协议到爬虫实践 什么是TCP/IP协议? TCP/IP协议(传输控制协议/互联网协议) 是互联网通信的核心协议套件,它定义了设备在互联网上如何通信的规则和方式。TCP/IP协议由多个层组成,其…

day05|计算机网络重难点之 HTTPS和HTTP的区别、HTTPS的工作原理(HTTPS建立连接的过程)、TCP和UDP的区别

day05|计算机网络重难点之 HTTPS和HTTP的区别、HTTPS的工作原理(HTTPS建立连接的过程)、TCP和UDP的区别 11.HTTPS和HTTP有哪些区别12.HTTPS的工作原理(HTTPS建立连接的过程)13.TCP和UDP的区别 11.HTTPS和HTTP有哪些区别 两者主要区别在于 安全性 和 数据…

力扣hot100-->递归/回溯

目录 递归/回溯 1. 17. 电话号码的字母组合 2. 22. 括号生成 3. 39. 组合总和 4. 46. 全排列 5. 78. 子集 递归/回溯 1. 17. 电话号码的字母组合 中等 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到…

服务器端请求微信登陆授权接口一直超时问题

环境: 服务器系统:centos 7.2 站点环境:nginx 遇到问题: 1、微信小程序端请求服务器登陆接口,服务端收到请求后向微信接口服务器请求数据,请求成功后返回数据给客户端,但是请求微信接口服务器经…

100种算法【Python版】第22篇——Dijkstra算法

本文目录 1 算法原理2 计算迷宫路径的步骤3 python代码:迷宫路径4 算法应用1 算法原理 Dijkstra算法由计算机科学家艾兹赫尔迪科斯彻(Edsger W. Dijkstra)于1956年提出,并于1959年发表。迪科斯彻在荷兰阿姆斯特丹的数学中心开发了这一算法,以解决最短路径问题。算法的设计…

NVR批量管理软件/平台EasyNVR多个NVR同时管理支持对接阿里云、腾讯云、天翼云、亚马逊S3云存储

随着云计算技术的日益成熟,越来越多的企业开始将其业务迁移到云端,以享受更为灵活、高效且经济的服务模式。在视频监控领域,云存储因其强大的数据处理能力和弹性扩展性,成为视频数据存储的理想选择。NVR批量管理软件/平台EasyNVR&…

Cannot read property ‘prototype’ of undefined 表单

问题就是Cannot read property ‘prototype’ of undefined 解决办法通过浏览器报错提示代码定位问题,解决问题 Vue项目中遇到视图不更新,方法不执行,埋点不触发等问题 一般解决方案查看浏览器报错,查看代码运行到那个阶段未之行…