【APUE】网络socket编程温度采集智能存储与上报项目技术------多路复用

作者简介: 一个平凡而乐于分享的小比特,中南民族大学通信工程专业研究生在读,研究方向无线联邦学习
擅长领域:驱动开发,嵌入式软件开发,BSP开发
作者主页:一个平凡而乐于分享的小比特的个人主页
文章收录专栏:网络socket编程之温度采集智能存储与上报项目,本专栏为网络socket编程之温度采集智能存储与上报项目技术简介,实现流程分析
项目源码:https://gitee.com/TJF865975/project1_ds18b20,README部分包含项目功能及实现,欢迎大家Watch、Star、Fork。
欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖💖

1.多路复用基础知识

1.1 阻塞和非阻塞,同步和异步

阻塞和非阻塞的概念描述的是用户线程调用内核IO操作的方式:阻塞是指IO操作在没有接收完数据或者没有得到结果之前不会返回,需要彻底完成后才返回到用户空间;而非阻塞是指IO操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完成。
同步和异步的概念描述的是用户线程与内核的交互方式:同步是指用户线程发起IO请求后需要等待或者轮询内核IO操作完成 后才能继续执行;而异步是指用户线程发起IO请求后仍继续执行,当内核IO操作完成后会通知用户线程,或者调用用户线程注册 的回调函数。

1.2 IO多路复用

IO多路复用:IO多路复用模型是建立在内核提供的多路分离函数select基础之上的,使用select函数 可以避免同步非阻塞IO模型中轮询等待的问题,此外poll、epoll都是这种模型。在该种模式下,用户首先将需要进行IO操作的 socket添加到select中,然后阻塞等待select系统调用返回。当数据到达时,socket被激活,select函数返回。用户线程正式发起 read请求,读取数据并继续执行。从流程上来看,使用select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了添加 监视socket,以及调用select函数的额外操作,效率更差。但是,使用select以后最大的优势是用户可以在一个线程内同时处理 多个socket的IO请求。用户可以注册多个socket,然后不断地调用select读取被激活的socket,即可达到在同一个线程内同时处 理多个IO请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。

2.实现函数

2.1 select多路复用

select()函数允许进程指示内核等待多个事件(文件描述符)中的任何一个发生,并只在有一个或多个事件发生或经历一段指定时 间后才唤醒它,然后接下来判断究竟是哪个文件描述符发生了事件并进行相应的处理。
在这里插入图片描述

2.2 poll多路复用

select的缺点

  1. 单个进程能够监视的文件描述符的数量存在最大限制,通常是1024,当然可以更改数量,但由于select采用轮询的方式扫 描文件描述符,文件描述符数量越多,性能越差。
  2. 内核 / 用户空间内存拷贝问题,select需要复制大量的句柄数据结构,产生巨大的开销。
  3. select返回的是含有整个句柄的数组,应用程序需要遍历整个数组才能发现哪些句柄发生了事件。
  4. select的触发方式是水平触发,应用程序如果没有完成对一个已经就绪的文件描述符进行IO操作,那么之后每次select调 用还是会将这些文件描述符通知进程。

poll()的机制与 select() 类 似,与 select() 在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是 poll() 没有最大文件 描述符数量的限制(但是数量过大后性能也是会下降)。
poll函数的原型说明如下:

poll的实现

#include <poll.h>
struct pollfd
{int		fd;			/*	文件描述符 */short  	events;		/*	等待的事件 */	short	revents;	/*	实际发生了的事件 */
} ;
int poll(struct pollfd *fds, nfds_t nfds, int timeout);

第一个参数:用来指向一个struct pollfd类型的数组,每一个pollfd结构体指定了一个被监视的文件描述符,指示poll()监视多个文件描述符。每个结构体的events域是监视该文件描述符的事件掩码,由用户来设置这个域。revents域是文件描述符的操作结果事件掩码,内核在调用返回时设置这个域,events域中请求的任何事件都可能在revents域中返回。下表列出指定 events 标志以及测试 revents 标志的一些常值:

常量说明是否能作为 events 的输入是否能作为revents的返回结果
POLLIN普通或者优先级带数据可读
POLLRDNORM普通数据可读
POLLRDBAND优先级带数据可读
POLLPRI高优先级数据
POLLOUT普通数据可写
POLLWRNORM普通数据可写
POLLWRBAND优先级带数据可写
POLLERR发生错误
POLLHUP发生挂起
POLLNVAL描述字不是一个打开的文件

POLLIN | POLLPRI等价于select()的读事件,POLLOUT |POLLWRBAND等价于select()的写事件。POLLIN等价于POLLRDNORM |POLLRDBAND,而POLLOUT则等价于POLLWRNORM。例如,要同时监视一个文件描述符是否可读和可写,我们可以设置 events为POLLIN |POLLOUT。在poll返回时,我们可以检查revents中的标志,对应于文件描述符请求的events结构体。如果POLLIN事件被设置,则文件描述符可以被读取而不阻塞。如果POLLOUT被设置,则文件描述符可以写入而不导致阻塞。这些标志并不是互斥的:它们可能被同时设置,表示这个文件描述符的读取和写入操作都会正常返回而不阻塞。
第二个参数: nfds 指定数组中监听的元素个数;
第三个参数 :timeout指定等待的毫秒数,无论I/O是否准备好,poll都会返回。timeout指定为负数值表示无限超时,使poll()一直挂起直到一个指定事件发生;timeout为0指示poll调用立即返回并列出准备好I/O的文件描述符,但并不等待其它的事件。这种情况下,poll()就像它的名字那样,一旦选举出来,立即返回。
该函数成功调用时,poll()返回结构体中revents域不为0的文件描述符个数;如果在超时前没有任何事件发生,poll()返回0;
失败时,poll()返回-1,并设置errno为下列值之一:

EBADF一个或多个结构体中指定的文件描述符无效。
EFAULTfds指针指向的地址超出进程的地址空间。
EINTR请求的事件之前产生一个信号,调用可以重新发起。
EINVALnfds参数超出PLIMIT_NOFILE值。
ENOMEM可用内存不足,无法完成请求。

2.3 epoll多路复用

poll() 和 select() 同样存在一个缺点
包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增
大。
epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著
提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。epoll除了提供select/poll那种IO事件的水平触发(Level Triggered)外,还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。

epoll的实现

创建epoll实例:epoll_create()

int epoll_create(int size);//创建epoll实例

系统调用epoll_create()创建了一个新的epoll实例,其对应的兴趣列表初始化为空。若成功返回文件描述符,若出错返回-1。参数size指定了我们想要通过epoll实例来检查的文件描述符个数。该参数并不是一个上限,而是告诉内核应该如何为内部数据结构划分初始大小。从Linux2.6.8版以来,size参数被忽略不用。
作为函数返回值,epoll_create()返回了代表新创建的epoll实例的文件描述符。这个文件描述符在其他几个epoll系统调用中用来表示epoll实例。当这个文件描述符不再需要时,应该通过close()来关闭。当所有与epoll实例相关的文件描述符都被关闭时,实例被销毁,相关的资源都返还给系统。从2.6.27版内核以来,Linux支持了一个新的系统调用epoll_create1()。该系统调用执行的任务同epoll_create()一样,但是去掉了无用的参数size,并增加了一个可用来修改系统调用行为的flags参数。目前只支持一个flag标志:EPOLL_CLOEXEC,它使得内核在新的文件描述符上启动了执行即关闭标志。

修改epoll的兴趣表:epoll_ctl()

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

系统调用epoll_ctl()能够修改由文件描述符epfd所代表的epoll实例中的兴趣列表。若成功返回0,若出错返回-1。
第一个参数:epfd是epoll_create()的返回值;
第二个参数:op用来指定需要执行的操作,它可以是如下几种值:

  • EPOLL_CTL_ADD:将描述符fd添加到epoll实例中的兴趣列表中去。对于fd上我们感兴趣的事件,都指定在ev所指向的结构体中。如果我们试图向兴趣列表中添加一个已存在的文件描述符,epoll_ctl()将出现EEXIST错误;
  • EPOLL_CTL_MOD:修改描述符上设定的事件,需要用到由ev所指向的结构体中的信息。如果我们试图修改不在兴趣列表中的文件描述符,epoll_ctl()将出现ENOENT错误;
  • EPOLL_CTL_DEL:将文件描述符fd从epfd的兴趣列表中移除,该操作忽略参数ev。如果我们试图移除一个不在epfd的兴趣列表中的文件描述符,epoll_ctl()将出现ENOENT错误。关闭一个文件描述符会自动将其从所有的epoll实例的兴趣列表移除;

第三个参数:fd指明了要修改兴趣列表中的哪一个文件描述符的设定。该参数可以是代表管道、FIFO、套接字、POSIX消息队列、inotify实例、终端、设备,甚至是另一个epoll实例的文件描述符。但是,这里fd不能作为普通文件或目录的文件描述符;
第四个参数:ev是指向结构体epoll_event的指针,结构体的定义如下:

typedef union epoll_data
{void		*ptr;	/* Pointer to user-defind data */int			fd;		/* File descriptor */uint32_t	u32;	/* 32-bit integer */uint64_t	u64;	/* 64-bit integer */
} epoll_data_t;
struct epoll_event
{uint32_t events; /* epoll events(bit mask) */epoll_data_t data; /* User data */
};

参数ev为文件描述符fd所做的设置(epoll_event)如下:

  • events字段是一个位掩码,它指定了我们为待检查的描述符fd上所感兴趣的事件集合;
  • data字段是一个联合体,当描述符fd稍后称为就绪态时,联合的成员可用来指定传回给调用进程的信息;

事件等待:epoll_wait()

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

系统调用epoll_wait()返回epoll实例中处于就绪态的文件描述符信息,单个epoll_wait()调用能够返回多个就绪态文件描述符的信息。调用成功后epoll_wait()返回数组evlist中的元素个数,如果在timeout超时间隔内没有任何文件描述符处于就绪态的话就返回0,出错时返回-1并在errno中设定错误码以表示错误原因。
第一个参数:epfd是epoll_create()的返回值;
第二个参数:evlist所指向的结构体数组中返回的是有关就绪态文件描述符的信息,数组evlist的空间由调用者负责申请;
第三个参数:maxevents指定所evlist数组里包含的元素个数;
第四个参数:timeout用来确定epoll_wait()的阻塞行为,有如下几种:

  • 如果timeout等于-1,调用将一直阻塞,直到兴趣列表中的文件描述符上有事件产生或者直到捕获到一个信号为止。
  • 如果timeout等于0,执行一次非阻塞式地检查,看兴趣列表中的描述符上产生了哪个事件。
  • 如果timeout大于0,调用将阻塞至多timeout毫秒,直到文件描述符上有事件发生,或者直到捕获到一个信号为止。

数组evlist中,每个元素返回的都是单个就绪态文件描述符的信息。events字段返回了在该描述符上已经发生的事件掩码。data字段返回的是我们在描述符上使用epoll_ctl()注册感兴趣的事件时在ev.data中所指定的值。注意,data字段是唯一可获取这个事件相关的文件描述符的途径。因此,当我们调用epoll_ctl()将文件描述符添加到感兴趣列表中时,应该要么将ev.date.fd设为文件描述符号,要么将ev.date.ptr设为指向包含文件描述符号的结构体。

当我们调用epoll_ctl()时可以在ev.events中指定的位掩码以及由epoll_wait()返回的evlist[].events中的值如下所示:

常量说明作为 epoll_ctl()的输入作为epoll_wait()的返回
EPOLLIN可读取非高优先级数据
EPOLLPRI可读取高优先级数据
EPOLLRDHUPsocket对端关闭(始于Linux 2.6.17)
EPOLLOUT普通数据可写
EPOLLET采用边沿触发事件通知
EPOLLONESHOT在完成事件通知之后禁用检查
EPOLLERR有错误发生
POLLHUP出现挂断

默认情况下,一旦通过epoll_ctl()的EPOLL_CTL_ADD操作将文件描述符添加到epoll实例的兴趣列表中后,它会保持激活状态(即,之后对epoll_wait()的调用会在描述符处于就绪态时通知我们)直到我们显示地通过epoll_ctl()的EPOLL_CTL_DEL操作将其从列表中移除。如果我们希望在某个特定的文件描述符上只得到一次通知,那么可以在传给epoll_ctl()的ev.events中指定EPOLLONESHOT标志。如果指定了这个标志,那么在下一个epoll_wait()调用通知我们对应的文件描述符处于就绪态之后,这
个描述符就会在兴趣列表中被标记为非激活态,之后的epoll_wait()调用都不会再通知我们有关这个描述符的状态了。如果需要,我们可以稍后用过调用epoll_ctl()的EPOLL_CTL_MOD操作重新激活对这个文件描述符的检查。

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

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

相关文章

渗透测试、人肉搜索算不算犯罪?

人肉搜索算不算犯罪&#xff1f; 根据《网络信息内容生态治理规定》&#xff0c;网络信息内容服务使用者和生产者、平台不得开展网络暴力、人肉搜索、深度伪造、流量造假、纵账号等违法活动。规定自2020年3月1日起施行。渗透测试 算不算犯罪? 《网络安全法》第二十六条开展网络…

补充导入多个文件

可以使用pandas的read_excel方法来导入多个Excel文件。下面是一种方法&#xff1a; import pandas as pd# 定义要导入的Excel文件列表 files [file1.xlsx, file2.xlsx, file3.xlsx]# 创建一个空的DataFrame来存储所有的数据 df pd.DataFrame()# 循环遍历文件列表&#xff0c…

Git命令(1)[删除,恢复与移动]

文章目录 1.删除文件1.1命令----rm <filename>1.2命令----git rm <filename>1.1命令----git rm <filename> -f 2.恢复文件2.1命令----git restore <filename>2.1命令----git restore --staged <filename> 3.重命名文件3.1命令----mv 旧文件 新文…

八股面试速成—Java语法部分

暑期实习面试在即&#xff0c;这几天八股和算法轮扁我>_ 八股部分打算先找学习视屏跟着画下思维导图&#xff0c;然后看详细的面试知识点&#xff0c;最后刷题 其中导图包含的是常考的题&#xff0c;按照思维导图形式整理&#xff0c;会在复盘后更新 细节研究侧重补全&a…

设计模式:组合模式示例

组合模式的典型例子通常涉及到树形结构的处理&#xff0c;下面是几个形象且易于理解的例子&#xff1a; 文件系统 在文件系统中&#xff0c;目录可以包含文件或者其他目录&#xff0c;但是从用户的角度来看&#xff0c;目录和文件都可以被“打开”或者“获取大小”。这里的目…

租用阿里云的服务器多少钱?30元、61元、99元、165元、199元

租个阿里云的服务器多少钱&#xff1f;很便宜&#xff0c;云服务器2核2G3M固定带宽99元一年、2核4G服务器30元3个月、199元一年&#xff0c;轻量应用服务器2核2G3M配置61元一年、2核4G4M带宽165元一年&#xff0c;可以在阿里云CLUB中心查看 aliyun.club 当前最新的优惠券和活动…

GitOps - 为 OpenShift GitOps 配置邮件通知

《OpenShift 4.x HOL教程汇总》 说明&#xff1a;本文已经 在OpenShift 4.15 OpenShift GitOps 1.11.2 环境中验证 文章目录 ArgoCD 的 Notification 功能简介启动 OpenShift GitOps 的 Notification 功能配置邮件通知验证参考 说明&#xff1a;先根据《OpenShift 4 之 GitOp…

xv6源码分析 001

xv6源码分析 001 我们先看看xv6这个项目的基本结构&#xff08;只看代码部分&#xff09; 主要就是两个目录kernel 和 user。 user是一些用户程序&#xff0c;也就是我们平时在shell上面执行的命令&#xff0c;每执行一个命令就会创建一个新的用户进程来执行这个命令 在user目…

学习 Git 基础知识 - 日常开发任务手册

欢迎来到我关于 Git 的综合指南&#xff0c;Git 是一种分布式版本控制系统&#xff0c;已经在软件开发中彻底改变了协作和代码管理方式。 无论你是经验丰富的开发者还是刚开始编程之旅的新手&#xff0c;理解 Git 对于正确掌控代码、高效管理项目和与他人合作至关重要。 在本…

App应用的服务器如何增加高并发能力

大家好&#xff01;我是你们的好朋友咕噜铁蛋&#xff01;近年来&#xff0c;随着移动互联网的蓬勃发展&#xff0c;各类App应用如雨后春笋般涌现&#xff0c;用户量呈现爆发式增长。然而&#xff0c;随之而来的高并发访问问题也开始频繁出现&#xff0c;给服务器带来了极大的挑…

UV胶水与聚氯乙烯PVC材料的塑料粘接,效果如何?

UV胶水可以与聚氯乙烯PVC很好地粘接。 PVC是一种常见的塑料材料&#xff0c;UV胶水通常对PVC具有良好的粘接性能。UV胶水可以在紫外线照射下迅速固化&#xff0c;形成坚固的粘接&#xff0c;因此通常被用于PVC制品的粘接和修复。 UV胶水与PVC粘接的优点&#xff1a; 1. 快速固…

java数据结构与算法刷题-----LeetCode172. 阶乘后的零

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 数学&#xff1a;阶乘的10因子个数数学优化:思路转变为求5的倍数…

wordpress全站开发指南-面向开发者及深度用户(全中文实操)--创建新主题

前言 你可以在wordpress里面下载使用人家打包好的主题&#xff0c;但可能不是很好用&#xff0c;接下来就自己做一个自己的主题。你需要先找到xampp文件夹–htdocs–wordpress(我给更名为wplocal)–wp-content–themes 进入该文件夹之后你可以看到你之前下载导入的所有主题文件…

【Python第三方库】lxml 解析器和xpath路径语言

1.lxml是做什么的 是xml/html的解析器&#xff0c;主要是用来解析和提取html/xml数据 2.lxml语法 使用etree.HTML(html字符串)&#xff0c;将字符串转换为Element对象通过使用Element对象.xpath(语法)提取信息,返回的是一个列表的内存地址&#xff0c;需要通过使用索引获取信…

实验5-表达式求值算法

要求&#xff1a; 优化表达式求值算法&#xff0c;使得该算法可实现多位整数甚至浮点数的四则运算&#xff0c;并写程序进行验证 一下代码由AI生成&#xff0c;这个作业我是实在不想做了&#xff0c;老师上课讲的时候也是一笔带过&#xff0c;然后就要求我们自己动手写......…

redis的List详细介绍

Redis的列表&#xff08;List&#xff09;是一个由字符串组成的双向链表。以下是关于Redis列表的一些详细介绍&#xff1a; 基本概念&#xff1a; Redis的列表数据构造模式是一个双向链表&#xff0c;这意味着你可以在表头或表尾追加元素&#xff0c;从表头或表尾弹出元素。每个…

Java实现ATM机模拟系统(Week2)

目录 前言 项目规划&#xff08;第二周&#xff09; 具体实现 用户大类 AccountOperations接口 UserOperations接口 Operations类&#xff08;实现类&#xff09; Account类 User类 Area类 &#xff08;父类&#xff09; 货币大类 Money类&#xff08;抽象类&…

redis的Set详细介绍

Redis的集合&#xff08;Set&#xff09;是由字符串组成的无序集合&#xff0c;和列表&#xff08;List&#xff09;不同的是&#xff0c;集合中的元素是唯一的&#xff0c;不存在重复的元素。以下是关于Redis集合的一些详细介绍&#xff1a; 基本概念&#xff1a; Redis的集合…

vue + koa + Sequelize + 阿里云部署 + 宝塔:宝塔数据库连接

之前文章已经介绍了宝塔上传前后端代码并部署&#xff0c;不清楚的请看这篇文章&#xff1a; vue koa 阿里云部署 宝塔&#xff1a;宝塔前后端部署 下面是宝塔创建数据库&#xff1a; 我用的 koa Sequelize 连接的数据库&#xff0c;Sequelize 非常适合前端使用&#xf…

【Python】字符串处理技巧大揭秘:从基础到高级

欢迎来CILMY23的博客 本篇主题为 字符串处理技巧大揭秘&#xff1a;从基础到高级 个人主页&#xff1a;CILMY23-CSDN博客 Python系列专栏&#xff1a;http://t.csdnimg.cn/HqYo8 上一篇博客&#xff1a; http://t.csdnimg.cn/5NRlT C语言专栏&#xff1a; http://t.csdnim…