网络 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 数量的限制。**
**