什么是IO?

io=input output,在单片机上是对MCU管脚电平的输入与输出;在linux上就是对文件的输入与输出;多路IO复用,也就是多个文件的输入输出同时监控

\\\插播一条:

自己在今年整理一套单片机物联网开发资料大全(附送网盘链接)

C语言基础 电路板设计 数模电 开发工具

Linnux 51 stm32 stm8实战项目代码

原理图 源代码 介绍视频 作品讲解

想要的同学私信找我。

多路复用技术需经过哪几个步骤(以后谁再问你什么是多路复用io)(1)

io是很多Java / python / go开发人员的重灾区,如果平时开发没接触过,可能就只知道个阻塞/非阻塞、同步/异步,厉害一点的再来个多路复用

多路复用技术需经过哪几个步骤(以后谁再问你什么是多路复用io)(2)

很多同学对这些概念根本没有明确的理解,真就是朗读并背诵全文~

今天,我就带着你探索一下io的发展史,以后再有人问你io,那他就是纯纯踢到钢板上了

多路复用技术需经过哪几个步骤(以后谁再问你什么是多路复用io)(3)

网络上关于io的文章多如牛毛,但是下面这段话你可能是第一次看到(看得懂就看,看不懂就跳过,该你懂的时候自然会懂):

不管是windows还是linux,所有牵涉到 IO的操作,都无法由应用程序直接完成,把文件操作权限开放给用户是很危险的,想执行io操作,必须使用操作系统内核提供的函数,但这些函数不需要我们亲自调用,Java已经帮我们做好了封装,我们在开发时调相关api即可,如下图这两个包

多路复用技术需经过哪几个步骤(以后谁再问你什么是多路复用io)(4)

io就是简单的read / write,而nio引入三个新的概念:

·Buffer:数据容器;

·Channel:这个东西太抽象了,中文名叫通道,知道通过它能完成内核间的io操作就行;

·Selector:nio实现多路复用的基础;

ok废话说完,我们先把相关概念梳理清楚

.同步或异步:同步即有序运行,当前任务执行完,才会执行下个任务;异步则相反,其他任务不需要等待当前任务执行完,通常依靠事件、回调机制来实现任务间次序关系;

.阻塞与非阻塞:在进行阻塞操作时,当前线程会处于阻塞状态,无法从事其他任务,只有当条件就绪才能继续,比如数据读取、写入;而非阻塞则是不管 IO操作是否结束,直接返回。

光看概念脑瓜子嗡嗡的吧,没关系,请看大屏幕~

干饭

公司附近有家网红饭店,每天去他们家吃饭的小姐姐特别多,当然你不要误解我的意思,我是想说他们家饭很好看,啊…不对,我的意思是他们家小姐姐很好吃,啊呸…也不对

总之我经常去,期间我的点餐方式经历了以下几个版本:

·v1.0:刚开始去的时候看别人都在排队点餐,身为老实人的我只能等前面人点完了再点,然后去取餐口等着取餐,有时候需要等15分钟才会取到餐,但是在取餐口站着可以看到更多小姐姐,所以最初也没在意;

·v2.0:后面某一天发现取餐口上面有一个屏幕,显示了当前的取餐号顺序,哇我恍然大明白,从那开始,我每次点完餐就找个小姐姐多的地方坐着,每隔5分钟就来取餐口看下是否轮到我取餐;

·v3.0:久而久之,每天看小姐姐也看腻了,有一天发现桌子上有个二维码,哇我又恍然大明白,原来可以扫码点餐啊,而且通过扫码点的餐,服务员会把餐送到你的位置;

取餐和io之间的关系:

v1.0(bio,blocking io):排队点餐、等餐,类似bio,同步阻塞,线程提交io操作后,在io操作完成前不能返回,也不能去干其它事,只能憨憨地等;

v2.0(nio,non-blocking io):排队点餐,时不时去取餐口查看是否轮到自己取餐,类似nio,同步非阻塞,线程提交io操作后可以先返回,但是需要主动找操作系统获取结果(nio可以构建多路复用io,这是本文的重点);

v3.0(nio2,或者叫aio,asynchronous io):扫码点餐,点好餐后找个位置坐着看小姐姐即可,餐好了服务员会送过来,类似aio,异步非阻塞,在nio基础上增加了事件和回调机制,操作系统准备好数据后,会主动通知线程。

关于操作系统对于io到nio的函数支持,演变过程比较复杂,你需要先搞懂操作系统如何实现零拷贝

关于io / nio,其实就上面这点东西,千万别被绕晕了,今天我们的主题是多路复用io

老规矩,先抛几个兄弟们最最最常见的疑惑,这些疑惑在下文都会找到答案:

·什么是多路复用?

·为什么需要多路复用?

·多路复用解决了什么问题?

·到底什么是多路复用,复用的是什么?

传统的网络通信都是使用socket,流程如下:

.创建socket

.将当前服务器ip和端口绑定到socket

.监听端口,通过accept处理请求

流程来看没啥毛病,但是你要知道,accept是阻塞的,也就是说同时只能处理一个请求,如果你想同时处理多个,可以用多线程,但这样又会引发另一个问题:操作系统中的线程多了后,需要耗费大量资源来管理线程和上下文切换(这里啰嗦一句,不管什么场景,线程并不是越多越好,到达某个阈值后,线程越多效率反而越低)

所以,想优雅地同时处理多个请求,操作系统内核必须实现单个线程监听多个socket,即 io多路复用

linux系统对多路复用的支持有三种:

·多路复用:select

·多路复用pro:poll

·多路复用pro max:epoll

来看看这三种函数的实现,关于他们三个的性能也就大概清楚了

相关视频推荐

面试中正经“八股文”网络原理tcp/udp,网络编程epoll/reactor

6种epoll的设计,让你吊打面试官,而且他不能还嘴

epoll原理剖析以及三握四挥的处理

LinuxC 后台服务器开发架构师免费学习地址

【文章福利】:小编整理了一些个人觉得比较好的学习书籍、视频资料有需要的可以自行联系我!~绿色软件airuimcu(需要自取)

//返回值是已就绪文件描述符个数

int select (int __nfds, fd_set *__readfds, fd_set *__writefds, fd_set *__exceptfds, struct timeval *__timeout)

参数解释:

?_ _nfds:监听的文件描述符数量(不要纠结文件描述符是什么东西,能够了解成每个io操作都对应一个文件描述符);

?_ _readfds、__writefds、__exceptfds:指定多路复用机制监听的事件类型,这三个分别是读数据事件、写数据事件、异常事件;

?_ _timeout:监听时阻塞等待的超时时长;

select通过监听多个文件描述符来管理多个连接,一次能监听1024个(默认值),当select函数返回后,遍历描述符汇合找到已就绪的描述符进行下一步处理

至此,一个线程只能管理一个连接的痛点算是攻克了,但还存在两个问题:

?每次监听的描述符数量有限,虽然能够通过修改宏文件修改,但这种做法太极端;

?我们须要不断执行select函数来获取已就绪的文件描述符,遍历过程也须要耗费cpu性能;

所以linux引入了poll,攻克文件描述符限制的问题

poll

//返回值也是已就绪文件描述符个数

int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout);

参数解释:

?_ _fds:pollfd构造体数组,包含了要监听的文件描述符和要监听的事件类型;

?_ _nfds: pollfd构造体数组的数量,没有大小限制;

?_ _timeout:监听时阻塞等待的超时时长;

相对于select,poll其实就突破了文件描述符的限制,但依然须要我们遍历每个文件描述符来检测是否就绪

多路复用技术需经过哪几个步骤(以后谁再问你什么是多路复用io)(5)

多路复用技术需经过哪几个步骤(以后谁再问你什么是多路复用io)(6)

绿色图标【‟で】liutianwang123

所以,Linux2.6引入了epoll

epoll

epoll有三个函数epoll_create、epoll_ctl和 epoll_wait

//创建epoll实例,size表示监听多少个文件描述符

int epoll_create(int size);

//将连接加入epoll监听列表,参数分别表示:

//epoll_create()的返回值

//需要执行的修改操作

//需要监听的文件描述符

//监听的事件类型

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

//阻塞等待返回已就绪的文件描述符,参数分别表示:

//epoll_create()的返回值

//事件的集合

//events大小

//超时时间

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

epoll模型支持自定义监听的描述符数量,也可以直接返回就绪的描述符

大名鼎鼎的redis在Linux系统的实现就是用的epoll模型,所以即使是单线程,也能轻松应对高并发的客户端访问

说了这么多,那到底什么是io多路复用?复用的是什么?

这两个问题我觉得很难解释,可能等以后真正接触到操作系统层面的知识了,才会真正悟透吧

个人对于复用的看法:

首先肯定不是对socket的复用,一个请求对应一个socket耶稣来了也改变不了;

也不是对线程的复用,但好像也可以这么理解,因为毕竟是一个线程管理多个socket;

所谓复用,可能并不是非得复用某一个东西,我觉得是通过一次到内核的请求实现对多个socket的管理这个行为

关于上面提到的各种函数了解即可,不用太深入研究,我也是翻了好些文档才得到这些信息,没必要在上面花太多时间,不然脑瓜子嗡嗡的~,我现在脑瓜子就嗡嗡的,果然懂得越多不懂得就越多。。。

ok我话说完

,