(一)多进程和多线程的区别
在说I/O多路复用之前,我们先聊聊之前学过的多进程和多线程。这样有助于我们了解为什么需要I/O多路复用,并且可以将其串联。
(1) 多进程和多线程的区别
方面 | 多进程 | 多线程 |
---|---|---|
数据共享、同步 | 子进程之间数据分隔(共享复杂:需要IPC进程间通信)。同步:简单 | 子线程共享主线程数据(共享简单)。同步:复杂 |
内存、CPU | 内存占用:多(每个进程2GB)。进程间切换复杂:CPU利用率低 | 内存占用:少(每个线程共享2GB),线程间切换简单:CPU利用率高 |
创建、销毁的速度 | 速度慢 | 速度快 |
编程、调试 | 简单 | 复杂 |
可靠性 | 进程间相互独立 | 线程间可能存在相互影响(如临界资源) |
分布式 | 适用:多核、多机器 | 适用:多核(为何不适用于多机器:从可靠性既可以看出) |
我们看到多进程和多线程的区别后,我们会提出一个问题:多进程和多线程之间如何进行选择?
在这里引用RCU发明者的话:在能使用一个多进程解决问题的时候不要使用多线程。(当然这并不是绝对的。)
(2) 多进程、多线程、I/O多路复用的关系
这里我喜欢用一个比喻,可能不恰当。
现在有一个供水厂,用多个大管道(多进程)去向各个地区供水。各个地区需要用多个小管道(多线程)向各个小区进行供水。各个小区要向多个住户进行供水,那么多住户,我要知道水到了住户没有(毕竟要计数来收水费的),如果要用多个大管道(多进程)和多个小管道(多线程)这得多麻烦啊,并且也会浪费资源。但是我又想知道水管口(I/0端口)是否有水流到达,那么我们是不是可以用摄像头(类似于select,poll,epoll的功能)去监控这些水管(I/O端口),让一个工作人员看着屏幕即可知道。
所以,进程、线程、I/O多路复用的搭配:
线程(一般)/进程+I/O多路复用
(3)从五种网络I/O模型了解I/O多路复用
在了解五种网络I/O模型前,我们要了解同步和异步,阻塞和非阻塞的基本含义。
同步和异步:描述用户线程与内核的交互方式。(同步:用户线程发起I/O请求后需要等待或轮询内核I/O操作完成后才能继续执行。异步:用户线程发起I/O请求后继续执行,当内核I/O操作完成则会通知用户线程,或调用用户注册的回调函数。)
阻塞和非阻塞:描述用户线程调用内核I/O操作的方式。(阻塞:I/O操作在没有接收完数据或者没有得到结果之前不返回,需要彻底完成后才返回到用户空间。非阻塞:I/O操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完
成。)
扩展:轮询法的概念是:由CPU定时发出询问,依序询问每一个周边设备是否需要其服务,有即给予服务,服务结束后再问下一个周边,接着不断周而复始。
(二)I/O多路复用的基础知识
I/O的多路复用定义( I/O multiplexing ):单个线程通过记录、跟踪每一个Sock(I/O流)的状态来同时管理多个I/O流。
红色的相当于我们物理做实验的时候经常用的单刀多掷开关。
那么大概知道I/O多路复用的概念后,我们就可以了解下其具体的实现,分别有select,poll,epoll三种实现方式。这里我们只是简单说说三者的定义,而具体怎么用会在下篇文章中提到。
(1) select
select:用于监视文件描述符的变化情况——读写或是异常。
特点:性能低,线程不安全。
性能低:会修改传入的参数数组。并且任何一个sock_fd(只能监视1024个)出现数据,select仅做返回,并不会挑出那个有数据的sock_fd,只有遍历去寻找那个有数据sock_fd。(就像是每次班上来新同学,让全班人进行自我介绍一样。)
线程不安全:如果select()监视的文件描述符在另一个线程中关闭,则结果是未指定的。
(2) poll
poll:和select实现的功能差不多,poll的作用是把当前的文件指针挂到等待队列。
特点:不会像select一样传入参数数组,并且解决select中1024个链接限制(连接限制解除了,这意味着链接增多了呀。这就变成了班上每次来个新同学,就让全校的人进行自我介绍。所以,这就导致了epoll出现),线程不安全。
(3) epoll
epoll:epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。
特点:高效,线程安全,只能在Linux下使用。
(4)select、poll、epoll高性能网络编程对比
其实,我们可以发现。如果并发数比较低的时候,三者的效率基本持平。但是,随着并发数的增加,select/poll的处理效率在急剧下降(poll() 和 select()缺点:包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大),但是epoll却可以维持在高效率上。