来源视频 [16,27]
文章目录
- 1.用户空间和内核空间
- 2.IO模型
-
- [2.1 阻塞IO](#2.1 阻塞IO)
- [2.2 非阻塞IO](#2.2 非阻塞IO)
- [2.3 IO多路复用](#2.3 IO多路复用)
-
- [2.3.1 阻塞和非阻塞的对比](#2.3.1 阻塞和非阻塞的对比)
- [2.3.2 IO多路复用](#2.3.2 IO多路复用)
- [2.3.3 监听FD方式、通知的方式,有多种实现](#2.3.3 监听FD方式、通知的方式,有多种实现)
- [2.4 信号驱动IO](#2.4 信号驱动IO)
- [2.5 异步IO](#2.5 异步IO)
- [2.6 真正的同步和异步](#2.6 真正的同步和异步)
- 3.Redis是单线程
-
- [3.1 Redis是单线程还是多线程?](#3.1 Redis是单线程还是多线程?)
- [3.2 为什么Redis选择单线程?](#3.2 为什么Redis选择单线程?)
- 4.Redis网络模型
1.用户空间和内核空间
空间划分
为了避免内存崩溃,需要分为用户空间和内核空间。进程的寻址空间会划分为这两部分,比如低位划分为内核空间,高位划分为用户空间。在权限上也划分不同等级。
缓冲区
在用户空间和内核空间都加入缓冲区,提高IO效率。
写数据时,把用户缓冲数据 拷贝到 -> 内核缓冲区-> 然后写入设备。
读数据时,要从设备读取数据 -> 内核缓冲区->拷贝到用户缓冲区,用户再读取。
2.IO模型
用户缓冲区调用内核指令,访问内核缓冲区,读取硬件设备的数据。
2.1 阻塞IO
阻塞IO,用户进程在两个阶段都在等待:用户进程向操作系统发起一个IO请求(系统调用)时,用户进程被阻塞 ,等待还没被准备好的数据;操作系统内核的IO子系统会监控硬件设备的状态,等待数据准备就绪。
如果数据已经准备好了,中断处理程序会通知操作系统内核的IO子系统。然后,操作系统内核会将数据从硬件设备拷贝到内核空间的缓冲区,再将数据从内核空间拷贝到用户空间的缓冲区。数据拷贝完成后,操作系统内核会通知之前被阻塞的应用程序线程。
2.2 非阻塞IO
非阻塞IO,用户调用时,会立即返回结果,而不阻塞用户进程。用户进程可以反复调用反复询问,直到结果是有数据了,就将数据从内核缓冲区拷贝到用户缓冲区。
在非阻塞IO中,用户进程在调用阶段是非阻塞的,在拷贝数据阶段还是阻塞的。而且忙等机制让CPU使用率暴增。因此,可能性能还不如阻塞IO。
2.3 IO多路复用
2.3.1 阻塞和非阻塞的对比
阻塞和非阻塞IO的区别主要在于没有数据时,一个阻塞一个不阻塞;
有数据时是一样的操作,直接进入第二阶段,读取并处理数据。
2.3.2 IO多路复用
每个文件都有一个对应的文件描述符FD(每个socket都是一个FD)。
IO多路复用:利用单个线程(复用的是线程)同时监听多个FD,并在某个FD可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。
用户进程监听多个FD,调用select。内核检查监听的多个FD,只要有任意数据就绪,就会返回结果给用户进程。(如果所有数据全都没有就绪,应用进程就阻塞等待)内核将数据存储在内核缓冲区,用户进程循环调用recvfrom,把每个数据从内核缓冲区拷贝到用户缓冲区,用户进程处理用户空间缓冲区的数据。(数据拷贝时同样是阻塞的)
recvfrom直接尝试读取某个FD。用户进程通常在一个循环中重复上述过程,以持续监听和处理多个文件描述符上的IO事件。
2.3.3 监听FD方式、通知的方式,有多种实现
常见的有:select, poll, epoll
区别:select和poll只会告诉用户进程有FD就绪,但不知道是哪一个FD 。用户进程需要遍历每一个FD确认是否就绪;epoll通知用户进程有FD就绪,同时把这个已就绪的FD写入用户空间。
(可以不看)不同方式的具体实现...
2.4 信号驱动IO
信号驱动IO,用户程序与内核建立信号关联后就返回(非阻塞)。当数据就绪后,内核发送信号通知用户程序。用户程序就可以调用获得数据。(数据从内核拷贝到用户空间仍是阻塞的)
2.5 异步IO
用户发起系统调用后就返回。后续工作由内核全部完成,完成后递交信号通知用户。
2.6 真正的同步和异步
阻塞IO、非阻塞IO、多路复用IO、信号驱动IO在拷贝数据时都是阻塞的,实际上都属于同步的IO操作。只有"异步IO"是真正的异步操作。
这种阻塞发生的原因是因为用户进程需要等待内核空间的拷贝操作完成,才能继续执行后续的操作。
用户进程在数据拷贝期间被阻塞的原因是:
内核空间操作:数据拷贝是在内核空间进行的,用户进程无法干预或控制这个过程,因此必须等待内核完成拷贝;
资源管理:内核需要确保数据拷贝过程中的内存管理是安全的,不会导致数据损坏或内存泄漏。因此,用户进程在这个阶段被阻塞,直到内核确认数据拷贝完成;
上下文切换:如果用户进程在数据拷贝期间不被阻塞,它可能会尝试访问或修改正在被内核拷贝的数据,这可能会导致数据不一致或其他问题。阻塞用户进程可以避免这种风险。
3.Redis是单线程
3.1 Redis是单线程还是多线程?
如果仅仅聊redis的核心业务(命令处理)部分,是单线程;如果聊整个redis,是多线程。
3.2 为什么Redis选择单线程?
抛开持久化,Redis是纯内存操作,速度快 。他的性能瓶颈是网络延迟而不是执行速度,因此多线程并不会带来巨大的性能提升;
多线程会带来上下文切换,不必要的开销;
多线程会面临线程安全问题,必然要引入线程锁这样的安全手段,实现复杂度增高性能下降;
4.Redis网络模型
Redis通过IO多路复用来提高网络性能,并支持不同方式的多路复用实现,并将这些实现封装,提供了统一的高性能的事件API库AE。