进程地址空间也就是每个进程所使用的内存,内核对进程地址空间的管理,也就是对用户态程序的内存管理。
主要内容:
- 地址空间(mm_struct)
- 虚拟内存区域(VMA)
- 地址空间和页表
1. 地址空间(mm_struct)
地址空间就是每个进程所能访问的内存地址范围。
这个地址范围不是真实的,是虚拟地址的范围,有时甚至会超过实际物理内存的大小。
现代的操作系统中进程都是在保护模式下运行的,地址空间其实是操作系统给进程用的一段连续的虚拟内存空间。
地址空间最终会通过页表映射到物理内存上,因为内核操作的是物理内存。
虽然地址空间的范围很大,但是进程也不一定有权限访问全部的地址空间(一般都是只能访问地址空间中的一些地址区间),
进程能够访问的那些地址区间也称为 内存区域。
进程如果访问了有效内存区域以外的内容就会报 “段错误” 信息。
内存区域中主要包含以下信息:
- - 代码段(text section),即可执行文件代码的内存映射
- - 数据段(data section),即可执行文件的已初始化全局变量的内存映射
- - bss段的零页(页面信息全是0值),即未初始化全局变量的内存映射
- - 进程用户空间栈的零页内存映射
- - 进程使用的C库或者动态链接库等共享库的代码段,数据段和bss段的内存映射
- - 任何内存映射文件
- - 任何共享内存段
- - 任何匿名内存映射,比如由 malloc() 分配的内存
注:bss是 block started by symbol 的缩写。
linux中内存相关的概念稍微整理了一下,供参考:
|
英文 |
含义 |
| SIZE | 进程映射的内存大小,这不是进程实际使用的内存大小 |
| RSS(Resident set size) | 实际驻留在“内存”中的内存大小,不包含已经交换出去的内存 |
| SHARE | RSS中与其他进程共享的内存大小 |
| VMSIZE | 进程占用的总地址空间,包含没有映射到内存中的页 |
| Private RSS | 仅由进程单独占用的RSS,也就是进程实际占用的内存 |
1.1 mm_struct介绍
linux中的地址空间是用 mm_struct 来表示的。
下面对其中一些关键的属性进行了注释,有些属性我也不是很了解......
struct mm_struct { struct vm_area_struct * mmap; /* [内存区域]链表 */ struct rb_root mm_rb; /* [内存区域]红黑树 */ struct vm_area_struct * mmap_cache; /* 最近一次访问的[内存区域] */ unsigned long (*get_unmapped_area) (struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags); /* 获取指定区间内一个还未映射的地址,出错时返回错误码 */ void (*unmap_area) (struct mm_struct *mm, unsigned long addr); /* 取消地址 addr 的映射 */ unsigned long mmap_base; /* 地址空间中可以用来映射的首地址 */ unsigned long task_size; /* 进程的虚拟地址空间大小 */ unsigned long cached_hole_size; /* 如果不空的话,就是 free_area_cache 后最大的空洞 */ unsigned long free_area_cache; /* 地址空间的第一个空洞 */ pgd_t * pgd; /* 页全局目录 */ atomic_t mm_users; /* 使用地址空间的用户数 */ atomic_t mm_count; /* 实际使用地址空间的计数, (users count as 1) */ int map_count; /* [内存区域]个数 */ struct rw_semaphore mmap_sem; /* 内存区域信号量 */ spinlock_t page_table_lock; /* 页表锁 */ struct list_head mmlist; /* 所有地址空间形成的链表 */ /* Special counters, in some configurations protected by the * page_table_lock, in other configurations by being atomic. */ mm_counter_t _file_rss; mm_counter_t _anon_rss; unsigned long hiwater_rss; /* High-watermark of RSS usage */ unsigned long hiwater_vm; /* High-water virtual memory usage */ unsigned long total_vm, locked_vm, shared_vm, exec_vm; unsigned long stack_vm, reserved_vm, def_flags, nr_ptes; 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 */ struct linux_binfmt *binfmt; cpumask_t cpu_vm_mask; /* Architecture-specific MM context */ mm_context_t context; /* Swap token stuff */ /* * Last value of global fault stamp as seen by this process. * In other words, this value gives an indication of how long * it has been since this task got the token. * Look at mm/thrash.c */ unsigned int faultstamp; unsigned int token_priority; unsigned int last_interval; unsigned long flags; /* Must use atomic bitops to access the bits */ struct core_state *core_state; /* coredumping support */ #ifdef CONFIG_AIO spinlock_t ioctx_lock; struct hlist_head ioctx_list; #endif #ifdef CONFIG_MM_OWNER /* * "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 *owner; #endif #ifdef CONFIG_PROC_FS /* store ref to file /proc/<pid>/exe symlink points to */ struct file *exe_file; unsigned long num_exe_file_vmas; #endif #ifdef CONFIG_MMU_NOTIFIER struct mmu_notifier_mm *mmu_notifier_mm; #endif };