加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

这货是内存与Flash的合体!?

发布时间:2020-12-15 20:00:29 所属栏目:百科 来源:网络整理
导读:读者们好。好久不见了。可好? 【引言】 忆当年!冬瓜哥大二暑假的时候,在一台赛扬II 800+256MB SDRAM+Sis300显卡的机器上,狂刷了一个月的《暗黑破坏神II》,Druid 狼人月飙87级,咋样?不咋样,冬瓜哥也曾经陷入过这些精神鸦片中不能自拔过。当然,和现在


读者们好。好久不见了。可好?


【引言】

忆当年!冬瓜哥大二暑假的时候,在一台赛扬II 800+256MB SDRAM+Sis300显卡的机器上,狂刷了一个月的《暗黑破坏神II》,Druid 狼人月飙87级,咋样?不咋样,冬瓜哥也曾经陷入过这些精神鸦片中不能自拔过。当然,和现在的高中生是没法比的。不得不说当前这些个破烂网游手游的逼格,相比暗黑2的时代真是差得远,那个时代,暗黑2可谓是前无古人,现在呢,遍地,哦不,遍网,充斥着垃圾!

【M1不是什么】

书归正传。暗黑破坏神这个游戏的英文名其实叫做“Diablo”,俗称大菠萝。冬瓜哥咋会跟菠萝较上劲呢?那是因为最近冬瓜哥得知有个叫做Diablo的公司,推出一款很奇特的内存条:Memory1,下文简称M1。如下图所示。


散热片上贴着Diablo的标志“DB”,下方赫然“MEMORY1”字样。乍一看可能看不出什么门道来,但是右下角的容量着实吓了一跳,128GB!!(其实还有一个256GB的型号)要知道目前主流的单条容量是16GB,性价比最高,128GB的内存条其价格将是天价,而且基本无货。那冬瓜哥摆出这条内存作甚?嘚瑟?不是的。当你揭开散热片,就会傻眼。(鉴于保密原因,冬瓜哥就不贴出开盖图了)。

?????? 其上的这些芯片完全不像SDRAM颗粒。最左侧两列是电容,其右侧并排两颗则是每颗64GB容量的MLC NAND Flash颗粒,刚好组成128GB的存储空间。原来,M1是用Flash来当做RAM?的确是。这其实还不算奇特,后面会介绍更奇特的。

?????? DIMM连接器缺口处上方长方形的是一片信号缓冲器,其上方方形封装的芯片冬瓜哥猜测应该是一小片带有GB当量的数据buffer,内含DDR控制器和SDRAM控制器,用来接收Host端的访存请求。右侧最大的那个封装芯片是一片FPGA,其内部运行着M1的主控逻辑,同时与数据缓冲、MLC NAND颗粒连接,并在二者之间移动数据。其右侧长方形封装是一片NOR Flash,内含一些配置信息,可能还有部分代码数据,供配置FPGA以及其内部核心运行使用。

说到这好像应该结束了,把Flash存储空间,以字节级访问的方式呈现出去,作为Flash DIMM,性能是Flash的性能。而实际上,这才刚开始,M1并不是你想象中的Flash RAM。那么它是不是一款用DDR4作为接口的闪存盘呢?相比PCIE接口闪存盘更快的那种?

请注意,M1并不是:

1.????DDR4接口的SSD

2.????128GB容量的内存

【M1是什么】

从产品归类上讲,M1像是NVDIMM-F,而且同时支持内存模式和块设备模式。但是其掉电后并不能保证数据的非易失性,相反,其需要将数据擦除,以保证Flash的写性能更佳。而JEDEC的规范中,的确有规定了一种特殊的NVDIMM-F模式,就是介质本身可以是非易失性的,而且是字节级寻址,但是掉电却不保存用户数据的。所以M1算是NVDIMM-F的一种了。

实际上,M1向系统呈现出来的,是8GB的内存条,系统可以直接寻址的也是这8GB的内存空间。但是这8GB的空间也不是任意程序都可以去访问的,甚至连OS都不能去使用这8GB的内存。有人可能会有疑问,还有连OS都碰不得的内存空间?哈哈,这种逼格甚高的问题,懂OS的也未必能说清楚了,得让懂BIOS和底层硬件的人出场了。


为了彰显逼格,冬瓜哥贴一张ACPI 6.0规范中的图,可以看到,系统内的所有存储器地址空间都有对应的属性,其中,AddressRangeReserved这类属性非常特殊,OS看到这种属性的地址空间,就不能将其分配给自己的代码/数据,更不能分配给用户态程序使用,这类存储器的典型例子是比如一些系统桥、内存控制器的配置/状态寄存器,这些寄存器将会被映射到固定的物理地址空间,并且被BIOS标明AddressRangeReserved,OS碰不得,只有BIOS自己在系统初始化的时候碰。还有一类OEM Defined,也是OS不能用来当做通用空间分配,但是可供内核态的驱动程序访问。

再说回来,M1的这8GB的空间,还是需要被程序访问的,但是不能是OS原生的程序,只能是由Diablo开发的特殊驱动程序。(懂设备驱动开发的朋友们估计看到下面这句话一下子就明白了:这8GB的空间相当于是M1所声明的设备寄存器空间,其仅可被M1的设备驱动读写,其它程序读写会出问题。只不过这8GB的空间是通过DDR4呈现出来的,而不是耳熟能详的那些接口(比如PCIE、COM等)。但是如果有其他内核态程序要强行访问这段空间的话(用户态绝对访问不了,因为OS就不可能将这块地址映射到用户态进程的页表中),也不是不行,因为内核态的所有程序的权限都是ring0,理论上可以做任何事。

也正因如此,该玩法真的是非常奇葩,需要对应的BIOS做一些适配支持。首先,BIOS读出M1的SPD信息之后,必须能够识别到“呦,这是大菠萝的M1啊,这货是个很特殊内存,它的空间不让一般程序用,不能被纳入OS的常规分配池里,我得把它的属性标志为OEM Defined”。

你看,这相当不灵活。对于PCIE设备,每个PCIE设备都会在自己的配置信息中明确声明自己是哪一类设备,比如存储适配器、网络适配器、显示适配器、多媒体适配器等,从而便于系统管理。那么如果对内存条也这样去搞,是否可以呢?技术上没问题,但是场景上不允许。比如某条普通的DDR4内存,某系统想使用单独的程序对该内存进行管理和使用,也就是这块空间必须是OEMDefined属性才可以,但是该内存又的确是一条普通内存,如果按照上述设计,直接在内存SPD里声明自己“就是一条普通内存”,那么OS启动的时候很有可能把自己的代码和数据就放到它上面去了,一些内核线程甚至用户态默认启动的进程的地址空间也可能被分配到这上面,那就相悖了。但是它又绝不可能声明自己为“OEM Defined”,这样的话市面上销售的内存都就没法被OS用来分配给程序运行了。所以,这件事只能是BIOS来根据SPD信息,case bycase的来玩。

所以,这里的本质问题我们就能看出来了,PCIE设备之所以可以这么去玩,是因为OS会明确的将PCIE BAR所声明的内存空间在CPU物理地址空间内的映射部分认为是MMIO空间,OS不可能将这段空间分配给程序使用。设备驱动程序安装时所注册的vendor id、model id、revisionid等与某PCIE设备配置信息里呈现出来的值相同时,才会被OS加载执行,之后驱动程序才有机会读写对应的MMIO空间,从而与设备通信。当然,你的设备驱动可以肆意读写任意地址空间,因为处于ring0权限上下文中,这就完全靠自觉了,要不然早期的Win98动辄蓝屏,就是这个原因,对驱动的编写没有加太多规范,大家写出来的代码鱼龙混杂,如果某个驱动如果不靠谱,直接把其它地址上的数据/代码覆盖了。

上述场景的一个典型的案例,冬瓜哥相信搞过传统存储系统的人都知道,那就是有些传统存储系统只对某条指定的内存做电池保护,为什么不保护所有内存条呢?因为耗电量太大啊!那其他内存不保护,掉了电数据丢了怎么办?脏数据只往这条特殊内存里放不就行了么?是的,数据库日志也是这么干的,比如redo log,将所有变更写入到日志文件中,保证redo log同步写盘不丢就可以了,而数据文件里的数据丢了没事,用log来redo,顾名思义。有些传统存储系统也是这么玩的。这条特殊的RAM叫做NVRAM,其实就是用电池保护的普通RAM。你说这样一条RAM,是随便就能让别的程序访问的么,绝对不行,OS已启动就坚决不能碰这块地址空间,等待专门处理日志的程序来访问并处理。所以,在这种存储系统里,就需要对BIOS做定制化,让BIOS明确将某个DIMM插槽上的RAM声明为OEM Defined。

另外一个例子大家都知道,就是共享显存。比如某显卡声明需要1GB的主存作为显存,那么BIOS会将某段物理地址映射成PCIE域的地址然后将指针写入该显卡对应的BAR寄存器里,并在ACPI表中标注该段地址为OEM Defined,只不过,这段内存只能由显卡访问,连显卡驱动都没法访问的。

除此之外,OS也必须配合支持,很简单,看到OEM Defined属性的地址空间,就不能将其指针纳入到内存管理模块所维护的可用物理地址空间池里,如果有些OS粗枝大叶,不做这些检查,只要是内存条,全拿来随便用,那就有问题了。

所以,BIOS在这里起的作用非常关键,这就是为什么一些定制化的底层功能必须去改BIOS的原因之一。同理,对于NVDIMM,BIOS必须声明为PersistentMemory类型,而OS也必须针对这种类型做适配,在此就不再多说了。

话说回来,根据冬瓜哥在上文中的介绍,我们可以定义M1了。M1是什么?M1的本质其实是一个将MMIO空间通过DDR4接口向系统进行声明的闪存IO设备。那么很显然,与它的通信方式就得是块/页的方式,比如“读出从LBA1024到LBA2048的数据”,也就是得用一种描述协议来封装成指令数据包传送给该设备,比如SCSI指令,或者完全可以是私有指令,冬瓜哥相信大菠萝用的一定是私有指令。对于PCIE存储卡,比如SAS卡,SCSI指令是被封装到CDB中,由SAS卡驱动再封装一层信息,然后将数据包指针传递给SAS主控上的指令寄存器,SAS主控采取DMA的方式根据指针从主存取指令。而对于M1的数据传送方式,可以通过CPU或者DMA引擎来拷贝。那就意味着,这8GB的地址空间中,有部分空间一定是作为读指令数据的暂存空间的。冬瓜哥猜测,M1的驱动程序一方面不断下发读指令,另一方面又不断的poll这8GB空间内某处的完成队列,从而将数据迅速读出,写入也是类似流程。

现在,我们该来回答关键问题了。

【M1的牛逼之处】

再回到原始问题,为何M1不把整个128GB存储容量直接映射到系统地址空间里呢,也就是直接虚拟成一块透明的内存,从而直接加大系统内存,提升性能或者提升可并发部署的应用实例数量呢?理论上绝对可以,但是这样做性能会很不均匀,因为OS感知不到该空间是靠Flash支撑的。

另外一种方式则更加平滑,那就是将物理内存中的冷数据自动迁移到M1上,M1上的热数据自动迁移到物理内存中。的确,OS内部的swap/pagefile机制就是这么做的。可以将swap放到M1上去。比如,如果M1的驱动能够向系统注册一个虚拟块设备的话,那就可以这样做。实际上,M1的确是在系统里虚拟出了一个块设备,其容量与M1容量一致。但是该设备并不是一个通用块设备,也就是并没有向系统内注册标准块设备的读写回调函数。该块设备只能由M1自己的驱动来读写。既然如此,OS的swap也无法放置到其上了。M1的确也提供了一个RAMdisk驱动,也就是一个标准块设备驱动,称为Persist Disk模式。访问该Persist Disk可以走标准流程,经过系统IO协议栈的VFS层、通用块层这两层,会带来微秒级的时延。

M1采用了自己优化过的换页逻辑。这可不简单了。这相当于替换、挂接、劫持OS内核里的关键模块。而且最牛逼的是,M1的驱动软件可以做到针对指定进程的冷热换页,并不是全局一锅粥,这又了不得了。只需要在一份配置文件中将需要监控的进程以及一些参数声明一下即可。

这么牛逼的做法,底层的机制基本是这样的:Hook到系统Malloc()函数下游,当某指定进程调用Malloc()分配内存时,如果没有M1,正常流程是OS从可用物理地址空间里选择对应的地址段,然后将其写入到该进程的页表项中。而被Hook了M1的函数之后,M1只是记录一下应该分配哪些页,但实际上并没有分配物理页,而只是将对应的页表项中的状态置为“已换出”状态。被监控的进程访问这些内存页面时,CPU内的MMU查页表会发现这个状态,则直接产生一个缺页中断,也就是俗称的page fault,由页面管理模块负责page in操作。所以,M1的驱动软件还必须hook到pagefault处理下游,由M1的函数负责将对应页面读出或者写入。从哪读,写到哪?大家也就明白了,M1的函数会采用我们之前描述的方法,直接对注册到系统里的虚拟块设备的对应偏移量进行读写,虚拟块设备驱动底层则直接通过读写8GB的私有地址空间从而与M1硬件通信,将对应页面数据写入闪存的某个page中或者读出。所以M1的驱动还必须维护一张“某个进程的某个虚拟页被换到了虚拟块设备的哪个Flash页上的映射关系表”。驱动会发送这样一条读指令:“读出第1024个块”。M1上的主控接收到读指令之后,将对应偏移量的数据读出并放置到缓冲中,供M1驱动读出。写到这里,搞闪存开发的朋友们应该很熟悉了,再往底层理解就是一马平川了。

缺页流程是这样的:page fault之后,虚拟内存管理模块会将数据从swap区读出,放置到某个未被占用的物理内存页面中,然后将页号写入对应页表的对应表项中。当之前被挂起的线程继续运行的时候,会继续访问对应的虚拟地址,此时CPU的MMU便会查找到对应的物理页号从而读出数据了。那么M1的驱动为了保持对应用透明,也得这么做。将从闪存读出的页面,写入到物理内存某个页中,然后更新对应进程的页表项指向该物理页。

所以大家看到了,系统内必须有DDR SDRAM物理内存来保存进程需要访问的数据,进程是无法直接读写M1的Flash地址空间的。怎么样,搞过块设备数据Tiering的朋友们是不是又似曾相识了?应用看到的Lun,是一个虚拟的东西,其中有些数据在SSD,有些在SATA,有些在SAS,用一张追踪表来追踪每个块到底在哪。没错,虚拟内存也是这么做的,这个表就是页表。块分层用软件来查表,内存页表用CPU里的MMU硬件查表。

有人可能最后有个地方没想通,既然进程看不到M1的空间,岂不是等效于内存容量并没有提高?这里犯了一个常识性错误。进程看到的永远都是虚拟地址空间,对于32位CPU,其看到的就是4GB的虚拟地址空间,而对于64位系统,其看到的则是64位的虚拟地址空间。但是这并不意味着真有2的64次方Byte容量的物理内存供进程访问,物理内存不够用是可以换页到硬盘上的,当然,也没有什么应用真的去申请2的64次方B的内存了。所以,“M1可以被字节级寻址”这个说法,对也不对,进程对虚拟地址空间的确可以任意字节级寻址,只不过对于未命中的页面,需要进入换页流程而已。

【收益】讲了一大堆技术。那么M1带来的收益到底是什么?冬瓜哥认为,能够用比SDRAM低不少的成本,获得一个相比RAM速度低一些,但是容量超大的(比如1TB,最高2TB)的选择,利用换页机制将RAM的冷数据换到比PCIE SSD还要更快的介质上,变相的实现了“扩大内存”的目的。在应用场景方面具体冬瓜哥还会写篇小文分析一下。

【产品】实际中,Diablo推荐采用1:8的比例来配比物理SDRAM和M1的容量,而且在同一个内存通道下面,最好离最近的DIMM槽位插SDRAM,后面的通道插M1。这看上去好像给人一种错觉,认为前置的SDRAM像是后面多条M1的缓存。实际上并非如此,我们上文中说过,整个过程是一个换页机制,而非缓存机制。M1的换页逻辑可以将读出的页面写入到任何空闲的、由SDRAM支撑的物理页中,所以冬瓜哥在此并不清楚如果从同一个通道后面的M1读出数据写入位于同一个通道前置的SDRAM中速度会不会有加成。因为这种拷贝过程一定是要通过CPU寄存器的,也就是先Load,然后Stor。虽然CPU内部一般带有DMA引擎,但是数据路径是没什么缩短的,依然还是从一条DIMM出,进DMA或者Load/Stor单元,然后进入另一个DIMM。难不成目前的CPU里有类似自主拷贝(让内存控制器直接从其后面的一条DIMM里读出数据然后自己拷贝到另一条DIMM里)的指令,以及对应的内存控制器也支持这种自主拷贝请求?否则冬瓜哥没怎么想通M1这样设计的初衷。了解的可以留言分享一下。

再一个考虑是,如果进程运行的CPU,与其访问的物理页分处两个不同的CPU芯片)(也就是NUMA Node)的话,那么M1可以做一些优化,将页面换入时选择离进程较近的物理页存放。

除了嵌入到虚拟内存管理模块中的被动的函数调用来执行对应逻辑之外,M1也启动了一个或者多个内核线程,实现主动冷热分层。这与OS后台负责swap的线程类似,但是算法更加根据主流应用场景优化过。

值得一提的是,M1并不是一款NVDIMM,其中保存的数据只是从物理内存中换出的冷数据,就算对这些冷数据用电容保护起来,也无济于事,因为整个进程空间的内存数据在物理内存中的部分已经丢失了。所以每次重启时,M1会将所有MLC中的数据擦除。

目前,M1已经在浪潮的部分机型中全面支持和量产,冬瓜哥认为,后续M1需要做的主要是对生态的Enable动作了。

【总结】

M1的牛逼之处在于其页面管理软件(Diablo将其内核态核心功能程序统称为DMX,意即Diablo Memory Expansion),以及能对单进程进行精细化加速的能力。当然,红花配绿叶,M1的硬件也需要足够给力,才能支撑起足够强悍的性能。

本质上,利用PCIE接口的闪存盘也可以做这件事,此时就无需BIOS的特殊支持了。有兴趣可自行研究。

?

本文先告一段落。冬瓜哥在本文中涉及到的部分知识体系受到了浪潮公司的黄家明总的指点,在此表示由衷感谢!冬瓜在下一篇中将分析一下M1的应用场景、性能体验等内容。

转载本文需要带有本文全部内容,包括尾部二维码!

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读