简单介绍Linux IO阻塞模型和Epoll原理

前言

       众所周知,从事Java开发的工程师们大多都不可避免地要接触web开发,既然要接触web,那就必须要对IO有所了解。本人工作期间常常不求甚解,只知道使用,却不知道有哪些特性在悄悄生效。这对于自己的职业发展来说是不利的。 由此契机,决定对Linux的阻塞模型进行一些了解,并将学习到的知识以简单的形式介绍给大家。

摘要

对于工作比较繁忙的小伙伴,直接浏览摘要即可。

所有以下的IO模型,都认为系统调用是一个非常耗时的操作。

1)BIO。这是最初的IO模型。没有客户端连接时阻塞,直到有客户端连接,此时启动一个新的线程来处理消息。
缺点:线程数量大,创建线程消耗资源,线程切换消耗资源。根本原因:阻塞。

2)NIO。这是升级版的IO模型,Linux的系统调用(recvfrom)提供了一个非阻塞的选项。此时单线程即可处理全部客户端。
缺点:每次都需要进行系统调用检测客户端是否准备好数据,系统调用频率极高。

3)IO多路复用器(selector)。每当有客户端进入,都将其存储到selector中,并且循环调用selector,使其进入内核让cpu遍历所有客户端,来确认哪些客户端已经就绪,然后将就绪的客户端返回给上层应用,上层应用来读取数据。读取仍然是同步的。
缺点:每次内核都要遍历大量未就绪的客户端。

4)epoll。epoll是事件驱动的。epoll会在内核空间开辟两块新的空间,一块用于存储所有已经建立连接的客户端(空间1),一块用于存储已经就绪的客户端(空间2)。在网卡接收到数据时,会硬中断cpu,让cpu将就绪的客户端转移到置顶的内核空间中(空间2)。此时epoll会返回已经就绪的客户端,上层应用程序就可以读写客户端发送的数据了。

正文

1、BIO


accept每进来一个客户端,就会开启一个新的线程来读写数据。每次有客户端进入,都要创建新的线程。
这个新的线程会发生系统调用,然后会阻塞住,等待数据输入。(如果此时只是建立了链接没有任何数据输入)
每个线程对应一个客户端连接。

问题:线程太多,创建线程都需要系统调用,并且线程都需要消耗资源。
根本原因:还是在于会阻塞,如果不会阻塞,那么一个线程就可以解决问题。

在java中:client.getInputStream和accept都会阻塞。

BIO
BIO

2、NIO


accept不再阻塞,会返回null(java)。【大循环】
读取数据也不再阻塞。
不为null时,进行读写,每次都要进行系统调用才能判断是否有客户端是否有数据进入。【大循环中嵌套的循环】
对应代码在下面的链接。
https://blog.csdn.net/jiushiNeil/article/details/107169039

问题:有一万个客户端,就需要系统调用一万次判断是否有数据。

在这里插入图片描述
NIO

3、IO多路复用器


新增加一个selector,每次有客户端进入,都存入到selector。
selector会发起系统调用,参数就是所有的已经连接的客户端,让内核去遍历判断到底哪些客户端是已经准备好数据的。这一步是阻塞的。
然后返回给上层应用程序,上层应用程序再去读取可用的客户端数据。
selector返回的是状态,必须上层应用自己去发生同步读写操作。

问题:尽管在读取数据时不再需要遍历了,但是内核每次都将主动遍历客户端(fd-文件描述符)来检查是否有准备好的数据。
在客户端很多的情况下,很浪费cpu资源,重复检查大量未准备好的数据。

在这里插入图片描述
Selector

4、epoll


在内核开辟一个新的空间,每当有一个客户端链接进入,就放入到这块新的空间。可以减少selector重复传递文件描述符给内核的步骤。【所有客户端空间】
再次开辟一个新的空间,专门用于存储已经就绪的客户端。【就绪空间】

(1、epoll_create,创建新的空间专门存储已经进入的客户端。
(2、epoll_ctl,可以创建、修改、删除。此时要将监听的文件描述符和accept作为参数传入。epoll_ctl(epoll_create_space,add,serverSocket,accept)。
(3、epoll_wait,可以监听已经准备好的客户端信息。
(4、如果此时有客户端连接进入,则epoll_ctl(epoll_create_space,clientSocket)。

此时网卡会将客户端发送的数据发送到DMA(直接内存访问),然后硬中断cpu。此处就是事件驱动。
驱动cpu执行将epoll_create_space的文件描述符从【所有客户端空间】移动到【就绪空间】
使用epoll时,cpu不再需要主动遍历来检查客户端数据是否就绪了。但是实际上,在读取数据时仍然是同步的。

在这里插入图片描述
epoll
主要函数:int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)

epfd:由epoll_create 生成的epoll专用的文件描述符;
epoll_event:用于回传代处理事件的数组;
maxevents:每次能处理的事件数;
timeout:设-1为阻塞,设0表示非阻塞立即返回,设>0则为指定的毫秒数
return:发生事件数;

结语

       本人只是一名初出茅庐的初级程序员,能力有限,无法给大家提供细致的实现和说明。在此记录自己对epoll的学习,来对IO有更深入的了解。如果错误,请各位大神不吝赐教。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值