计算机组成原理

nothing here

image-20220804231124575
image-20220804231124575

需要熟练掌握整个工作流程!

image-20220804231425633
image-20220804231425633

image-20220804231606097
image-20220804231606097

性能指标也很多:

总容量 = 存储单元个数*存储字长

CPU频率 = 1/CPU周期

CPI 执行指令的时钟周期

指令的耗时 = CPI * CPU周期

CPU执行时间 = CPU时钟周期数/CPU频率 = (指令数*CPI)/CPU频率

(KM)IPS 每秒执行的指令 = CPU频率/平均CPI

(KMGT)FLOPS 每秒执行浮点运算的次数

数据通路带宽:数据总线一次传输的位数

吞吐量:单位时间的请求数量(看具体场景)

向后兼容指兼容以前的。

BCD码:二进制编码到十进制,包括8421(1001之后没了,加法越界时+6),余三码(从0011开始),2421码(0~4首位为0,5~9首位为1)

无符号数的加法右手就行,减法需要减数取反加一后与被减数相加

带符号整数就需要了解:原码、反码、补码。原码最大位0,1表示正负(0,11010011),符号位不变数值取反就变反码,在此基础上加一变成补码。(不建议补码减一变反码,不如先按下面的方法转为原码)

image-20220807214923920
image-20220807214923920

由于减法器十分昂贵,所以通常使用加负数实现。

image-20220807220838692
image-20220807220838692

计算机进行加减运算是使用补码表示的,同时c语言中的int类型也是补码表示的。

image-20220807221427348
image-20220807221427348

image-20220807221549123
image-20220807221549123

上表熟记!

移码只能表示整数,通常用于表示浮点数的阶码。而其它三种通常都能表示整数和浮点数。定点小数表示([0.75]0.11000000)顶点小数的转换和加减之前的相同。(注意,上界相同,但是下界不同!)

image-20220807221944367
image-20220807221944367

一位全加器,串行加法器,(串行进位的)并行加法器,(并行进位的)并行加法器:

image-20220807223846633

可惜很逊,一般并行到4个就够了。

image-20220807224859441

有符号整数和无符号数的ALU电路相同,唯一不同的在于溢出的判断。

OF只有有符号数才有意义(最高位和次高位异或),SF取结果最高位(有符号数有意义),ZF是否全为0,CF最高位进位和输入进位的异或(只有无符号数有意义)。

移位运算有手就行。正数算术移位补0,负数取决于移位方向:原码补0,补码左移补0右移补1,反码补1。逻辑移位可视为无符号数的算数移位。循环移位设置了一个CF位,存放移掉的数据,之后补上去。

原码乘法,肥肠巧妙:现在就能看懂书上的竖式是什么原理了。

image-20220807230543491
image-20220807230543491

相比之下补码就恶心多了:

image-20220807231008143

呜呜呜好难。

原码除法难度不大,就是正常的除法。但是在计算机中以恢复余数+左移完成:

image-20220807233013963

但是上面的算法太拉了,所以有一种简便的算法:加减交替法

补码的除法就与加减交替法类似,但是比较容易理解:

image-20220807235410992 image-20220807235538563

C的数据转换只改变解释方式!但是不改变内容。从高字节到低字节会截断高位。而低字节到高字节会根据类型补数字(补码看正负,无符号数补0)

大端方式把高位数据放在低地址,小端方式把高位数据放在高地址。从小到大读取就很方便。现代计算机以字节编址,但也支持半字、字编址。每次访存只读一个字。

image-20220808000500538

浮点数由阶码和尾数组成:

image-20220808205500582
image-20220808205500582

通常阶码用移码或补码表示,尾数用原码或补码表示。尾数规格化使得尾数最高位有意义,从而增加精度(抛弃高位的空位,左规(尾数左移,阶码减一)),而在运算之后有时候高位不符合科学计数法,就可以使用右规(尾数右移,阶码加一)。

双符号位是一种浮点数溢出判断的有效手段。由于浮点数尾数和阶码有不同的编码方式,所以统一使用了IEEE754。其阶码部分为移码(补码符号位取反,但是偏置值为$2^{n-1}-1$即数字减127,-128为11111111)

image-20220808213256392
image-20220808213256392

IEEE754标准位数如下,其中尾数为原码,且由于经过了规格化,我们默认最前面存在一个1,因此尾数数字可以从0开始,在127为偏置值的情况下我们会认为阶码的-128(11111111)和-127(00000000)为特殊状态,所以真值的实际范围为(-126~127):

image-20220808213320875
image-20220808213320875

所以短浮点数(单精度浮点数)的真值为$(-1)^S1.M2^{E-127}$,规格化长浮点数(双精度浮点数):$(-1)^S1.M2^{E-1023}$

image-20220808214332647
image-20220808214332647

但是会出现一些特殊情况(捏妈妈的屁事真多):

image-20220808214612429
image-20220808214612429

浮点数的加减更是恶心:

image-20220808220439632
image-20220808220439632

舍入部分有两种操作方式,0舍1入法和横置1法,前者进行舍入时,若抛弃的值为1,则+1,否则不变,但是+1时可能会导致其再次溢出从而进一步移位。

注意:下溢作为0,而上溢会作为异常抛出。最后是强制类型转换:

image-20220808222130232
image-20220808222130232

image-20220808222233780
image-20220808222233780

第二章终于结束了,云里雾里,还得多练。

存储芯片的原理倒是不难理解,接下来就是DRAM(动态RAM主存)和SRAM(静态RAM缓存)的。

DRAM使用栅极电容实现存储,而SRAM使用双稳态触发器,其可以保持状态。所以对于DRAM其读出(破坏性读出)之后必须重新写入(所以慢),注意的一点是两者都是易失性存储器。

由于电容每2ms就会消失,所以需要不断刷新。一次刷新一整行,花费一个读写周期。每次读写之后都会刷新(分散刷新),或者集中安排(集中刷新)2ms内分时间刷新(异步刷新)

image-20220813213240939
image-20220813213240939

DRAM地址线复用技术(分两次送,所以引脚数量减半),现在主要使用的是SDRAM。

ROM(非易失),MROM(只写一次),PROM(写一次就不能改)EPROM(可重写可读),FLASH Memory(U盘,但是写比读慢),SSD。(注意flash芯片和Flash memory不同 )

BIOS的ROM自举装入程序,注意,我们通常把这个ROM放主存中,同时RAM的首地址在ROM之后。

扩展部分倒是不难,但是要注意条理清楚。首先是引脚数量:

image-20220813222419424
image-20220813222419424

为了增强多核读取或是主存速度,一般有一些独特的方法:双端口RAM(不能同时对同一地址进行操作,因此需要复杂的控制逻辑),多体并行存储器

image-20220813223457613
image-20220813223457613
显然一个竖着一个横着,连续的数据前者比较拉,但是对于后者就可以很好的操作,相反,前者损坏的时候更容易恢复(关于后者的方式可以控制模块的数量来控制速度(既不能多也不能少$m\geq \frac{T}{r}$))

外存分为磁存储(一次一位不能同时读写且不破坏)

格式化容量小于非格式化容量,各种相关参数例如道密度,位密度,扇区数(一般都是8个且存储数据相同)。

image-20220813224555618
image-20220813224555618

注意在上面列出的事件中,寻道时间和旋转延迟时间都是占大头。数据传输率=r(转速)N(磁道容量)。磁盘的编址主要由:驱动器号|柱面号|盘面号|扇区号。

然而磁存储太拉了,有时候还会组RAID,一般从0-5安全性增加,0容量最大,1容量直接半价但是性能高。

另一种外存是基于闪存的:

image-20220813225730033
image-20220813225730033

cache是另一个重点,用于缓和CPU和主存的速度差。cache和主存以块为单位进行交换,其中主存的块也叫页,cache的块也叫行。cache中的重点主要有:映射关系、替换算法和写策略。

image-20220814215034832
image-20220814215034832

为了得到cache到主存的关系,需要在cache中设置标记来指出主存的位置(主存块号),并且需要标记标出无效位。对于全相联,并没有什么特殊的,但是对于直接映射,由于是主存取余,对应的每个主存地址只能存放在固定的位置,所以标记的高位其实并不需要。组相联对应的就是两种的中间版,但是对应的标记位同样不需要保存全部地址(n路组相联指的就是n个cache行为一组)

完成了映射之后就需要考虑替换策略了,不同的映射关系也会产生不同的替换算法,主要有:随机算法(逊),FIFO(注意,被访问之后仍保持之前进入的顺序,没有考虑局部性原理),LRU(为每个块设置一个计数器来计数Cache块没有被访问的时间,命中时对应的清0其他的+1,装入的时候也需要+1,为了简化计算,可以比较新块q与最近替换距离最远的块),LFU(计数器,记录被被访问次数,调出访问次数最小的,但是不如LRU,而且有段时间可能会增加到一个很大的值)

cache我们只考虑写策略(因为会导致数据不一致),写回法(之后cache被换出时写回主存),全写法(只要cache命中就要主存cache都写,为了提速一般搞一个SRAM的写缓冲,写缓冲通过其它其它电路搞,但是也有可能造成阻塞)。当写不命中时,写分配法(当前要写的不在cache里,就需要先把主存的块调入cache,换出的时候同步回主存),非写分配法(直接写到主存)。现代计算机的cache之间用全写+非写分配,cache与主存使用写回+写分配。

存储的最后两部分和操作系统进行了高度融合。首先页表和整体的处理原理

image-20220814223855125
image-20220814223855125

但是好像只有页表。虚拟存储系统就是将赋存里的数据调入内存,可惜重点在操作系统

image-20220814224741230
image-20220814224741230

所以整个存储的体系:

image-20220814224812103
image-20220814224812103

接下来是段式虚拟存储器,其中每个段的大小可以不同:

image-20220814224912468
image-20220814224912468

至于段页式,在操作系统中来搞即可。

指令系统,核心就是控制器。

指令指计算机运行的最小功能单位。所有的指令集合组成指令系统也叫指令集。

指令根据地址数量分为零地址、一地址、二地址、三地址和四地址指令。注意,零地址指令仍然可能会需要使用操作数(例如使用堆栈),一地址同样不一定只有一个操作数,可能存在另一个操作数在寄存器中(ACC),(注意一种表述$A_1$表示地址,$(A_1)$表示内容),二三地址指令就同理(注意可能会考到指令的访存次数)。

按照指令长度进行分类:指令字长可能会变,机器字长和CPU中ALU有关,存储字长与MDR有关。指令字长可能有半字长、单字长和双字长指令,这也会影响取指的时间,因而存在指令字长相同或是不同的结构。对应的,如果操作码的长度不变,那么为定长操作码,否则为可变长操作码,操作码的长度决定操作的数量

按操作类型:数据传输,算数逻辑操作,逻辑操作,转移操作,输入输出操作(也可以分为数据传送类、运算类、程序控制类和输入输出类)

由于指令分为操作码和地址码,其中操作码可能定长也可能变长,那么边长操作码可能就是一个考点:

image-20220816095235615
image-20220816095235615

指令的寻址有两种,一种是直接“+1”得到下一个指令的地址,另一种就是使用JMP指令跳到对应的地址。

数据寻址就很蛋疼了,一共十种,前6种正常寻址:

image-20220818220802832
image-20220818220802832

三种偏移寻址,基址寻址,相对寻址,变址寻址,三种都是偏移,但是起点不一样,基址根据程序的起始位置,相对根据PC,变址则是用户自定的寄存器。基址寻址(BR),将CPU中的基址寄存器(不设置基址寄存器的CPU需要从通用寄存器中指出)+偏移量(OS中的重定位寄存器),基址有利于程序浮动,从而实现多道程序并发运行,另一个重点就是BR是面向操作系统的。变址寻址与BR类似(可以设置也可以不设置变址寄存器),但是IX与BR的区别在于,变址寄存器的内容可以由用户改变(主要是将IX视为偏移量,A视为基地址)。在实际使用中往往同时需要使用BR和IX。相对寻址,PC视为基址,A(补码)视为偏移量,注意,A是相对于PC的下一条指令进行的。

程序中的大小比较是通过减法运算后PSW寄存器中记录的结果所得到的。

最后一种寻址方式是堆栈寻址,这个不难理解,不需要给出操作数地址,计算机会使用一个专门的寄存器SP堆栈指针来指向栈顶,这叫做硬堆栈,另一种方式叫做软堆栈,在内存中进行。

哈哈,没想到吧,你还得学x86。

image-20220818221802481
image-20220818221802481

通用寄存器中E表示Extended,如果不加E,使用的就是低16位的数据,甚至可以指向单字:

image-20220818222131243
image-20220818222131243

其次是指令,对于算数指令,通常使用add d,s表示指令,但是d表示目的地,其通常不能为常数而必须指向地址。

image-20220818222822321
image-20220818222822321

除法比较特殊,会将被除数放在edx和eax中,将32位被除数扩展为改为64位从而放在两个寄存器中。

逻辑运算就不难了

image-20220818223033197
image-20220818223033197

在进行程序流程控制的指令学习之前可能需要学习AT&T和intel格式的汇编,虽然之前考的都是intel的汇编:

image-20220818223248859
image-20220818223248859

程序流程控制指令主要学习:jmp,cmp,

image-20220818224145152
image-20220818224145152

(markdown无端联系)

每次指令的执行指令都会对PSW产生影响,

image-20220818225158887
image-20220818225158887

实际上使用上面的条件转移就完全能够实现顺序分支和循环,但是还是设计了一个loop指令直接综合了跳转处理,但是loop指令必须使用ECX:

image-20220818225615827
image-20220818225615827

最后一点就是CISC(复杂指令集)和RISC(经典指令集),前者主要是x86架构,后者使用于ARM架构

image-20220818230453188
image-20220818230453188

然后就是CPU了,这部分主要是CPU的控制、指令执行、数据通路和流水线的内容

image-20220820111400343
image-20220820111400343

CPU由ALU和控制器组成,其中运算其中具有通用寄存器组。这里就涉及到寄存器与ALU的数据传输了,主要有两种方式:专用数据通路,结构复杂,内部单总线方式,结构简单,容易冲突,需要使用两个暂存寄存器来解决冲突问题:

image-20220820112735339
image-20220820112735339

image-20220820151017851
image-20220820151017851

MDRin和MDRinE指CPU内部总线的输入和数据总线的输入,所以整体的结构图如下:

image-20220820151143140
image-20220820151143140

指令周期是指CPU从主存中取出并执行一条指令所需的全部时间。一般是由多个机器周期组成,一个机器周期又会又多个时钟周期组成:

image-20220820212309967
image-20220820212309967

image-20220820213954591
image-20220820213954591

指令的执行方案有两种,单指令周期、多指令周期和流水线方案。

内部总线和系统总线,前者指CPU内部各寄存器和运算部件连接的总线,后者指同一计算机各部件互相连接的总线

内部单总线的数据传输:寄存器-寄存器,寄存器-主存,寄存器-ALU。

image-20220820222307318
image-20220820222307318

单总线太拉了,不如多总线。

硬布线:

image-20220820231710271
image-20220820231710271

硬布线控制器的原理相当复杂,好在不需要自己设计,步骤如下:

image-20220820235711669
image-20220820235711669

不难理解,只能用RISC,而且难以扩充,好在效率高。但是重点是微操作控制器。

指令是对程序执行步骤的描述,微指令是对指令执行步骤的描述。一堆垃圾概念的关系:

image-20220821000130791
image-20220821000130791

微程序控制器的结构如下:我称之为套娃CPU

image-20220821000524302
image-20220821000524302

image-20220821000607594
image-20220821000607594

前3条都能理解了,到2结束时,根据顺序逻辑的标志的输入会使程序选择进入执行周期或是间址周期,在执行周期中到最后一条时就会重新回到0。(计组实验)。n条机器指令的CM中的微程序的个数至少为n+1个。

控制部件通过控制线向执行部件发出各种控制命令,通常这种控制命令叫做微命令,是最小单位,组成微指令 。 在机器的一个CPU周期中,一组实现一定操作功能的微命令的组合,构成一条微指令 事实上一条机器指令的功能是由许多条微指令组成的序列来实现的。这个微指令序列通常叫做微程序。

一个微操作对应一条微指令。微操作是动作,微指令是个编码。微操作是微命令作用在硬件上的一个动作。课本上的微操作是在写指令流程时一条即一个箭头,这粒度比较粗。

指令周期:从主存取出并执行所需的时间。微周期:从CM取出一条微指令并执行所需的时间。

微指令类似前面的指令,但也是存在固定的格式的:

image-20220821152544735
image-20220821152544735

当然,考试的重点是前两种。知道格式之后需要固定的编码方式,首先对于水平型微指令:

image-20220821152709163
image-20220821152709163

image-20220821153059795
image-20220821153059795

image-20220821153225630
image-20220821153225630

第三种就拉了。

程序执行的执行需要有地址,对于微指令同样需要地址指明,地址的形成方式如下:

image-20220821153423530
image-20220821153423530

最后就是微程序控制单元的设计(对标硬布线设计)

相较于

硬布线,微程序的指令实际上是可以变化的,所以对应的有两种:静态微程序(ROM)和动态微程序(EPROM)

image-20220821154817013
image-20220821154817013

前面提到了指令的过程执行,那么指令流水就是指指令执行的过程,例如前面的取值、分析、执行三部分。

那么正常的程序指令就是顺序执行的,但是可能会导致较多的时间浪费。所以引入了指令流水线,包括一次重叠,两次重叠。

image-20220821155309103
image-20220821155309103

可惜我们通常不使用上图来进行程序执行过程的描述,而是使用时空图:

image-20220821155411863
image-20220821155411863

流水线的性能指标有:吞吐率(完成n个任务所需要的时间T),加速比(不使用流水线与使用流水线之比),效率(时空图的有效面积/时空图总面积)

指令流水线经典步骤有5步:取指、解码、执行、访存、写回。在MIPS、RISC这些即使不执行哪个步骤,也是需要的。同时每个阶段的耗时对齐最长的阶段。

image-20220821161056734
image-20220821161056734

流水线可能会出现资源冲突(多条指令同一时刻争用统一资源(互斥或增加资源))、数据冲突(必须等前一条指令执行完之后才能执行后一条指令的情况,插入空指令即可,或者使用数据旁路多个流水线交互,或者使用编译优化,改变执行顺序)和控制冲突(流水线遇到转移指令和其它改变PC值得指令而造成断流,这种问题解决起来比较复杂:分支预测(简单预测或是根据历史预测),预取转移成功和不成功,加快和提前形成条件码,提高转移方向的猜准率)

流水线分类有多种分类依据。除此之外还有流水线的多发技术,其中包括:超标量技术:

image-20220821163101074
image-20220821163101074

超流水技术:

image-20220821163209644
image-20220821163209644

超长指令字:

image-20220821163255333
image-20220821163255333

关于实践的,主要还是五段式流水线。正常的五段指令执行是没有什么特殊情况的,基本都是执行步骤、放锁存器这些。但是对于条件转移指令,由于满足条件时需要将目标PC值写入PC,由于步骤不是过于复杂,所以一般在访存阶段完成这一操作。而对于无条件转移指令,在EX阶段直接就可以完成PC值的写入。之所以WrPC会尽可能执行,就会为了尽可能避免控制冲突。

然后是体系结构sad

image-20220821171353007
image-20220821171353007

image-20220821171450339
image-20220821171450339

最后是硬件多线程:

image-20220821171651525
image-20220821171651525

总线的内容还是比较少的:

image-20220823215420281
image-20220823215420281

地址线和数据线可以并行的传输较多的数据,只有一个设备可以发送数据,同时有多个数据进行接收。为了解决多个设备的数据发送冲突,总线仲裁应运而生。

总线的定义:一组能为多个部件分时共享的公共信息传送线路

总线具有:机械特性、电气特性、功能特性和时间特性

image-20220823231138625
image-20220823231138625

并行不一定比串行快(由于电器特性导致的频率限制)

片内总线指芯片内部的总线:例如CPU中寄存器与寄存器、ALU的连接线

image-20220823232508839
image-20220823232508839

数据通路值数据流经的路径、数据总线是承载的媒介

通信总线是计算机与计算机的,例如网线。

这里的重点就是系统总线。系统总线的结构分为单总线结构(结构简单易于接入但是带宽低、负载重)、双总线结构(一个主存中线一个IO总线)、三总线结构

image-20220823232914254
image-20220823232914254

现在主要使用四总线结构(但是不考)

总线的性能指标有:总线周期(包括申请、寻址、传输、结束,由若干个总线时钟周期组成)、总线时钟周期(机器的时钟周期,可能包含一个或多个时钟周期)、总线工作频率(周期倒数)、总线的时钟频率(机器的时钟频率)、总线的带框(并行传输的位数)、总线带宽(频率*带宽* 是理论最高)、总线复用技术(一种线传输不同的信息,节约成本但是速度拉了)、信号线数

总线仲裁不怎么考,先查个眼。

总线的操作和定时:总线周期包含申请分配(包括传输请求和总线仲裁)、寻址阶段、传输阶段、结束阶段。

image-20220823235103383
image-20220823235103383

定时的重点是同步通信和异步通信

image-20220824000408631
image-20220824000408631

image-20220824000513906
image-20220824000513906

所以主要还是异步定时方式:

image-20220824000718464
image-20220824000718464

异步定时方式总线周期可变,且能使得工作速度相差大的部件可以可靠的进行信息交换,缺点就是控制起来比同步复杂且慢

半同步也有同一时钟,但是增加了一个wait信号,从而能够进行适中的匹配

image-20220824001048733
image-20220824001048733

输入输出最后一章了

主机通过I/O接口与I/O设备进行交互。

I/O控制方式有程序查询方式、程序中断方式

image-20220824223332350
image-20220824223332350

但是对于例如磁盘的快速I/O设备,每准备一个数据就中断,所以有了DMA(直接内存访问)

image-20220824223354032
image-20220824223354032

然而CPU还是无法搞完较多I/O的管理,所以有了通道控制方式

image-20220824223628664
image-20220824223628664

I/O系统=I/O软件(包括驱动程序、用户程序等)+I/O硬件。I/O指令通常使用I/O指令和通道指令来实现主机个I/O设备信息交换

image-20220824223834244
image-20220824223834244

I/O设备需要了解的是鼠标键盘显示器,其中显示器有VRAM概念,容量=分辨率*位深,带宽=容量*帧频。

I/O接口主要具有数据缓冲、错误或状态监测、控制和定时、数据格式转换、与主机和设备的数据传送

image-20220824230212527
image-20220824230212527

注意,主机侧现在已经可以串行了,设备侧可以并行可以串行。

image-20220824230620324
image-20220824230620324

然后是接口和端口

image-20220824230747929
image-20220824230747929

对I/O端口的编制有统一编制(存储器映射方式)和独立编址两种方式,统一编制可以通过CPU的指令对对应的数据进行获取和写入。而独立编址需要设置专门的I/O指令来对I/O端口进行访问

image-20220824231009780
image-20220824231009780

image-20220824231230983
image-20220824231230983

I/O接口分类在后面有一个重点

I/O方式分为程序查询方式、程序中断方式和DMA方式(前面应该已经涉及过了)

image-20220824232431346
image-20220824232431346

程序查询方式(出了独占查询也有定时查询,每过一段时间查一下)拉中之拉,所以还是得中断或DMA。在了解中断方式之前还得先了解一下中断方式。

程序中断:

image-20220824234117627
image-20220824234117627

中断请求分类(来自OS)

image-20220824234223562
image-20220824234223562

中断的核心问题之一是中断判优,即多个设备同时发出中断信号时的处理方式。主要包括硬件排队器或是查询程序。优先级如下:

image-20220825000108043
image-20220825000108043

中断处理过程:首先是执行中断隐指令:进行关中断和保存断电并引出中断服务程序,其中中断服务程序主要有软件查询法和硬件向量法,这里的重点就是硬件向量法:

image-20220825164142970
image-20220825164142970

之所以不将项链地址直接指向入口地址,是因为入口地址可能会发生变化

中断服务程序主要完成如下工作:

image-20220825165844259
image-20220825165844259

image-20220825165856690
image-20220825165856690

然而上面只不过是单重中断,为了满足更多的需求,出现了多重中断:

image-20220825170423660
image-20220825170423660

多重中断与单重中断最主要的区别就是屏蔽字:

image-20220825172238641
image-20220825172238641

注意,1表示屏蔽,0表示正常申请。1越多说明优先级越高

现在回到I/O系统,整体的一个流程如下:

image-20220825174814158
image-20220825174814158

最后一节:DMA方式

CPU向DMA控制器指明输入输出、传输数据、主存外设地址。

image-20220825175802056
image-20220825175802056

image-20220825180313132
image-20220825180313132

DMA请求不等于DMA中断请求(前者进行数据传输,后者负责中断传输 )

下图只有在三总线结构出现访存冲突是才会出现

image-20220825180554895
image-20220825180554895

image-20220825180724811
image-20220825180724811

Licensed under CC BY-NC-SA 4.0
豫ICP备2021032923号
Built with Hugo
Theme Stack designed by Jimmy