Linuxオペレーティングシステムの研究ノート(8)タスクスペース管理

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_rssRSSの大量使用
  • hiwater_vm:大量の仮想メモリの使用
  • total_vm:マップされたページの総数
  • locked_vm:ロックされていてスワップアウトできないページの数
  • pinned_vm:スワップアウトまたは移動できないページの数
  • data_vm:データを格納しているページ数
  • exec_vm:実行可能ファイルを保存しているページの数
  • stack_vm:スタックを格納するページの数
  • arg_lockspin_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_startcurrent->mm->start_stack
  • elf_map ELFファイルのコードの一部をメモリにマップします
  • set_brkヒープvm_area_structが設定されcurrent->mm->start_brk = current->mm->brk内部が設定されました。つまり、ヒープはまだ空です
  • load_elf_interpsoメモリのメモリマップ領域に依存する依存関係
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ビットのカーネル状態の仮想空間分布図を示しています。

img
  1. 直接マッピング領域

    最初の896Mは直接マッピング領域であり、物理メモリとの直接マッピングに使用されます。仮想メモリアドレスから3Gを引いて、対応する物理メモリの場所を取得します。カーネルには、2つのマクロがあります。

  • __pa(vaddr)vaddr関連付けられている仮想アドレスの物理アドレスを返します

  • __va(paddr)paddr仮想アドレスに対応する物理アドレスを計算します。

  仮想アドレスのこの部分へのアクセスもページング方式で実行されますが、ページテーブルアドレスは比較的単純で、1対1の対応で十分です。

  システムの起動時、物理メモリの最初の1Mはすでに占有されており、カーネルコードセグメントは1Mからロードされ、カーネルグローバル変数、BSSなどもELFでカバーされます。このようにして、カーネルコードセグメント、グローバル変数、およびBSSも3G後に仮想アドレス空間にマップされます。特定の物理メモリレイアウトを表示できます。特定の物理メモリレイアウトは/proc/iomem、各人のシステムと構成によって異なります。

  1. high_memory

  ハイエンドメモリの名前は、物理アドレス空間をx86アーキテクチャの3つの部分、ZONE_DMA、ZONE_NORMAL、およびZONE_HIGHMEMに分割したことから来ています。ZONE_HIGHMEMはハイメモリです。

  ハイエンドメモリは、メモリ管理モジュールが物理メモリを調べる名前であり、896Mダイレクトマッピング領域の上の領域を指します。**カーネルのメモリ管理モジュールを除いて、残りは仮想アドレスで動作します。**メモリ管理モジュールは物理アドレスを直接操作し、仮想アドレスの割り当てとマッピングを実行します。その存在の意味は、32ビットシステムの限られたカーネルスペースから無限の物理メモリスペースにアクセスすることです。この論理アドレススペースを借りて、アクセスする物理メモリへのマッピングを作成し(つまり、カーネルページテーブルを埋めます)、一時的に使用します。 、使用後は返却してください。

  1. カーネルダイナミックマッピングスペース(不連続なメモリ割り当て)

  内VMALLOC_START及びVMALLOC_END空間の領域との間のカーネルのダイナミックマッピングを呼び出して、ユーザモードに対応する処理mallocカーネル・モードのようにアプリケーションのメモリができvmalloc適用します。カーネルモードには、ユーザーモードとは別のページテーブル管理があります。

  1. 永続的なカーネルマッピング領域(永続的なカーネルマッピング)

  PKMAP_BASEFIXADDR_STARTカーネル空間のアクセスは、4G-8Mから4G-4Mのアドレス範囲間の永続的マッピングと呼ばれます。alloc_pages()アッパーメモリの結果のstruct page構造体の物理メモリがkmapこの領域にマップされるとき関数を使用することができます許可される永続的なマッピングの数は限られているため、ハイエンドメモリが不要になった場合は、マッピングをマッピング解除する必要がありkunmap()ます。これは、関数を使用して行うことができます

  1. 固定マッピングエリア

  FIXADDR_STARTFIXADDR_TOP(0xFFFF F000)固定マッピング領域と呼ばれるスペースは、主に特別なニーズを満たすために使用されます。

  1. 一時的なカーネルマッピング

  一時的なカーネルマッピングが渡されkmap_atomickunmap_atomic実装され、主にファイルの書き込みなど、物理メモリまたはメインメモリへの書き込みが必要な場合の操作に使用されます。

3.2.2 64ビットカーネルモードの構造

  64ビットカーネルモードはスペースが大きいため、32ビットモードのように慎重に計画する必要はなく、多くの空き領域が保護のために直接分割されます。構造を次の図に示します。

img
  • 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]ウィキ

[2] elixir.bootlin.com/linux

[3] woboq

[4] Linuxインサイド

[5] Linuxカーネルに対する深い理解

[6] Linuxカーネル設計の芸術

[7] Geek TimeがLinuxオペレーティングシステムについて語る

おすすめ

転載: blog.csdn.net/u013354486/article/details/106960441