# Day 20 High Memory Handling

今天直奔新主题!XDD
昨天提要 trace 的程序码,trace 的不多,今天就还是先来看个文件,实体记忆体管理相关的程序码,也许之後在另辟一篇或是就放在附录好了~XD
今天读的文件是 High Memory Handling,一个增加 32 bit 机器能够存取实体记忆体大小的技术。

文件

.. _highmem:

================
High Memory 处理
================

By: Peter Zijlstra <[email protected]>

.. contents:: :local:

什麽是 High Memory?
===================

High memory(highmem)是当实体记忆体的大小接近
或超过虚拟记忆体的大小时使用的,
此时核心已经不可能时时刻刻保存着所有的实体记忆体映射。
这意味着核心需要开始使用临时的映射,
来存取想要存取的实体记忆体片段。

永久性映射未映射到的(实体)记忆体即是'highmem'。
有各种和架构相关的限制来决定那个highmem 的边界在哪里。

例如,在 i386 架构中,我们选择将核心映射到每个程序的虚拟位址空间,
这样我们就不必在进出核心时花费使 TLB 失效的代价。
这意味着可用的虚拟记忆体空间(4GiB on i386) 
必须在使用者空间和核心空间之间进行划分。

使用这种方法的架构通常是 3:1 的拆分,
3GiB 用於使用者空间,高位 1GiB 则是核心空间::

        +--------+ 0xffffffff
        | Kernel |
        +--------+ 0xc0000000
        |        |
        | User   |
        |        |
        +--------+ 0x00000000

这意味着核心可以在任何一个时刻映射最多 1GiB 的实体记忆体,
但因为我们需要虚拟地址空间 - 包括
暂时性映射存取实体记忆体的其余部分 - 
实际直接映射的虚拟记忆体位址通常会更少(通常约为 896MiB)。

其他具有 mm 上下文标记 TLBs (mm context tagged TLBs) 的架构,
可以具有单独的核心和使用者映射。
但是,某些硬体(如某些 ARM)使用 mm 上下文标签时,
仅有有限的虚拟空间。

暂时性的虚拟映射
===============

核心包含几种创建暂时性映射的方法:

* vmap().  这可用於制作多个持续时间较长的实体分页映射
  到一个连续的虚拟空间。它需要全域的同步机制来取消映射。

* kmap().  这实现了单一分页的短期映射。它需要全域同步,
  但有所摊销。使用巢状结构时,它容易出现死锁,
  因此不建议用於新程序码。

* kmap_atomic().  这实现了单一分页非常短期的映射。
  由於映射仅限於使用它的 CPU;
  它能正常运作,但需要让使用它的任务保持在该 CPU 上直到它完成,
  以免其他任务替换掉它的映射。

  kmap_atomic() 也可以被中断上下文使用,
  因为它不会 sleep 并且呼叫者也不能 sleep 直到 kunmap_atomic() 被呼叫。
  
  可以假设 k[un]map_atomic() 不会失败。

使用 kmap_atomic
================

何时、在哪使用 kmap_atomic() 很直觉。
使用时机是当程序码时使用想要存取的
可能是从 high memory 分配到的分页内容
(参见 __GFP_HIGHMEM),例如分页快取中的分页。 
API 有两个函数,它们可以以类似於以下的方式使用::

    /* Find the page of interest. */
    struct page *page = find_get_page(mapping, offset);

    /* Gain access to the contents of that page. */
    void *vaddr = kmap_atomic(page);

    /* Do something to the contents of that page. */
    memset(vaddr, 0, PAGE_SIZE);

    /* Unmap that page. */
    kunmap_atomic(vaddr);

请注意 kunmap_atomic() 使用的是 
kmap_atomic() 的结果,而非它的参数。

如果需要映射两个分页,因为想从其中一个分页
复制到另一个分页,需要严格的巢状呼叫 kmap_atomic,例如::

    vaddr1 = kmap_atomic(page1);
    vaddr2 = kmap_atomic(page2);

    memcpy(vaddr1, vaddr2, PAGE_SIZE);

    kunmap_atomic(vaddr2);
    kunmap_atomic(vaddr1);


暂时性映射的代价
===============

创建暂时性映射的成本可能非常高。
架构相关程序码必须操作核心的分页表、dTLB 和/或 MMU 的暂存器。

如果未设置 CONFIG_HIGHMEM,
则核心将尝试使用简单的算数来创建映射,
这些映射能将 struct page 位址转换为指向分页内容的指标,
而不是像 highmem 映射一样,做一大堆映射的操作。
在这种情况下,取消映射操作可能是 nop。

如果没有设置 CONFIG_MMU,
那麽就没有暂时性映射,也没有 highmem。
在这种情况下,也将使用简单的运算方法。

i386 实体位址扩充 (PAE, Physical Address Extension)
==================================================

在某些情况下,i386 arch 将可以支援高达到 64GiB 的 RAM 
在 32 位元的机器上。而这会有一些後果:

* Linux 需要为系统中的每个分页提供 page-frame 的结构,
  而 pageframse 需要存在在永久性的映射中,这意味着:

* 最多会有 896M/sizeof(struct page) page-frames;
  而 struct page 是 32 位元组,最终将是 112G 数量级的页数;
  然而,核心需要储存的不仅仅是那个记忆体中的 page-frames...

* PAE 使分页表更大 - 这会减慢系统速度
  因为有更多的资料需要被存取,才能在 TLB 填充等中遍历。
  有一个优点是 PAE 有更多的 PTE 位元,可以提供进阶特性像 NX 和 PAT。

一般建议是在 32 位元机器上不要使用超过 8GiB - 
尽管更多可能适合您和您的需求,但您很大概率只能靠自己 - 
不要期待核心开发人员会在意如果无法运作。

我的理解

  • 实体记忆体大小超出虚拟记忆体可以定址的范围,那麽多出来的实体记忆体需要透过 high memory 才能存取
  • kernel 需要打开 CONFIG_MMU 和 CONFIG_HIGHMEM 才会有 highmem 支援
  • 大多数架构的程序都是如文中所提的,在 32 位元的设置下,会把 kernel 映射到虚拟位址高位的 1G 而剩余的 3G 是使用者可使用的,highmem 则是透过使用部分 (约 128 MB) 的 kernel 虚拟位址,拿来作为暂时性的映射空间。
  • 建立这些暂时性映射有 vmapkmapkmap_atomic 这些 API,分别对应的是映射存在的时间长短,vmap 最长; kmap_atomic 最短
    • 其中有 kmap_atomic 的示例说明
  • 暂时性映射的 cost 很高是因为,在 context swtich 下,kernel page table 的更动、tlb flush 等等的操作会很频繁。
  • mm context tagged TLBs:TLB entry 中有包含 mm (各个 process) 的资讯 (( 猜测
  • 112G 还不太确定这个数字怎麽来的XD

延伸阅读


<<:  [神经机器翻译理论与实作] Google Translate的神奇武器- Seq2Seq (III)

>>:  D28 第十五周 (回忆篇)

全方位对比:SmartQuery VS FineReport来自报表工程师的经验

写在前面 相比起BI这些当红炸子鸡概念,报表工具大家可能不太熟悉,希望这篇文章能够给大家提供一些新的...

冒险村11 - frozen_string_literal

11 - frozen_string_literal 延续 Begin from linter : ...

中阶魔法 - 范围链 Scope Chain

前情提要 上回与艾草玩游戏输了要接受处罚。 「都躲这麽远了,她应该找不到我了吧!」 艾草:「啊哈,原...

D14. 学习基础C、C++语言

D14. 字元阵列(1) 字元: %c 字串: %s 字元阵列: char a[] 如果要输出一串h...

day4 - config 选用: cli & viper 简介

你是专案里的那条龙吗? 假设我们都不是一人统包开发, 而且工作的环境也不是开发到正式一个环境的状况下...