网络I/O模型优化
1.阻塞式I/O
在整个socket通信工作流程中,socket的默认状态是阻塞的。当发出一个不能立即完成的套接字调用时,其进程将被阻塞,被系统挂起,进入睡眠状态,一直等待相应的操作响应。
2.非阻塞式I/O
我们需要设置一个线程对该操作进行轮询检查,这也是最传统的非阻塞I/O模型。
3. I/O复用
如果使用用户线程轮询查看一个I/O操作的状态,在大量请求的情况下,这对于CPU的使用率无疑是种灾难。
Linux提供了I/O复用函数select/poll/epoll:
select()函数:它的用途是,在超时时间内,监听用户感兴趣的文件描述符上的可读可写和异常事件的发生。
调用后select() 函数会阻塞,直到有描述符就绪或者超时,函数返回。
poll()函数:在每次调用select()函数之前,系统需要把一个fd从用户态拷贝到内核态,这样就给系统带来了一定的性能开销。再有单个进程监视的fd数量默认是1024。
poll() 管理多个描述符也是通过轮询,根据描述符的状态进行处理,但 poll() 没有最大文件描述符数量的限制。
poll() 和 select() 存在一个相同的缺点**,那就是包含大量文件描述符的数组被整体复制到用户态和内核的地址空间之间,而无论这些文件描述符是否就绪,他们的开销都会随着文件描述符数量的增加而线性增大。
epoll()函数:select/poll是顺序扫描fd是否就绪,而且支持的fd数量不宜过大,epoll使用事件驱动的方式代替轮询扫描fd,**epoll的性能更胜一筹,而且不会受到fd数量的限制。