I.はじめに
この記事の冒頭から、研究の記憶部分に入ります。最初に、前のタスクに従ってtask_struct
タスクスペース管理構造を説明し、mm_struct
物理メモリと仮想メモリの関連知識を簡単に紹介します。詳細な基本的な知識と概念については、CSAPPブックを参照してください。ここではあまり詳しく説明しませんが、デフォルトそのマッピング関係の理解に基づく学習。次の記事では、物理メモリの管理と、ユーザーモードとカーネルモードのメモリマッピングについて引き続き説明します。
2.コーミングの基本概念
- CPU、キャッシュ、メモリ、メインメモリのアーキテクチャは、デバイスが高速であるほど高価になるため、経済性(qiong)のために多層アーキテクチャが設計されており、CPUにMMUがある
- 物理メモリが制限されており、マルチプロセス共有物理メモリにセキュリティの問題があるため、仮想メモリの設計が表示されます
- 仮想メモリはELFの構造に従って設計されており、ヒープ、マッピング領域、スタック、データセグメントなどの部分があります。
- 仮想メモリの構造を考慮して、動的メモリであるヒープアプリケーションがあります。
- 仮想メモリは、プロセスごとに個別のアドレス空間を割り当て、それを実行のために物理メモリにマップするため、物理メモリと仮想メモリのマッピング方法があります。
- 仮想メモリを管理するために、ページテーブルとマルチレベルのページテーブルが表示されます
- マッピングを高速化するために、CPUにTLBが登場
- 共有のニーズに応えるため、メモリマップに共有メモリが登場
- メモリの断片化が存在するため、フラグメント管理デザインとガベージコレクターが登場
3.プロセスメモリ管理
プロセスの場合、ユーザーモードおよびカーネルモードでカーネルに格納する必要があるさまざまな構造を考慮する必要があります。
ユーザーモードには
- コードスニペット
- グローバル変数
- 定数文字列
- 関数呼び出し、ローカル変数、関数パラメーターなどを含む関数スタック
- ヒープ:mallocなどによって割り当てられたメモリ
glibc
呼び出しglibc
コードなどのメモリマッピングは、ファイルもメモリに配置する必要がある形式です。
カーネルモードには
- コードのカーネル部分
- カーネルのグローバル変数
- task_struct
- カーネルスタック
- カーネルには動的に割り当てられたメモリもあります
- 仮想アドレスから物理アドレスへのマッピングテーブル
プロセスはカーネルモードtask_struct
で管理され、task_struct
メモリには次のメンバー変数があります
struct mm_struct *mm;
struct mm_struct *active_mm;
/* Per-thread vma caching: */
struct vmacache vmacache;
mm_struct
構造も複雑になり、私たちは、ステップバイステップで紹介します。まず、カーネルモードとユーザーモードの間のアドレス分割について見てみましょう。highest_vm_end
現在の仮想メモリアドレスの最大アドレスはここに格納されますtask_size
が、ユーザーモードのサイズです。
struct mm_struct {
......
unsigned long task_size; /* size of task vm space */
unsigned long highest_vm_end; /* highest vma end address */
......
}
task_size
定義は次のとおりです。コメントから、ユーザーモードは4G仮想メモリに3Gスペースを割り当て、64ビットのスペースが大きいため、カーネルモードとユーザーモードの間に空き領域が確保され、分離されます。ユーザーモードは47ビット(128TB)のみを使用します。カーネルモードでは、最上位ビットである128TBも割り当てられます。
#ifdef CONFIG_X86_32
/*
* User space process size: 3GB (default).
*/
#define TASK_SIZE PAGE_OFFSET
#define TASK_SIZE_MAX TASK_SIZE
/*
config PAGE_OFFSET
hex
default 0xC0000000
depends on X86_32
*/
#else
/*
* User space process size. 47bits minus one guard page.
*/
#define TASK_SIZE_MAX ((1UL << 47) - PAGE_SIZE)
#define TASK_SIZE (test_thread_flag(TIF_ADDR32) ? \
IA32_PAGE_OFFSET : TASK_SIZE_MAX)
......
3.1ユーザーモードのメモリ構造
ユーザーモードmm_struct
には、次のメンバー変数があります。
mmap_base
:メモリマップの開始アドレスmmap_legacy_base
:マッピングのベースアドレスを示します。これは32ビットTASK_UNMAPPED_BASE
に固定されており、64ビットには仮想アドレスのランダムマッピングメカニズムがあるため、TASK_UNMAPPED_BASE + mmap_rnd()
hiwater_rss
:RSSの大量使用hiwater_vm
:大量の仮想メモリの使用total_vm
:マップされたページの総数locked_vm
:ロックされていてスワップアウトできないページの数pinned_vm
:スワップアウトまたは移動できないページの数data_vm
:データを格納しているページ数exec_vm
:実行可能ファイルを保存しているページの数stack_vm
:スタックを格納するページの数arg_lock
:spin_lock
次のエリア変数への並列アクセスを保護するために導入されましたstart_code 和 end_code
:実行可能コードの開始位置と終了位置start_data 和 end_data
:初期化データの開始位置と終了位置start_brk
:ヒープの開始位置brk
:ヒープの現在の終了位置start_stack
:スタックの開始位置、スタックの終了位置はレジスターのトップポインターにありますarg_start 和 arg_end
:スタックの最上位アドレスにあるパラメーターリストの位置。env_start 和 env_end
:スタックの最上位アドレスにある環境変数の場所。
struct mm_struct {
......
unsigned long mmap_base; /* base of mmap area */
unsigned long mmap_legacy_base; /* base of mmap area in bottom-up allocations */
......
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 */
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 */
......
}
これらのメンバー変数によれば、ユーザー状態での各パーツの位置を計画できますが、これらの領域の属性を記述するための構造も必要です。vm_area_struct
struct mm_struct {
......
struct vm_area_struct *mmap; /* list of VMAs */
struct rb_root mm_rb;
......
}
vm_area_struct
次のように特定の構造の定義である。これは、実際に合わせ二重連結リストによってvm_next
、およびvm_prev
、コンテンツのシリーズである、vm_area_struct
ユーザ・モードで割り当てられた各領域のプロセスを表現します。
vm_start
そしてvm_end
、ブロック領域の始まりと終わりを表現しますvm_rb
赤黒木に対応するこの赤黒木はvm_area_struct
、簡単に追加、削除、検索できるようにすべてを組み合わせています。rb_subtree_gap
現在のエリアと前のエリアの間隔を保存して、以降の割り当てを行います。vm_mm
構造が属する構造を指すvm_struct
vm_page_prot
このページのアクセス権限をvm_flags
マークビットとして管理しますrb
そしてrb_subtree_last
:自由な位置を持つ区間ツリー構造ano_vma 和 ano_vma_chain
:匿名マッピング。仮想メモリ領域は、物理メモリまたはファイルにマップできます。物理メモリにマップされる場合は、匿名マッピングと呼ばvm_file
れますvm_pgoff
。ファイルにマップする場合は、マップされたファイルを指定してオフセットを保存する必要があります。vm_opts
:構造を処理するために使用される、構造への関数ポインタvm_private_data
:プライベートデータストレージ
/*
* This struct defines a memory VMM 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. */
pgprot_t vm_page_prot; /* Access permissions of this VMA. */
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_sem & 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) */
atomic_long_t swap_readahead_info;
#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;
の場合mm_struct
、その多くのvm_area_struct
意志ELFファイルがロードされますload_elf_binary()
。つまり、構築されます。関数がELFファイル形式を解析した後、メモリマップを確立します。
- 呼び出し
setup_new_exec
、メモリマップ領域を設定mmap_base
- 呼び出し
setup_arg_pages
、スタックのセットアップ、スタックの一番下のポイントvm_area_struct
が提供され、スタックの一番下ですmm->arg_start
current->mm->start_stack
elf_map
ELFファイルのコードの一部をメモリにマップしますset_brk
ヒープvm_area_struct
が設定されcurrent->mm->start_brk = current->mm->brk
、内部が設定されました。つまり、ヒープはまだ空ですload_elf_interp
so
メモリのメモリマップ領域に依存する依存関係
static int load_elf_binary(struct linux_binprm *bprm)
{
......
setup_new_exec(bprm);
......
/* Do this so that we can load the interpreter, if need be. We will
change some of these later */
retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP),
executable_stack);
......
error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt,
elf_prot, elf_flags, total_size);
......
/* Calling set_brk effectively mmaps the pages that we need
* for the bss and break sections. We must do this before
* mapping in the interpreter, to make sure it doesn't wind
* up getting placed where the bss needs to go.
*/
retval = set_brk(elf_bss, elf_brk, bss_prot);
......
elf_entry = load_elf_interp(&loc->interp_elf_ex,
interpreter,
&interp_map_addr,
load_bias, interp_elf_phdata);
......
current->mm->end_code = end_code;
current->mm->start_code = start_code;
current->mm->start_data = start_data;
current->mm->end_data = end_data;
current->mm->start_stack = bprm->p;
......
}
3.2カーネル状態構造
32ビットシステムと64ビットシステムの間には大きなギャップがあるため、構造にはいくつかの違いがあります。ここでは、2つの構造について個別に説明します。
3.2.1 32ビットカーネルモードの構造
カーネルモードの仮想空間はプロセスに依存しません。つまり、すべてのプロセスがシステムコールを介してカーネルに入った後は、仮想アドレス空間は同じになります。次の図は、32ビットのカーネル状態の仮想空間分布図を示しています。
-
直接マッピング領域
最初の896Mは直接マッピング領域であり、物理メモリとの直接マッピングに使用されます。仮想メモリアドレスから3Gを引いて、対応する物理メモリの場所を取得します。カーネルには、2つのマクロがあります。
-
__pa(vaddr)
vaddr
関連付けられている仮想アドレスの物理アドレスを返します。 -
__va(paddr)
paddr
仮想アドレスに対応する物理アドレスを計算します。
仮想アドレスのこの部分へのアクセスもページング方式で実行されますが、ページテーブルアドレスは比較的単純で、1対1の対応で十分です。
システムの起動時、物理メモリの最初の1Mはすでに占有されており、カーネルコードセグメントは1Mからロードされ、カーネルグローバル変数、BSSなどもELFでカバーされます。このようにして、カーネルコードセグメント、グローバル変数、およびBSSも3G後に仮想アドレス空間にマップされます。特定の物理メモリレイアウトを表示できます。特定の物理メモリレイアウトは/proc/iomem
、各人のシステムと構成によって異なります。
- high_memory
ハイエンドメモリの名前は、物理アドレス空間をx86アーキテクチャの3つの部分、ZONE_DMA、ZONE_NORMAL、およびZONE_HIGHMEMに分割したことから来ています。ZONE_HIGHMEMはハイメモリです。
ハイエンドメモリは、メモリ管理モジュールが物理メモリを調べる名前であり、896Mダイレクトマッピング領域の上の領域を指します。**カーネルのメモリ管理モジュールを除いて、残りは仮想アドレスで動作します。**メモリ管理モジュールは物理アドレスを直接操作し、仮想アドレスの割り当てとマッピングを実行します。その存在の意味は、32ビットシステムの限られたカーネルスペースから無限の物理メモリスペースにアクセスすることです。この論理アドレススペースを借りて、アクセスする物理メモリへのマッピングを作成し(つまり、カーネルページテーブルを埋めます)、一時的に使用します。 、使用後は返却してください。
- カーネルダイナミックマッピングスペース(不連続なメモリ割り当て)
内VMALLOC_START
及びVMALLOC_END
空間の領域との間のカーネルのダイナミックマッピングを呼び出して、ユーザモードに対応する処理malloc
カーネル・モードのようにアプリケーションのメモリができvmalloc
適用します。カーネルモードには、ユーザーモードとは別のページテーブル管理があります。
- 永続的なカーネルマッピング領域(永続的なカーネルマッピング)
PKMAP_BASE
FIXADDR_START
カーネル空間へのアクセスは、4G-8Mから4G-4Mのアドレス範囲間の永続的マッピングと呼ばれます。alloc_pages()
アッパーメモリの結果のstruct page
構造体の物理メモリがkmap
この領域にマップされるときに関数を使用することができます。許可される永続的なマッピングの数は限られているため、ハイエンドメモリが不要になった場合は、マッピングをマッピング解除する必要がありkunmap()
ます。これは、関数を使用して行うことができます。
- 固定マッピングエリア
FIXADDR_START
FIXADDR_TOP(0xFFFF F000)
固定マッピング領域と呼ばれるスペースは、主に特別なニーズを満たすために使用されます。
- 一時的なカーネルマッピング
一時的なカーネルマッピングが渡されkmap_atomic
てkunmap_atomic
実装され、主にファイルの書き込みなど、物理メモリまたはメインメモリへの書き込みが必要な場合の操作に使用されます。
3.2.2 64ビットカーネルモードの構造
64ビットカーネルモードはスペースが大きいため、32ビットモードのように慎重に計画する必要はなく、多くの空き領域が保護のために直接分割されます。構造を次の図に示します。
- 0xffff800000000000から始まるのはカーネルの一部ですが、最初は8Tのギャップ領域があります。
__PAGE_OFFSET_BASE(0xffff880000000000)
64Tを開始仮想アドレス空間減算される領域の直接マッピングされPAGE_OFFSET
た物理アドレスです。ほとんどの場合、仮想アドレスと物理アドレス間のマッピングは、ページテーブルを確立することによって引き続きマッピングされます。VMALLOC_START(0xffffc90000000000)
先頭にVMALLOC_END(0xffffe90000000000)
32Tのスペース与えることですvmalloc
。VMEMMAP_START(0xffffea0000000000)
構造struct page
の最初の物理ページ記述を格納するための1Tスペースから。__START_KERNEL_map(0xffffffff80000000)
512Mグローバル変数を格納するために使用されるカーネル・コード・セグメント、およびので、BSSの始まり。これは、物理メモリの開始位置に対応し、__START_KERNEL_map
物理メモリアドレスを取得できます。これは直接マッピング領域と少し似ていますが、直接マッピング領域の前に8Tの空の領域があり、カーネルコードが物理メモリに読み込まれる場所を長い間通過しているため、矛盾はありません。
総括する
この記事では、メモリ内のユーザーモードとカーネルモードの構造をより詳細に分析し、これに基づいて、後でメモリの管理とマッピングの分析を開始できます。
コード情報
[1] linux / include / linux / mm_types.h
参照
[1]ウィキ
[3] woboq
[4] Linuxインサイド
[5] Linuxカーネルに対する深い理解
[6] Linuxカーネル設計の芸術
[7] Geek TimeがLinuxオペレーティングシステムについて語る