select,poll,epoll

服务器端面临着这样的问题,多个客户端请求来请求服务器端,服务器端怎样解决这种问题。

一种思路是IO多路复用。通过一个监视器监视请求,当有请求来时,就通过某种机制来通知相应的处理。

第一种方案是,每当一个请求来就创建一个线程来处理请求,

多线程

if(fd f : fds){//---------1
    if(f has event){//-----2
        //处理事件
    }
}

在这个模式下,有两个缺点,第一点是线程上下文切换,当请求过多就会造成上下文切换的资源消耗超过处理。第二个缺点是在注释第二处每次都需要向内核查看是否有时间,这就有一次用户态内核态的切换。这两个缺点是非常致命的。

在liunx下有专门的处理该场景的函数select

select

基本思想跟多线程相似,不过要解决怎样标记有事件的fd的问题。

首先每一个请求在计算机类的表现是形式是文件,我们同来文件描述符fd来描述。用bitmap来标记监视的文件描述符,比如我有1,2,4,5。bitmap表现为011011......,首先把监视的文件描述符的集合rset从用户态拷贝到内核态,内核需要对有事件文件描述符对应的位置位。把rset返回,然后用户态通过轮询的方式来找到有事件的fd,然后处理。

缺点:对于select,首先bitmap是有大小限制的,默认1024。内核返回的rset是修改的,不能重用。

  1. 监视的文件描述符大小有限制,这是由于bitmap造成的。
  2. 描述文件描述符是有有事件的rset不能重用。
  3. 又一次用户态内核态的切换,相对多线程每次判断文件描述符是否有事件都需要一次切换来讲,确优化的很多,但是仍然存在。
  4. o(n)复杂度的轮询方式,用户态通过轮询的方式来判断哪些fd有事件。

对于select,怎样解决fd的描述问题,减少内核切换是非常重要的

poll

首先,poll定义了一个pollfd这样的一个数据结构,里面有fd,events, revents。fd是文件描述符,events表示事件类型,是读,写还是读写都有。revents是表示有事件的标志。

首先用户态把pollfds拷贝到内核态,内核态轮询来判断哪些fd有事件,有的吧对应的revents标志,然后把它返回。客户态通过轮询方式来找出哪些有事件的fd来处理。

首先pollfds是一个数组以页的方式存储,但是会有链表poll_list指向他,这样就可以避免一次内存分配的情况。

缺点:

  1. 有一次内核切换
  2. o(n)的轮询方式

内核切换,轮询的方式是消耗内存比较大。

epoll

epoll的数据结构有epollfd, 有fd(文件描述符)events(描述事件种类,读,写还是读写)。通过内存映射的方式来避免内核切换。

用户态并不需要把pollfds拷贝到内核态,内核通过内存映射的方式共享内存,然后判断哪的fd有事件,然后通过水平触发来处理事件。

首先水平触发,是通过回调函数,把他调到一个任务队列里,进行处理,本事存储epollfd的是一棵红黑树,

优点:没有文件限制,没有o(n)轮询遍历,没有内核切换。

由于作者水平有限,如有错误,欢迎在评论区指出

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值