最后的话
最近很多小伙伴找我要Linux学习资料,于是我翻箱倒柜,整理了一些优质资源,涵盖视频、电子书、PPT等共享给大家!
资料预览
给大家整理的视频资料:
给大家整理的电子书资料:
如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
如上图所示,这个cache中有4路(块)
,每一路有4个cache line
,总共有4组
。
组
:相同索引域的cache line组成一个组路
:在组相连结构的cache中,cache被分成几个相同大小的块cache line
:cache line是cache中最小的访问单元
除了上面的内容,cache中还有几个概念,是由于查询cache是否命中的重要概念
cache地址编码
:处理器访问cache,就需要靠cache的地址编码,分成三部分,分别是偏移域(offset)
、索引域(index)
和标记域(tag)
偏移域(offset)
:用于查找某一个cache line中的具体字节索引域(index)
:用于查找数据是在cache中的哪一个组中标记(tag)
:用于判断cache line中存放的数据是否和处理器想要的一致,每一个cache line都有它唯一的一个tag值。
举个例子:在一个32KB的4路组相连的cache中,其中cache line为32Byte,请画出这个cache的结构图
每一路(每一块)的大小就是cache的总大小除以路数:32KB/4 = 8KB
每一路(每一块)包含的cache line数目等于块大小除以cache line大小:8KB/32Byte = 256
1.4.1、如何判断cache是否命中?
如果CPU从0x0654地址读取一个字节,cache控制器是如何判断数据是否在cache中命中的呢?我们如何根据地址在有限大小的cache中查找数据呢?
如上图,我们一共有8行cache line,cache line大小是8 Bytes。所以我们可以利用地址低3 bits(蓝色部分
)用来寻址8 bytes中某一个字节,我们称这部分bit组合为offset
。同理,为了覆盖所有cache line,我们需要3 bits(黄色部分
)查找某一行,这部分地址称为index
。所以,当两个地址的bit3、bit4、bit5的值完全一样,那么说明这两个地址都会找到同一个cache line,所以,当我么找到了cache line之后,只代表我们访问的地址对应的数据可能存在这个cache line中。 所以,我们又引入了tag array区域。
每一个cache line都对应唯一的一个tag,tag中保存的是整个地址位宽减去index和offset的长度。这样tag、index、offset三者组合就可以确定唯一的地址了。
所以,当我们根据地址中index位找到cache line后,取出当前cache line对应的tag,然后和地址中的tag进行比较,如果相等,就说明cache命中了,否则就是缺失。上面我们提出了一个问题,为什么硬件cache line不做成一个字节?
这样会导致硬件成本的上升,原本8字节对应一个tag,现在需要8个tag,占用了很多内存。
1.5、cache的映射方式
1.5.1、直接映射缓存
直接映射的方式比较简单,我们先看一张图:
我们把整个cache只分成一个块,那么一个组就只有一个cache line,这种方式就叫做直接映射方式
。
上面那个图,假设cache只有4个cache line。那么直接映射的地址就是0x0到0x30,这段内存地址直接映射到cache中。如果cpu要访问0x40到0x70,那么又会把0x40到0x70的地址直接映射到cache里,这个时候,0x0到0x30这段内存地址的数据就需要从cache里面踢出去,否则0x40到0x70的地址就没办法映射。cache的直接映射有可能会发生严重的高速缓存颠簸,性能会很差。
void add\_array(int \*data1, int \*data2, int result, int size)
{
int i;
for (i == 0; i < size; i++) {
result = data1[i] + data2[i];
}
}
假使上面的程序,result、data1、data2分别指向0x00,0x40和0x80的地址,并且它们都会使用到同一个cache line
- 第一次读取data1的值即0x40地址的值时,由于不在cache里面,所以读取从0x40到0x4f地址的数据填充到cache里面
- 当读取data2的值即0x80地址的值时,由于不在cache里面,所以需要读取0x80到0x8f地址的数据填充到cache里面,由于之前cache里面已经保存了0x40地址的数据,所以数据发生了替换
- 当data1和data2相加后,需要把结果赋值给result,写入到0x00中,这时cache又发生了替换
- 所以当cache使用直接映射缓存时,会发生严重的cache颠簸(不断发生cache line替换),严重影响性能。
1.5.2、全关联
当cache只有一个组,即主存中只有一个地址与n个cache line对应,称为全关联
1.5.3、组相连
为了解决cache直接映射方式
中的高速缓存颠簸问题,组相连
的高速缓存结构在现代处理器中得到了广泛的应用。
上图是两路cache,每一路cache都有4个cache line,每个组有两个cache line可以提供高速缓存行替换
当要发生高速缓存颠簸情况的时候,就有50%的概率可以不被替换,从而减小了高速缓存颠簸。
1.6、虚拟cache和物理cache
1.6.1、物理cache
当处理器查询MMU和TLB得到物理地址之后,只用物理地址去查询高速缓存,我们称为物理高速缓存
。使用物理高速缓存
的缺点就是处理器在查询MMU和TLB之后才能够访问高速缓存,增加了延迟。
1.6.2、虚拟cache
CPU使用虚拟地址来寻址高速缓存,我们成为虚拟高速缓存。处理器在寻址时,首先把虚拟地址发送到高速缓存中,若在高速缓存里找到需要的数据,那么就不再需要访问TLB和物理内存。处理器在寻址时,首先把虚拟地址发送到高速缓存中,若在高速缓存里找到需要的数据,那么就不再需要访问TLB和物理内存。
1.7、cache的分类
在查询cache的时候使用了index和tag,那么查询cache时用的是虚拟地址还是物理地址的index?当找到cache组的时候,我们用的是虚拟地址还是物理地址的tag来匹配cache line呢?
cache可以设计成通过虚拟地址来访问,也可以设计成通过物理地址来访问,这个在CPU设计时就确定下来了,并且对cache的管理有很大的影响。cache可以分成以下三类:
VIVT(Virtual Index Virtual Tag)
: 使用虚拟地址的index和tag,相当于虚拟高速缓存PIPT(Physical Index Physical Tag)
: 使用物理地址的index和tag,相当于物理高速缓存VIPT(Virtual Index Physical Tag)
: 使用虚拟地址的index和物理地址的tag
在早期的ARM处理器中采用的是VIVT的方式,不经过MMU的翻译,直接使用虚拟地址的index和tag来查找cache line,这种方式会导致cache别人的问题。也就是一个物理地址的内容可能出现在多个cache line中,当系统改变了虚拟地址到物理地址的映射时,需要清洗和无效这些cache,导致系统性能下降
1.7.1、VIPT的工作原理
现在很多cortex系列的处理器的L1 data cache采用VIPT方式,即CPU输出的虚拟地址同时会发送到TLB/MMU单元进行地址翻译,以及在高速缓存中进行索引和查询高速缓存。在TLB/MMU单元里,会把虚拟页帧号(VPN)翻译成物理页帧号(PFN),与此同时,虚拟地址的索引域和偏移会用来查询高速缓存。这样高速缓存和TLB/MMU可以同时工作,当TLB/MMU完成地址翻译后,再用物理标记域来匹配高速缓存行。采用VIPT方式的好处之一是在多任务操作系统中,修改了虚拟地址到物理地址映射关系,不需要把相应的高速缓存进行无效操作。
1.7.2、VIVT(虚拟高速缓存)造成的重名同名问题
重名问题:(不同虚拟地址指向相同的物理地址)
重名问题是怎么产生的呢?
我们知道,在操作系统中,多个不同的虚拟地址有可能映射相同的物理地址。由于采用VIPI架构,那么这些不同的虚拟地址会占用高速缓存中不同的高速缓存行(cache line),但是它们对应的是相同的物理地址,这样会引发问题:一是浪费了高速缓存空间,造成高速缓存等效容量的减少,减低整体性能;第二,在执行写操作的时候,只更新其中一个虚拟地址对应的高速缓存,而其他虚拟地址对应的高速缓存并没有更新。那么处理器访问其他虚拟地址可能得到旧数据。
举个例子,比如我们的cache使用的是VIPI,VA1映射到PA,VA2也映射到PA,那么在cache中有可能同时缓存了VA1和VA2两个虚拟地址。当程序往VA1虚拟地址写入数据的时候,PA的内容会被更改,但是虚拟地址VA2对应的cache里面还保存着旧数据,当CPU去读取VA2的值时,读到就是旧地址。一个物理地址在VIPI中就保存了两份数据,这样会产生歧义。
同名问题:(相同的虚拟地址指向不同的物理地址)
同名问题是怎么产生的呢?
同名问题指的是相同的虚拟地址对应着不同的物理地址。因为操作系统中不同的进程会存在很多相同的虚拟地址,而这些相同的虚拟地址在经过MMU转换后得到不同的物理地址,这样就产生了同名问题
。
同名问题最常出现的地方就是进程切换。当一个进程切换到另一个进程时,新进程使用虚拟地址来访问cache的话,新进程会访问到旧进程遗留下来的高速缓存,这些高速缓存数据对于新进程来说是错误和没用的,解决办法就是在进程切换时把旧进程遗留下来的高速缓存都设置为无效,这样就能保证新进程执行时得到一个干净的虚拟高速缓存。同样,TLB也需要设置为无效,因为新进程在切换后得到一个旧进程使用的TLB,里面存放了旧进程和虚拟地址到物理地址的转换结果。
重名问题实际上是多个虚拟地址映射到同一个物理地址引发的歧义问题,而同名问题是一个虚拟地址可能因为进程切换等原因映射到不同的物理地址而引发的问题。
1.7.3、VIPT的重名问题
采用VIPT方式也有可能导致高速缓存别人的问题。
使用虚拟地址的index来查找高速缓存的cache line,这时有可能导致多个高速缓存组映射到同一个物理地址上。
以Linux内核为例,它是以4KB大小为一个页面进行管理的,那么对于一个页来说,虚拟地址和物理地址的低
1.8、cache一致性
什么是cache一致性?
cache一致性,需要保证系统中所有的CPU、所有的bus主从,例如GPU、DMA等,他们观察到的内存是一直的。举个例子,外设都用DMA,如果你的软件通过CPU来产生一些数据,然后相通过DMA来搬移这些数据到外设,如果CPU和DMA看到的数据不一致,比如CPU产生的数据还在cache里,而DMA却从内存中直接去搬移数据,那么DMA就会看到一个旧的数据,那么就产生了数据的不一致性。
1.8.1、cache一致性的解决方案
一般情况下实现系统cahce一致性有三种方案
- 关闭cache
这是最简单的办法,但是它会严重影响性能。以上面那个例子为例,CPU产生数据,然后把数据先放入到DMA buffer里,如果采用关闭cache的方式,那么CPU在产生数据的过程中,CPU不能利用cache,这就会严重影响到性能 - 软件管理cache一致性
这是最常用的方式。软件需要在合适的时候去clean or flush dirty cache,或者invalidata old data
优点:硬件RTL实现简单
缺点:软件复杂度增加,软件需要手动clean/flush cache或者invalidate cache
增加调试难度
降低性能,增加功耗 - 硬件管理cache一致性
对于多核之间的cache一致性,通常的做法就是在多核里实现一个MESI协议,实现一种snoop的控制单元。
1.8.2、MESI协议
目前,ARM或者x86等处理器广泛使用MESI
协议来维护高速缓存一致性。MESI
协议的名字源于该名字使用修改(Modified, M)
,独占(Exclusive, E)
,共享(Shared,S)
和失效(Invalid, I)
四个状态。高速缓存中的状态比如是上述四个状态中的一个。MESI状态机的转换是硬件自动实现的
高速缓存行(cache line)中有两个标志:脏(dirty)
和干净(valid)
。它们很好地描述了高速缓存和内存之间的数据关系,如数据是否有效、数据是否被修改过。在MESI协议中,每个高速缓存行有四个状态,可以使用高速缓存行中的2位地址来表示这些状态(00 01 10 11)。
状态 | 描述 |
---|---|
M | 这行数据有效,数据被修改,和内存中的数据不一致,数据只存在本cache中 |
E | 这行数据有效,数据和内存中的数据一致,数据只存在于本cache中 |
S | 这行数据有效,数据和内存中数据一致,多个cache中存在这个数据副本 |
I | 这行数据无效 |
1.8.3、MESI的操作
类型 | 描述 |
---|---|
初始状态 | 缓存行还没加载任何数据时,状态为I |
本地读 | 表示本地CPU读取缓存行数据 |
本地写 | 表示本地CPU更新缓存行数据 |
总线读 | 总线侦听到一个来自其他CPU的读缓存请求。收到信号的CPU先检查自己的高速缓存中是否有缓存该数据,然后广播应答信号 |
总线写 | 总线侦听到一个来自其他CPU的写缓存请求。收到信号的CPU先检查自己的高速缓存中是否有缓存该数据,然后广播信号 |
总线更新 | 总线侦听到更新请求,请求其他CPU做一些额外事情。其他CPU收到请求后,若CPU上有缓存副本,则需要做额外一些更新操作,比如无效本地的高速缓存行等 |
刷新 | 总线侦听到刷新请求。收到请求的CPU把自己的高速缓存行的内容写回到主内存中 |
刷新到总线 | 收到该请求的CPU会把高速缓存行内容发送到总线上,这样发送请求的CPU就可以获取到这个高速缓存行的内容 |
1.8.3.1 初始化状态为I的cache line
最全的Linux教程,Linux从入门到精通
======================
-
linux从入门到精通(第2版)
-
Linux系统移植
-
Linux驱动开发入门与实战
-
LINUX 系统移植 第2版
-
Linux开源网络全栈详解 从DPDK到OpenFlow
第一份《Linux从入门到精通》466页
====================
内容简介
====
本书是获得了很多读者好评的Linux经典畅销书**《Linux从入门到精通》的第2版**。本书第1版出版后曾经多次印刷,并被51CTO读书频道评为“最受读者喜爱的原创IT技术图书奖”。本书第﹖版以最新的Ubuntu 12.04为版本,循序渐进地向读者介绍了Linux 的基础应用、系统管理、网络应用、娱乐和办公、程序开发、服务器配置、系统安全等。本书附带1张光盘,内容为本书配套多媒体教学视频。另外,本书还为读者提供了大量的Linux学习资料和Ubuntu安装镜像文件,供读者免费下载。
本书适合广大Linux初中级用户、开源软件爱好者和大专院校的学生阅读,同时也非常适合准备从事Linux平台开发的各类人员。
需要《Linux入门到精通》、《linux系统移植》、《Linux驱动开发入门实战》、《Linux开源网络全栈》电子书籍及教程的工程师朋友们劳烦您转发+评论
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
植》、《Linux驱动开发入门实战》、《Linux开源网络全栈》电子书籍及教程的工程师朋友们劳烦您转发+评论
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!