【Linux】从零开始使用多路转接IO --- poll

在这里插入图片描述

碌碌无为,则余生太长;
欲有所为,则人生苦短。
--- 中岛敦 《山月记》---

从零开始使用多路转接IO

  • 1 前言
  • 1 poll接口介绍
  • 3 代码编写
  • 4 总结

1 前言

上一篇文章我们学习了多路转接中的Select,其操作很简单,但有一些缺陷:

  1. 每次调用 select,都需要手动设置 fd 集合, 从接口使用角度来说也非常不便。
  2. 每次调用 select, 都需要把 fd 集合从用户态拷贝到内核态, 这个开销在 fd 很多时会很大。这个是多路转接IO无法避免的问题!
  3. 同时每次调用 select 都需要在内核遍历传递进来的所有 fd,这个开销在 fd 很多时很大。
  4. select 支持的文件描述符数量太小!虽然操作系统中文件描述符也有限制,但是这是操作系统的缺陷。同样select也是缺点

而poll方案可以解决其中的两个缺点:

  • select支持的文件描述符少,poll理论上可以支持无限个文件描述符。
  • select每次调用接口都需要手动设置fd集合,poll不需要!

那么接下来我们就来看poll是怎样实现的。

1 poll接口介绍

首先poll的作用与select一模一样:等待多个文件描述符!只负责等待!

我们来看看poll接口:

OLL(2)                                                                   Linux Programmer's Manual                                                                   POLL(2)NAMEpoll, ppoll - wait for some event on a file descriptorSYNOPSIS#include <poll.h>int poll(struct pollfd *fds, nfds_t nfds, int timeout);#define _GNU_SOURCE         /* See feature_test_macros(7) */#include <signal.h>#include <poll.h>int ppoll(struct pollfd *fds, nfds_t nfds,const struct timespec *tmo_p, const sigset_t *sigmask);

poll接口中只有三个参数:

  1. struct pollfd *fds:这时一个文件描述符数组,其中每个元素是一个结构体,其中包含文件描述符,需要处理的事件类型。
  2. nfds_t nfds:表示文件描述符的数量!
  3. timeout:输入性参数,这里直接采用的是毫秒,不使用结构体!等于0时是非阻塞IO,等于-1时是阻塞IO!
  4. 返回值表示是否成功:大于0 即有n个就绪了;等于0表示超时了;小于0就是poll出错了!

我们来看看struct pollfd内部是怎么样的 :

struct pollfd {int   fd;         /* file descriptor */short events;     /* requested events */short revents;    /* returned events */};

我们对比一下select,select需要传入三个事件集,输入输出性参数,每次都会发生改变!所以才需要每次调用都要进行初始化。而poll使用一个结构体,对于这个文件描述符有两种事件:requested events 与 returned events!输入输出并不互相干扰!那么就解决了select需要不断初始化的问题。

那么事件类型有哪些呢?

宏定义描述
POLLIN普通或优先级带数据可读
POLLRDNORM同POLLIN
POLLRDBAND数据可读(优先级带数据)
POLLPRI高优先级数据可读
POLLOUT普通数据可写
POLLWRNORM同POLLOUT
POLLWRBAND数据可写(优先级带数据)
POLLERR发生错误
POLLHUP挂起,对方关闭连接
POLLNVAL描述字不是一个打开的文件

这些都是宏定义,short events;是一个16位位图,可以通过宏定义进行匹配设置!我们想要查看哪些事件,或者有哪些事件就绪了,就都可以通过位运算进行判断就可以了!

通过结构体的两个位图:

  1. 用户就可以告诉内核需要帮我们对fd的哪些事件进行等待了
  2. 内核也可以通过位图告诉用户fd的哪些事件就绪了!

3 代码编写

我们仅仅需要对select的代码做出一些修改即可:

首先,poll需要一个struct pollfd数组,这里储存需要处理的fd。初始化事遍历进行将对应fd设置为-1,事件设置为0,将listen套接字加入就可以:

	void Initserver(){// 对数组进行初始化for (int i = 0; i < gnum; i++){fd_array[i].fd = gdefault;fd_array[i].events = 0;fd_array[i].revents = 0;}// 加入监听套接字fd_array[0].fd = _listensock->GetSockfd();fd_array[0].events = POLLIN;}//...// pollstruct pollfd fd_array[gnum];

然后对Loop函数进行修改,我们不在需要对数据遍历更新rfds了,这样代码看起来就整洁了许多!

void Loop(){// 进入服务while (true){// 创建timeoutint timeout = 1000;// 进行selectint n = ::poll(fd_array, gnum, timeout);switch (n){case 0:// 超时LOG(DEBUG, "timeout \n");break;case -1:// 出错了LOG(ERROR, "select error\n");break;default:// 正常LOG(INFO, "have event ready: n = %d\n", n);// 处理事件HandlerEvent();PrintDebug();break;}}}

接下来就是HandlerEvent函数,进行判断的策略依然是遍历,这里只关心读事件:

void HandlerEvent(){// 遍历fd_array判断是否有就绪的新事件for (int i = 0; i < gnum; i++){if (fd_array[i].fd == gdefault)continue;// 如果有新事件if (fd_array[i].revents & POLLIN){// 进行判断是scokfd 还是普通fdif (fd_array[i].fd == _listensock->GetSockfd()){Accepter();}// 普通fd 进行正常读写else{HandlerIO(fd_array[i]);}}}}

然后就是对于普通套接字和监听套接字的处理,针对数组进行稍微修改即可:

void Accepter(){// 连接事件就绪InetAddr addr;int sockfd = _listensock->Accepter(&addr); // 已经就绪 ,不会阻塞// 这时会得到一个新连接if (sockfd > 0){LOG(DEBUG, "get a new link , client info %s:%d\n", addr.Ip().c_str(), addr.Port());// 将新获取的fd加入到数组中LOG(INFO, "get new fd :%d\n", sockfd);bool flag = false;for (int i = 0; i < gnum; i++){if (fd_array[i].fd == gdefault){flag = true;fd_array[i].fd = sockfd;fd_array[i].events = POLLIN;break;}elsecontinue;}if (flag == false){LOG(WARNING, "fd_array have fill!\n");return;// 可以进行扩容}}}void HandlerIO(struct pollfd &sp){char buffer[1024];int n = ::recv(sp.fd, buffer, sizeof(buffer) - 1, 0);if (n > 0){// 读取到了数据buffer[n] = 0;std::string echo_str = "[client say]#";echo_str += buffer;std::cout << echo_str << std::endl;// 返回一个报文std::string content = "<html><body><h1>hello bite</h1></body></html>";std::string ret_str = "HTTP/1.0 200 OK\r\n";ret_str += "Content-Type: text/html\r\n";ret_str += "Content-Length: " + std::to_string(content.size()) + "\r\n\r\n";ret_str += content;// echo_str += buffer;::send(sp.fd, ret_str.c_str(), ret_str.size(), 0); // 临时方案}else if (n == 0){// 此时fd退出了LOG(INFO, "fd:%d quit!\n", sp.fd);::close(sp.fd);sp.fd = gdefault;sp.events = 0;sp.revents = 0;}else{LOG(ERROR, "recv error! errno:%d\n", errno);::close(sp.fd);sp.fd = gdefault;}}

来看效果:
在这里插入图片描述
很好的实现了我们的需求!代码也比select更加的简单了!

4 总结

Poll的底层其实也是遍历,对我们传入的数据进行遍历,这样的效率其实比select并不能高出太多!也就是说poll依然有这样的缺点:

  1. 每次调用 select, 都需要把 fd 集合从用户态拷贝到内核态, 这个开销在 fd 很多时会很大。这个是多路转接IO无法避免的问题!
  2. 同时每次调用 select 都需要在内核遍历传递进来的所有 fd,这个开销在 fd 很多时很大。

这样poll 的处境就很尴尬,没有select资历早,适配性不如select。性能又比不过epoll!
下一篇文章我们来学习epoll!

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

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

相关文章

Verilog实现的莫尔斯电码发生器

莫尔斯或者摩尔斯电码(Morse Code)&#xff0c;发明于1837年(另有一说是1836年)&#xff0c;通过不同的排列顺序来表达不同的英文字母、数字和标点符号&#xff0c;在这里作一简单处理&#xff0c;仅产生点(Dit)和划(Dah)&#xff0c;时长在0.25秒之内为点&#xff0c;超过为划…

Maven介绍,IDEA集成方式

概述 什么是Maven&#xff1f; Maven 的正确发音是[ˈmevən],Maven在美国是一个口语化的词语&#xff0c;代表专家、内行的意思。 一个对 Maven 比较正式的定义是这么说的&#xff1a; Maven 是一个项目管理工具&#xff0c;它包含了一个项目对象模型 (POM&#xff1a;Proj…

stl_stack/queue

一.适配器 stack和queue实际上并不能算是一种容器&#xff0c;而是一种容器适配器。而适配器作为stl的6大组件之一&#xff0c;其实是一种设计模式。适配器模式其实就是将一个类的接口&#xff08;该接口无法直接满足客户的需求&#xff09;转换成客户希望的另一个接口&#x…

利用Docker Compose构建微服务架构

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 利用Docker Compose构建微服务架构 引言 Docker Compose 简介 安装 Docker Compose 创建项目结构 编写 Dockerfile 前端 Dockerf…

Redis未授权访问漏洞复现和修复建议

Redis未授权访问漏洞利用&#xff08;总结&#xff09; 一、漏洞介绍及危害1.1 原理1.2 漏洞影响版本1.3 漏洞危害1.4 实战中redis常用命令 二、漏洞复现2.1 环境准备2.1.1 靶机安装redis服务器2.1.2 kali安装Redis客户端&#xff08;Redis-cli&#xff09; 三、漏洞利用3.1 利…

无人机之集群控制方法篇

无人机的集群控制方法涉及多个技术和策略&#xff0c;以确保多架无人机能够协同、高效地执行任务。以下是一些主要的无人机集群控制方法&#xff1a; 一、编队控制方法 领航-跟随法&#xff08;Leader-Follower&#xff09; 通过设定一架无人机作为领航者&#xff08;长机&am…

流水线商品标签如何快速打印?商品标签自定义打印软件操作方法

一、概述 【软件可定制详情点文章最后信息卡片】 流水线商品标签如何快速打印&#xff1f;商品标签自定义打印软件操作方法 ‌定义与用途‌ 商品标签打印软件&#xff0c;即用于打印商品标签的应用软件。标签包含产品上的文字、商品详情等说明信息 如图&#xff0c;可以预先…

不只是任务分配!管理者应具备的核心认知

背景 二十年&#xff0c;中国的互联网行业飞速发展&#xff0c;让无数年轻人有了从技术岗走向管理岗的机会。然而&#xff0c;许多工程师在走上管理岗位时往往是“仓促上任”&#xff0c;没有足够时间适应管理工作和责任。少数悟性高、能力突出的工程师能够迅速胜任&#xff0…

第二十五章 Vue父子通信之sync修饰符

目录 一、概述 二、完整代码 2.1. main.js 2.2. App.vue 2.3. BaseDialog.vue 三、运行效果 一、概述 前面的章节我们讲到&#xff0c;通过v-model我们可以实现父子组件间的通信&#xff0c;但是使用v-model的时候&#xff0c;子组件接收的prop属性名必须固定为valu…

【浪潮商城-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

轻松成为文本文件管理大师,将每个文件夹中的所有TXT分别合并成一个文本文档,发现办公软件TXT合并功能的无限可能

文本文件如潮水般涌来&#xff0c;管理它们成为了一项令人头疼的任务。但是&#xff0c;别怕&#xff0c;有了首助编辑高手软件&#xff0c;你将成为办公软件达人&#xff0c;轻松驾驭这些文本文件&#xff0c;体验无限魅力&#xff01;想象一下&#xff0c;杂乱无章的文件夹瞬…

安卓13默认连接wifi热点 android13默认连接wifi

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改5.编译6.彩蛋1.前言 有时候我们需要让固件里面内置好,相关的wifi的ssid和密码,让固件起来就可以连接wifi,不用在手动操作。 2.问题分析 这个功能,使用普通的安卓代码就可以实现了。 3.代…

青春的海洋:海滨学院班级回忆录项目

3系统分析 3.1可行性分析 通过对本海滨学院班级回忆录实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本海滨学院班级回忆录采用SSM框架&#xff0c;JAVA作为开…

基于matlab的线性卷积演示系统

文章目录 前言1. 卷积的简单介绍1.1 翻褶1.2 移位1.3 相乘1.4相加1.5 整体的运行效果展示 2.App Designer的介绍3.具体的开发步骤3.1 声明成员变量3.2 设计基本布局3.3 编写回调函数 4.运行展示结语 前言 本篇文章按照如下要求&#xff0c;完成线性卷积演示系统 (1)用matlab完…

如何在Linux命令行中使用GhatGPT

2、验明正身&#xff0c;证明我的所在地是国内 3、第一次提问 4、第二次提问 5、问他一首古诗 6、话不多说&#xff0c;现在来展示他的安装过程 7、输入GitHub的网址 https://github.com/aandrew-me/tgpt 8、详情页向下翻 9、到终端输入 下列命令&#xff0c;等待安装&#x…

java并发编程-volatile的作用

文章目录 volatile的作用1.改变线程间的变量可见性2.禁止指令重排序 参考的学习视频 volatile的作用 1.改变线程间的变量可见性 每个线程都有一个专用的工作集内存&#xff0c;下图里面粉色的表示专用工作集内存&#xff0c;黄色的是共享内存工作区&#xff0c;如果加入了vol…

linux中级(防火墙firewalld)

一。firewalld与iptables区别1.firewalld可以动态修改单条规则&#xff0c;不需要像iptables那样&#xff0c;修改规则后必须全部刷新才可生效。firewalld默认动作是拒绝&#xff0c;则每个服务都需要去设置才能放行&#xff0c;而iptables里默认是每个服务是允许&#xff0c;需…

C#/.NET/.NET Core学习路线集合,学习不迷路!

前言 C#、.NET、.NET Core、WPF、WinForm、Unity等相关技术的学习、工作路线集合&#xff08;持续更新&#xff09;&#xff01;&#xff01;&#xff01; 全面的C#/.NET/.NET Core学习、工作、面试指南&#xff1a;https://github.com/YSGStudyHards/DotNetGuide C#/.NET/.N…

Linux 实例:无法通过 SSH 方式登录

现象描述 使用 SSH 登录 Linux 实例 时&#xff0c;提示无法连接或者连接失败&#xff0c;导致无法正常登录 Linux 实例。 现象描述 处理措施 SSH 登录报错 User root not allowed because not listed in AllowUsers 排查 SSH 登录报错 User root not allowed because not …

后端Java学习:springboot之文件上传(阿里云OSS存储)

一、什么是阿里云存储&#xff1f; 阿里云对象存储OSS&#xff08;Object Storage Service&#xff09;&#xff0c;是一款海量、安全、低成本、高可靠的云存储服务。使用OSS&#xff0c;您可以通过网络随时存储和调用包括文本、图片、音频和视频等在内的各种文件。 二、阿里云…