IO多路转接

1.select

初识select

系统提供 select 函数来实现多路复用输入 / 输出模型 .
select 系统调用是用来让我们的程序监视多个文件描述符的状态变化的 ;
程序会停在 select 这里等待,直到被监视的文件描述符有一个或多个发生了状态改变 ;

select函数模型

select的函数原型如下: #include <sys/select.h>

int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

参数解释

参数 nfds 是需要监视的最大的文件描述符值 +1
rdset,wrset,exset 分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集 合及异常文件描述符的集合;
参数 timeout 为结构 timeval ,用来设置 select() 的等待时间

参数timeout取值

NULL :则表示 select ()没有 timeout select 将一直被阻塞,直到某个文件描述符上发生了事件 ;
0 :仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生。
特定的时间值:如果在指定的时间段里没有事件发生, select 将超时返回。

关于fd_set结构

其实这个结构就是一个整数数组 , 更严格的说 , 是一个 " 位图 ". 使用位图中对应的位来表示要监视的文件描述符 .
提供了一组操作 fd_set 的接口 , 来比较方便的操作位图 .

void FD_CLR(int fd, fd_set *set); // 用来清除描述词组 set 中相关 fd 的位
int FD_ISSET(int fd, fd_set *set); // 用来测试描述词组 set 中相关 fd 的位是否为真
void FD_SET(int fd, fd_set *set); // 用来设置描述词组 set 中相关 fd 的位
void FD_ZERO(fd_set *set); // 用来清除描述词组 set 的全部位

timeval结构

timeval 结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。

函数返回值

执行成功则返回文件描述词状态已改变的个数
如果返回 0 代表在描述词状态改变前已超过 timeout 时间,没有返回
当有错误发生时则返回 -1 ,错误原因存于 errno ,此时参数 readfds writefds, exceptfds timeout 的值变成不可预测。
错误值可能为:
EBADF 文件描述词为无效的或该文件已关闭
EINTR 此调用被信号所中断
EINVAL 参数 n 为负值。
ENOMEM 核心内存不足

select执行流程

理解 select 模型的关键在于理解 fd_set, 为说明方便,取 fd_set 长度为 1 字节, fd_set 中的每一 bit 可以对应一个文件描述符fd 。则 1 字节长的 fd_set 最大可以对应 8 fd
* 1 )执行 fd_set set; FD_ZERO(&set); set 用位表示是 0000,0000 * 2 )若 fd 5, 执行 FD_SET(fd,&set); 后set 变为 0001,0000( 5 位置为 1) * 3 )若再加入 fd 2 fd=1, set 变为 0001,0011 * 4 )执行select(6,&set,0,0,0)阻塞等待 * 5 )若 fd=1,fd=2 上都发生可读事件,则 select 返回,此时 set 变为0000,0011。注意:没有事件发生的 fd=5 被清空

select就绪条件

读就绪

socket 内核中 , 接收缓冲区中的字节数 , 大于等于低水位标记 SO_RCVLOWAT. 此时可以无阻塞的读该文件描述符, 并且返回值大于 0;
socket TCP 通信中 , 对端关闭连接 , 此时对该 socket , 则返回 0;
监听的 socket 上有新的连接请求 ;
socket 上有未处理的错误 ;

写就绪

socket 内核中 , 发送缓冲区中的可用字节数 ( 发送缓冲区的空闲位置大小 ), 大于等于低水位标记SO_SNDLOWAT, 此时可以无阻塞的写 , 并且返回值大于 0;
socket 的写操作被关闭 (close 或者 shutdown). 对一个写操作被关闭的 socket 进行写操作 , 会触发 SIGPIPE信号;
socket 使用非阻塞 connect 连接成功或失败之后 ;
socket 上有未读取的错误 ;

异常就绪

socket 上收到带外数据 . 关于带外数据 , TCP 紧急模式相关 ( 回忆 TCP 协议头中 , 有一个紧急指针的字段 )

select的特点

可监控的文件描述符个数取决与 sizeof(fd_set) 的值 . 我这边服务器上 sizeof(fd_set) 512 ,每 bit 表示一个文件描述符,则我服务器上支持的最大文件描述符是512*8=4096.
fd 加入 select 监控集的同时,还要再使用一个数据结构 array 保存放到 select 监控集中的 fd
一是用于再 select 返回后, array 作为源数据和 fd_set 进行 FD_ISSET 判断。
二是 select 返回后会把以前加入的但并无事件发生的 fd 清空,则每次开始 select 前都要重新从 array 取得fd逐一加入 (FD_ZERO 最先 ) ,扫描 array 的同时取得 fd 最大值 maxfd ,用于 select 的第一个参数。

select的优点

1.可移植性好:select几乎在所有的平台上都支持,具有良好的跨平台兼容性。

2.超时精度高:select对于超时值的精度可以达到微秒级别,比poll的毫秒级别精度更高。

select的缺点

1.每次调用 select, 都需要手动设置 fd 集合 , 从接口使用角度来说也非常不便 .
2.每次调用 select ,都需要把 fd 集合从用户态拷贝到内核态,这个开销在 fd 很多时会很大
3.同时每次调用 select 都需要在内核遍历传递进来的所有 fd ,这个开销在 fd 很多时也很大
4.select 支持的文件描述符数量太小
5.代码编写难度大

 select的一般编码格式

1.需要有一个第三方数组,用来保存所以合法的fd

2.while(true)

{

        1.遍历数组,更新出最大值

        2.遍历数组,添加所有需要关心的fd到fd_set位图中

        3.调用select进行事件检测

        4.遍历数组,找到就绪的事件,根据就绪事件,完成对应的动作

}

2.poll

poll函数接口

#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
// pollfd 结构
struct pollfd {
        int fd; /* file descriptor */
        short events; /* requested events */
        short revents; /* returned events */
};

参数说明

fds 是一个 poll 函数监听的结构列表 . 每一个元素中 , 包含了三部分内容 : 文件描述符 , 监听的事件集合 , 返回的事件集合.
nfds 表示 fds 数组的长度 .
timeout 表示 poll 函数的超时时间 , 单位是毫秒 (ms) 

events和revents的取值

返回结果

返回值小于 0, 表示出错 ;
返回值等于 0, 表示 poll 函数等待超时 ;
返回值大于 0, 表示 poll 由于监听的文件描述符就绪而返回 .

poll的优点

1.效率高
2.输入输出参数分离,不需要进行大量的重置
3.poll参数级别,没有可以管理的fd上限

poll的缺点

1.poll依旧需要不少的遍历,在用户层检测事件就绪与内核检测fd就绪,都是一样的,用户还需要维护数组

2.poll需要内核到用户的拷贝

3.poll的代码也比较复杂 

3.epoll

按照man手册的说法: 是为处理大批量句柄而作了改进的poll.

epoll 3 个相关的系统调用 .
1.epoll_create

 int epoll_create(int size);

2.epoll_ctl

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

它不同于 select() 是在监听事件时告诉内核要监听什么类型的事件 , 而是在这里先注册要监听的事件类型 .
第一个参数是 epoll_create() 的返回值 (epoll 的句柄 ).
第二个参数表示动作,用三个宏来表示 .
第三个参数是需要监听的 fd.
第四个参数是告诉内核需要监听什么事

第二个参数的取值

EPOLL_CTL_ADD :注册新的 fd epfd 中;
EPOLL_CTL_MOD :修改已经注册的 fd 的监听事件;
EPOLL_CTL_DEL :从 epfd 中删除一个 fd

struct epoll_event结构如下:

events 可以是以下几个宏的集合:
EPOLLIN : 表示对应的文件描述符可以读 ( 包括对端 SOCKET 正常关闭 );
EPOLLOUT : 表示对应的文件描述符可以写 ;
EPOLLPRI : 表示对应的文件描述符有紧急的数据可读 ( 这里应该表示有带外数据到来 );
EPOLLERR : 表示对应的文件描述符发生错误 ;
EPOLLHUP : 表示对应的文件描述符被挂断 ;
EPOLLET : EPOLL 设为边缘触发 (Edge Triggered) 模式 , 这是相对于水平触发 (Level Triggered) 来说的 .
EPOLLONESHOT :只监听一次事件 , 当监听完这次事件之后 , 如果还需要继续监听这个 socket 的话 , 需要再次把这个socket 加入到 EPOLL 队列里 .

3.epoll_wait

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout); 

收集在 epoll 监控的事件中已经发送的事件 .
参数 events 是分配好的 epoll_event 结构体数组 .
epoll 将会把发生的事件赋值到 events 数组中 (events 不可以是空指针,内核只负责把数据复制到这个events数组中,不会去帮助我们在用户态中分配内存 ).
maxevents 告之内核这个 events 有多大,这个 maxevents 的值不能大于创建 epoll_create() 时的 size.
参数 timeout 是超时时间 ( 毫秒, 0 会立即返回, -1 是永久阻塞 ).
如果函数调用成功,返回对应 I/O 上已准备好的文件描述符数目,如返回 0 表示已超时 , 返回小于 0 表示函数失败.

epoll的工作原理 

首先,epoll区别于select和poll的点在于,就绪是通过下层主动来的。

 

epoll模型: 

当某一进程调用 epoll_create 方法时, Linux 内核会创建一个 eventpoll 结构体,这个结构体中有两个成员与epoll 的使用方式密切相关。
每一个 epoll 对象都有一个独立的 eventpoll 结构体,用于存放通过 epoll_ctl 方法向 epoll 对象中添加进来的事件.
这些事件都会挂载在红黑树中,如此,重复添加的事件就可以通过红黑树而高效的识别出来 ( 红黑树的插入时间效率是lgn ,其中 n 为树的高度 ).
而所有添加到 epoll 中的事件都会与设备 ( 网卡 ) 驱动程序建立回调关系,也就是说,当响应的事件发生时会调用这个回调方法.
这个回调方法在内核中叫 ep_poll_callback, 它会将发生的事件添加到 rdlist 双链表中 .
epoll 中,对于每一个事件,都会建立一个 epitem 结构体 .
struct eventpoll{
....
/* 红黑树的根节点,这颗树中存储着所有添加到 epoll 中的需要监控的事件 */
struct rb_root rbr;
/* 双链表中则存放着将要通过 epoll_wait 返回给用户的满足条件的事件 */
struct list_head rdlist;
....
};

struct epitem{
struct rb_node rbn;// 红黑树节点
struct list_head rdllink;// 双向链表节点
struct epoll_filefd ffd; // 事件句柄信息
struct eventpoll *ep; // 指向其所属的 eventpoll 对象
struct epoll_event event; // 期待发生的事件类型
}
当调用 epoll_wait 检查是否有事件发生时,只需要检查 eventpoll 对象中的 rdlist 双链表中是否有 epitem元素即可.
如果 rdlist 不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户 . 这个操作的时间复杂度
O(1).

epoll的优点

1.接口使用方便 : 虽然拆分成了三个函数 , 但是反而使用起来更方便高效 . 不需要每次循环都设置关注的文件描述符, 也做到了输入输出参数分离开
2.数据拷贝轻量 : 只在合适的时候调用 EPOLL_CTL_ADD 将文件描述符结构拷贝到内核中 , 这个操作并不频繁( select/poll 都是每次循环都要进行拷贝 )
3.事件回调机制 : 避免使用遍历 , 而是使用回调函数的方式 , 将就绪的文件描述符结构加入到就绪队列中 ,epoll_wait 返回直接访问就绪队列就知道哪些文件描述符就绪 . 这个操作时间复杂度 O(1). 即使文件描述符数目很多, 效率也不会受到影响 .
4.没有数量限制 : 文件描述符数目无上限

epoll工作方式

epoll2种工作方式-水平触发(LT)和边缘触发(ET).

水平触发 Level Triggered 工作模式
epoll 默认状态下就是 LT 工作模式 .
epoll 检测到 socket 上事件就绪的时候 , 可以不立刻进行处理 . 或者只处理一部分 .
如上面的例子 , 由于只读了 1K 数据 , 缓冲区中还剩 1K 数据 , 在第二次调用 epoll_wait , epoll_wait 仍然会立刻返回并通知socket 读事件就绪 .
直到缓冲区上所有的数据都被处理完 , epoll_wait 才不会立刻返回 .
支持阻塞读写和非阻塞读写
边缘触发 Edge Triggered 工作模式
如果我们在第 1 步将 socket 添加到 epoll 描述符的时候使用了 EPOLLET 标志 , epoll 进入 ET 工作模式 .
epoll 检测到 socket 上事件就绪时 , 必须立刻处理 .
如上面的例子 , 虽然只读了 1K 的数据 , 缓冲区还剩 1K 的数据 , 在第二次调用 epoll_wait 的时候 ,
epoll_wait 不会再返回了 .
也就是说 , ET 模式下 , 文件描述符上的事件就绪后 , 只有一次处理机会 .
ET 的性能比 LT 性能更高 ( epoll_wait 返回的次数少了很多 ). Nginx 默认采用 ET 模式使用 epoll.
只支持非阻塞的读写

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

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

相关文章

Vue开发实例(三)项目引入Element-UI

项目引入Element-UI 一、引入Element-UI二、注册组件1、vue2使用element-ui2、vue3使用element-ui 三、使用Element组件1、轻微改造2、验证element是否生效 一、引入Element-UI npm i element-ui --save npm install element-ui -S等待安装完成 二、注册组件 1、vue2使用ele…

C++真题列表

题目解析&#xff1a;RAM是闪存&#xff0c;只要一关机一拔电&#xff0c;就会丢失数据 题目解答&#xff1a;A 题目解析&#xff1a;TXT格式是文本文档 题目解答&#xff1a;B 题目解析&#xff1a;IP地址中每一个字节的取值范围是[0~255]&#xff0c;是不可能有256的 题目…

Mongodb基础(node.js版)

一、Mongodb 介绍 Mongodb 是一个文档数据库&#xff0c;以文档形式存储数据&#xff0c;格式类似于 JSON 与 Mysql 的特点及选型对照 MongodbMysql关系类型非关系型关系型存储类型文档存储&#xff08;类似于写 Word &#xff09;表格存储 &#xff08;类似于写 Excle&…

【Python】进阶学习:pandas--isin()用法详解

【Python】进阶学习&#xff1a;pandas–isin()用法详解 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; 希望得到您的订阅…

midjourney提示词语法

更高级的提示可以包括一个或多个图像URL、多个文本短语和一个或更多个参数 Image Prompts 可以将图像URL添加到提示中&#xff0c;以影响最终结果的样式和内容。图像URL总是位于提示的前面。 https://docs.midjourney.com/image-prompts Text Prompt 要生成的图像的文本描述。…

C++_运算符_逻辑运算符

逻辑运算符的作用 用于根据表达式的值返回真值或假值 逻辑运算符有以下符号 示例1&#xff1a;逻辑非 示例2&#xff1a;逻辑与 总结 同真为真&#xff0c;其余为假 示例3&#xff1a;逻辑或 总结 同假为假&#xff0c;其余为真

Git 如何上传本地的所有分支

Git 如何上传本地的所有分支 比如一个本地 git 仓库里定义了两个远程分支&#xff0c;一个名为 origin&#xff0c; 一个名为 web 现在本地有一些分支是 web 远程仓库没有的分支&#xff0c;如何将本地所有分支都推送到 web 这个远程仓库上呢 git push web --all

蓝桥杯(3.2)

1209. 带分数 import java.io.*;public class Main {static BufferedReader br new BufferedReader(new InputStreamReader(System.in));static PrintWriter pw new PrintWriter(new OutputStreamWriter(System.out));static final int N 10;static int n, cnt;static int[…

云服务器租用哪家好?不要忽视第3点

注册资本&#xff1a;5亿元 权威认证&#xff1a;中央网信办云服务安全审查DJCP网络安全等级保护ITSS工信部云计算服务能力评估CSA STAR认证管理体系认证信息安全安全管理体系认证可信云服务认证可信云云主机等级评估5星 PCI-DSS支付卡行业数据安全认证 租哪家云服务器比较好&…

ssm666社区流浪动物救助领养系统的设计与开发

** &#x1f345;点赞收藏关注 → 私信领取本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345;** 一 、设计说明 1.1 课题…

如何解决微服务的数据一致性分发问题?

介绍 系统架构微服务化以后,根据微服务独立数据源的思想,每个微服务一般具有各自独立的数据源,但是不同微服务之间难免需要通过数据分发来共享一些数据,这个就是微服务的数据分发问题。Netflix/Airbnb等一线互联网公司的实践[参考附录1/2/3]表明,数据一致性分发能力,是构…

windows下安装cnpm

cnpm是淘宝团队开发的一个针对中国用户的npm镜像源&#xff0c;它是npm的一个定制版本。由于国外的npm源在国内访问速度较慢&#xff0c;所以cnpm镜像源可以提供更快的下载速度。cnpm的使用方式与npm基本相同&#xff0c;只需将npm替换为cnpm即可。 要想使用cnpm等先安装node.…

创建型模式之原型模式

一、概述 1、工作原理&#xff1a;将一个原型对象传给要发动创建的对象(即客户端对象),这个要发动创建的对象通过请求原型对象复制自己来实现创建过程 2、通过克隆方法所创建的对象是全新的对象&#xff0c;它们在内存中拥有新的地址&#xff0c;每一个克隆对象都是独立的 3…

WordPress建站入门教程:如何在本地电脑搭建WordPress网站?

前面跟大家分享了『WordPress建站入门教程&#xff1a;如何安装本地WordPress网站运行环境&#xff1f;』&#xff0c;接下来boke112百科就继续跟大家分享本地电脑如何搭建WordPress网站。 小皮面板&#xff08;phpstudy&#xff09;的“软件管理 – 网站程序”虽然可以一键部…

鸿蒙Harmony应用开发—ArkTS声明式开发(通用属性:Popup控制)

给组件绑定popup弹窗&#xff0c;并设置弹窗内容&#xff0c;交互逻辑和显示状态。 说明&#xff1a; 从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 popup弹窗的显示状态在onStateChange事件回调中反馈&#xff0c;其显…

RNA-Seq 笔记 [4]

***********************该笔记为初学者笔记&#xff0c;仅供个人参考谨慎搬运代码****************************** samtools 排序压缩和 featureCounts 生成基因计数表 SAM文件和BAM文件 1.SAM格式&#xff1a;是一种通用的比对格式&#xff0c;用来存储reads到参考序列的比…

重学SpringBoot3-@EnableConfigurationProperties注解

重学SpringBoot3-EnableConfigurationProperties注解 1. 引言2. EnableConfigurationProperties 的作用3. 使用示例4. 总结 1. 引言 Spring Boot 提供了一种便捷的方式来管理和校验应用程序的配置&#xff0c;即通过类型安全的配置属性。EnableConfigurationProperties 注解在…

[GXYCTF2019]BabyUpload1 -- 题目分析与详解

目录 一、题目分析 1、判断题目类型&#xff1a; 2、上传不同类型的文件进行测试&#xff1a; 二、题目详解 1、写出.htaccess文件&#xff1a; 2、.htaccess 文件配合 .jpg 上传&#xff1a; 3、利用 中国蚁剑/中国菜刀 获取flag&#xff1a; 一、题目分析 1、判断题目…

禅道:提bug、管理case 7.0

一、禅道的介绍 &#xff08;1&#xff09;定义禅道是一个项目管理工具&#xff0c;也是一个bug管理工具&#xff0c;还是一个用例管理工具。 &#xff08;2&#xff09;作用&#xff1a;为了解决众多企业在管理中出现混乱&#xff0c;无序的现象&#xff0c;开发出来 &…

EchoServer回显服务器简单测试

目录 工具介绍 工具使用 测试结果 工具介绍 github的一个开源项目,是一个测压工具 EZLippi/WebBench: Webbench是Radim Kolar在1997年写的一个在linux下使用的非常简单的网站压测工具。它使用fork()模拟多个客户端同时访问我们设定的URL&#xff0c;测试网站在压力下工作的…