冯诺依曼体系计算机的硬件组成其实是总线、I/O设备、主存、处理器。 而 I/O的话,按照《深入理解计算机操作系统》这本书的定义,在主存和外部设备(例如磁盘驱动器、终端和网络)之间复制的过程。 而对于每一次 IO 来说,都会分为两个过程:
- 数据从存储设备拷贝到操作系统内核的缓冲区
- 从操作系统的缓冲区拷贝到应用程序的地址空间
而因为这两阶段,linux 系统中有以下五种网络模式的方案。
阻塞 IO
上面的两个阶段进程都在阻塞
非阻塞 IO
不阻塞,但是采用轮询的方式
IO 多路复用
select, poll ,epoll本质上都是同步 IO,他们需要在读写事件就绪后自己来负责读写
select与 poll 的区别是,poll 没有文件描述符的限制,而 select 有 1024 的限制。 select 和 poll 都要在返回后,通过遍历来获取已经就绪的 sokcet。弊端是随着文件描述符数量的增长,其速度也会线性变慢。
epoll从 linux 内核 2.6 版本开始支持。
epoll 的话支持水平触发和边沿触发。 按照读和写两种情况来描述。 读:水平触发,如果读事件未被处理,文件描述符对应的读缓冲区非空,每次 epoll_wait返回的事件列表都会包括该事件。而边沿触发在读事件就绪后只会触发一次。 写:水平触发,文件描述符对应的内核写缓冲区未满,就会一直通知可写事件。边沿触发,如果内核写缓冲区由满变为未满时,只会触发一次。
nginx 采用边沿触发,在请求数很多的情况下,边沿触发有比水平触发更高的效率。nginx 在用户态缓存了 IO 状态。
IO 多路复用也被称为 reactor 模式,也叫 dispatch 模式。
异步 IO
非阻塞,kernel 会将信息拷贝到用户内存。