本記事の信頼性
- リアルタイムシステムの研究歴12年.
- 東大教員の時に,英語でOS(Linuxカーネル)の授業.
- 2012年9月~2013年8月にアメリカのノースカロライナ大学チャペルヒル校(UNC)コンピュータサイエンス学部で客員研究員として勤務.C言語でリアルタイムLinuxの研究開発.
- プログラミング歴15年以上,習得している言語: C/C++,Python,Solidity/Vyper,Java,Ruby,Go,Rust,D,HTML/CSS/JS/PHP,MATLAB,Assembler (x64,ARM).
- 東大教員の時に,C++言語で開発した「LLVMコンパイラの拡張」,C言語で開発した独自のリアルタイムOS「Mcube Kernel」をGitHubにオープンソースとして公開.
- 2020年1月~現在はアメリカのノースカロライナ州チャペルヒルにあるGuarantee Happiness LLCのCTOとしてECサイト開発やWeb/SNSマーケティングの業務.2022年6月~現在はアメリカのノースカロライナ州チャペルヒルにあるJapanese Tar Heel, Inc.のCEO兼CTO.
- 最近は自然言語処理AIとイーサリアムに関する有益な情報発信に従事.
- (AI全般を含む)自然言語処理AIの論文の日本語訳や,AIチャットボット(ChatGPT,Auto-GPT,Gemini(旧Bard)など)の記事を50本以上執筆.アメリカのサンフランシスコ(広義のシリコンバレー)の会社でプロンプトエンジニア・マネージャー・Quality Assurance(QA)の業務委託の経験あり.
- (スマートコントラクトのプログラミングを含む)イーサリアムや仮想通貨全般の記事を200本以上執筆.イギリスのロンドンの会社で仮想通貨の英語の記事を日本語に翻訳する業務委託の経験あり.
こういった私から学べます.
前回を読んでいない方はこちらからどうぞ.
Linuxカーネルの記事一覧はこちらからどうぞ.
LinuxカーネルはC言語で書かれています.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!
今回のテーマはプロセスアドレス空間です.
プロセスアドレス空間を学ぶとページテーブルや仮想メモリがわかります!
目次
アドレス空間,メモリアドレス,仮想メモリ,ページテーブル
アドレス空間とは,プロセスがアクセスできるメモリのことです.
プロセスがシステムメモリの100%にアクセスできるように錯覚させます.
仮想メモリを使用すると,実際の物理メモリ量よりもはるかに大きくなる可能性があります.
Linuxカーネルが設定するプロセスページテーブルによって定義されます.
メモリアドレスは,アドレス空間内のインデックスとして,特定のバイトを識別します.
各プロセスには32/64ビットのフラットなアドレス空間が与えられています.
仮想メモリとは,2次メモリを主記憶の一部のように使用できるメモリ管理手法のことで,OSでよく使われる技術です.
Linuxカーネルは,プロセスのメモリマッピングを追跡するために仮想メモリ領域(VMA:Virtual Memory Area)を使用します.
ページテーブルとは,OSの中の仮想メモリシステムにおいて仮想アドレスと物理アドレスを対応付けるために使われるデータ構造です.
Linuxはブートプロセスの早い段階でページングを有効にします.
CPUが行う全てのメモリアクセスは仮想であり,ページテーブルを通じて物理アドレスに変換されます.
Linuxがページテーブルを設定し,MMUがページテーブルの内容に従って自動的に変換を行います.
アドレス空間はVMAによって定義され,まばらに配置されます.
1プロセスあたり1つのアドレス空間は,1プロセスあたり1つのページテーブルとなり,多くの「空白」(empty)領域があります.
ページテーブルは以下の動画がわかりやすいです.
mm_struct構造体:メモリディスクリプタ
Linuxカーネルのアドレス空間はメモリディスクリプタ「mm_struct構造体」で管理されます.
linux/include/linux/mm_types.hにmm_struct構造体の定義があります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
struct mm_struct { struct { struct vm_area_struct *mmap; /* list of VMAs */ struct rb_root mm_rb; u64 vmacache_seqnum; /* per-thread vmacache */ #ifdef CONFIG_MMU unsigned long (*get_unmapped_area) (struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags); #endif unsigned long mmap_base; /* base of mmap area */ unsigned long mmap_legacy_base; /* base of mmap area in bottom-up allocations */ #ifdef CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES /* Base addresses for compatible mmap() */ unsigned long mmap_compat_base; unsigned long mmap_compat_legacy_base; #endif unsigned long task_size; /* size of task vm space */ unsigned long highest_vm_end; /* highest vma end address */ pgd_t * pgd; #ifdef CONFIG_MEMBARRIER /** * @membarrier_state: Flags controlling membarrier behavior. * * This field is close to @pgd to hopefully fit in the same * cache-line, which needs to be touched by switch_mm(). */ atomic_t membarrier_state; #endif /** * @mm_users: The number of users including userspace. * * Use mmget()/mmget_not_zero()/mmput() to modify. When this * drops to 0 (i.e. when the task exits and there are no other * temporary reference holders), we also release a reference on * @mm_count (which may then free the &struct mm_struct if * @mm_count also drops to 0). */ atomic_t mm_users; /** * @mm_count: The number of references to &struct mm_struct * (@mm_users count as 1). * * Use mmgrab()/mmdrop() to modify. When this drops to 0, the * &struct mm_struct is freed. */ atomic_t mm_count; #ifdef CONFIG_MMU atomic_long_t pgtables_bytes; /* PTE page table pages */ #endif int map_count; /* number of VMAs */ spinlock_t page_table_lock; /* Protects page tables and some * counters */ /* * With some kernel config, the current mmap_lock's offset * inside 'mm_struct' is at 0x120, which is very optimal, as * its two hot fields 'count' and 'owner' sit in 2 different * cachelines, and when mmap_lock is highly contended, both * of the 2 fields will be accessed frequently, current layout * will help to reduce cache bouncing. * * So please be careful with adding new fields before * mmap_lock, which can easily push the 2 fields into one * cacheline. */ struct rw_semaphore mmap_lock; struct list_head mmlist; /* List of maybe swapped mm's. These * are globally strung together off * init_mm.mmlist, and are protected * by mmlist_lock */ unsigned long hiwater_rss; /* High-watermark of RSS usage */ unsigned long hiwater_vm; /* High-water virtual memory usage */ unsigned long total_vm; /* Total pages mapped */ unsigned long locked_vm; /* Pages that have PG_mlocked set */ atomic64_t pinned_vm; /* Refcount permanently increased */ unsigned long data_vm; /* VM_WRITE & ~VM_SHARED & ~VM_STACK */ unsigned long exec_vm; /* VM_EXEC & ~VM_WRITE & ~VM_STACK */ unsigned long stack_vm; /* VM_STACK */ unsigned long def_flags; /** * @write_protect_seq: Locked when any thread is write * protecting pages mapped by this mm to enforce a later COW, * for instance during page table copying for fork(). */ seqcount_t write_protect_seq; spinlock_t arg_lock; /* protect the below fields */ unsigned long start_code, end_code, start_data, end_data; unsigned long start_brk, brk, start_stack; unsigned long arg_start, arg_end, env_start, env_end; unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */ /* * Special counters, in some configurations protected by the * page_table_lock, in other configurations by being atomic. */ struct mm_rss_stat rss_stat; struct linux_binfmt *binfmt; /* Architecture-specific MM context */ mm_context_t context; unsigned long flags; /* Must use atomic bitops to access */ struct core_state *core_state; /* coredumping support */ #ifdef CONFIG_AIO spinlock_t ioctx_lock; struct kioctx_table __rcu *ioctx_table; #endif #ifdef CONFIG_MEMCG /* * "owner" points to a task that is regarded as the canonical * user/owner of this mm. All of the following must be true in * order for it to be changed: * * current == mm->owner * current->mm != mm * new_owner->mm == mm * new_owner->alloc_lock is held */ struct task_struct __rcu *owner; #endif struct user_namespace *user_ns; /* store ref to file /proc/<pid>/exe symlink points to */ struct file __rcu *exe_file; #ifdef CONFIG_MMU_NOTIFIER struct mmu_notifier_subscriptions *notifier_subscriptions; #endif #if defined(CONFIG_TRANSPARENT_HUGEPAGE) && !USE_SPLIT_PMD_PTLOCKS pgtable_t pmd_huge_pte; /* protected by page_table_lock */ #endif #ifdef CONFIG_NUMA_BALANCING /* * numa_next_scan is the next time that the PTEs will be marked * pte_numa. NUMA hinting faults will gather statistics and * migrate pages to new nodes if necessary. */ unsigned long numa_next_scan; /* Restart point for scanning and setting pte_numa */ unsigned long numa_scan_offset; /* numa_scan_seq prevents two threads setting pte_numa */ int numa_scan_seq; #endif /* * An operation with batched TLB flushing is going on. Anything * that can move process memory needs to flush the TLB when * moving a PROT_NONE or PROT_NUMA mapped page. */ atomic_t tlb_flush_pending; #ifdef CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH /* See flush_tlb_batched_pending() */ bool tlb_flush_batched; #endif struct uprobes_state uprobes_state; #ifdef CONFIG_HUGETLB_PAGE atomic_long_t hugetlb_usage; #endif struct work_struct async_put_work; #ifdef CONFIG_IOMMU_SUPPORT u32 pasid; #endif } __randomize_layout; /* * The mm_cpumask needs to be at the end of mm_struct, because it * is dynamically sized based on nr_cpu_ids. */ unsigned long cpu_bitmap[]; }; |
mm_usersはアドレス空間を利用しているプロセス(スレッド)の数です.
mm_countは参照カウントで,mm_users > 0 ならば+1,カーネルがアドレス空間を使用している場合は+1になります.
mm_countが0になると,mm_structを解放することができます.
mmapとmm_rbはそれぞれ,このアドレス空間内のすべてのVMAを含むリンクリストとツリーです.
すべてのVMAを昇順で反復処理するために使用されるリストです.
ツリーは特定のVMAを見つけるために使用されます.
mm_structの場合,mmlistメンバ変数を通じて,すべてのmm_structは双方向リストにリンクされています.
メモリディスクリプタの割り当てと解放
メモリディスクリプタの割り当てに関して,タスクメモリディスクリプタは,対応するtask_struct構造体のmmメンバ変数に配置されます.
task_struct構造体を知りたいあなたはこちらからどうぞ.
現在のタスクのメモリディスクリプタはcurrent->mmになります.
fork関数で呼び出すcopy_mm関数は親メモリディスクリプタのコピーを子タスク用に作成します.
copy_mm関数はdup_mm関数を呼び,allocate_mm関数を呼び出します.
Slab Cacheからmm構造体オブジェクトを割り当てます.
同じアドレス空間を共有する2つのスレッドは,そのtask_structのmmメンバ変数が同じmm_structオブジェクトを指します.
スレッドはclone関数に渡されたCLONE_VMフラグで生成され,allocate_mm関数は呼ばれません.
メモリディスクリプタの解放では,プロセスが終了するとき,do_exit関数が呼ばれ,exit_mm関数を呼び出します.
exit_mm関数では,いくつかのハウスキーピング/統計情報の更新を行い,mmput関数を呼び出します.
mm_struct構造体とカーネルスレッド
カーネルスレッドにはユーザ空間のアドレス空間がないため,カーネルスレッドのtask_structのmmフィールドがNULLになります.
しかし,カーネルスレッドはカーネルアドレス空間にアクセスする必要があります.
カーネルスレッドがスケジュールされたとき,カーネルはそのmmがNULLであることに気づくので,以前のアドレス空間をロードしたままにします(ページテーブル).
カーネルは,カーネルスレッドのactive_mmメンバ変数が借りたmm_struct構造体を指すようにします.
カーネルのアドレス空間はすべてのタスクで同じなので正常に動作します.
仮想メモリ領域(VMA:Virtual Memory Area)
仮想メモリ領域(VMA:Virtual Memory Area)は,プロセスがアクセスする権利を持つアドレスの区間です.
プロセスのアドレス空間に対して動的に追加または削除が可能です.
また,関連するパーミッションとして読み取り,書き込み,実行があります.
もし不正なアクセスをした場合,セグメンテーションフォールトが発生します.
VMAは以下を含むことができます.
- 実行ファイルのコードへのマッピング(テキストセクション)
- 実行ファイルの初期化変数へのマッピング(データセクション)
- 未初期化変数のゼロページ(中身がすべて0のページ)へのマッピング(BSSセクション)
- ユーザ空間スタックのゼロページへのマッピング
- 使用する各共有ライブラリのテキスト,データ,BSS
- メモリマップドファイル,共有メモリセグメント,匿名マッピング(malloc関数で使用)
/proc/1/mapsでinitプロセス(PIDが1)のVMAを確認できます.
各行が1つのVMAに対応します.
3列目のパーミッションは以下になります.
- r:read(読み込み)
- w:write(書き込み)
- x:execute(実行)
- s:shared(共有)(以下の例ではありません.)
- p:private(プライベート,排他)(CoW:Copy-on-Write)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
$ sudo cat /proc/1/maps 55dffd587000-55dffd5bd000 r--p 00000000 08:03 1364617 /usr/lib/systemd/systemd 55dffd5bd000-55dffd69d000 r-xp 00036000 08:03 1364617 /usr/lib/systemd/systemd 55dffd69d000-55dffd6fc000 r--p 00116000 08:03 1364617 /usr/lib/systemd/systemd 55dffd6fc000-55dffd74b000 r--p 00174000 08:03 1364617 /usr/lib/systemd/systemd 55dffd74b000-55dffd74c000 rw-p 001c3000 08:03 1364617 /usr/lib/systemd/systemd 55dffd74c000-55dffd74d000 rw-p 00000000 00:00 0 55dffe32c000-55dffe607000 rw-p 00000000 00:00 0 [heap] 7fe064000000-7fe064021000 rw-p 00000000 00:00 0 7fe064021000-7fe068000000 ---p 00000000 00:00 0 7fe06c000000-7fe06c021000 rw-p 00000000 00:00 0 7fe06c021000-7fe070000000 ---p 00000000 00:00 0 7fe0702db000-7fe0702dc000 ---p 00000000 00:00 0 7fe0702dc000-7fe070adc000 rw-p 00000000 00:00 0 7fe070adc000-7fe070add000 ---p 00000000 00:00 0 7fe070add000-7fe071304000 rw-p 00000000 00:00 0 7fe071304000-7fe071308000 r--p 00000000 08:03 1317348 /usr/lib/x86_64-linux-gnu/libgpg-error.so.0.32.1 7fe071308000-7fe07131e000 r-xp 00004000 08:03 1317348 /usr/lib/x86_64-linux-gnu/libgpg-error.so.0.32.1 7fe07131e000-7fe071328000 r--p 0001a000 08:03 1317348 /usr/lib/x86_64-linux-gnu/libgpg-error.so.0.32.1 7fe071328000-7fe071329000 r--p 00023000 08:03 1317348 /usr/lib/x86_64-linux-gnu/libgpg-error.so.0.32.1 7fe071329000-7fe07132a000 rw-p 00024000 08:03 1317348 /usr/lib/x86_64-linux-gnu/libgpg-error.so.0.32.1 7fe07132a000-7fe07132c000 r--p 00000000 08:03 1316975 /usr/lib/x86_64-linux-gnu/libcap-ng.so.0.0.0 7fe07132c000-7fe07132f000 r-xp 00002000 08:03 1316975 /usr/lib/x86_64-linux-gnu/libcap-ng.so.0.0.0 7fe07132f000-7fe071330000 r--p 00005000 08:03 1316975 /usr/lib/x86_64-linux-gnu/libcap-ng.so.0.0.0 7fe071330000-7fe071331000 r--p 00005000 08:03 1316975 /usr/lib/x86_64-linux-gnu/libcap-ng.so.0.0.0 7fe071331000-7fe071332000 rw-p 00006000 08:03 1316975 /usr/lib/x86_64-linux-gnu/libcap-ng.so.0.0.0 7fe071332000-7fe071334000 r--p 00000000 08:03 1329062 /usr/lib/x86_64-linux-gnu/libpcre2-8.so.0.10.4 7fe071334000-7fe07139f000 r-xp 00002000 08:03 1329062 /usr/lib/x86_64-linux-gnu/libpcre2-8.so.0.10.4 7fe07139f000-7fe0713c7000 r--p 0006d000 08:03 1329062 /usr/lib/x86_64-linux-gnu/libpcre2-8.so.0.10.4 7fe0713c7000-7fe0713c8000 r--p 00094000 08:03 1329062 /usr/lib/x86_64-linux-gnu/libpcre2-8.so.0.10.4 7fe0713c8000-7fe0713c9000 rw-p 00095000 08:03 1329062 /usr/lib/x86_64-linux-gnu/libpcre2-8.so.0.10.4 7fe0713c9000-7fe0713cc000 r--p 00000000 08:03 1317602 /usr/lib/x86_64-linux-gnu/liblzma.so.5.2.5 7fe0713cc000-7fe0713e7000 r-xp 00003000 08:03 1317602 /usr/lib/x86_64-linux-gnu/liblzma.so.5.2.5 7fe0713e7000-7fe0713f2000 r--p 0001e000 08:03 1317602 /usr/lib/x86_64-linux-gnu/liblzma.so.5.2.5 7fe0713f2000-7fe0713f3000 r--p 00028000 08:03 1317602 /usr/lib/x86_64-linux-gnu/liblzma.so.5.2.5 7fe0713f3000-7fe0713f4000 rw-p 00029000 08:03 1317602 /usr/lib/x86_64-linux-gnu/liblzma.so.5.2.5 7fe0713f4000-7fe0713fe000 r--p 00000000 08:03 1318315 /usr/lib/x86_64-linux-gnu/libzstd.so.1.4.8 7fe0713fe000-7fe0714b0000 r-xp 0000a000 08:03 1318315 /usr/lib/x86_64-linux-gnu/libzstd.so.1.4.8 7fe0714b0000-7fe0714c1000 r--p 000bc000 08:03 1318315 /usr/lib/x86_64-linux-gnu/libzstd.so.1.4.8 7fe0714c1000-7fe0714c2000 r--p 000cc000 08:03 1318315 /usr/lib/x86_64-linux-gnu/libzstd.so.1.4.8 7fe0714c2000-7fe0714c3000 rw-p 000cd000 08:03 1318315 /usr/lib/x86_64-linux-gnu/libzstd.so.1.4.8 7fe0714c3000-7fe0714c5000 rw-p 00000000 00:00 0 7fe0714c5000-7fe071577000 r--p 00000000 08:03 1313678 /usr/lib/x86_64-linux-gnu/libcrypto.so.3 7fe071577000-7fe0717d4000 r-xp 000b2000 08:03 1313678 /usr/lib/x86_64-linux-gnu/libcrypto.so.3 7fe0717d4000-7fe0718a6000 r--p 0030f000 08:03 1313678 /usr/lib/x86_64-linux-gnu/libcrypto.so.3 7fe0718a6000-7fe071901000 r--p 003e0000 08:03 1313678 /usr/lib/x86_64-linux-gnu/libcrypto.so.3 7fe071901000-7fe071904000 rw-p 0043b000 08:03 1313678 /usr/lib/x86_64-linux-gnu/libcrypto.so.3 7fe071904000-7fe071907000 rw-p 00000000 00:00 0 7fe071907000-7fe071909000 r--p 00000000 08:03 1317600 /usr/lib/x86_64-linux-gnu/liblz4.so.1.9.3 7fe071909000-7fe071922000 r-xp 00002000 08:03 1317600 /usr/lib/x86_64-linux-gnu/liblz4.so.1.9.3 7fe071922000-7fe071924000 r--p 0001b000 08:03 1317600 /usr/lib/x86_64-linux-gnu/liblz4.so.1.9.3 7fe071924000-7fe071925000 ---p 0001d000 08:03 1317600 /usr/lib/x86_64-linux-gnu/liblz4.so.1.9.3 7fe071925000-7fe071926000 r--p 0001d000 08:03 1317600 /usr/lib/x86_64-linux-gnu/liblz4.so.1.9.3 7fe071926000-7fe071927000 rw-p 0001e000 08:03 1317600 /usr/lib/x86_64-linux-gnu/liblz4.so.1.9.3 7fe071927000-7fe071929000 r--p 00000000 08:03 1317523 /usr/lib/x86_64-linux-gnu/libip4tc.so.2.0.0 7fe071929000-7fe07192d000 r-xp 00002000 08:03 1317523 /usr/lib/x86_64-linux-gnu/libip4tc.so.2.0.0 7fe07192d000-7fe07192f000 r--p 00006000 08:03 1317523 /usr/lib/x86_64-linux-gnu/libip4tc.so.2.0.0 7fe07192f000-7fe071930000 r--p 00007000 08:03 1317523 /usr/lib/x86_64-linux-gnu/libip4tc.so.2.0.0 7fe071930000-7fe071931000 rw-p 00008000 08:03 1317523 /usr/lib/x86_64-linux-gnu/libip4tc.so.2.0.0 7fe071931000-7fe071940000 r--p 00000000 08:03 1317266 /usr/lib/x86_64-linux-gnu/libgcrypt.so.20.3.4 7fe071940000-7fe071a26000 r-xp 0000f000 08:03 1317266 /usr/lib/x86_64-linux-gnu/libgcrypt.so.20.3.4 7fe071a26000-7fe071a64000 r--p 000f5000 08:03 1317266 /usr/lib/x86_64-linux-gnu/libgcrypt.so.20.3.4 7fe071a64000-7fe071a65000 ---p 00133000 08:03 1317266 /usr/lib/x86_64-linux-gnu/libgcrypt.so.20.3.4 7fe071a65000-7fe071a68000 r--p 00133000 08:03 1317266 /usr/lib/x86_64-linux-gnu/libgcrypt.so.20.3.4 7fe071a68000-7fe071a6e000 rw-p 00136000 08:03 1317266 /usr/lib/x86_64-linux-gnu/libgcrypt.so.20.3.4 7fe071a6e000-7fe071a6f000 rw-p 00000000 00:00 0 7fe071a6f000-7fe071a71000 r--p 00000000 08:03 1317034 /usr/lib/x86_64-linux-gnu/libcrypt.so.1.1.0 7fe071a71000-7fe071a85000 r-xp 00002000 08:03 1317034 /usr/lib/x86_64-linux-gnu/libcrypt.so.1.1.0 7fe071a85000-7fe071a9e000 r--p 00016000 08:03 1317034 /usr/lib/x86_64-linux-gnu/libcrypt.so.1.1.0 7fe071a9e000-7fe071a9f000 ---p 0002f000 08:03 1317034 /usr/lib/x86_64-linux-gnu/libcrypt.so.1.1.0 7fe071a9f000-7fe071aa0000 r--p 0002f000 08:03 1317034 /usr/lib/x86_64-linux-gnu/libcrypt.so.1.1.0 7fe071aa0000-7fe071aa1000 rw-p 00030000 08:03 1317034 /usr/lib/x86_64-linux-gnu/libcrypt.so.1.1.0 7fe071aa1000-7fe071aa9000 rw-p 00000000 00:00 0 7fe071aa9000-7fe071aac000 r--p 00000000 08:03 1316977 /usr/lib/x86_64-linux-gnu/libcap.so.2.44 7fe071aac000-7fe071ab0000 r-xp 00003000 08:03 1316977 /usr/lib/x86_64-linux-gnu/libcap.so.2.44 7fe071ab0000-7fe071ab2000 r--p 00007000 08:03 1316977 /usr/lib/x86_64-linux-gnu/libcap.so.2.44 7fe071ab2000-7fe071ab3000 r--p 00008000 08:03 1316977 /usr/lib/x86_64-linux-gnu/libcap.so.2.44 7fe071ab3000-7fe071ab4000 rw-p 00009000 08:03 1316977 /usr/lib/x86_64-linux-gnu/libcap.so.2.44 7fe071ab4000-7fe071ab6000 rw-p 00000000 00:00 0 7fe071ab6000-7fe071abd000 r--p 00000000 08:03 1316929 /usr/lib/x86_64-linux-gnu/libblkid.so.1.1.0 7fe071abd000-7fe071add000 r-xp 00007000 08:03 1316929 /usr/lib/x86_64-linux-gnu/libblkid.so.1.1.0 7fe071add000-7fe071ae6000 r--p 00027000 08:03 1316929 /usr/lib/x86_64-linux-gnu/libblkid.so.1.1.0 7fe071ae6000-7fe071ae7000 ---p 00030000 08:03 1316929 /usr/lib/x86_64-linux-gnu/libblkid.so.1.1.0 7fe071ae7000-7fe071aec000 r--p 00030000 08:03 1316929 /usr/lib/x86_64-linux-gnu/libblkid.so.1.1.0 7fe071aec000-7fe071aed000 rw-p 00035000 08:03 1316929 /usr/lib/x86_64-linux-gnu/libblkid.so.1.1.0 7fe071aed000-7fe071aef000 r--p 00000000 08:03 1316836 /usr/lib/x86_64-linux-gnu/libacl.so.1.1.2301 7fe071aef000-7fe071af3000 r-xp 00002000 08:03 1316836 /usr/lib/x86_64-linux-gnu/libacl.so.1.1.2301 7fe071af3000-7fe071af4000 r--p 00006000 08:03 1316836 /usr/lib/x86_64-linux-gnu/libacl.so.1.1.2301 7fe071af4000-7fe071af5000 ---p 00007000 08:03 1316836 /usr/lib/x86_64-linux-gnu/libacl.so.1.1.2301 7fe071af5000-7fe071af6000 r--p 00007000 08:03 1316836 /usr/lib/x86_64-linux-gnu/libacl.so.1.1.2301 7fe071af6000-7fe071af7000 rw-p 00008000 08:03 1316836 /usr/lib/x86_64-linux-gnu/libacl.so.1.1.2301 7fe071af7000-7fe071b1f000 r--p 00000000 08:03 1312865 /usr/lib/x86_64-linux-gnu/libc.so.6 7fe071b1f000-7fe071cb4000 r-xp 00028000 08:03 1312865 /usr/lib/x86_64-linux-gnu/libc.so.6 7fe071cb4000-7fe071d0c000 r--p 001bd000 08:03 1312865 /usr/lib/x86_64-linux-gnu/libc.so.6 7fe071d0c000-7fe071d10000 r--p 00214000 08:03 1312865 /usr/lib/x86_64-linux-gnu/libc.so.6 7fe071d10000-7fe071d12000 rw-p 00218000 08:03 1312865 /usr/lib/x86_64-linux-gnu/libc.so.6 7fe071d12000-7fe071d1f000 rw-p 00000000 00:00 0 7fe071d1f000-7fe071d22000 r--p 00000000 08:03 1314344 /usr/lib/x86_64-linux-gnu/libapparmor.so.1.8.2 7fe071d22000-7fe071d2b000 r-xp 00003000 08:03 1314344 /usr/lib/x86_64-linux-gnu/libapparmor.so.1.8.2 7fe071d2b000-7fe071d32000 r--p 0000c000 08:03 1314344 /usr/lib/x86_64-linux-gnu/libapparmor.so.1.8.2 7fe071d32000-7fe071d33000 r--p 00012000 08:03 1314344 /usr/lib/x86_64-linux-gnu/libapparmor.so.1.8.2 7fe071d33000-7fe071d34000 rw-p 00013000 08:03 1314344 /usr/lib/x86_64-linux-gnu/libapparmor.so.1.8.2 7fe071d34000-7fe071d38000 r--p 00000000 08:03 1317565 /usr/lib/x86_64-linux-gnu/libkmod.so.2.3.7 7fe071d38000-7fe071d49000 r-xp 00004000 08:03 1317565 /usr/lib/x86_64-linux-gnu/libkmod.so.2.3.7 7fe071d49000-7fe071d4e000 r--p 00015000 08:03 1317565 /usr/lib/x86_64-linux-gnu/libkmod.so.2.3.7 7fe071d4e000-7fe071d4f000 r--p 00019000 08:03 1317565 /usr/lib/x86_64-linux-gnu/libkmod.so.2.3.7 7fe071d4f000-7fe071d50000 rw-p 0001a000 08:03 1317565 /usr/lib/x86_64-linux-gnu/libkmod.so.2.3.7 7fe071d50000-7fe071d53000 r--p 00000000 08:03 1316880 /usr/lib/x86_64-linux-gnu/libaudit.so.1.0.0 7fe071d53000-7fe071d5b000 r-xp 00003000 08:03 1316880 /usr/lib/x86_64-linux-gnu/libaudit.so.1.0.0 7fe071d5b000-7fe071d70000 r--p 0000b000 08:03 1316880 /usr/lib/x86_64-linux-gnu/libaudit.so.1.0.0 7fe071d70000-7fe071d71000 r--p 0001f000 08:03 1316880 /usr/lib/x86_64-linux-gnu/libaudit.so.1.0.0 7fe071d71000-7fe071d72000 rw-p 00020000 08:03 1316880 /usr/lib/x86_64-linux-gnu/libaudit.so.1.0.0 7fe071d72000-7fe071d80000 rw-p 00000000 00:00 0 7fe071d80000-7fe071d83000 r--p 00000000 08:03 1317772 /usr/lib/x86_64-linux-gnu/libpam.so.0.85.1 7fe071d83000-7fe071d8c000 r-xp 00003000 08:03 1317772 /usr/lib/x86_64-linux-gnu/libpam.so.0.85.1 7fe071d8c000-7fe071d90000 r--p 0000c000 08:03 1317772 /usr/lib/x86_64-linux-gnu/libpam.so.0.85.1 7fe071d90000-7fe071d91000 r--p 0000f000 08:03 1317772 /usr/lib/x86_64-linux-gnu/libpam.so.0.85.1 7fe071d91000-7fe071d92000 rw-p 00010000 08:03 1317772 /usr/lib/x86_64-linux-gnu/libpam.so.0.85.1 7fe071d92000-7fe071d9a000 r--p 00000000 08:03 1317634 /usr/lib/x86_64-linux-gnu/libmount.so.1.1.0 7fe071d9a000-7fe071dc6000 r-xp 00008000 08:03 1317634 /usr/lib/x86_64-linux-gnu/libmount.so.1.1.0 7fe071dc6000-7fe071dd3000 r--p 00034000 08:03 1317634 /usr/lib/x86_64-linux-gnu/libmount.so.1.1.0 7fe071dd3000-7fe071dd4000 ---p 00041000 08:03 1317634 /usr/lib/x86_64-linux-gnu/libmount.so.1.1.0 7fe071dd4000-7fe071dd5000 r--p 00041000 08:03 1317634 /usr/lib/x86_64-linux-gnu/libmount.so.1.1.0 7fe071dd5000-7fe071dd6000 rw-p 00042000 08:03 1317634 /usr/lib/x86_64-linux-gnu/libmount.so.1.1.0 7fe071dd6000-7fe071ddc000 r--p 00000000 08:03 1317965 /usr/lib/x86_64-linux-gnu/libselinux.so.1 7fe071ddc000-7fe071df6000 r-xp 00006000 08:03 1317965 /usr/lib/x86_64-linux-gnu/libselinux.so.1 7fe071df6000-7fe071dfd000 r--p 00020000 08:03 1317965 /usr/lib/x86_64-linux-gnu/libselinux.so.1 7fe071dfd000-7fe071dfe000 ---p 00027000 08:03 1317965 /usr/lib/x86_64-linux-gnu/libselinux.so.1 7fe071dfe000-7fe071dff000 r--p 00027000 08:03 1317965 /usr/lib/x86_64-linux-gnu/libselinux.so.1 7fe071dff000-7fe071e00000 rw-p 00028000 08:03 1317965 /usr/lib/x86_64-linux-gnu/libselinux.so.1 7fe071e00000-7fe071e02000 rw-p 00000000 00:00 0 7fe071e02000-7fe071e04000 r--p 00000000 08:03 1317962 /usr/lib/x86_64-linux-gnu/libseccomp.so.2.5.3 7fe071e04000-7fe071e12000 r-xp 00002000 08:03 1317962 /usr/lib/x86_64-linux-gnu/libseccomp.so.2.5.3 7fe071e12000-7fe071e1f000 r--p 00010000 08:03 1317962 /usr/lib/x86_64-linux-gnu/libseccomp.so.2.5.3 7fe071e1f000-7fe071e20000 ---p 0001d000 08:03 1317962 /usr/lib/x86_64-linux-gnu/libseccomp.so.2.5.3 7fe071e20000-7fe071e21000 r--p 0001d000 08:03 1317962 /usr/lib/x86_64-linux-gnu/libseccomp.so.2.5.3 7fe071e21000-7fe071e22000 rw-p 0001e000 08:03 1317962 /usr/lib/x86_64-linux-gnu/libseccomp.so.2.5.3 7fe071e37000-7fe071e88000 r--p 00000000 08:03 1364615 /usr/lib/systemd/libsystemd-shared-249.so 7fe071e88000-7fe072031000 r-xp 00051000 08:03 1364615 /usr/lib/systemd/libsystemd-shared-249.so 7fe072031000-7fe0720d9000 r--p 001fa000 08:03 1364615 /usr/lib/systemd/libsystemd-shared-249.so 7fe0720d9000-7fe0720ec000 r--p 002a1000 08:03 1364615 /usr/lib/systemd/libsystemd-shared-249.so 7fe0720ec000-7fe0720ed000 rw-p 002b4000 08:03 1364615 /usr/lib/systemd/libsystemd-shared-249.so 7fe0720ed000-7fe0720f1000 rw-p 00000000 00:00 0 7fe0720f1000-7fe0720f3000 r--p 00000000 08:03 1312857 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 7fe0720f3000-7fe07211d000 r-xp 00002000 08:03 1312857 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 7fe07211d000-7fe072128000 r--p 0002c000 08:03 1312857 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 7fe072129000-7fe07212b000 r--p 00037000 08:03 1312857 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 7fe07212b000-7fe07212d000 rw-p 00039000 08:03 1312857 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 7ffc1713f000-7ffc17160000 rw-p 00000000 00:00 0 [stack] 7ffc17160000-7ffc17164000 r--p 00000000 00:00 0 [vvar] 7ffc17164000-7ffc17166000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall] |
linux/include/linux/mm_types.hで定義されているvm_area_struct構造体でVMAを管理します.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
/* * This struct describes a virtual memory area. There is one of these * per VM-area/task. A VM area is any part of the process virtual memory * space that has a special rule for the page-fault handlers (ie a shared * library, the executable area etc). */ struct vm_area_struct { /* The first cache line has the info for VMA tree walking. */ unsigned long vm_start; /* Our start address within vm_mm. */ unsigned long vm_end; /* The first byte after our end address within vm_mm. */ /* linked list of VM areas per task, sorted by address */ struct vm_area_struct *vm_next, *vm_prev; struct rb_node vm_rb; /* * Largest free memory gap in bytes to the left of this VMA. * Either between this VMA and vma->vm_prev, or between one of the * VMAs below us in the VMA rbtree and its ->vm_prev. This helps * get_unmapped_area find a free area of the right size. */ unsigned long rb_subtree_gap; /* Second cache line starts here. */ struct mm_struct *vm_mm; /* The address space we belong to. */ /* * Access permissions of this VMA. * See vmf_insert_mixed_prot() for discussion. */ pgprot_t vm_page_prot; unsigned long vm_flags; /* Flags, see mm.h. */ /* * For areas with an address space and backing store, * linkage into the address_space->i_mmap interval tree. */ struct { struct rb_node rb; unsigned long rb_subtree_last; } shared; /* * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma * list, after a COW of one of the file pages. A MAP_SHARED vma * can only be in the i_mmap tree. An anonymous MAP_PRIVATE, stack * or brk vma (with NULL file) can only be in an anon_vma list. */ struct list_head anon_vma_chain; /* Serialized by mmap_lock & * page_table_lock */ struct anon_vma *anon_vma; /* Serialized by page_table_lock */ /* Function pointers to deal with this struct. */ const struct vm_operations_struct *vm_ops; /* Information about our backing store: */ unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE units */ struct file * vm_file; /* File we map to (can be NULL). */ void * vm_private_data; /* was vm_pte (shared mem) */ #ifdef CONFIG_SWAP atomic_long_t swap_readahead_info; #endif #ifndef CONFIG_MMU struct vm_region *vm_region; /* NOMMU mapping region */ #endif #ifdef CONFIG_NUMA struct mempolicy *vm_policy; /* NUMA policy for the VMA */ #endif struct vm_userfaultfd_ctx vm_userfaultfd_ctx; } __randomize_layout; |
VMAは対応するアドレス空間の[vm start, vm end)で存在します.
つまり,VMAのバイトサイズはvm_end - vm_startになります.
アドレス空間はmm_struct構造体のvm_mmメンバ変数で指定されます.
各VMAは関連するmm_struct構造体に対してユニークです.
同じファイルをマッピングする2つのプロセスは,2つの異なるmm_structオブジェクトと,2つの異なるvm_area_structオブジェクトを持ちます.
mm_structオブジェクトを共有する2つのスレッドは,vm_area_structオブジェクトも共有します.
主なVMAフラグは下表になります.
VMAフラグ | VMAとそのページへの影響 |
---|---|
VM_READ | ページを読み込み可能 |
VM_WRITE | ページを書き込み可能 |
VM_EXEC | ページを実行可能 |
VM_SEQ_READ | ページへのアクセスをシーケンシャルに実行 |
VM_RAND_READ | ページへのアクセスをランダムに実行 |
VM_HUGETLB | hugetlbのページ(hugepage)を利用 |
VM_READ,VM_WRITE,VM_EXECを組み合わせると,全領域の権限が与えられます.
例えば,オブジェクトコードはVM_READとVM_EXEC,スタックはVM_READとVM_WRITEになります.
VM_SEQ_READとVM_RAND_READはmadviseシステムコールで設定されます.
madviseシステムコールは,ファイルプリフェッチアルゴリズム「readahead」にプリフェッチウィンドウを増減するよう指示します.
VM_HUGETLBは,その領域が通常のサイズより大きなhugetlbのページ(hugepage)を利用することを示します.
通常のページと比較して,hugepageを利用することは,TLBミスが少なくメモリアクセスが速いことを意味します.
x86-64のhugepageは2Mと1Gです.
ARM64のhugepageは以下の2つの要素を組み合わせて実現します.
- pud/pmdレベルでのブロックマッピング:pud/pmdページテーブルエントリがメモリブロックを指す通常のhugepageです.TLBでサポートされるエントリのサイズに関係なく,ブロックマッピングは,hugepageアドレスを変換するために必要なページテーブルウォークの深さを減少させます.
- contiguous bit(連続ビット)の利用:1つのTLBエントリにキャッシュできる連続したエントリのセットの1つであることをMMUに示唆する変換テーブルエントリ内のcontiguous bitを提供します.
Linuxカーネルでは,contiguous bitのpmdおよびpte(最終)レベルでのマッピングサイズを大きくするために使用されます.
サポートされる連続エントリの数は,ページサイズとページテーブルのレベルによって異なります.
まとめるとARM64のhugepageは下表になります.
通常のページサイズ | contiguous bitのpteを利用するhugepage | pmdを利用するhugepage | contiguous bitのpmdを利用するhugepage | pudを利用するhugepage |
---|---|---|---|---|
4K | 64K | 2M | 32M | 1G |
16K | 2M | 32M | 1G | - |
64K | 2M | 512M | 16G | - |
VMAの操作
vm_area_struct構造体のvm_opsメンバ変数は,特定のVMAを操作するための関数ポインタのvm_operations_struct構造体です.
vm_operations_struct構造体は,linux/include/linux/mm.hで定義されています.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
/* * These are the virtual MM functions - opening of an area, closing and * unmapping it (needed to keep files on disk up-to-date etc), pointer * to the functions called when a no-page or a wp-page exception occurs. */ struct vm_operations_struct { void (*open)(struct vm_area_struct * area); void (*close)(struct vm_area_struct * area); /* Called any time before splitting to check if it's allowed */ int (*may_split)(struct vm_area_struct *area, unsigned long addr); int (*mremap)(struct vm_area_struct *area); /* * Called by mprotect() to make driver-specific permission * checks before mprotect() is finalised. The VMA must not * be modified. Returns 0 if eprotect() can proceed. */ int (*mprotect)(struct vm_area_struct *vma, unsigned long start, unsigned long end, unsigned long newflags); vm_fault_t (*fault)(struct vm_fault *vmf); vm_fault_t (*huge_fault)(struct vm_fault *vmf, enum page_entry_size pe_size); vm_fault_t (*map_pages)(struct vm_fault *vmf, pgoff_t start_pgoff, pgoff_t end_pgoff); unsigned long (*pagesize)(struct vm_area_struct * area); /* notification that a previously read-only page is about to become * writable, if an error is returned it will cause a SIGBUS */ vm_fault_t (*page_mkwrite)(struct vm_fault *vmf); /* same as page_mkwrite when using VM_PFNMAP|VM_MIXEDMAP */ vm_fault_t (*pfn_mkwrite)(struct vm_fault *vmf); /* called by access_process_vm when get_user_pages() fails, typically * for use by special VMAs. See also generic_access_phys() for a generic * implementation useful for any iomem mapping. */ int (*access)(struct vm_area_struct *vma, unsigned long addr, void *buf, int len, int write); /* Called by the /proc/PID/maps code to ask the vma whether it * has a special name. Returning non-NULL will also cause this * vma to be dumped unconditionally. */ const char *(*name)(struct vm_area_struct *vma); #ifdef CONFIG_NUMA /* * set_policy() op must add a reference to any non-NULL @new mempolicy * to hold the policy upon return. Caller should pass NULL @new to * remove a policy and fall back to surrounding context--i.e. do not * install a MPOL_DEFAULT policy, nor the task or system default * mempolicy. */ int (*set_policy)(struct vm_area_struct *vma, struct mempolicy *new); /* * get_policy() op must add reference [mpol_get()] to any policy at * (vma,addr) marked as MPOL_SHARED. The shared policy infrastructure * in mm/mempolicy.c will do this automatically. * get_policy() must NOT add a ref if the policy at (vma,addr) is not * marked as MPOL_SHARED. vma policies are protected by the mmap_lock. * If no [shared/vma] mempolicy exists at the addr, get_policy() op * must return NULL--i.e., do not "fallback" to task or system default * policy. */ struct mempolicy *(*get_policy)(struct vm_area_struct *vma, unsigned long addr); #endif /* * Called by vm_normal_page() for special PTEs to find the * page for @addr. This is useful if the default behavior * (using pte_page()) would not find the correct page. */ struct page *(*find_special_page)(struct vm_area_struct *vma, unsigned long addr); }; |
VMAの操作関数は以下になります.
- find_vma関数:addr < vm_endを満たす最初のVMAを返します.
- find_vma_prev関数:find_vma関数と同じですが,直前のVMAへのポインタを*pprev(第3引数**pprev)に設定する.
- find_vma_intersection関数:[start_addr, end_addr)の最初のVMAを返す.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
/* Look up the first VMA which satisfies addr < vm_end, NULL if none. */ extern struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr); extern struct vm_area_struct * find_vma_prev(struct mm_struct * mm, unsigned long addr, struct vm_area_struct **pprev); /** * find_vma_intersection() - Look up the first VMA which intersects the interval * @mm: The process address space. * @start_addr: The inclusive start user address. * @end_addr: The exclusive end user address. * * Returns: The first VMA within the provided range, %NULL otherwise. Assumes * start_addr < end_addr. */ static inline struct vm_area_struct *find_vma_intersection(struct mm_struct *mm, unsigned long start_addr, unsigned long end_addr) { struct vm_area_struct *vma = find_vma(mm, start_addr); if (vma && end_addr <= vma->vm_start) vma = NULL; return vma; } |
アドレスの間隔を操作する関数は以下になります.
- do_mmap関数:新しいメモリアドレス範囲を作成するために使用されます.新しいVMAが作成される可能性があります.同じパーミッションの場合,作成した領域と隣接する領域をマージします.また,mmap2関数によりユーザ空間にエクスポートします.
- do_munmap関数:メモリアドレス範囲を削除します.また,munmap関数によりユーザ空間にエクスポートします.
1 2 3 4 5 |
extern unsigned long do_mmap(struct file *file, unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long pgoff, unsigned long *populate, struct list_head *uf); extern int do_munmap(struct mm_struct *, unsigned long, size_t, struct list_head *uf); |
まとめ
今回はプロセスアドレス空間を紹介しました.
具体的には,以下の内容を解説しました.
- アドレス空間,メモリアドレス,仮想メモリ,ページテーブル
- mm_struct構造体:メモリディスクリプタ
- 仮想メモリ領域(VMA:Virtual Memory Area)
- VMAの操作
プロセスアドレス空間を深掘りしたいあなたは,以下の記事を読みましょう!
- Memory Translation and Segmentation
- Complete virtual memory map with 4-level page tables
- Kernel page-table isolation
- Addressing Meltdown and Spectre in the kernel
- Meltdown and Spectre
- AutoNUMA
- Speeding Up The Linux Kernel With Transparent Hugepage Support
- Five-level page tables
- Heterogeneous Memory Management Made It For Linux 4.14
- Kernel same-page merging
- Memory management notifiers
- Scalable Address Spaces Using RCU Balanced Trees,Slides
以下の動画もおすすめです!
LinuxカーネルはC言語で書かれています.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!
次回はこちらからどうぞ.