BIO/AIO和IO多路复用常见问题

什么是BIO

顾名思义 blockingIO 是阻塞IO 一个连接一个线程
作为服务端开发 我们使用serverSocket绑定完端口之后 会监听该端口 等待accept事件 accept事件会阻塞当前线程 当我们收到accept事件的时候 会拿到一个客户端与当前服务端的socket 针对这个socket我们可以进行读写 这就有个问题 如果1w个连接呢 就要建立1w个线程 CPU是肯定顶不住的 所以就有了NIO

NIO

我理解就是not blocking IO
它从两方面解决了问题
1.Java层面
提供selector轮询器 把要检查的socket注册到这个轮询器里面 主线程就阻塞了 当socket准备好了 再唤醒主线程进行相应处理
2.OS层面
提供了select poll epoll函数

IO多路复用

是什么?
多路指的是多个网络连接
复用的是一个进程
实现一个线程监视多个文件句柄(可以理解为文件ID) 当句柄准备就绪 我们就可以对其进行读写 没有文件句柄就会阻塞主线程

实现方式

select函数

流程:
1.将fds文件描述符集合从用户态拷贝到内核态
2.轮询fds 描述读写是否就绪
3.将fds从内核态拷贝到用户态

缺点

1.由于数据结构是bitmap 所以最多只能有1024个fd (bitmap只有1024位)
2.用户态和内核态的切换开销大
3.每次都要轮询整个fds 而其中可能只有部分fd准备就绪
时间复杂度O(n)

poll函数

和select差不多 但是数据结构是链表 没有1024个fd的限制

epoll函数

数据结构是红黑树 时间复杂度是O(1)
流程:
1.创建epoll对象 像epoll对象中添加fd和fd上发生的事件
2.通过epoll_ctl 事件注册函数 像fd中注册事件 os(操作系统)将fd和事件作为一个节点插入到底层的红黑树中
3.事件发生时进行回表 将与事件绑定的fd插入到就绪队列中
4.调用epoll_wait函数 从就绪队列中拿到所有就绪的fd

工作方式

1.LT(默认) 水平触发
内核会告诉你fd是否就绪 如果不做恩和操作 会一直通知
只要fd关联的读内核缓冲区非空 就一直发出可读信号
写内核缓冲区非空 就一直发出可写信号
2.ET 边缘触发
高速模式 仅当读写缓冲区的状态发生改变的时候才通知 假设发送完就知道文件描述符是就绪状态了 然后不再通知 只有当文件描述符不再是就绪状态的时候 才会再次通知

常见问题

select函数在第一次轮询fds的时候 如果没发现就绪的socket 后续在某个socket就绪后 是如何感知的 是不停轮询吗?

那肯定不是
第一遍轮询没有发现就绪的socket 就把当前线程从工作队列移除 select函数处于阻塞状态
假设客户端向服务器发送一个请求是走网线 网卡 DMA硬件 这个过程是不走CPU的 是直接将数据写进内存里面 数据传输完成后会触发网络传输完毕的中断程序 这个中断程序会把CPU执行的线程顶掉 然后执行我们中断程序的逻辑
根据数据包和端口号找到对应的socket 看看等待队列里面是不是又等待者 如果有就移到工作队列里面 至此 中断程序执行结束 进程又回到了工作队列 又获得了CPU时间片 又可以再次检查

为什么会有epoll这个技术呢?

解决了2个痛点
1.函数调用参数拷贝问题
2.select poll的返回值是个int值 咱也不知道到底代表具体是啥 只能知道有几个socket就绪了

epoll_wait的返回值是int 那怎么具体知道是啥呢?

通过epoll事件指针 epoll_wait函数调用的时候 会传入一个epoll事件指针 在程序正常返回之前 就会把就绪的文件socket信息 拷贝到这个指针里面 然后返回给上层程序

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值