后台开发核心技术与应用实践读书笔记(七)
后台开发核心技术与应用实践读书笔记(七)
第4章 网络IO模型
7.0 IO操作
不同的IO设备有不同的特点
-
同步IO与异步IO
- 同步:必须等到IO操作完成后控制权才返回给用户进程
- 异步:无需等IO操作完成,就将控制权返回给用户进程
-
当一个IO操作发生时(如read)会涉及两个对象
- 调用这个IO的进程
- 系统内核
当一个read操作发生时,会经历两个阶段:
- 等待数据准备
- 将数据从内核拷贝到进程中
7.1 4 种网络IO模型
分别是:阻塞IO模型、非阻塞IO模型、多路复用IO模型、异步IO模型;《Unix网络编程》中多了一种 信号驱动型IO模型。以上五种,除了异步IO模型是异步的,其他的都是同步的
以下是各种IO模型的示意图:
-
阻塞IO与分阻塞IO的区别
- 阻塞IO操作需要彻底完成后才能返回到用户空间
- 非阻塞IO操作被调用后会立即给用户状态值,不需要等IO操作彻底完成
-
阻塞IO
几乎所有的IO接口都是阻塞的
- 缺点 :在调用send()的同时,线程处于阻塞状态,在此期间线程无法执行任何运算和响应任何网络请求
- 解决方案:服务端使用多线程(多进程)。这样做的目的让每个连接都拥有独立的线程(进程),注意
- 进程的开销远远大于线程,若要同时为较多的客户端提供服务,使用多线程
- 如果单个服务执行体需要消耗较多的CPU资源(大规模或长时间的数据运算或数据访问),使用较为安全的进程
- 对于多线程模型,如果同时响应成百上千路的连接请求,则无论多线程或多进程都会严重占据系统资源,降低响应系统对外界的响应效率,多进程多线程也容易进入假死状态
- 线程池:降低创建和销毁线程的频率
- 连接池:维持连接的缓存池,尽量重用已有的连接,降低创建和关闭连接的频率
- 要根据响应规模来调整“池”的大小
- 多线程模型可以应对小规模,对于大规模(成千上万),多线程模型遇到瓶颈,可以使用非阻塞模型
-
非阻塞IO
在Linux下可以设置socket使得IO变为非阻塞状态
-
过程
非阻塞IO需要应用进程不断的主动询问kernel数据是否准备好。如果返回错误(没准备好)用户进程再发送read操作
-
recv()调用开销
服务器线程可以通过循环调用recv接口,可以在单个线程内实现对所有连接的数据接收工作,但不推荐
- 循环调用该接口大幅度占用CPU使用率
- 可以使用下面是说道的多路复用模式(一次检测多个连接是否活跃)
-
-
多路IO复用模型(事件驱动IO)
- 原理
- 函数(如select)会不断地轮询所负责的所有socket,当某个socket有数据了,就通知用户进程
- 调用select整个进程会阻塞,内核会监视所有的select负责的socket,有一个准备好了,就返回
- 优缺点
- 使用了两个系统调用(select和recvfrom),阻塞IO只有后面一个系统调用
- 优势在于可以处理多个连接,不是更快,而是更多
- 所以如果连接数不是很多,select/poll的优势可能不如多线程阻塞IO的WebServer性能更好
- 原理
-
异步IO模型
- 过程
- 一方面用户进程发起read操作后,立刻去做其他的事情
- 另一方面,内核收到一个异步的read请求操作后,首先会立刻返回,不会对用户进程造成任何阻塞。然后内核会等待数据准备完成,然后将数据拷贝到用户内存中,这一切完成后,内核会给用户进程发送一个信号,表示read操作已经完成
- 非阻塞IO与异步IO的区别
- 非阻塞IO中,虽然大部分时间都不会被阻塞,但是需要进程主动检查,并且数据准备完成后,也需要进程调用recvfrom来将进程去主动检查
- 异步IO不同,更像是用户进程将整个IO操作交给了他人(内核)完成,然后内核做完后发信号通知,在这个期间,用户进程不需要检查IO操作状态,也不需要准点拷贝数据
- 过程
7.2 select
-
基本原理
select 函数监视的文件描述符分3类,分别是writefds、readfds、和exceptfds。调用后select函数会阻塞,直到有描述符就绪(有数据 可读、可写、或者有except),或者超时(timeout指定等待时间,如果立即返回设为null即可),函数返回。当select函数返回后,可以通过遍历fdset,来找到就绪的描述符。
-
优缺点
- 优点
- 良好的跨平台
- 缺点
-
在于单个进程能够监视的文件描述符的数量存在最大限制
,在Linux上一般为1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制
,但是这样也会造成效率的降低 - 对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低。
-
- 优点
7.3 poll
- 基本原理:
poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间
, - 优缺点
- 相对于select没有了最大连接数的限制,因为是链表来存储的
-
大量的fd的数组被整体复制于用户态和内核地址空间之间
,而不管这样的复制是不是有意义 -
poll还有一个特点是“水平触发”
,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。
7.4 epoll
epoll是在2.6内核中提出的,是之前的select和poll的增强版本。相对于select和poll来说,epoll更加灵活,没有 描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事 件表中,这样在用户空间和内核空间的copy只需一次。
-
基本原理:
epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就绪态,并且只会通知一次
。还有一个特点是,epoll使用“事件”的就绪通知方式
,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来**该fd
,epoll_wait便可以收到通知。 -
优点
- 没有最大并发连接的限制`,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口)
- 效率提升,不是轮询的方式,不会随着FD数目的增加效率下降
- 只有活跃可用的FD才会调用callback函数;
即Epoll最大的优点就在于它只管你“活跃”的连接
-
内存拷贝
,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销
。
select、poll、epoll的区别
select、poll、 epoll都是多路IO复用的机制
具体区别上面已经说明了,以下简要总结
- select与poll
- poll没有最大文件描述符限制
- poll在大数目描述符的速度更快
- select可监控的文件描述是固定的1024/2048
- select的可移植性更好
- select超时有更好的精度,poll较差
- epoll优点
- 有FD限制,但是很大
- 不是轮询,效率高,只有活跃的才调用回调函数
- 内存拷贝