리눅스 운영 체제 연구 노트에 대한 뭔가 - 메모리 관리 (강의 22)

분할 사용자 모드와 커널 모드

메모리 관리 정보 : task_struct에있는 mm_struct

전체 가상 메모리 공간 :

1) 사용자 모드 주소 공간

2) 커널 모드 주소 공간입니다

어디에서 두 부분 사이의 분할 선은? 이것은 것 TASK_SIZE 정의

1  CONFIG_X86_32 #ifdef와
 2  / * 
3  * 사용자 공간 프로세스 크기 : 3기가바이트 (기본값).
4   * / 
5  #DEFINE TASK_SIZE PAGE_OFFSET
 6  #DEFINE TASK_SIZE_MAX TASK_SIZE
 7  / * 
8  구성 PAGE_OFFSET
 9  (10)          0xC0000000과 기본적
 11          X86_32에 따라
 12  * / 
13  #else 
14  / * 
15  * 사용자 공간 프로세스 크기. 47bits 뺀 가드 페이지.
16  * / 
17  #DEFINE TASK_SIZE_MAX ((1UL << 47) - PAGE_SIZE)
18  #DEFINE TASK_SIZE (test_thread_flag (TIF_ADDR32) \?
 19            IA32_PAGE_OFFSET : TASK_SIZE_MAX)
 20 ......

32 비트 시스템의 경우 : 어드레싱 가능한 최대 2 (32)는 커널 모드 1G 사용자 모드 가상 주소 공간의 3G이고 4G이다 = ^

64 비트 시스템의 경우 :

단지 48 비트를 이용하여 가상 주소. 내부 코드와 같은 기록으로,도 1은 48 비트 어드레스 공간에 위치 0x0000800000000000의 절반,이어서 페이지 0x00007FFFFFFFF000 공동 128T이다 빼기 당량 47 떠났다. 마찬가지로, 커널 공간 128T이다. 커널 공간과 사용자 공간 사이의 큰 격차에 걸쳐, 분리를 위해

 

 

레이아웃 사용자 모드

레이아웃 사용자 모드

레이아웃 사용자 모드 가상 공간

 1 unsigned long mmap_base;  /* base of mmap area */
 2 unsigned long total_vm;    /* Total pages mapped */
 3 unsigned long locked_vm;  /* Pages that have PG_mlocked set */
 4 unsigned long pinned_vm;  /* Refcount permanently increased */
 5 unsigned long data_vm;    /* VM_WRITE & ~VM_SHARED & ~VM_STACK */
 6 unsigned long exec_vm;    /* VM_EXEC & ~VM_WRITE & ~VM_STACK */
 7 unsigned long stack_vm;    /* VM_STACK */
 8 unsigned long start_code, end_code, start_data, end_data;
 9 unsigned long start_brk, brk, start_stack;
10 unsigned long arg_start, arg_end, env_start, env_end;

1 ) mmap_base: 表示虚拟地址空间中用于内存映射的起始地址。一般情况下,这个空间是从高地址到低地址增长的。前面咱们讲 malloc 申请一大块内存的时候,就是通过 mmap 在这里映射一块区域到物理内存。咱们加载动态链接库 so 文件,也是在这个区域里面,映射一块区域到 so 文件

2 ) total_vm: 是总共映射的页的数目。我们知道,这么大的虚拟地址空间,不可能都有真实内存对应,所以这里是映射的数目。当内存吃紧的时候,有些页可以换出到硬盘上,有的页因为比较重要,不能换出。

3 ) locked_vm: 是被锁定不能换出

4 ) pinned_vm 是不能换出,也不能移动

5 ) data_vm: 是存放数据的页的数目,exec_vm 是存放可执行文件的页的数目,stack_vm 是栈所占的页的数目

6 ) start_code 和 end_code 表示可执行代码的开始和结束位置,start_data 和 end_data 表示已初始化数据的开始位置和结束位置

7 ) start_brk 是堆的起始位置,brk 是堆当前的结束位置。前面咱们讲过 malloc 申请一小块内存的话,就是通过改变 brk 位置实现的

8 ) start_stack 是栈的起始位置,栈的结束位置在寄存器的栈顶指针中

9 ) arg_start 和 arg_end 是参数列表的位置, env_start 和 env_end 是环境变量的位置。它们都位于栈中最高地址的地方

示图:

 

 

 

 load_elf_binary 实现vm_area_struct 和上面的内存区域关联

 1 static int load_elf_binary(struct linux_binprm *bprm)
 2 {
 3 ......
 4   setup_new_exec(bprm);
 5 ......
 6   retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP),
 7          executable_stack);
 8 ......
 9   error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt,
10         elf_prot, elf_flags, total_size);
11 ......
12   retval = set_brk(elf_bss, elf_brk, bss_prot);
13 ......
14   elf_entry = load_elf_interp(&loc->interp_elf_ex,
15               interpreter,
16               &interp_map_addr,
17               load_bias, interp_elf_phdata);
18 ......
19   current->mm->end_code = end_code;
20   current->mm->start_code = start_code;
21   current->mm->start_data = start_data;
22   current->mm->end_data = end_data;
23   current->mm->start_stack = bprm->p;
24 ......
25 }

 

 

1)load_elf_binary 会完成以下的事情:

2)调用 setup_new_exec,设置内存映射区 mmap_base;

3)调用 setup_arg_pages,设置栈的 vm_area_struct,这里面设置了 mm->arg_start 是指向栈底的,current->mm->start_stack 就是栈底;

4)elf_map 会将 ELF 文件中的代码部分映射到内存中来;

5)set_brk 设置了堆的 vm_area_struct,这里面设置了 current->mm->start_brk = current->mm->brk,也即堆里面还是空的;

6)load_elf_interp 将依赖的 so 映射到内存中的内存映射区域

 

映射完毕后,什么情况下会修改呢?

第一种情况是函数的调用,涉及函数栈的改变,主要是改变栈顶指针。

第二种情况是通过 malloc 申请一个堆内的空间,当然底层要么执行 brk,要么执行 mmap

 

内核态的布局

内核态的布局   

32 位的内核态的布局

 

 

32 位的内核态虚拟地址空间一共就 1G,占绝大部分的前 896M,我们称为直接映射区

直接映射区:就是这一块空间是连续的,和物理内存是非常简单的映射关系,其实就是虚拟内存地址减去 3G,就得到物理内存的位置。

 

 

64 位的内存布局

 

 64 位的内核主要包含以下几个部分:

从 0xffff800000000000 开始就是内核的部分,只不过一开始有 8T 的空档区域。

从 __PAGE_OFFSET_BASE(0xffff880000000000) 开始的 64T 的虚拟地址空间是直接映射区域,也就是减去 PAGE_OFFSET 就是物理地址。虚拟地址和物理地址之间的映射在大部分情况下还是会通过建立页表的方式进行映射。

从 VMALLOC_START(0xffffc90000000000)开始到 VMALLOC_END(0xffffe90000000000)的 32T 的空间是给 vmalloc 的。

从 VMEMMAP_START(0xffffea0000000000)开始的 1T 空间用于存放物理页面的描述结构 struct page 的。

从 __START_KERNEL_map(0xffffffff80000000)开始的 512M 用于存放内核代码段、全局变量、BSS 等。这里对应到物理内存开始的位置,减去 __START_KERNEL_map 就能得到物理内存的地址。这里和直接映射区有点像,但是不矛盾,因为直接映射区之前有 8T 的空当区域,早就过了内核代码在物理内存中加载的位置

 

总结时刻

进程运行状态在 32 位下对应关系

 

  64 位的对应关系

 

추천

출처www.cnblogs.com/mysky007/p/12315334.html