1、select
调用select发生以下事情:
1)从用户空间拷贝fd_set到内核空间;
2)注册回调函数_pollwait;
3)遍历所有的fd,对全部指定的设备做一次poll(poll指一个文件操作,有两个参数,一个是文件fd本身,一个是当设备尚未就绪时调用的回调函数,这个函数将设备自己特有的等待队列传给内核,让内核把当前进程挂载到其中)。
4)设备就绪就唤醒自己特有等待队列中的所有节点,当前线程获得完成信号;
5)所有设备的掩码都木有显示任何事件触发,就去回调函数指针,进入睡眠,再恢复进行poll,再休眠,直到事件触发为止。
6)只要有事件触发,系统调用返回,将fd_set从内核空间拷贝到用户空间,回到用户态,用户就可以对相关的fd做进一步的读或者写操作;
2、epoll
调用epoll_create做下面事情:
1)内核帮我们再epoll文件系统建立了file结点;
2)在内核cache里建红黑树用于存储epoll_ctl传来的socket;
3)建立list链表,用于存储准备就绪的事件;
调用epoll_ctl:
1)把socket放到epoll文件系统里file对象对应的红黑树;
2)给内核中断处理程序注册一个回调函数,告诉内核,中断了,放到就绪list链表里面;
调用epoll_wait:
list链表是否有数据,有数据就返回,木有数据就sleep,等到timeout即使木有数据也返回。
两者比较:
1)select
并发数限制:使用32位整数标识fd,即32*32=1024;
效率低:每次都会线性扫描整个fd_set,集合大,速度慢;
内核/用户空间内存拷贝问题;
2) epoll
没有最大并发限制,仅受系统中进程能打开的最大文件数目限制;
效率更高,只有活跃的socket才会主动调用callback函数;
省去内存拷贝;
转载:https://blog.csdn.net/jiange_zh/article/details/50811553