【计算机网络】poll | epoll

文章目录

  • 1. poll
    • poll函数参数解析
    • 代码解析
      • PollServer代码
    • poll 特点
  • 2. epoll
    • 认识接口
      • epoll_create
      • epoll_ctl
      • epoll_wait
    • 基本原理
      • 红黑树
      • 就绪队列

1. poll

poll函数参数解析

输入 man poll

poll的第一个参数是文件描述符
poll的第二个参数为 等待的多个文件描述符(fd)数字层面 最大的+1

poll函数中的最后一个参数 timeout 是一个 纯输入型参数,单位是毫秒
若 timeout 为-1,则表示永久阻塞,直到文件描述符就绪
若 timeout为0,则表示 非阻塞
若timeout 大于0,则表示 在timeout事件以内 以阻塞等待,超时则进行非阻塞等待


poll的返回值的含义与select 相同
第一种 大于0表示有几个文件描述符 是就绪的
第二种 等于0进入timeout状态 ,即 5s以内没有任何一个文件描述符 就绪
第三种 小于0等待失败 返回-1如:想要等待下标为1 和2的文件描述符,但是下标为2的文件描述符根本不存在,就会等待失败


在pollfd 结构体 中
fd 表示 文件描述符
events: 用户告诉内核,需要关心那些文件描述符上的那些事件
revents :内核会告诉用户,关心的那些文件描述符上的那些事件已经就绪

poll将 输入参数 和输出参数进行分离


poll就有对应的事件
常用的有
POLLIN 表示 有数据可以读
POLLOUT 表示 当前写的时候不会被阻塞


POLLIN 表示第一个比特位为1
POLLOUT 表示 第三个比特位为1

代码解析

主要将第一个初始版本的select代码进行修改

由于poll 自带结构体,内部包含
fd (文件描述符)
events (用户告诉操作系统 那些文件描述符上的事件需要关心)
revents (操作系统告诉用户 关心的那些文件描述符上的事件已经就绪)


此时的fdaaray作为结构体指针,可以通过该指针 去指向 pollfd结构体成员


当想要使用 数组当前元素表示对应的文件描述符时,需指向对应的fd成员

想要表示 (用户告诉操作系统 那些文件描述符上的事件需要关心)
需要通过指针去指向对应的成员 events
想要表示 (操作系统告诉用户 关心的那些文件描述符上的事件已经就绪)
需要通过指针去指向对应的成员 revents

PollServer代码

#include<iostream>
#include<string>
#include<sys/poll.h>
#include<cstring>
#include"Sock.hpp"
#include"Log.hpp"
#include"Err.hpp"
using namespace std;const static int gport=8888;    const static int N=4096;const static short defaultevent=0;typedef pollfd type_t;class PollServer
{public:PollServer(uint16_t port=gport):port_(port),fdarray_(nullptr){}void InitServer()//初始化{listensock_.Socket();//创建套接字listensock_.Bind(port_);//绑定listensock_.Listen();//设置监听状态fdarray_=new type_t[N];//对fdarray数组进行初始化for(int i=0;i<N;i++){fdarray_[i].fd= defaultfd;fdarray_[i].events= defaultevent;fdarray_[i].revents=defaultevent;}}void Accepter()//获取新连接的动作{//这里再使用accept 就不会阻塞了//listen套接字底层一定有就绪的事件 即连接已经到来了string clientip;uint16_t  clientport;int sock=listensock_.Accept(&clientip,&clientport);//获取客户端IP和端口号if(sock<0){return;}//当得到对应新连接的sock套接字,是不能进行read/recv//并不知道sock上的数据是否就绪的//所以需要将sock交给select,由select进行管理logMessage(Debug,"[%s:%d],sock:%d",clientip.c_str(),clientport,sock );//只需把新获取的sock 添加到 数组中int pos=1;for(;pos<N;pos++){if(fdarray_[pos].fd==defaultfd)//说明没有被占用{break;}}if(pos>=N)//整个数组中的位置全被占用了{//由于fdarray_是动态开辟空间的,所以可以动态扩容//若扩容失败,则closeclose(sock);logMessage(Warning,"sockfd[] array full");}else //找到了对应的位置{fdarray_[pos].fd=sock;fdarray_[pos].events=POLLIN;fdarray_[pos].revents=defaultevent;}}void  HandlerEvent()//处理就绪事件{ for(int i=0;i<N;i++){int fd=fdarray_[i].fd;int revent= fdarray_[i].revents;if( (fd==defaultfd)&&(revent &POLLIN))//读事件就绪{ continue;}//合法fd//若套接字为listensock套接字,并且读事件就绪if(fd==listensock_.Fd() &&(revent &POLLIN)){Accepter();}//若套接字不是listensock套接字,并且读事件就绪 即普通的读取数据就绪else if ((fd != listensock_.Fd()) && (revent &POLLIN)) {char buffer[1024];ssize_t s=recv(fd,buffer,sizeof(buffer)-1,0);//读取不会被阻塞if(s>0)//读取成功{buffer[s-1]=0;cout<<"client# "<<buffer<<endl;//发送回去 也要被select管理string echo=buffer ;echo+= "[select server echo ]";send(fd,echo.c_str(),echo.size(),0);//发送消息 将echo内的数据 交给fd}else {if(s==0)//读到文件结尾{logMessage(Info,"client quit...,fdarray_[i] -> defaultfd:%d->%d",fd,defaultfd);}else //读取失败 {logMessage(Warning,"recv error,client quit...,fdarray_[i] -> defaultfd:%d->%d",fd,defaultfd);}  close(fd);fdarray_[i].fd=defaultfd;fdarray_[i].events=defaultevent;fdarray_[i].revents=defaultevent;}   } } }void DebugPrint(){cout<<"fdarray_[]:"<<endl;for(int i=0;i<N;i++){if(fdarray_[i].fd==defaultfd){continue;}cout<<fdarray_[i].fd<<" ";}cout<<"\n";}void Start() //启动{//在网络中,新连接到来被当作 读事件就绪//对应不同的事件就绪,做出不同的动作fdarray_[0].fd=listensock_.Fd();fdarray_[0].events=POLLIN;//数据可读while(true) {int timeout= -1;//永久阻塞  int n= poll(fdarray_,N,timeout);//timeout 设为nullptr后,全部为阻塞等待switch(n){case 0:   //表示没有任何一个文件描述符就绪 logMessage(Debug,"timeout,%d: %s",errno,strerror(errno));break;case -1:  //等待失败 返回-1logMessage(Warning,"%d: %s",errno,strerror(errno));break;default:  //大于0 ,则表示成功 返回有多少文件描述符就绪logMessage(Debug,"有一个就绪事件发生了:%d",n);HandlerEvent();//处理就绪事件DebugPrint();//打印数组内容break;}}}~PollServer(){listensock_.Close();if(fdarray_){delete[]fdarray_;}}private:uint16_t port_;//端口号Sock listensock_;//创建Sock对象type_t* fdarray_;//自己定义一个数组,与位图大小相同,来进行已经获得的sock进行管理
};

poll 特点

poll 就相当于在 select 的基础上进行优化
poll自带结构体,只需将读写 异常 放入 events 事件即可

poll 跟 select 一样 也是以数组的形式 传递多个文件描述符,传进去后,需要操作系统继续遍历

  • 每次调用poll,都需要把fd集合从用户态拷贝到内核态,在fd很多时开销会很大
    (每次都需要用户需要告诉内核,那些文件描述符的那些事件需要关心)
  • 每次调用poll,都需要在内核遍历传递过来的所有fd,在fd很多时 开销会很大
  • (每次都需要内核需要告诉用户,关心的文件文件描述符上的那些事件就绪)

poll 解决了文件描述符 有上限的问题

(定义的数组是在堆上开辟的,若空间满了,还可以动态扩容)
select由于定义的是一个固定长度的数组大小,当到达整个数组长度时,就只能打印信息

2. epoll

epoll 是为处理大批句柄而作改进的poll

认识接口

epoll_create

输入 man epoll_create

参数size可以被忽略,但是必须大于0

返回值 :
若返回epoll文件描述符,则表示返回成功
若返回-1,则表示返回失败

epoll_create 作用:创建出epoll模型


epoll_ctl

输入 man epoll_ctl

第一个参数 epfd 为 epoll_create 的返回值
第二个参数 op 表示你想作什么样的操作
一般常见设置为三个值

EPOLL_CTL_ADD 添加
EPOLL_CTL_MOD 修改
EPOLL_CTL_DEL 删除


第三个参数 fd 表示 哪一个文件描述符

最后一个参数 event 表示关心什么事件
events 表示 输入
fd表示 输入时 表示那些文件描述符上的什么样事件要关心
epoll_ctl 作用: 用户告诉内核,帮我关心 增加/修改/删除那个文件描述符上的那一个事件

epoll_wait

输入 man epoll_wait

返回值含义 与select和poll相同
第一种 大于0表示有几个文件描述符 是就绪的
第二种 等于0进入timeout状态 ,即 5s以内没有任何一个文件描述符 就绪
第三种 小于0等待失败 返回-1如:想要等待下标为1 和2的文件描述符,但是下标为2的文件描述符根本不存在,就会等待失败


第一个参数 epfd v 为 epoll_create的返回值
最后一个参数 timeout 与poll中含义相同

第二个参数 events 为 返回的就绪事件
第三个参数 maxevents为 epoll模型的最大个数

epoll_wait作用:内核告诉 用户 那些文件描述符上的那些事件就绪


与poll的宏基本一致
主要使用 EPOLLIN 和 EPOLLOUT
EPOLLIN 表示 有数据可以读
EPOLLOUT 表示 当前写的时候不会被阻塞


基本原理

红黑树

创建epoll时,在底层就会创建一颗红黑树
使用红黑树 使用户告诉操作系统 来关心 增加/修改/删除那个文件描述符上的那一个事件

点击查看:红黑树概念


红黑树的节点假设为 sruct rb_node
内部包含 文件描述符fd 和 对应事件 event


eopll_ctl 本质 为 通过epoll模型来对红黑树操作
向红黑树中新增 删除 修改 某一个节点
而每一个节点 都对应的是文件描述符和对应的事件
epoll_ctl 用来对红黑树 进行增删改 操作


在内核中,一个结构体对象,既可以属于结构A,又可以属于结构B
所以struct rb_node 既可以属于红黑树,又可以属于其他结构


就绪队列

创建epoll时,同时也会创建一个就绪队列

当特定的文件描述符上有对应的事件发生了,就可以将对应已经发生事件的节点 链入就绪队列中
(所以struct rb_node 既可以属于红黑树,又可以属于就绪队列)

就绪队列中只保存已经准备好的文件描述符上的对应事件


作为就绪队列的节点,需要包含文件描述符fd 以及 revent (操作系统告诉用户 关心的文件描述符的那些事件就绪)


epoll_wait 以事件复杂度为O(1)的方式,检测有没有事件就绪 即检测就绪队列是否为空


数据就绪 形成节点放入就绪队列中 ,将红黑树中节点关系 也添加到就绪队列中
这样一个结构体对象就可以既属于红黑树 ,又属于就绪队列了


整体称为 epoll
当调用 epoll_create 时,就是创建epoll模型


epoll避免使用 遍历,而是通过回调函数的方式,将就绪的文件描述符加入 就绪队列中
epoll_wait 返回直接访问 就绪队列 就知道那些文件描述符就绪

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

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

相关文章

【计算机视觉 05】YOLO论文讲解:V1-V7

https://ai.deepshare.net/live_pc/l_63243a65e4b050af23b79338 Part1.目标检测与YOLO系列 1. 目标检测任务及发展脉络 2. YOLO的发展史 Anchors Base原理&#xff1a; Part2.YOLOV1-V3 3. YOLO V1的网络结构 4. YOLO V3的网络结构与实验结果 Part3.YOLO的进化 5. YOLO V4的网络…

【JavaEE】多线程进阶(一)饿汉模式和懒汉模式

多线程进阶&#xff08;一&#xff09; 文章目录 多线程进阶&#xff08;一&#xff09;单例模式饿汉模式懒汉模式 本篇主要引入多线程进阶的单例模式&#xff0c;为后面的大冰山做铺垫 代码案例介绍 单例模式 非常经典的设计模式 啥是设计模式 设计模式好比象棋中的 “棋谱”…

服务器启用SGX(以PowerEdge R750为例)

一、检查处理器是否支持SGX 在shell中输入以下命令查看CPU型号 cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c在Product Specifications中找到对应的处理器参数信息&#xff0c;如果支持SGX&#xff0c;可以在Security & Reliability中看到如下信息 二、以“软…

初学者如何选择:前端开发还是后端开发?

#开发做前端好还是后端好【话题征文】# 作为一名有多年开发经验的过来人&#xff0c;我认为前端开发和后端开发都有其独特的魅力和挑战。下面我将就我的个人经历和观点来分享一些关于前端开发和后端开发的看法。 首先&#xff0c;让我们将编程世界的大城市比作前端开发和后端开…

Git 学习笔记 | Git 基本理论

Git 学习笔记 | Git 基本理论 Git 学习笔记 | Git 基本理论Git 工作区域Git 工作流程 Git 学习笔记 | Git 基本理论 在开始使用 Git 创建项目前&#xff0c;我们先学习一下 Git 的基础理论。 Git 工作区域 Git本地有三个工作区域&#xff1a;工作目录&#xff08;Working Di…

计算机网络笔记3 数据链路层

计算机网络系列笔记目录&#x1f447; 计算机网络笔记6 应用层计算机网络笔记5 运输层计算机网络笔记4 网络层计算机网络笔记3 数据链路层计算机网络笔记2 物理层计算机网络笔记1 概述 文章前言 &#x1f497; 站在巨人的肩膀上&#xff0c;让知识的获得更加容易&#xff01…

vue,前端打包项目、部署上线

前端项目是在本地的IDE开发的。流程是&#xff1a;开发》打包》上线到生产环境》使用。 vue脚手架只是开发过程中,协助开发的工具,当真正开发完了&#xff0c;脚手架不参与上线。 这时候要用到打包了。 打包后,可以生成,浏览器能够直接运行的网页>就是需要上线的源码! 打…

Clion中使用C/C++开发stm32程序

前言 从刚开始学习阶段&#xff0c;一直是用的keil5开发stm32程序&#xff0c;自从看到稚晖君推荐的CLion开发嵌入式程序后&#xff0c;这次尝试在CLion上开发stm32程序。 1、配置CLion用于STM32开发的环境 这里我就不详细写了&#xff0c;没必要重新写&#xff0c;网上教程很多…

华为OD机试 - 数字反转打印(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#…

dataframe保存excel格式比csv格式小很多很多

问题描述&#xff1a; 一个3万行的数据保存成csv大概10个G&#xff0c;但保存成excel格式只有100多M 原因分析&#xff1a; 因为xlsx 实际上就是 zip 压缩包&#xff0c;同时&#xff0c;如果有大量重复的数据&#xff0c;XLSX 会提取文本值&#xff0c;将其存储在查找表中&…

华为云云耀云服务器L实例评测|部署项目管理工具 Focalboard

华为云云耀云服务器L实例评测&#xff5c;部署项目管理工具 Focalboard 一、云耀云服务器L实例介绍1.1 云服务器介绍1.2 产品优势1.3 产品规格1.4 应用场景 二、云耀云服务器L实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置 三、部署 Focalboard3.1 Focalboard 介绍3.2 Doc…

2023年中国CEM-1型覆铜板产量、需求量及行业销售收入分析[图]

CEM-1指覆铜板的一种&#xff0c;以玻纤布半固化片与纸基半固化片层压铜箔达到固化后形成的板材&#xff0c;属于复合型基材&#xff0c;CEM-1能用来制作频率特性要求高的PCB&#xff0c;如电视机的调谐器、电源开关、超声波设备、计算机电源和键盘&#xff0c;也可以用于电视机…

Xcode 15 编译出错问题解决

正常升级xcode 15以后发现原来没有出现报错的代码&#xff0c;现在出现了编译错误。&#xff08;如果没有出现请忽略&#xff09;下面教你如何解决这个问题。 1、pod update更新cocoapods&#xff0c;因为其根据xcode15做了很多的更新&#xff0c;保证cocoapods是最新的。 千…

【19】c++设计模式——>桥接模式

桥接模式的定义 C的桥接模式&#xff08;Bridge Pattern&#xff09;是一种结构型设计模式&#xff0c;它将抽象部分与实现部分分离&#xff0c;使得它们可以独立地变化。桥接模式的核心思想是利用组合关系代替继承关系&#xff0c;将系统划分成多个独立的、功能不同的类层次结…

Suricata + Wireshark离线流量日志分析

目录 一、访问一个404网址&#xff0c;触发监控规则 1、使用python搭建一个虚拟访问网址 2、打开Wireshark,抓取流量监控 3、在Suricata分析数据包 流量分析经典题型 入门题型 题目&#xff1a;Cephalopod(图片提取) 进阶题型 题目&#xff1a;抓到一只苍蝇(数据包筛选…

c语言练习题83:#include“ “和#include<>的区别

#include" "和#include<>的区别 #include<> 默认根据环境变量的值去先搜索标准库&#xff0c;搜索系统文件会比较快。 #include“” 先搜索当前工程的路径&#xff0c;搜索自己自定义的文件会比较快。 因此自定义的头文件的名称包含在<>中的话…

【Docker】Docker的工具实践及root概念,时间戳的概念和Docker容器安全性设置的详细讲解

作者简介&#xff1a; 辭七七&#xff0c;目前大二&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 七七的闲谈 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f…

运行软件提示丢失msvcr120.dll文件怎么办?msvcr120.dll丢失的5个最新解决方法

找不到msvcr120.dll是一个常见的错误信息&#xff0c;通常会在用户尝试运行某些程序或游戏时出现。msvcr120.dll是 Windows 操作系统中的一个动态链接库文件&#xff0c;它包含了 C 运行时库的一些函数和类&#xff0c;对于许多程序和游戏的正常运行至关重要。 当用户遇到找不到…

POI 和 EasyExcel 操作 Excel

一、概述 目前操作 Excel 比较流行的就是 Apache POI 和阿里巴巴的 easyExcel。 1.1 POI 简介 Apache POI 是用 Java 编写的免费开源的跨平台的 Java API&#xff0c;Apache POI 提供 API 给 Java 程序对 Microsoft Office 格式文档读和写的常用功能。POI 为 “Poor Obfuscati…

一般香港服务器带宽选多大够用?(带宽计算方法)

​  在海外IDC市场份额中&#xff0c;香港服务器依托自身优越的服务器资源条件&#xff0c;在各个行业中发挥的重要作用。但是&#xff0c;不同业务对网络带宽的要求各不相同&#xff0c;弄清楚如何计算带宽需求对于确保业务平稳运行至关重要&#xff0c;最好从一开始就使用正…