select polll和epoll

2020-01-25  本文已影响0人  就这些吗

在这之前先看一下阻塞IO和非阻塞IO的区别

阻塞IO:

当你去读一个阻塞的文件描述符时,如果在该文件描述符上没有数据可读,那么它会一直阻塞(通俗一点就是一直卡在调用函数那里),直到有数据可读。当你去写一个阻塞的文件描述符时,如果在该文件描述符上没有空间(通常是缓冲区)可写,那么它会一直阻塞,直到有空间可写。以上的读和写我们统一指在某个文件描述符进行的操作,不单单指真正的读数据,写数据,还包括接收连接accept(),发起连接connect()等操作...

非阻塞IO:

当你去读写一个非阻塞的文件描述符时,不管可不可以读写,它都会立即返回,返回成功说明读写操作完成了,返回失败会设置相应errno状态码,根据这个errno可以进一步执行其他处理。它不会像阻塞IO那样,卡在那里不动!!!

参考:
实例浅析epoll的水平触发和边缘触发,以及边缘触发为什么要使用非阻塞IO
select将fd的copy复制到内核,内核会遍历这个数组,如果这个数组中的FD有可以I/O的,select返回一个位图并且将这个位图从内核拷贝到用户空间 然后再次调用select继续监控

select:

有一个数组()存储所有FD
1.把这个数组从用户空间拷贝到内核空间
2.注册回调函数pollwait 主要工作就是把当前进程放到指定设备的等待队列中,会在下面的poll方法里用到
3.遍历数组,调用FD对应的poll方法(方法参数一个是文件fd本身,一个是当设备尚未就绪时调用的回调函数__pollwait), 会返回一个掩码,通过这个掩码来判断是否准备好了,如果遍历完了还没有一个能读写的,就睡眠,
如果设备发生资源可以读写或者超过一定时间,会唤醒线程再遍历一次,看看有没有准备好的
4.如果准备好了,就从内核空间复制到用户空间

__缺点:__1.数组大小有限制
2.多次遍历去查哪些准备好了
3多次拷贝 用户到内核 内核到用户

poll:

poll的实现与select差不多,没有了数组大小的限制

epoll

epoll实现了三个函数
1 epoll_create 创建了文件节点(linux中一切皆文件)、红黑树(用来保存FD,红黑树能快速查找、插入、删除)、双向链表(用来放就绪的事件)
2.epoll_ctl 如果增加fd(socket),则检查在红黑树中是否存在,存在立即返回,不存在则添加到红黑树上,想然后向内核注册回调函数
还有给FD添加、删除修改事件的方法,如果有事件来临就把对应的FD到链表中然后唤醒epoll_wait
3.epoll_wait直接判断就绪链表有无数据,有数据就返回,没有数据就sleep,等到timeout时间到后即使链表没数据也返回。当有数据时,还需要将内核就绪事件拷贝到传入参数的events中的用户空间,
只拷贝一次(从用户到内核),用mmap加速内核和用户空间的消息传递
大小没限制 跟硬件有关
不用遍历,看链表就好
Level_triggered(水平触发):当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据一次性全部读写完(如读写缓冲区太小),那么下次调用 epoll_wait()时,它还会通知你在上没读写完的文件描述符上继续读写,当然如果你一直不去读写,它会一直通知你!!!如果系统中有大量你不需要读写的就绪文件描述符,而它们每次都会返回,这样会大大降低处理程序检索自己关心的就绪文件描述符的效率!!!

Edge_triggered(边缘触发):当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll_wait()时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你!!!这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符!!!

epoll的ET模式下是必须使用非阻塞IO的。
ET模式指的是当数据从无到有时,才通知该fd。数据读不完,也不会再次通知,所以read时一定要采用循环的方式一直读到read函数返回-1为止。此时采用阻塞的read,那么就阻塞了整个线程。

参考:
select和epoll的性能差别

了解select,poll,epoll

上一篇下一篇

猜你喜欢

热点阅读