打开视频链接显示初始化是什么情况下需要对CACHE进行初始化

adobe CC套件在安装时出现各种各样的问題虽然绿化版安装方便,但是平均5分钟一崩溃实在让人头大

安装时出现“安装程序无法初始化。请下载Adobe Support Advisor检测该问题”的弹窗下载运荇后问题仍然存在。

经过Google搜索在Java堂找到方法:

}

一直以来我都非常着迷于两种電影拍摄手法:一种是慢镜头,将每一个细节全方位的展现给观众另外一种就是快镜头,多半是反应一个时代的变迁从非常长的时间段中,截取几个典型的snapshot合成在十几秒的镜头中,可以让观众很快的了解一个事物的发展脉络对应到技术层面,慢镜头有点类似情景分析把每一行代码都详细的进行解析,了解技术的细节快镜头类似数据流分析,勾勒一个过程中数据结构的演化。本文采用了快镜头嘚方法对内存初始化部分进行描述,不纠缠于具体函数的代码实现只是希望能给大家一个概略性的印象(有兴趣的同学可以自行研究玳码)。BTW在本文中我们都是基于ARM64来描述体系结构相关的内容。

在详细描述linux kernel对内存的初始化过程之前我们必须首先了解kernel在执行第一条语呴之前所面临的处境。这时候的内存状况可以参考下图:

memory(memory address最低)呢也不一定,但是对于kernel而言低于kernel image的内存,kernel是不会纳入到自己的内存管理系统中的对于dtb image的位置,linux并没有特别的要求由于这时候MMU是turn off的,因此CPU只能看到物理地址空间对于cache的要求也比较简单,只有一条:kernel

一旦跳转到linux kernel执行内核则完全掌控了内存系统的控制权,它需要做的事情首先就是要打开MMU而为了打开MMU,必须要创建linux kernel正常运行需要的页表這就是本节的主要内容。

在体系结构相关的汇编初始化阶段我们会准备二段地址的页表:一段是identity mapping,其实就是把地址等于物理地址的那些虛拟地址mapping到物理地址上去打开MMU相关的代码需要这样的mapping(别的CPU不知道,但是ARM ARCH强烈推荐这么做的)第二段是kernel image mapping,内核代码欢快的执行当然需偠将kernel running需要的地址(kernel txt、rodata、data、bss等等)进行映射了具体的映射情况可以参考下图:

block的物理地址是A地址,那么它还被映射到了A地址开始的虚拟地址上去虽然上图中表示的A地址似乎要大于PAGE_OFFSET,不过实际上不一定需要这样的关系这和具体处理器的实现有关。

编译器感知的是kernel image的虚拟地址(左侧)在内核的链接脚本中定义了若干的符号,都是虚拟地址但是在内核刚开始,没有打开MMU之前这些代码实际上是运行在物理哋址上的,因此内核起始刚开始的汇编代码基本上是PIC的,首先需要定位到页表的位置然后在页表中填入kernel image mapping和identity 1G的地址空间范围。当然这種方法有一点副作用就是:PAGE_OFFSET必须2M对齐。对于16K或者64K的page size使用section mapping就有点不合适了,因为这时候对齐的要求太高了对于16K page size,需要32M对齐对于64K page size,需要512M對齐不过,这也没有什么毕竟这时候page size也变大了,不使用section

上面的结论起始是适合大部分情况下的identity mapping但是还是有特例(需要考虑的点主要囷其物理地址的位置相关)。我们假设这样的一个配置:虚拟地址配置为39bit而物理地址是48个bit,同时IDMAP_TEXT这个block的地址位于高端地址(大于39 bit能表礻的范围)。在这种情况下上面的结论失效了,因为PGTABLE_LEVELS 是和虚拟地址的bit数、PAGE_SIZE的定义相关而是和物理地址的配置无关。linux kernel使用了巧妙的方法解决了这个问题大家可以自己看代码理解,这里就不多说了

一旦设定完了页表,那么打开MMU之后kernel正式就会进入虚拟地址空间的世界,媄中不足的是内核的虚拟世界没有那么大原来拥有的整个物理地址空间都消失了,能看到的仅仅剩下kernel image mapping和identity mapping这两段地址空间是可见的不过沒有关系,这只是刚开始内存初始化之路还很长。

虽然可以通过kernel image mapping和identity mapping来窥探物理地址空间但终究是管中窥豹,不了解全局那么内核是洳何了解对端的物理世界呢?答案就是DTB但是问题来了,这时候内核还没有为DTB这段内存创建映射,因此打开MMU之后的kernel还不能直接访问,需要先创建dtb mapping而要创建address mapping,就需要分配页表内存而这时候,还没有了解内存布局内存管理模块还没有初始化,如何来分配内存呢

下面這张图片给出了解决方案:

整个虚拟地址空间那么大,可以被平均分成两半上半部分的虚拟地址空间主要各种特定的功能,而下半部分主要用于物理内存的直接映射对于DTB而言,我们借用了fixed-mapped address这个概念fixed map是被linux kernel用来解决一类问题的机制,这类问题的共同特点是:(1)在很早期嘚阶段需要进行地址映射而此时,由于内存管理模块还没有完成初始化不能动态分配内存,也就是无法动态分配创建映射需要的页表內存空间(2)物理地址是固定的,或者是在运行时就可以确定的对于这类问题,内核定义了一段固定映射的虚拟地址让使用fix map机制的各个模块可以在系统启动的早期就可以创建地址映射,当然这种机制不是那么灵活,因为虚拟地址都是编译时固定分配的

好,我们可鉯考虑创建第三段地址映射了当然,要创建地址映射就要创建各个level中描述符对于fixed-mapped address这段虚拟地址空间,由于也是位于内核空间因此PGD当嘫就是复用swapper进程的PGD了(其实整个系统就一个PGD),而其他level的Translation

table都必须静态定义为什么呢?因为这时候内存管理模块还没有初始化即便是memblock模塊(初始化阶段分配内存的模块)都尚未初始化(没有内存布局的信息),不能动态分配内存

除了DTB,在启动阶段还有其他的模块也想偠创建地址映射,当然对于这些需求,内核统一采用了fixmap的机制来应对fixmap的具体信息如下图所示:

从上面这个图片可以看出fix-mapped虚拟地址分成兩段,一段是permanent fix map一段是temporary fixmap。所谓permanent表示映射关系永远都是存在的例如FDT区域,一旦完成地址映射内核可以访问DTB之后,这个映射关系一直都是存在的而temporary fixmap则不然,一般而言某个模块使用了这部分的虚拟地址之后,需要尽快释放这段虚拟地址以便给其他模块使用。

你可能会很渏怪因为传统的驱动模块中,大家通常使用ioremap函数来完成地址映射为了还有一个early IO remap呢?其实ioremap函数的使用需要一定的前提条件的在地址映射过程中,如果某个level的Translation tabe不存在那么该函数需要调用伙伴系统模块的接口来分配一个page size的内存来创建某个level的Translation table,但是在启动阶段内存管理的夥伴系统还没有ready,其实这时候内核连系统中有多少内存都不知道的。而early io remap则在early_ioremap_init之后就可以被使用了更具体的信息请参考mm/early_ioremap.c文件。

结论:如果想要在伙伴系统初始化之前进行设备寄存器的访问那么可以考虑early IO remap机制。

完成DTB的映射之后内核可以访问这一段的内存了,通过解析DTB中嘚内容内核可以勾勒出整个内存布局的情况,为后续内存管理初始化奠定基础收集内存布局的信息主要来自下面几条途径:

(2)memory node。这個节点主要定义了系统中的物理内存布局主要的布局信息是通过reg属性来定义的,该属性定义了若干的起始地址和size条目

header中的一部分。更具体的信息可以参考文档了解DTB的结构。

node这个节点及其子节点定义了系统中保留的内存地址区域。保留内存有两种一种是静态定义的,用reg属性定义的address和size另外一种是动态定义的,只是通过size属性定义了保留内存区域的长度或者通过alignment属性定义对齐属性,动态定义类型的子節点的属性不能精准的定义出保留内存区域的起始地址和长度在建立地址映射方面,可以通过no-map属性来控制保留内存区域的地址映射关系嘚建立更具体的信息可以阅读参考文献[1]。

通过对DTB中上述信息的解析其实内核已经基本对内存布局有数了,但是如何来管理这些信息呢这也就是著名的memblock模块,主要负责在初始化阶段用来管理物理内存一个参考性的示意图如下:

type的数组中(上图中的绿色block,按照地址的大尛顺序排列)而那些需要预留的,不需要内核管理的内存被保存在memblock的reserved type的数组中(上图中的青色block也是按照地址的大小顺序排列)。要想叻解进一步的信息请参考内核代码中的setup_machine_fdt和arm64_memblock_init这两个函数的实现。

了解到了当前的物理内存的布局但是内核仍然只是能够访问部分内存(kernel image mapping囷DTB那两段内存,上图中黄色block)大部分的内存仍然处于黑暗中,等待光明的到来也就是说需要创建这些内存的地址映射。

在这个时间点仩创建内存的地址映射有一个悖论:创建地址映射需要分配内存,但是这时候伙伴系统没有ready无法动态分配。也许你会说memblock不是已经ready了嗎,不可以调用memblock_alloc进行物理内存的分配吗当然可以,memblock_alloc分配的物理内存仍然需要通过虚拟地址访问而这些内存都还没有创建地址映射,因此内核一旦访问memblock_alloc分配的物理内存悲剧就会发生了。

怎么办呢内核采用了一个巧妙的办法:那就是控制创建地址映射,memblock_alloc分配页表内存的順序也就是说刚开始的时候创建的地址映射不需要页表内存的分配,当内核需要调用memblock_alloc进行页表物理地址分配的时候很多已经创建映射嘚内存已经ready了,这样在调用create_mapping的时候不需要分配页表内存。更具体的解释参考下面的图片:

我们知道在内核编译的时候,在BSS段之后分配叻几个page用于swapper进程地址空间(内核空间)的映射当然,由于kernel image不需要mapping那么多的地址因此swapper进程translation table的最后一个level中的entry不会全部的填充完毕。换句话說:swapper进程页表可以支持远远大于kernel image

region的地址映射之后可以解除限制,任意分配memory了而这时候,所有memory type的地址区域(上上图中绿色block)都已经可见而这些宝贵的内存资源就是内存管理模块需要管理的对象。具体代码请参考paging_init--->map_mem函数的实现

目前为止,所有为内存管理做的准备工作已经唍成:收集了整个内存布局的信息memblock模块中已经保存了所有需要管理memory region的信息,同时系统也为所有的内存(reserved除外)创建了地址映射。虽然整个内存管理系统没有ready但是通过memblock模块已经可以在随后的初始化过程中进行动态内存的分配。 有了这些基础随后就是真正的内存管理系統的初始化了,我们下回分解

}

Header可以设置请求的头:

// 指示客户机鈳以接收超出超时期间的响应消息即过期后的10秒内缓存可以继续使用,cache的响应头部会有提示 // 至少在10秒内缓存要保持更新


}

我要回帖

更多关于 打开视频链接显示初始化是什么情况 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信