全部博文(18)
分类: LINUX
2009-04-20 15:37:12
内核需要管理进程地址空间,即系统中每个用户空间进程所看到的内存。
通过虚拟内存技术,进程可以拥有远大于物理内存的地址空间。
每个进程都拥有32位或64位平坦(flat:独立的连续区间)的地址空间。
不同进程可在各自地址空间的相同地址存放不同数据;
进程间可以共享地址空间,称这样的进程为线程。
可被访问的合法地址区间——内存区域
进程只能访问有效范围内的内存地址,每个内存区域也有相应进程必须遵循的特定访问属性,若进程访问违规,则返回“段错误”信息,并终止该进程。
内存区域包括:
可执行文件代码的内存映射,称为代码段;
可执行文件的已初始化全局变量的内存映射,称为数据段;
包含未初始化全局变量,也就是bss段的零页的内存映射;
用于进程用户空间栈的零页的内存映射;
每一个诸如C库或动态连接程序等共享库的代码段、数据段和bss也会被载入进程的地址空间;
任何内存映射文件;
任何共享内存段;
任何匿名的内存映射,比如由malloc()分配的内存。
14.1 内存描述符
mm_struct结构体——表示进程的地址空间,包含与之相关的所有信息。
|
分配内存描述符:
进程描述符的mm域存放着该进程的内存描述符,即current -> mm指向当前进程的内存描述符。
fork()函数利用copy_mm()函数复制父进程的内存描述符current -> mm域给子进程,子进程中的mm_struct是通过文件kernel/fork.c中的allocate_mm()宏从mm_cachep slab缓冲中分配得到的。
每个进程都有唯一的mm_struct结构体,即唯一的进程地址空间。
父进程希望和子进程共享地址空间,可在调用clone()时,设置CLONE_VM标志,这样的进程称为线程。
当CLONE_VM被指定后,内核就不需要调用allocate_mm()函数,只需要在调用copy_mm()函数复制父进程的内存描述符current -> mm域给子进程。
销毁内存描述符:
进程退出时,内核调用exit_mm()函数:
(1) 调用mmput()函数减少mm_users用户计数
(2) 若用户计数降到0,调用mmdrop()函数,减少mm_count使用计数
(3) 使用计数也降为0,调用free_mm()宏通过kmem_cache_free()函数将mm_struct街头体归还到mm_cachep slab缓存中。
内核线程没有进程地址空间,也没有相关的内存描述符,所以内核线程对应的进程描述符中mm域为空,内核线程会直接使用前一个进程的内存描述符。
14.2 内存区域
内存区域也称为虚拟内存区域或VMA
vm_area_struct结构体卖哦输了指定地址空间内连续空间上的一个独立内存范围。
|
内存区间长度vm_end-vm_start;
vm_mm域指向和VMA相关的mm_struct结构体;
两个独立的进程将同一个文件映射到各自的地址空间,分别有vm_area_struct来标志各自的内存区域;
两个线程共享一个地址空间,它们同时共享其中所有的vm_area_struct结构体。
VMA标志:
反映的是内核处理页面所需要遵守的行为准则,而不是硬件要求,也包含内存区域中页面的信息。
Flag |
Effect on the VMA and its pages |
VM_READ |
Pages can be read from |
VM_WRITE |
Pages can be written to |
VM_EXEC |
Pages can be executed |
VM_SHARED |
Pages are shared共享映射/私有映射 |
VM_MAYREAD |
The VM_READ flag can be set |
VM_MAYWRITE |
The VM_WRITE flag can be set |
VM_MAYEXEC |
The VM_EXEC flag can be set |
VM_MAYSHARE |
The VM_SHARE flag can be set |
VM_GROWSDOWN |
The area can grow downward |
VM_GROWSUP |
The area can grow upward |
VM_SHM |
The area is used for shared memory |
VM_DENYWRITE |
The area maps an unwritable file |
VM_EXECUTABLE |
The area maps an executable file |
VM_LOCKED |
The pages in this area are locked |
VM_IO |
The area maps a device's I/O space |
VM_SEQ_READ |
The pages seem to be accessed sequentially |
VM_RAND_READ |
The pages seem to be accessed randomly |
VM_DONTCOPY |
This area must not be copied on fork() |
VM_DONTEXPAND |
This area cannot grow via mremap() |
VM_RESERVED |
This area must not be swapped out |
VM_ACCOUNT |
This area is an accounted VM object |
VM_HUGETLB |
This area uses hugetlb pages |
VM_NONLINEAR |
This area is a nonlinear mapping |
操作VMA:
|
内存区域的树型结构和链表结构:
可用内存描述符的mmap域或mm_rp域访问内存区域,它们包括相同的vm_area_struct结构体的指针,组织方法不同。
mmap域使用单独链表连接所有内存区域对象,,mmap域指向链表的第一个内存区域,每个vm_area_struct结构体通过vm_next域连入链表;
mm_rb域使用红-黑树连接所有内存区域对象,mm_rb域指向根节点,每个vm_area_struct结构体通过vm_rb域连接到树中。
实际使用中的内存区域:
/proc/
pmap工具将信息以更方便阅读的形式输出。
14.3 操作内存区域
find_vma()——寻找第一个包含addr的内存区域,定义在
|
find_vma_prev()——寻找第一个小于addr的内存区域
|
find_vma_intersection()——返回第一个与指定地址区间相交的VMA
|
14.4 mmap()和do_mmap():创建地址区间
内核使用do_mmap()函数创建一个新的线性地址区间
|
do_mmap()函数将一个地址区间加入进程的地址空间,如果与已存在的地址空间相邻且具有相同的访问权限,则与之合并,即扩展已存在的内存区域,否则创建一个新的区域。
prot参数指定内存区域中页面的访问权限,
Flag |
Effect on the Pages in the New Interval |
PROT_READ |
Corresponds to VM_READ |
PROT_WRITE |
Corresponds to VM_WRITE |
PROT_EXEC |
Corresponds to VM_EXEC |
PROT_NONE |
Page cannot be accessed |
flag参数指定了VMA标志,
Flag |
Effect on the New Interval |
MAP_SHARED |
The mapping can be shared |
MAP_PRIVATE |
The mapping cannot be shared |
MAP_FIXED |
The new interval must start at the given address addr |
MAP_ANONYMOUS |
The mapping is not file-backed, but is anonymous |
MAP_GROWSDOWN |
Corresponds to VM_GROWSDOWN |
MAP_DENYWRITE |
Corresponds to VM_DENYWRITE |
MAP_EXECUTABLE |
Corresponds to VM_EXECUTABLE |
MAP_LOCKED |
Corresponds to VM_LOCKED |
MAP_NORESERVE |
No need to reserve space for the mapping |
MAP_POPULATE |
Populate (prefault) page tables |
MAP_NONBLOCK |
Do not block on I/O |
用户空间通过mmap()系统调用获取内核函数do_mmap()的功能。
mmap的系统调用:
|
14.5 munmap()和do_munmap():删除地址区间
int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)
munmap()为用户空间提供从地址空间删除指定地址空间的方法。
int munmap(void *start, size_t length)
系统调用:
|
14.6 页表
应用程序操作的对象是映射到物理内存之上的虚拟内存,但是处理器直接操作的却是物理内存。
当用程序访问一个虚拟地址时,需要首先将虚拟地址转换成物理地址,然后处理器才能解析地址访问请求。
三级页表完成地址转换。
页全局变量(PGD);
中间页目录(PMD);
页表(PTE)。
多数体系结构中,搜索页表的工作由硬件完成——翻译后缓冲器(TLB,先检查是否有缓存,否则再通过页表索引物理地址)