select与epoll

select

 
监听一组句柄fd_set,第一次调用的时候循环所有句柄对应的驱动函数xx_poll,socket的话就是sock_poll。
循环遍历完毕之后会如果发现有可用的(活跃状态的)fd,则返回,返回的时候会返回活跃的fd个数,同时会
把不活跃的fd在fd_set移除。如果循环fd_set一遍以后发现没有活跃的fd。假设此时socket在非阻塞模式下,
那么select会重复遍历这些fd_set直到超过了我们设置的时间限制,可是在阻塞模式下呢,怎么处理?
阻塞模式下我们没有必要设置超时限制,因为如果第一遍变量fd_set发现没有活跃的fd那么,这条进程会被
挂起的。进程怎么被激活呢,实际上进程是被挂在了n个fd的等待队列里,只要一个fd准备就绪那么进程就
会被唤醒,网卡接收了数据包发送一个中断给内核,内核处理这个中断,唤醒进程,这里是涉及到软中断的
,有兴趣可以看看。select被唤醒之后,并不知道是哪一个fd把自己唤醒的,所以还要来一个遍历,过程跟
上面说的是一样的。select阻塞到返回经历的内核和用户空间的拷贝,先把fd_set从用户空间拷贝给内核空
间,内核空间处理完毕之后把活动的fd再copy回来。其实后者还好,但是前者当fd_set百万级别的时候,就
费劲了。个中细节我并非完全了解,可是单看这个设计模式就觉着有效率问题了,感觉真的很傻,copy不说,
这个遍历所有描述符实在是太费劲了,而且每次在调用开始时,要把当前进程放入各个文件描述符的等待队列
在调用结束后,又把进程从各个等待队列中删除。
 
 
epoll
 
epoll的实现说白了,就是新发明了一个迷你文件系统:eventpollfs。其实,这需要sock_poll驱动的辅助
才能完成这样的设计,话说如果关注到epoll和select了,还不了解一下驱动的话,是看不动的。epoll的一
组函数:epoll_create,epoll_ctl,epoll_wait,就这三个函数完成百万并发醉了么?简单的可以这么理解,
epoll_create : 创建红黑树和就绪列表,实际他创建了一个专属于epoll文件系统的一个文件
epoll_ctl      :向红黑树添加socket句柄,向内核注册回调函数,用于当中断事件来临时,向准备就绪列表插
                  入数据
epoll_wait    :返回准备就绪列表的数据
 
准备就绪列表怎么维护的呢?当我们在做epoll_ctl时会给内核中断处理程序注册一个函数,告诉内核,如果这个句柄的中断到了,就把他放在准备就绪list链表里。
 
另外还有就是epoll的水平触发和边缘触发
 
水平触发level-triggered lT 只要满足条件就触发一个事件,只要数据没有被获取,内核会不断通知你
边缘出发edge-triggered ET 每当状态变化,触发一个事件。
具体例子吧,假设一个socket本来无数据可读,现在来了100个字节,这时候状态发生了变化,那么水平触发
和边缘出发都会被激活,可是呢,我们这次只读了20个字节,对于水平触发来说,还有80字节没读呢,再去监
听端口还是应该被触发可读调用,但是对于边缘触发的设计模式来说,因为刚才状态是从不可读变为可读,而现在是可读维持为可读,状态没变化,所以现在没必要再触发可读调用了。
这只是种设计思想而已,比如epoll,在就绪列表做点名堂就可以了,比如一个socket,假设没有被读完,提供是否再次放回到就绪列表中这两个选项,就成了两种触发了。
原文地址:https://www.cnblogs.com/SimpleISP/p/5280285.html