• <acronym id='d0gs7'><em id='d0gs7'></em><td id='d0gs7'><div id='d0gs7'></div></td></acronym><address id='d0gs7'><big id='d0gs7'><big id='d0gs7'></big><legend id='d0gs7'></legend></big></address>
    <i id='d0gs7'><div id='d0gs7'><ins id='d0gs7'></ins></div></i>

      <fieldset id='d0gs7'></fieldset>

        <code id='d0gs7'><strong id='d0gs7'></strong></code>

        1. <ins id='d0gs7'></ins>

            <span id='d0gs7'></span><dl id='d0gs7'></dl>

            <i id='d0gs7'></i>

          1. <tr id='d0gs7'><strong id='d0gs7'></strong><small id='d0gs7'></small><button id='d0gs7'></button><li id='d0gs7'><noscript id='d0gs7'><big id='d0gs7'></big><dt id='d0gs7'></dt></noscript></li></tr><ol id='d0gs7'><table id='d0gs7'><blockquote id='d0gs7'><tbody id='d0gs7'></tbody></blockquote></table></ol><u id='d0gs7'></u><kbd id='d0gs7'><kbd id='d0gs7'></kbd></kbd>
          2. 解析Linux系统下的高端内存

            • 时间:
            • 浏览:14
            • 来源:124软件资讯网

                Linux内核地址空间划分

                通常32位Linux内核虚拟地址空间划分0~3G为用户空间 ,3~4G为内核空间(注重  ,内核可以使用的线性地址只有1G) 。注重这里是32位内核地址空间划分  ,64位内核地址空间划分是差别的  。

                通常32位Linux内核虚拟地址空间划分0~3G为用户空间  ,3~4G为内核空间(注重  ,内核可以使用的线性地址只有1G) 。注重这里是32位内核地址空间划分 ,64位内核地址空间划分是差别的 。

                Linux内核高端内存的由来

                当内核模块代码或线程会见内存时  ,代码中的内存地址都为逻辑地址 ,而对应到真正的物理内存地址  ,需要地址一对一的映射  ,如逻辑地址0xc0000003对应的物理地址为0×3  ,0xc0000004对应的物理地址为0×4  ,… …  ,逻辑地址与物理地址对应的关系为

                物理地址 = 逻辑地址 – 0xC0000000:这是内核地址空间的地址转换关系  ,注重内核的虚拟地址在“高端” ,可是ta映射的物理内存地址在低端 。

                现实上  ,“内核直接映射空间”也达不到 1G , 还得留点线性空间给“内核动态映射空间” 呢  。

                因此  ,Linux 划定“内核直接映射空间” 最多映射 896M 物理内存  。

                对于高端内存  ,可以通过 alloc_page() 或者其它函数获得对应的 page  ,可是要想会见现实物理内存  ,还得把 page 转为线性地址才行(为什么?想想 MMU 是怎样会见物理内存的)  ,也就是说 ,我们需要为高端内存对应的 page 找一个线性空间 ,这个历程称为高端内存映射  。

                假 设根据上述简朴的地址映射关系  ,那么内核逻辑地址空间会见为0xc0000000 ~ 0xffffffff  ,那么对应的物理内存规模就为0×0 ~ 0×40000000  ,即只能会见1G物理内存  。若机械中安装8G物理内存  ,那么内核就只能会见前1G物理内存 ,后面7G物理内存将会无法会见 ,由于内核 的地址空间已经所有映射到物理内存地址规模0×0 ~ 0×40000000 。纵然安装了8G物理内存  ,那么物理地址为0×40000001的内存  ,内核该怎么去会见呢?代码中必须要有内存逻辑地址 的  ,0xc0000000 ~ 0xffffffff的地址空间已经被用完了  ,以是无法会见物理地址0×40000000以后的内存 。

                显 然不能将内核地址空间0xc0000000 ~ 0xfffffff所有用来简朴的地址映射  。因此x86架构中将内核地址空间划分三部门:ZONE_DMA、ZONE_NORMAL和 ZONE_HIGHMEM 。ZONE_HIGHMEM即为高端内存  ,这就是内存高端内存观点的由来  。

                在x86结构中  ,三种类型的区域(从3G最先盘算)如下:

                ZONE_DMA 内存最先的16MB

                ZONE_NORMAL 16MB~896MB

                ZONE_HIGHMEM 896MB ~ 竣事(1G)

                高端内存是指物理地址大于 896M 的内存 。对于这样的内存  ,无法在“内核直接映射空间”举行映射  。

                为什么?

                由于“内核直接映射空间”最多只能从 3G 到 4G  ,只能直接映射 1G 物理内存  ,对于大于 1G 的物理内存  ,无能为力 。

                高端内存映射有三种方式:

                1、映射到“内核动态映射空间”

                这种方式很简朴 ,由于通过 vmalloc() ,在“内核动态映射空间”申请内存的时间  ,就可能从高端内存获得页面(参看 vmalloc 的实现) ,因此说高端内存有可能映射到“内核动态映射空间” 中 。

                2、永世内核映射

                若是是通过 alloc_page() 获得了高端内存对应的 page  ,怎样给它找个线性空间?

                内核专门为此留出一块线性空间 ,从 PKMAP_BASE 到 FIXADDR_START  ,用于映射高端内存 。在 2.4 内核上  ,这个地址规模是 4G-8M 到 4G-4M 之间 。这个空间起叫“内核永世映射空间”或者“永世内核映射空间”

                这个空间和其它空间使用同样的页目录表  ,对于内核来说  ,就是 swapper_pg_dir  ,对通俗历程来说  ,通过 CR3 寄存器指向  。

                通常情形下 ,这个空间是 4M 巨细  ,因此仅仅需要一个页表即可  ,内核通过来 pkmap_page_table 寻找这个页表  。

                通过 kmap() , 可以把一个 page 映射到这个空间来

                由于这个空间是 4M 巨细  ,最多能同时映射 1024 个 page  。因此  ,对于不使用的的 page  ,应该实时从这个空间释放掉(也除映射关就是解系) ,通过 kunmap()  ,可以把一个 page 对应的线性地址从这个空间释放出来  。

                3、暂时映射

                内核在 FIXADDR_START 到 FIXADDR_TOP 之间保留了一些线性空间用于特殊需求  。这个空间称为“牢固映射空间”

                在这个空间中  ,有一部门用于高端内存的暂时映射  。

                这块空间具有如下特点:

                1、 每个 CPU 占用一块空间

                2、 在每个 CPU 占用的那块空间中  ,又分为多个小空间 ,每个小空间巨细是 1 个 page ,每个小空间用于一个目的  ,这些目的界说在 kmap_types.h 中的 km_type 中  。

                当要举行一次暂时映射的时间  ,需要指定映射的目的  ,凭据映射目的  ,可以找到对应的小空间  ,然后把这个空间的地址作为映射地址 。这意味着一次暂时映射会导致以前的映射被笼罩  。

                通过 kmap_atomic() 可实现暂时映射 。

                下图简朴简朴表达怎样对高端内存举行映射

                Linux内存线性地址空间巨细为4GB ,分为2个部门:用户空间部门(通常是3G)和内核空间部门(通常是1G) 。在此我们主要关注内核地址空间部门  。

                内核通过内核页全局目录来治理所有的物理内存  ,由于线性地址前3G空间为用户使用 ,内核页全局目录前768项(恰好3G)除0、1两项外所有为0  ,后256项(1G)用来治理所有的物理内存 。内核页全局目录在编译时静态地界说为swapper_pg_dir数组  ,该数组从物理内存地址0x101000处最先存放 。

                由图可见  ,内核线性地址空间部门从PAGE_OFFSET(通常界说为3G)最先 ,为了将内核装入内存  ,从PAGE_OFFSET最先8M线性地址用来映射内核所在的物理内存地址(也可以说是内核所在虚拟地址是从PAGE_OFFSET最先的);接下来是mem_map数组  ,mem_map的起始线性地址与系统结构相关  ,好比对于UMA结构  ,由于从PAGE_OFFSET最先16M线性地址空间对应的16M物理地址空间是DMA区  ,mem_map数组通常最先于PAGE_OFFSET+16M的线性地址;从PAGE_OFFSET最先到VMALLOC_START – VMALLOC_OFFSET的线性地址空间直接映射到物理内存空间(逐一对应影射  ,物理地址<==>线性地址-PAGE_OFFSET)  ,这段区域的巨细和机械现实拥有的物理内存巨细有关  ,这儿VMALLOC_OFFSET在X86上为8M  ,主要用来防止越界错误;在内存比力小的系统上  ,余下的线性地址空间(还要再减去空缺区即VMALLOC_OFFSET)被vmalloc()函数用来把不一连的物理地址空间映射到一连的线性地址空间上  ,在内存比力大的系统上  ,vmalloc()使用从VMALLOC_START到VMALLOC_END(也即PKMAP_BASE减去2页的空缺页巨细PAGE_SIZE(诠释VMALLOC_END))的线性地址空间 ,此时余下的线性地址空间(还要再减去2页的空缺区即VMALLOC_OFFSET)又可以分成2部门:第一部门从PKMAP_BASE到FIXADDR_START用理由kmap()函数来建设永世映射高端内存;第二部门  ,从FIXADDR_START到FIXADDR_TOP  ,这是一个牢固巨细的暂时映射线性地址空间  ,(引用:Fixed virtual addresses are needed for subsystems that need to know the virtual address at compile time such as the APIC)  ,在X86系统结构上 ,FIXADDR_TOP被静态界说为0xFFFFE000,此时这个牢固巨细空间竣事于整个线性地址空间最后4K前面  ,该牢固巨细空间巨细是在编译时盘算出来并存储在__FIXADDR_SIZE变量中  。

                正是由于vmalloc()使用区、kmap()使用区及牢固巨细区(kmap_atomic()使用区)的存在才使ZONE_NORMAL区巨细受到限制  ,由于内核在运行时需要这些函数 ,因此在线性地址空间中至少要VMALLOC_RESERVE巨细的空间  。VMALLOC_RESERVE的巨细与系统结构相关  ,在X86上  ,VMALLOC_RESERVE界说为128M ,这就是为什么ZONE_NORMAL巨细通常是16M到896M的缘故原由 。