【计算机网络】高级IO之select

文章目录

  • 1. 什么是IO?
    • 什么是高效 IO?
  • 2. IO的五种模型
    • 五种IO模型的概念理解
      • 同步IO与异步IO
      • 整体理解
  • 3. 阻塞IO
  • 4. 非阻塞IO
    • setnonblock函数
    • 为什么非阻塞IO会读取错误?
    • 对错误码的进一步判断
    • 检测数据没有就绪时,返回做一些其他事情
    • 完整代码
      • mytest.cc
      • makefile
  • 5. select
    • 为什么要有select?
    • select 接口
      • 第一个参数 nfds的理解
      • 什么是 输入 输出型参数
      • 最后一个参数 timeout 的理解
      • readfds writefds exceptfds 参数的理解
      • select的返回值

1. 什么是IO?

IO:表示 输入 输出


当对方把连接建立好,但是不发数据
而我是一个线程,正在调用 read 来读,就会阻塞,一直等数据发送过来
读取条件不满足的情况下,read或recv 只会等待


无论是有数据时的拷贝 ,还是没有数据时的等待
两者的时间成本,全都算到了用户头上

在用户的角度,IO= 等+数据拷贝

什么是高效 IO?

单位时间内, 等的比重越低, IO效率越高

当IO条件满足时,称为 IO事件就绪

2. IO的五种模型

五种IO模型的概念理解

如:钓鱼假设分为两步 , 钓鱼 = 等 + 钓

在鱼竿的钩子上挂一个鱼漂,浮在水面上,用来恒定鱼竿下水的深度
当鱼漂上下摆动时,就可以知道当前有鱼上钩了


1. 张三一般喜欢专注于一件事, 所以当张三钓鱼等待时,就会一直盯着鱼漂看 是否有鱼上钩
过了一段时间,鱼漂动了,张三拉动鱼竿,将鱼钓上来了,放入桶中
继续刚才钓鱼的动作
张三在钓鱼的过程中,只盯着鱼漂看,不干其他事情


2. 李四天生好动, 所以当李四钓鱼时,就不怎么看鱼漂,会左顾右看的张望
当发现鱼咬钩后,就把鱼钓上来,放入桶中
继续刚才钓鱼的动作
李四在钓鱼的过程中,除了看鱼漂,还做其他事情


3. 王五比较特别, 当王五钓鱼时,在鱼竿的顶部放上一个铃铛,等待鱼上钩
等待过程中,王五做着自己的事情
当王五听到铃铛响了时,就拉动鱼竿,将鱼钓上来了,放入桶中
继续刚才钓鱼的动作
王五在钓鱼的过程中,不看鱼漂,只听铃铛来判断是否有鱼上钩


4. 赵六是周围的首富,开皮卡来钓鱼,皮卡上装了10根鱼竿 (首富是来体验生活的)
使用10根鱼竿一起钓鱼,赵六就从前往后 依次查看 是否有鱼漂在动


5. 田七是方圆500公里的首富 (田七比赵六有钱)
田七很忙,每天都有各种会议要开,而且田七并不是想钓鱼,而是喜欢吃鱼
所以就 让司机小王帮忙去钓鱼
当鱼桶满了后,给田七打电话,就会来人把鱼带走
小王在钓鱼时,田七也正在开会


张三和李四钓是一样的,差别在等待鱼上钩的方式不同
张三为 鱼漂不动,他不动
李四为 鱼漂不动,会立马返回去做其他事情


张三的钓鱼方式 称为 阻塞IO
(数据没有就绪,调用的read接口不会返回)


李四的钓鱼方式 称为 非阻塞IO
(检测一次若没有数据,则会立马返回,过一段时间可以再次检测)


王五在鱼还没有钓上来之前,就知道当铃铛响了,就应该拉动鱼竿
王五的钓鱼方式 称为 信号驱动IO


赵六一次管理多个鱼竿,赵六的钓鱼方式称为 多路复用或多路转接

在这几个人中,赵六的钓鱼效率比较高
因为赵六的鱼竿比较多,所以鱼上钩的概率大 即等待时间比较短
所以赵六的钓鱼效率比较高


同步IO与异步IO

前四个人都要钓鱼的过程,所以都称为 同步IO

田七没有参与钓鱼的过程,没有等 ,也没有钓,只是 发起钓鱼的过程
田七的钓鱼方式 称为 异步IO


整体理解

钓 可以看作 数据拷贝
张三 李四 等人 可以看作 进程
田七可以看作是一个进程,司机小王可以看作是操作系统
鱼竿可以看作 文件描述符
鱼 可以看作是 数据
鱼咬钩 或 鱼漂动 、铃铛响 可以看作 IO事件就绪


一个进程 在文件描述符上读取数据时,若数据没有就绪,当前进程只能挂起等待
直到有IO时间就绪,数据才可以拷贝到对应的上层

3. 阻塞IO

阻塞IO:数据没有就绪,调用的read接口不会返回


通过使用 read 函数 从键盘中读,当代码写好时,若什么也不输入,则什么也不显示 则为阻塞IO

输入 man 2 read

从一个文件描述符 中 去读count个数据 到 buf缓冲区中
若获取成功,则返回 字节数据
若获取 为0,则表示读到文件结尾
若获取为-1,则表示失败,并设置错误码


0表示标准输入流
从标准输入流中 读buffer数组大小的数据 发送到 buffer中

运行可执行程序后,一直不输入,则导致read在等待,直到有数据输入才进行数据拷贝

4. 非阻塞IO

非阻塞IO:检测一次若没有数据,则会立马返回 做其他事情,过一段时间可以再次检测


通过使用 read 函数 从键盘中读,当代码写好时,就是不输入
通过这样的方式,模拟读取条件不满足的情况下,read只会等待的情况

在上述阻塞IO的代码的基础上 进行修改


setnonblock函数

输入 man fcntl

第一个参数为 文件描述符
第二个参数 表示 你要对文件描述符干什么
获得/设置文件状态标记(cmd=F_GETFLF_SETFL)

通过设置文件状态标记,就可以将一个文件描述符 变为 非阻塞

使用 F_GETFL,将当前文件描述符的属性取出来
使用 F_SETFL,将文件描述符 状态进行设置,并加上一个 O_NONBLOCK (非阻塞) 参数

若函数返回 -1,则表示失败


创建一个函数 setnonblock,将文件描述符设置为非阻塞状态
先使用F_GETFL,获取对应文件描述符的属性
若获取失败,则返回错误原因和错误码
若获取成功,则使用 F_SETFL 将文件描述符状态设为非阻塞状态


为什么非阻塞IO会读取错误?

在主函数main中,将标准输入流改为非阻塞状态
并根据read的三种返回值,分别设置 返回提示 : 读取成功、文件结尾 和 读取错误


当将标准输入流设置为非阻塞状态后
再次运行可执行程序,直接就会读取失败
在调用read时,发现数据没有就绪 (当前读取检测速度太快,还没有输入,就报错了)

所以一旦底层数据没有就绪,就以出错的形式返回,但是不算真正的出错


但这样就没办法区分是真正出错还是 底层没有数据了
所以就通过出错码 进行进一步判断

对错误码的进一步判断

EAGAIN EWOULDBLOCK 都是系统设置的,错误码都是11
用于判断没出错,但是以出错的形式返回 的错误码
若为真,则下次继续检测即可


若IO被信号中断,则重新检测


检测数据没有就绪时,返回做一些其他事情

非阻塞IO,是可以做到 当检测数据没有就绪 时,就返回做一些其他事情


定义一个 包装器 其参数为void 返回值为void ,并将其重命名为 func_t 类型
定义一个vetcor数组 ,其类型为 func_t


设置三个任务,分别为PrintLog OperMysql CheckNet


在创建LoadTask函数,将任务分别插入到funcs数组中


在主函数main中,调用 LoadTask函数 以此加载任务


创建一个 HandlerALLTask函数,用于遍历 vector数组 ,数组元素为任务
当数据没有就绪时,就返回 处理任务


完整代码

mytest.cc

#include<iostream>
#include<unistd.h>
#include<fcntl.h>
#include<cstdio>
#include<cstring>
#include<vector>
#include<functional>
using namespace std;//任务
void PrintLog()//打印日志
{cout<<"这是一个打印日志例程"<<endl;
}void OperMysql()
{cout<<"这是一个操作数据库的例程"<<endl;
}void CheckNet()
{cout<<"这是一个检测网络状态的例程"<<endl;
}using func_t =function<void(void)>;
vector<func_t>  funcs;void LoadTask()
{funcs.push_back(PrintLog);funcs.push_back( OperMysql);funcs.push_back(CheckNet);
}void HandlerALLTask()
{//遍历vector数组for(auto& func:funcs){func();}
}void SetNonBlock(int fd)//将文件描述符设为非阻塞
{int fl=fcntl(fd,F_GETFL);//获取当前文件描述符的指定状态标志位if(fl<0)//获取失败{cerr<<"error string: "<<strerror(errno)<<"error code: "<<errno<<endl;}fcntl(fd,F_SETFL,fl | O_NONBLOCK);//将文件描述符状态设为非阻塞状态
}int main()
{char buffer[64];SetNonBlock(0);//将标准输入流 改为非阻塞状态LoadTask();//加载任务while(true){//0表示标准输入流ssize_t n=read(0,buffer,sizeof(buffer)-1);//检测条件是否就绪if(n>0)//读取成功{buffer[n-1]=0;    cout<<"echo# "<<buffer<<endl; }else if(n==0)//读到文件结尾{cout<<"end file"<<endl;}else//读取失败 {if(errno==EAGAIN || errno ==EWOULDBLOCK){//若为真,说明没出错,只是以出错返回//底层数据没有准备好,下次继续检测HandlerALLTask();//遍历数组 处理任务sleep(1);cout<<"data not  ready"<<endl;continue;}else if(errno == EINTR){//IO被信号中断,需要重新检测continue;}else //真正的错误{cout<<"read error"<<"error string: "<<strerror(errno)<<"error code: "<<errno<<endl;break;}}sleep(1);}return 0;
}

makefile

mytest:mytest.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f mytest

5. select

为什么要有select?

read/recv 等 文件接口只有一个文件描述符
想要 让一个接口等待多个文件描述符,而read等接口是不具备这个能力的
操作系统就设计一个接口 select,用于多路复用


select 作用
1.等待多个文件描述符
2.只负责等(没有数据拷贝的能力)


select 接口

输入 man select

由于select只负责等待,不负责拷贝,所以没有缓冲区

第一个参数 nfds的理解

第一个参数 nfds,是一个输入型参数 ,表示 select等待的多个文件描述符(fd)数字层面 最大的+1
(文件描述符的本质为 数组下标,多个文件描述符中 数值最大的文件描述符值+1 即nfds )

什么是 输入 输出型参数

用户把数据交给操作系统,同样操作系统也要 通过这些输出型参数 把结果 交给用户
为了让 用户 和 操作系统之间进行信息传递,就把参数设置为 输入 输出型参数

最后一个参数 timeout 的理解

timeout 是一个 输入 输出型参数


timeout的数据类型 为struct timeval

可以一个时间结构体,tv_sec 表示 秒, tv_usec 表示 微秒


对于 struct timeval的对象 可设置三种值

第一种 对象被设为 NULL ,对于select来说 表示 阻塞等待
(多个文件描述符任何一个都不就绪,select就一直不返回)

第二种 struct timeval对象定义出来,并将其中变量都设为0
对于select来说 表示 非阻塞等待
(多个文件描述符任何一个都不就绪,select就会立马出错 并返回)

第三种 struct timeval对象定义出来,并将其中变量设为 5 和 0
表示 5s以内 阻塞等待,否则 就 timeout(非阻塞等待) 一次
若在第3s时 有一个文件描述符就绪,则select就会返回 其中参数 timeout 表示 剩余的时间 2s(5-3=2)


readfds writefds exceptfds 参数的理解

readfds writefds exceptfds 这三个参数 是同质的
readfds 表示 读事件
writefds 表示 写事件
excepttfds表示 异常事件

三者类型都为 fd_set


fd_set是一个位图结构,用其表示多个文件描述符
通过比特位的位置, 就代表文件描述符数值是谁


位图结构想要使用 按位与、按位或 这些操作,必须使用操作系统提供的接口

FD_CLR :将指定的文件描述符从 指定的集合中清除

FD_ISSET:判断文件描述符是否在该集合中被添加

FD_SET: 将一个文件描述符添加到对应的set集合中

FD_ZERO:将文件描述符整体清空


以readfds 读事件为例

若放入 readfds 集合中,用户告诉内核 ,那些文件描述符对应的读事件需要由 内核 来关心
返回时,内核要告诉用户,那些文件描述符的读事件已经就绪


假设想让操作系统去关心八个文件描述符对应的事件

用户想告诉内核时,用户需 定义 fd_set 对象 rfds ,其中八个比特位设置为1
比特位的位置表示几号文件描述符
比特位被置1,则操作系统就需要关心 对应的几号文件描述符
如:需要关心 1-8号文件描述符,即查看是否就绪


当select返回时, 内核会告诉用户,rfds重置,并将 就绪的文件描述符 对应 的 比特位位置 置1

如: 3号和5号就绪,则对应比特位 位置 置1 ,表示3号和5号文件描述符 对应的内容就绪


select的返回值

select的返回值 同样也有三种情况
第一种 大于0
表示有几个文件描述符 是就绪的

第二种 等于0
进入timeout状态 ,即 5s以内没有任何一个文件描述符 就绪

第三种 小于0
等待失败 返回-1
如:想要等待下标为1 和2的文件描述符,但是下标为2的文件描述符根本不存在,就会等待失败


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

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

相关文章

Linux和本地Windows如何互传文件(sz和rz指令)

目录 关于 rzsz 注意事项 安装软件 rz的使用&#xff08;本地主机文件传到Windows中&#xff09; sz的使用(Linux中的文件传到本地Windows主机中) 关于 rzsz 这个工具用于 windows 机器和远端的 Linux 机器通过 XShell 传输文件. 安装完毕之后可以通过直接拖拽的方式将文件…

【源码】hamcrest 源码阅读及空对象模式、模板方法模式的应用

文章目录 前言1. 类图概览2. 源码阅读2.1 抽象类 BaseMatcher2.1 接口 Description提炼模式&#xff1a;空对象模式 2. 接口 Description 与 SelfDescribing 配合使用提炼模式 模板方法 后记 前言 hamcrest &#xff0c;一个被多个测试框架依赖的包。听说 hamcrest 的源码质量…

Linux性能优化--性能工具:系统内存

3.0.概述 本章概述了系统级的Linux内存性能工具。本章将讨论这些工具可以测量的内存统计信息&#xff0c;以及如何使用各种工具收集这些统计结果。阅读本章后&#xff0c;你将能够&#xff1a; 理解系统级性能的基本指标&#xff0c;包括内存的使用情况。明白哪些工具可以检索…

解决u盘在我的电脑中重复显示两个

删除注册表&#xff1a; [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\DelegateFolders\{F5FB2C77-0E2F-4A16-A381-3E560C68BC83}]

910数据结构(2019年真题)

算法设计题 问题1 有一种排序算法叫做计数排序。这种排序算法对一个待排序的表&#xff08;采用顺序存储&#xff09;进行排序&#xff0c;并将排序结果存放到另一个新的表中。必须注意的是&#xff0c;表中所有待排序的关键字互不相同&#xff0c;计数排序算法针对表中的每个…

视频增强修复工具Topaz Video AI mac中文版安装教程

Topaz Video AI mac是一款使用人工智能技术对视频进行增强和修复的软件。它可以自动降噪、去除锐化、减少压缩失真、提高清晰度等等。Topaz Video AI可以处理各种类型的视频&#xff0c;包括低分辨率视频、老旧影片、手机录制的视频等等。 使用Topaz Video AI非常简单&#xff…

5-1.(OOP)初步分析MCV架构模式

组成&#xff1a;模型&#xff08;model&#xff09;、视图&#xff08;view&#xff09;、控制器&#xff08;controller&#xff09; view&#xff1a;界面、显示数据 model&#xff1a;数据管理、负责在数据库中存取数据以及数据合法性验证 controller&#xff1a;负责转…

Python大数据之PySpark(四)SparkBaseCore

文章目录 SparkBase&Core环境搭建-Spark on YARN扩展阅读-Spark关键概念[了解]PySpark角色分析[了解]PySpark架构后记 SparkBase&Core 学习目标掌握SparkOnYarn搭建掌握RDD的基础创建及相关算子操作了解PySpark的架构及角色 环境搭建-Spark on YARN Yarn 资源调度框…

Linux 下如何调试代码

debug 和 release 在Linux下的默认模式是什么&#xff1f; 是release模式 那你怎么证明他就是release版本? 我们知道如果一个程序可以被调试&#xff0c;那么它一定是debug版本&#xff0c;如果它是release版本&#xff0c;它是没法被调试的&#xff0c;所以说我们可以来调试一…

FPGA project : TFT_LCD

实验目标&#xff1a; 驱动TFT_LCD显示十色彩条。 重点掌握的知识&#xff1a; 1&#xff0c;液晶显示器&#xff0c;简称LCD(Liquid Crystal Display)&#xff0c;相对于上一代CRT显示器(阴极射线管显示器)&#xff0c;LCD显示器具有功耗低、体积小、承载的信息量大及不伤眼…

王道考研操作系统——I/O管理

I/O设备的基本概念 键盘&#xff1a;输入设备&#xff08;把设备准备好的数据读入计算机当中&#xff09;&#xff1b; 显示器&#xff1a;输出设备&#xff08;把计算机中准备好的数据写出到设备上&#xff09;&#xff1b; 移动硬盘&#xff1a;既是输入又是输出 中断驱动…

数据挖掘(2)数据预处理

一、数据预处理 1.1概述 数据预处理的重要性 杂乱性&#xff1a;如命名规则。重复性&#xff1a;同一客观事再不完整性&#xff1a;噪声数据&#xff1a;数据中存在错误或异常的现象。 数据预处理的常见方法 数据清洗&#xff1a;去掉数据中的噪声&#xff0c;纠正不一致。数…

HTML的学习 Day02(列表、表格、表单)

文章目录 一、列表列表主要分为以下三种类型&#xff1a;1. 无序列表&#xff08;Unordered List&#xff09;&#xff1a;2. 有序列表&#xff08;Ordered List&#xff09;&#xff1a;将有序列表的数字改为字母或自定义内容li.../li 列表项标签中value属性&#xff0c;制定列…

OpenCV实现视频的追踪(meanshift、Camshift)

目录 1&#xff0c;meanshift 1.1 算法流程 1.2 算法实现 1.3 代码实现 1.4 结果展示 1&#xff0c;meanshift 1.1 算法流程 1.2 算法实现 1.3 代码实现 import numpy as np import cv2 as cv# 读取视频 cap cv.VideoCapture(video.mp4)# 检查视频是否成功打开 if n…

分布式应用程序协调服务 ZooKeeper 详解

目录 1、ZooKeeper简介 2、ZooKeeper的使用场景 3、ZooKeeper设计目的 4、ZooKeeper数据模型 5、ZooKeeper几个重要概念 5.1、ZooKeeper Session 5.2、ZooKeeper Watch 5.3、Consistency Guarantees 6、ZooKeeper的工作原理 6.1、Leader Election 6.2、Leader工作流…

NPDP产品经理知识(产品创新管理)

复习文化&#xff0c;团队与领导力 产品创新管理&#xff1a; 如何树立愿景&#xff1a; 如何实现产品战略 计划 实施产品开发&#xff1a; 商业化&#xff0c;营销计划&#xff0c;推广活动 管理产品生命周期&#xff1a; 新式走向市场的流程&#xff1a;

【Docker】docker拉取镜像错误 missing signature key

问题 当我使用docker拉取一个特定的镜像时&#xff0c;提示错误&#xff1a; 错误 missing signature key 但是拉取其他镜像又可以访问&#xff0c;&#xff0c;&#xff0c;&#xff0c;于是&#xff0c;我怀疑是否是docker版本问题。 docker --version结果确实&#xff0…

操作系统原理-习题汇总

临近毕业&#xff0c;整理一下过去各科习题及资料等&#xff0c;以下为操作系统原理的习题汇总&#xff0c;若需要查找题目&#xff0c;推荐CtrlF或commandF进行全篇快捷查找。 操作系统原理 作业第一次作业选择题简答题 第二次作业选择题简答题 第三次作业选择题简答题 第四次…

ctfshow—1024系列练习

1024 柏拉图 有点像rce远程执行&#xff0c;有四个按钮&#xff0c;分别对应四份php文件&#xff0c;开始搞一下。一开始&#xff0c;先要试探出 文件上传到哪里&#xff1f; 怎么读取上传的文件&#xff1f; 第一步&#xff1a;试探上传文件位置 直接用burp抓包&#xff0c;…

凉鞋的 Godot 笔记 105. 第一个通识:编辑-测试 循环

105. 第一个通识&#xff1a;编辑-测试 循环 在这一篇&#xff0c;我们简单聊聊此教程中所涉及的一个非常重要的概念&#xff1a;循环。 我们在做任何事情都离不开某种循环&#xff0c;比如每天的 24 小时循环&#xff0c;一日三餐循环&#xff0c;清醒-睡觉循环。 在学习一…