文章目录
- 1、用户空间和内核态空间
- 2、网络模型-阻塞IO
- 3、网络模型-非阻塞IO
- 4、网络模型-IO多路复用
- 5、网络模型-信号驱动
- 6、网络模型-异步IO
- 7、对比
1、用户空间和内核态空间
服务器大多都采用Linux系统,例如:
ubuntu和Centos
都是Linux的发行版
,发行版可以看成对linux包了一层壳
,任何Linux发行版,其系统内核都是Linux
。我们的应用都需要通过Linux内核与硬件交互
.- 用户的应用,比如redis,mysql等其实是
没有办法去执行访问我们操作系统的硬件
的,所以我们可以通过发行版的这个壳子去访问内核,再通过内核去访问计算机硬件
计算机硬件
包括,如cpu,内存,网卡等等,内核(通过寻址空间)可以操作硬件的
,但是内核需要不同设备的驱动
,有了这些驱动之后,内核就可以去对计算机硬件去进行 内存管理,文件系统的管理,进程的管理等等
进程的寻址空间划分成两部分:内核空间、用户空间
什么是
寻址空间
呢?我们的应用程序也好,还是内核空间也好,都是没有办法直接去物理内存的,而是通过分配一些虚拟内存映射到物理内存中
在linux中,他们权限分成两个等级,0和3,用户空间只能执行受限的命令(Ring3)
,而且不能直接调用系统资源,必须通过内核提供的接口来访问内核空间可以执行特权命令(Ring0)
,调用一切系统资源,所以一般情况下,用户的操作是运行在用户空间
,而内核运行的数据是在内核空间的
,而有的情况下,一个应用程序需要去调用一些特权资源
,去调用一些内核空间
的操作,所以此时他俩需要在用户态和内核态之间进行切换
。
很好的一个用户程序读取磁盘数据的例子:
Linux系统为了提高IO效率,会在用户空间和内核空间都加入缓冲区:
写数据时,要把用户缓冲数据拷贝到内核缓冲区
,然后写入设备
读数据时,要从设备读取数据到内核缓冲区
,然后拷贝到用户缓冲区
针对这个操作:我们的用户在写读数据时,会去向内核态申请
,想要读取内核的数据,而内核数据要去等待驱动程序从硬件上读取数据
,当从磁盘上加载到数据之后,内核会将数据写入到内核的缓冲区中
,然后再将数据拷贝到用户态的buffer中
,然后再返回给应用程序
,整体而言,速度慢,就是这个原因,为了加速,我们希望read也好,还是wait for data也最好都不要等待,或者时间尽量的短。
2、网络模型-阻塞IO
阻塞IO就是两个阶段都必须阻塞等待:
用户去读取数据时,会去先发起recvform一个命令
,去尝试从内核上加载数据
,如果内核没有数据,那么用户就会等待
,此时内核会去从硬件上读取数据
,内核读取数据之后,会把数据拷贝到用户态
,并且返回ok,整个过程,都是阻塞等待
的,这就是阻塞IO.
3、网络模型-非阻塞IO
非阻塞IO的recvfrom操作会立即返回结果而不是阻塞用户进程。
4、网络模型-IO多路复用
- 无论是阻塞IO还是非阻塞IO,用户应用在一阶段都需要调用recvfrom来获取数据,差别在于无数据时的处理方案。
- 而在单线程情况下,
只能依次处理IO事件
,如果正在处理的IO事件恰好未就绪(数据不可读或不可写),线程就会被阻塞,所有IO事件都必须等待,性能自然会很差。
很好的一个例子:服务员给顾客点餐分两步:
- 顾客思考要吃什么(等待数据就绪)
- 顾客想好了,开始点餐(读取数据)
要提高效率有几种办法?
方案一:增加更多服务员(多线程
)
方案二:不排队,谁想好了吃什么(数据就绪了),服务员就给谁点餐
(用户应用就去读取数据)
问题来了:用户进程如何知道内核中数据是否就绪呢?
FD就出现了,文件描述符
(File Descriptor):简称FD,是一个从0 开始的无符号整数,用来关联Linux中的一个文件。在Linux中,一切皆文件,例如常规文件、视频、硬件设备等,当然也包括网络套接字(Socket)。
通过FD,我们的网络模型可以利用一个线程监听多个FD
,并在某个FD可读、可写时得到通知
,从而避免无效的等待
,充分利用CPU资源
。
相当于一群顾客中,部分顾客想好了自己要吃什么,然后就扫码下单告诉柜台某某桌点单了,这样就避免了柜台工作人员一直什么都不做,就在等顾客点餐去了
-
当用户去读取数据的时候,
不再去直接调用recvfrom了
,而是调用select的函数
,select函数会将需要监听的数据交给内核
,由内核去检查这些数据是否就绪了
,如果说这个数据就绪了,就会通知应用程序数据就绪
,然后来读取数据
,再从内核中把数据拷贝给用户态
,完成数据处理,如果N多个FD一个都没处理完,此时就进行等待。 -
用IO复用模式,可以确保去读数据的时候,数据是一定存在的,他的效率比原来的阻塞IO和非阻塞IO性能都要高
IO多路复用是利用单个线程来同时监听多个FD
,并在某个FD可读、可写时得到通知,从而避免无效的等待
,充分利用CPU资源。不过监听FD的方式、通知的方式又有多种实现
,常见的有: -
select
-
poll
select和pool相当于是当被监听的数据准备好之后,他会把你监听的FD整个数据都发给你,你需要到整个FD中去找,哪些是处理好了的,需要
通过遍历的方式,所以性能也并不是那么好
- epoll
epoll,则相当于内核准备好了之后,他会把
准备好的数据
,直接发给你,咱们就省去了遍历的动作。
5、网络模型-信号驱动
信号驱动IO是与内核建立SIGIO的信号关联并设置回调,当内核有FD就绪时,会发出SIGIO信号通知用户,期间用户应用可以执行其它业务,无需阻塞等待。
当有大量IO操作时
,信号较多
,SIGIO处理函数不能及时处理
可能导致信号队列溢出
,而且内核空间与用户空间的频繁信号交互性能也较低。
6、网络模型-异步IO
7、对比
参考来自黑马程序员–Redis原理篇
Redis入门到实战教程