IO 多路复用

I/O 是指网络 I /O ,多路指多个 TCP 连接,复用指一个或几个线程。 简单来说:就是使用一个或者几个线程处理多个 TCP 连接,最大优势是减少系统开销,不必创建过多的线程进程,也不必维护这些线程进程 #### select 文件描述符 writefds、readdfs、exceptfds 30w个连接会阻塞住,等数据可读、可写、出异常、或者超时返回 select 函数正常返回后,通过遍历 fdset整个数组才能发现哪些句柄发生了事件,来找到就绪的描述符fd,然后进行对应的 IO操作,几乎在所有的平台上支持,跨平台支持性好 缺点: select采用轮询的方式扫描文件描述符,全部扫描,随着文件描述符 FD 数量增多而性能下降。 每次调用 slect (),需要把 fd集合从用户态拷贝到内核态,并进行遍历(消息传递都是内核到用户空间) 最大缺陷就是单个进程打开的FD 有限制,默认是 1024 #### poll 基本流程和 select差不多,处理多个描述符也是轮询,根据描述符的状态进行处理,一样需要把fd集合从用户态拷贝到内核态,并进行遍历。 区别是poll 没有最大文件描述符限制(使用链表方式存储fd) #### epoll 没有描述符限制,用户态拷贝到内核态只需要一次使用事件通知,通过epoll_ctl注册fd,一旦该fd 就绪,内核就采用callback机制激活对应的fd 优点: 1. 没有fd限制,所支持的 FD 上限是操作系统的最大文件句柄数(65535),1G 内存大概支持 10W 句柄,支持百万连接的话,16G 内存就可以搞定 2. 效率高,使用回调通知而不是轮询方式,不会随着 FD 数目增加效率下降 3. 通过 callback 机制通知,内核和用户空间 mmap 同一块内存实现 缺点: 编程模型比 select / poll 复杂 linux内核核心函数 1. epoll_create() 系统启动时,会向linux内核申请一个文件系统,b+树,返回epoll 对象,也是一个fd 2. epoll_ctl() 操作epoll对象,在这个对象里面修改添加删除对应的链接fd,绑定一个callback函数 3. epoll_wait() 判断并完成对应的 IO 操作 例子:100W 个连接,1W 个活跃,在 select ,poll,epoll中怎么样表现 select :不修改宏定义,需要 1000 个进程才能支持 100W 连接 poll:100W连接,遍历都响应不过来,还有空间的拷贝消耗大量的资源 epoll: 不用遍历fd,不用内核空间和用户空间数据的拷贝 如果 100W 个连接中,95W 活跃,则 poll 和 epoll差不多