読者です 読者をやめる 読者になる 読者になる

Linux 4.0(x86_64)のメモリレイアウト

linux kernel

この記事はLinux Advent Calendar 2015の2日目の記事です。 今日は復習も兼ねてメモリレイアウトの確認です。

見るのはDocumentation/x86/x86_64/mm.txtです。見ているのは4.0ですが、4.3も同じでした。

で、メモリーマップですが下記のように説明されています。

  6 0000000000000000 - 00007fffffffffff (=47 bits) user space, different per mm
  7 hole caused by [48:63] sign extension
  8 ffff800000000000 - ffff87ffffffffff (=43 bits) guard hole, reserved for hypervisor
  9 ffff880000000000 - ffffc7ffffffffff (=64 TB) direct mapping of all phys. memory
 10 ffffc80000000000 - ffffc8ffffffffff (=40 bits) hole
 11 ffffc90000000000 - ffffe8ffffffffff (=45 bits) vmalloc/ioremap space
 12 ffffe90000000000 - ffffe9ffffffffff (=40 bits) hole
 13 ffffea0000000000 - ffffeaffffffffff (=40 bits) virtual memory map (1TB)
 14 ... unused hole ...
 15 ffffec0000000000 - fffffc0000000000 (=44 bits) kasan shadow memory (16TB)
 16 ... unused hole ...
 17 ffffff0000000000 - ffffff7fffffffff (=39 bits) %esp fixup stacks
 18 ... unused hole ...
 19 ffffffff80000000 - ffffffffa0000000 (=512 MB)  kernel text mapping, from phys 0
 20 ffffffffa0000000 - ffffffffff5fffff (=1525 MB) module mapping space
 21 ffffffffff600000 - ffffffffffdfffff (=8 MB) vsyscalls
 22 ffffffffffe00000 - ffffffffffffffff (=2 MB) unused hole

まず一番上、これはアドレス0から0x00007fffffffffffまでの47bitsが使われています。用途はユーザースペースとなっているので、ユーザーランドのプログラムが使用可能な領域です。

サイズは247 bytesなので128TiBとなります。

irb(main):003:0> (2**47) / 1024 / 1024 / 1024 / 1024
=> 128

残りは0xffffffffffffffff - 0xffff800000000000 = 0x7fffffffffffなので、こちらも128TiBですね。

irb(main):012:0> Math.log2(0x7fffffffffff + 1)
=> 47.0

この内訳を見ていきます。

 8 ffff800000000000 - ffff87ffffffffff (=43 bits) guard hole, reserved for hypervisor

ここはサイズが8TiBでハイパーバイバー用に予約とあります。

 9 ffff880000000000 - ffffc7ffffffffff (=64 TB) direct mapping of all phys. memory

ここはダイレクトマッピングに使うとあります。

ここはLinuxカーネル解読室の10.1.2 ストレートマップ領域で説明されている部分ですね。カーネル実メモリにアクセスするため、仮想アドレス = 実アドレス + PAGE_OFFSETという形でマッピングされます。

PAGE_OFFSETはarch/x86/include/asm/page_64_types.hにて定義されています。

38 #define __PAGE_OFFSET           _AC(0xffff880000000000, UL)

0xffff880000000000が設定されていて、この値はダイレクトマップ領域の最初のアドレスと一致していますね。

次の1TiBは空き領域です。

 10 ffffc80000000000 - ffffc8ffffffffff (=40 bits) hole

次はvmallocとioremap用とあり、サイズは32TiBになっています。

 11 ffffc90000000000 - ffffe8ffffffffff (=45 bits) vmalloc/ioremap space

この辺りの定義もarch/x86/include/asm/pgtable_64_types.hにあります。

 58 #define VMALLOC_START    _AC(0xffffc90000000000, UL)
 59 #define VMALLOC_END      _AC(0xffffe8ffffffffff, UL)

つぎはまた1TiBの空き領域です。

 12 ffffe90000000000 - ffffe9ffffffffff (=40 bits) hole

次は1TiBの領域で、Virtual Memory Map(VMM)に使用しています。

 13 ffffea0000000000 - ffffeaffffffffff (=40 bits) virtual memory map (1TB)

これの定義もarch/x86/include/asm/pgtable_64_types.hにあります。

 60 #define VMEMMAP_START    _AC(0xffffea0000000000, UL)

ここはstruct page構造体を置いていて、

arch/x86/include/asm/pgtable_64.hにてvmemmapというマクロで、このようにアクセスできるようになっています。

176 #define vmemmap ((struct page *)VMEMMAP_START)

そして、include/asm-generic/memory_model.hにてこのようにページフレーム番号とstruct pageのマッピングで使用されます。うちのカーネルFedoraカーネルの.configベースですがCONFIG_SPARSEMEM_VMEMMAPはyになってました。

 48 #elif defined(CONFIG_SPARSEMEM_VMEMMAP)
 49 
 50 /* memmap is virtually contiguous.  */
 51 #define __pfn_to_page(pfn)      (vmemmap + (pfn))
 52 #define __page_to_pfn(page)     (unsigned long)((page) - vmemmap)

次はKernel Address sanitizerの使う領域で16TiBほどあります。。

 14 ... unused hole ...
 15 ffffec0000000000 - fffffc0000000000 (=44 bits) kasan shadow memory (16TB)

次は512GiBほどの領域です。ここはlinux-insidesのlinux-initialization-10.mdを見た感じではこの範囲にあるアドレスがスタックの開始アドレスになるというところでしょうか。stack randomizationですかね。

 16 ... unused hole ...
 17 ffffff0000000000 - ffffff7fffffffff (=39 bits) %esp fixup stacks
 18 ... unused hole ...

次がカーネルのテキストセクションで物理メモリ0から512MiBをマッピングしてます。ここのマッピングの話は新装改訂版 Linuxのブートプロセスをみる (アスキー書籍)にも書かれています。

 19 ffffffff80000000 - ffffffffa0000000 (=512 MB)  kernel text mapping, from phys 0

次はカーネルモジュールがマッピングされる領域です。

 20 ffffffffa0000000 - ffffffffff5fffff (=1525 MB) module mapping space

その次にvsyscallの領域です。ここは今までと違って8MiBしか無いですね。まあ、そんなにサイズを要求するとこでもないですが。

 21 ffffffffff600000 - ffffffffffdfffff (=8 MB) vsyscalls

そして、最後に2MiBの未使用領域があって終了です。

 22 ffffffffffe00000 - ffffffffffffffff (=2 MB) unused hole

詳解 Linuxカーネル 第3版

詳解 Linuxカーネル 第3版