网络IO(BIO,NIO)、多路复用器

cpu运行原理:

在这里插入图片描述
假设现在只有一个cpu,说一下在这里的产生的两种中断:

1.时钟中断:一个cpu需要执行所有的程序,那么就需要晶振产生时钟中断cpu,cpu再进行进程调度执行下一个程序。
由于同一时间只能执行一个程序,所以需要将上一个程序的现场保护起来,把将要执行的程序的现场恢复。这里的保护现场和恢复现场涉及到IO,假设有1w个进程,那么cpu势必有大量的时间浪费在进程的调度上。

2.系统调用中断:程序自身也有可能会需要中断来做其他的事情,由于中断程序是在内核里面的,所以程序会调用中断。

3.外部中断:比如鼠标的移动,cpu需要计算鼠标位置的更新,也会产生中断(自己的理解,不知道对不对)

之所以说上面的知识是为我们理解下面的IO打下基础。


BIO(阻塞式IO)

先看代码:
在这里插入图片描述

写过Socket的话应该知道,这里有两个方法是阻塞的。一个是serverSocket的accept方法,一个是从客户端读取数据的方法。
这个时候,为了能一直接收客户端请求,在接收到客户端请求以后就会抛出一个线程(一个轻量级的进程,但是显示依然是主进程的id号

在这里插入图片描述
在linux使用strace命令追踪进程的执行过程,我们得到上面的过程。首先会创建一个标识符为3的socket,然后绑定到8090端口,执行监听方法。接着调动accpet方法等待客户端连接,如果有客户点连接上来,就抛出一个线程,即把内存复制一份到其他地方去。

但是,如果线程过多不仅消耗大量内存,同时cpu资源调度的消耗也很严重。所以我们需要一种非阻塞式IO来解决这个问题。


NIO(非阻塞式IO)

NIO的理解:
对操作系统:nonbloking IO 非阻塞式IO
对Java:new IO 新的IO体系

先看代码:
在这里插入图片描述
可以看见,ServerSocket的创建与上面类似,但是可以配置block为false即非阻塞式IO。并且配置了客户端链表来存放新的连接。

在这里插入图片描述
接下来是一个死循环,调用accept方法,但是这个方法不会阻塞了。如果有客户端连接过来返回一个client,如果没有就返回null。接下来判断client是否为空,不为空则把client加入到客户端链表中。
在这里插入图片描述

对所有的client遍历,如果有新的发送内容则接收。

在这里插入图片描述

在linux使用strace命令追踪进程的执行过程,我们得到上面的过程。前面创建socket,绑定端口号,开启监听,与BIO不同的是这里设置了nonbloking。接下来进入到死循环,不停的查看是否有新的连接过来,然后遍历所有客户端是否有新的数据发送。

这里只有一个主线程,解决了BIO的内存和cpu消耗的问题。但是新的问题是,如果连接数很多,但是发送数据的又很少,这样遍历一次所有的客户端是很浪费的。所以出现了多路复用器来解决上面的问题。


多路复用器

对于select、poll、epoll来说它们都是同步的,就是需要程序自己来读取IO。

select、poll

在这里插入图片描述

对于select、poll来说,只需要将所有的client文件描述符(fds)传递给它们,就会返回一个状态告知程序有几个可读,接下来只需要遍历可读的即可。

这样的方式把所有fds传递给内核,内核遍历减少了系统调用的次数,也就减小了IO的开销。但是这种模型依然存在弊端:fds每次需要重复传递、只返回状态还需要自己来遍历。

epoll

先看代码:

在这里插入图片描述

1.这里selector调用open方法(底层是create方法),即在内存里面开辟一块空间,然后调用register方法(底层是ctl方法)把上面创建的server放进这个空间。

在这里插入图片描述
2.接下来是一个死循环,调用select方法(底层是wait方法,阻塞,但是一旦有新的连接或者已连接有IO的话就会执行下一步),然后得到所有状态有更新的fd(文件描述符)进行遍历。

在这里插入图片描述
3.对取得的fd进行判断。
如果是可读的,那么肯定就是客户端有消息发送过来,调用方法接收信息。
如果是可接收的,那么肯定是客户端的连接来了,需要把这个新的客户端放入这个空间(即调用1里面的register方法)。看下图
在这里插入图片描述
在这里插入图片描述
在linux使用strace命令追踪进程的执行过程,我们得到上面的过程。前面创建server,绑定端口号,开启监听,设置了nonbloking。然后epoll开辟内存空间,把server放进去,然后阻塞等待新的客户端连接或者接收客户端信息。当有一个新的客户端连接上来,得到fd为8,把这个客户端也放进内存空间,然后继续阻塞。

即使是向上面那样,我们依然可以看见存在的问题,如果这里的客户端很多,那么接收信息肯定需要大量的时间,而新的连接将很久不能连接上,因为需要等待所有客户端读写完毕。这个时候,可以创建多个selector,一个负责接收客户端,另外几个负责把客户端注册到他们各自的空间里面去,这样负责接收的就可以快速响应客户端。
比如nginx就是一个master,多个worker在工作。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值