少说话,多看代码,多思考,多动手
io操作是一个程序必不可少的部分,io可以是对块设备的读写,文件的读写,基于网络上的io,这里通过内核select和epoll源码比较下这两种io的优劣。平常服务器需要处理客户端多个连接(基于tcp)的io事件。
对io的读写是有阻塞和非阻塞之分的,对于tcp连接上的读写实际是基于它的读写缓存区,如果fd是阻塞的话,对读来说,缓冲区没数据,对写来说,缓冲区不足够装下需要写的数据或已经满了同样会阻塞。下面是一个阻塞和非阻塞读取标准输入的程序。
阻塞:
非阻塞:
echo "123456" >> test.txt
在不用select或epoll接口时,如果程序需要处理客户端多个连接将会导致程序阻塞,不适合处理多个阻塞流,如果非阻塞流,就需要一直轮循,导至浪费cpu时间。有了系统的select和epoll接口后就不需要用户去查找哪些fd是需要处理的。所以调用select和epoll后我们可以拿到我们需要处理的fd。下面来分析下内核源码。
select
相关接口:
内核代码:
这个函数大概就是把传进来的读,写、异常的fd集合做了个遍历,返回了准备好的fd个数,用户需要在内核空间返回后再根据这个返回值做一次遍历。
epoll
相关接口:
内核代码:
内核在获取多准备好的fd的时候是通过ep_poll_callback(wait_queue_t *wait, unsigned mode, int sync, void *key);
回调函数放到准备好的队列,然后再把准备好的队列放到epoll_wait
里面的events参数里面.
epoll相比select的多路复用的机制监控的fd不受fd_set数组的限制,准备好的fd是通过回调(epoll_ctl新增时注册)放到rdlist里面然后再放回用户空间,用户空间就不需要再像select那样去遍历数组去检查是否可读.