前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >大话Queue、Buffer、Cache

大话Queue、Buffer、Cache

作者头像
冬瓜哥
发布2019-06-10 17:14:03
8180
发布2019-06-10 17:14:03
举报
文章被收录于专栏:大话存储

这个问题似乎跨越了十几年甚至更长。而且跨越了IT领域后端前端,跨越了硬件和软件。

Queue

队列用于两个模块(或者硬件模块,或者软件模块)之间传递消息,一般采用FIFO(先进先出)方式。下文中会解释这些消息里都是什么。在芯片内部,两个硬件模块(或者是CPU+固件,或者直接是组合逻辑电路)之间通常采用寄存器~寄存器对连的方式来传递数据/信号,但是寄存器对连的话,每次只能往寄存器里放一条数据,如果两端步调不一致,你处理快我处理慢的话,自然就有需求形成一个队列,那就是排布多个寄存器形成一列,然后再加上用于记录这一列寄存器中数据保存到什么位置的队列指针寄存器。生产者将消息从队列尾部入队,更新写指针,消费者从队列头部读走消息,更新读指针。有限的队列槽位形成一个虚拟的环形,不断生产消费,当写指针追赶上读指针时,队列满,有专门寄存器的控制位记录这个状态,有些设计还会产生一个中断来通知生产者。

如果生产者和消费者各自处于不同时钟域,对寄存器的信号采样时刻点不同,会采到错误的值,此时需要使用异步FIFO。异步FIFO的关键是必须采用格雷码来编码队列指针,格雷码可以保证每次只翻转1bit,保证消费者读取指针时不会产生误判。关于同步异步FIFO、格雷码等更详细内容请见大话计算机第1章。

如果Queue容量较大,一般不采用寄存器来充当,因为寄存器成本太高,存储1bit需要几十个晶体管,寄存器是为了响应时钟下边沿将输入端数据瞬间锁定并输送到输出端的,内部逻辑比较复杂。而成本低一些,响应速度也可以做到一个时钟周期的另一种存储器则是SRAM。SRAM无法充当寄存器因为它不具备下沿锁定并输出的能力,无法用于组合逻辑之间的隔离。CPU内部的各级缓存其实都是SRAM,有些L4缓存甚至用DRAM。

芯片内两个模块之间到底在传些什么东西?举例来讲。Raid控制器是一个芯片,其内部有通用CPU+固件代码在运行总控逻辑,芯片内部后端有多个SAS通道控制器,固件控制着DMA控制器从Host端主存的队列(软队列,下文讲)中取回对应的指令包(由Raid卡驱动准备好,当然最终是app发起I/O调用,沿着bio、scsi协议栈一路下来,生成了i/o request,并通过queue_command()调用到Raid卡驱动注册的request_fn()回调函数,后者将这个request封装到一个大包中,包中同时还描述了该指令对应的数据所在的主存位置等信息),固件解析该指令包内容,提取出scsi指令,并通过芯片内部queue将指令入队,然后通知后端硬件模块处理该指令,假设该scsi指令是读指令,则通过队列下发给后端SAS通道控制器,后者封装sas帧并传送给硬盘执行,写回来的数据在被DMA到主存。

网卡、显卡同样是这种套路,网卡拿到的是以太网帧,显卡拿到的是drawcall指令包,FPGA也是这么玩,都一样。I/O套路详见请见大话计算机第7章。

其他硬件队列还有很多,比如CPU的取指令单元取回的指令就在一个FIFO队列中等待被译码执行。芯片内部可以说到处都是队列。

软件队列,那就必须位于SDRAM里,比如刚才说的,各种I/O设备的驱动初始化时一般都会初始化至少两个队列,一个用于下发I/O,一个用于接收I/O完成回执(由I/O设备亲自写入完成队列并中断)。在驱动里你会发现总有一个函数是类似init_queue、init_queuepair等名字的。底层基本上就是kmalloc之类分配一段连续的内核内存,并将队列基地址写入I/O设备相关寄存器中,让后者知道去哪找这个队列。每次下发I/O之后驱动将队列的写指针更新到I/O控制器的相关寄存器让后者知道host端准备了多少i/o了。

所以,队列,就是一个队列,如其名一样,这个没有什么歧义。FIFO队列,顺着来,也有不顺着来的,不过一般不那么用。关于队列还有很多高级内容,比如VOQ(virtual output queue)、Virtual Channel等Qos方面概念,就不展开讲了,这些在大话计算机中第1/6/7章都有场景和原理介绍。

关于队列的扩展阅读:

大话流水线1

大话流水线2

大话流水线3

Buffer

Buffer,缓冲,不要叫它缓存,就是缓冲。就像不要把Fibre Channel叫做光纤网络或者光网络一样,说出去丢人。以太网也可以用光纤,光网络是指OTN,有些名词不要乱用。

缓冲缓的什么冲?就是两个模块之间生产和消费速度不匹配,导致积压。Queue不就是干这个的么?没错,很多时候,Queue就是Buffer,Buffer就是Queue,Buffer可以认为是一个队列深度很高的队列,能容纳相当量的数据。缓冲里的内容将会在一定时间内迅速消耗掉,而不是长期呆在里面,否则就成了缓存了。

Cache

缓存本质上是一块存储器,追求速度的硬件中一般采用SRAM来充当,比如CPU的各级缓存。不追求速度的可以用DRAM来盛放,比如Raid卡上的SDRAM缓存。缓存可以兼顾缓冲的作用,数据往缓存里仍,也可以匹配两边处理速度的差异,那是当然。但是缓存还有另一个作用,就是提升命中率。假设有个i/o写入0号扇区,而0好扇区的数据此时正在缓存里还没有被写入硬盘,那么缓存中的0扇区数据将被替换为最新写下来的这份,并继续留在缓存里。如果是缓冲,则缓冲队列头部假设已经存在之前写过的0扇区数据,又写了一次的话,那么之前的数据不会被删掉,会继续写到硬盘,后来新写入的0扇区数据也会被再写入硬盘一次。或许有些buffer有高级功能,能够发现这种重复而剔除掉之前的写数据只保留最新的,但是最新的数据一定不会长久待在buffer赖着不走。而缓存最关键的区别就在于数据会赖着不走,为什么,因为可能马上会被访问到。

既然如此,缓存容量有限,让谁待在里面,谁不待在里面,就是个需要决策的问题,让频繁访问的数据待在缓存会提升命中率,缓存就是看命中率,为命中率而生。于是有各种缓存替换算法比如LRU等,各类替换算法,以及缓存更高级的知识比如多核心缓存一致性等详见大话计算机第6章。(冬瓜哥总结19个关于缓存的知识点,写成了19节,美其名曰 “缓存19式”)。

有人说,“写buffer,读cache”,意思是指生产者将数据写到哪里?一般是写到buffer里,消费者从哪里读数据?一般是从cache读,也就是说buffer用于写加速,而cache用于读加速。这个说法并不准确,buffer一样用于读加速,经典的比如CPU内部的取指令缓冲,取指令单元从下层cache中取回若干条指令放到这个缓冲FIFO队列中,用于译码单元载入,这就是一段缓冲,但是确是用于读加速的,因为直接从cache中读还是不方便,速度也慢,做不到一个周期就载入指令,目前L1缓存起码也得经过两三个周期才能把数据读上来。

那么再看看,cache一定就是加速读操作的么?那可不一定。CPU算出数值之后,store指令可以将其存回主存,这个数据其实是被存到了L1 cache中存放,这个过程是典型的写加速,当然这个数据也用于后续访问的读加速。

所以,buffer和cache的区别并不在于读还是写,而是看它的目的只是为了缓冲,匹配速率不一致,还是为了缓存,提升命中率。

解决了你的疑惑了么?解决了的话请赏一下。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-09-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 大话存储 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云硬盘
云硬盘(Cloud Block Storage,CBS)为您提供用于 CVM 的持久性数据块级存储服务。云硬盘中的数据自动地在可用区内以多副本冗余方式存储,避免数据的单点故障风险,提供高达99.9999999%的数据可靠性。同时提供多种类型及规格,满足稳定低延迟的存储性能要求。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档