I/O模型演变


1 BIO

  在Linux系统早期,由于socket是阻塞的,用户进程需要阻塞等待请求到达,所以需要为每个socket开启一个进程。这时缺点显而易见,开启越多的进程则需要越多的内存,同时CPU的调度成本也会上升。
在这里插入图片描述

2 NIO

  随着Linux系统的不断完善,socket可以是非阻塞了。这时,用户空间只需要开启一个进程,轮询所有的socket,解决了BIO时期需要开启多个进程的问题。但是,这个方案依然有问题,如果有很多个socket,意味着用户进程需要访问多次内核调用,这依然需要很高的成本。
在这里插入图片描述

3 select

  当Linux有了select之后,用户进程可以一次把所有的socket文件描述通过select调用传递给内核,由内核遍历并筛选出就绪的socket文件描述符返回给用户进程,这样用户进程只需要遍历少量的文件描述符。
在这里插入图片描述
  但是,这个方案也有缺点,那就是用户空间和内核空间是通过select进行数据传输,而且select每次最多只能传输1024个文件描述符,这种用户空间和内核空间的来回拷贝数据也是需要很高的成本。

4 epoll

在这里插入图片描述
  epoll提供了三个系统调用:

  • epoll_create:创建一个epoll实例并返回其文件描述符。
  • epoll_ctl:往epoll中注册指定的文件描述符。
  • epoll_wait:阻塞等待注册的事件发生,返回事件的数目,并将触发的事件写入events数组中。

  epoll相比于select,其优越之处在于,因为select每次调用时都要传递需要监控的所有socket给内核,这意味着需要将用户态的socket列表拷贝到内核空间,如果数以万计的句柄会导致每次都要拷贝几十几百KB的内存到内核态,非常低效。而我们调用epoll_wait时就相当于调用select,但是这时却不用传递socket句柄给内核,因为内核已经通过epoll_ctl拿到了要监控的句柄列表。
  epoll在初始化时,会开辟出epoll自己的内核高速缓冲区,用于存放每一个我们想监控的socket,这些socket会以红黑树的结构存储,以支持快速的查找、插入、删除。
  epoll的高效就在于,当我们调用epoll_ctl往里塞入百万个句柄时,epoll_wait仍然可以快速返回,并有效地将发生事件的句柄给用户进程。这是由于我们在调用epoll_create时,内核除了建立一个红黑树用于存储以后epoll_ctl传来的socket外,还会再建立一个list链表,用于存储准备就绪的事件。当我们执行epoll_ctl时,除了把socket放到epoll文件系统里file对象对应的红黑树上之外,还会给内核中断处理程序注册一个回调函数,告诉内核,如果这个句柄的中断到了,就把它放到准备就绪list链表里。所以,当一个socket上有数据到了,内核在把网卡上的数据copy到内核中后就来把socket插入到准备就绪链表里了。当epoll_wait调用时,仅仅观察这个list链表里有没有数据即可。有数据就返回,没有数据就sleep,等到timeout时间到后即使链表没数据也返回。所以,epoll_wait非常高效。
  而且,通常情况下即使我们要监控百万计的句柄,大多一次也只返回很少量的准备就绪句柄而已,所以,epoll_wait仅需要从内核态copy少量的句柄到用户态而已。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值