Linux进程地址空间
1.虚拟内存
Linux操作系统采用虚拟内存管理技术,使得每个进程都拥有独立的进程地址空间,该地址空间为3G,用户看到和接触的都是虚拟地址,无法看到实际的物理地址。利用这种虚拟地址不但能起保护操作系统的作用,更重要的是用户程序可以使用比实际物理内存更大的地址空间。
Linux将4G的虚拟地址空间划分为两个部分:用户空间和内核空间。用户空间从0x00000000~0xbfffffff,内核空3G~4G,通常情况下,用户是不能访问内核地址空间,但用户进程通过系统调用访问内核空间。用户空间对应进程,所以每个进程切换,用户空间就跟着变化。把同一个程序同时运行10次会看到10个进程使用的线性地址一模一样。不一样的是其对应的页表和页目录。
创建进程fork()、程序载入execve()、动态内存分配malloc()的进程相关操作需要分配内存给进程,这是进程申请和获得的不是物理地址,仅仅是虚拟地址。
实际的物理内存只有当进程真的去访问新获取的虚拟地址时,才会由“请页机制”产生“缺页”异常,从而进入分配实际页框的程序。该异常是虚拟内存机制赖以存在的基本保证。它会告诉内核去为进程分配物理页,并建立对应的页表。这之后虚拟地址才实实在在地映射到物理地址上。
2.内核内存分配
2.1在应用程序中,常使用malloc函数进行动态内存分配。而在Linux内核中,通常使用kmalloc来动态内存分配。
kmalloc原型:
#include <linux/slab.h>
void *kmalloc(size_t size,int flags)
参数:
size:要分配内存的大小(分配的是实际的物理内存);
flags:分配标志,用来控制kmalloc的行为。
分配标志
1.GFP_KERNEL:进程上下文中分配。当空闲进程太少时,kmalloc函数使当前进程进入休眠,等待空闲页的出现。(16M-896)
2.GFP_ATOMIC:进程上下文之外的代码(包括中断处理)中分配内存,从不睡眠。分配正确正确返回,分配错误错误返回。
3.__GFP_DMA:这个标志要求分配能够DMA传输的内存去(物理地址在16M以下的页帧)
4.__GFP_HIGHMEM:这个标志分配的内存位于高端内存。(896M以上的)
2.2按页分配(4k)
如果模块需要分配大块的内存,那使用面向页的分配技术会更好。
1).get_zeroed_page (unsigned int flags)
返回指向新页面的指针,并将页面清零。
2).__get_free_page(unsigned int flags)
和上一样,但页面不清零。
3).__get_free_pages(unsigned int flags)
分配若干个连续的页面, 返回指向该内存区域的指针,但不清零这段内存区域。
3.释放内存
当程序用完这些页,可以使用下列之一来释放他们:
void free_page(unsigned long addr)
void free_pages(unsigned long addr, unsigned long order)
4.内存使用
由上图知,分配物理内存的路线有3条:
第一条:来自app应用,如malloc分配内存,拿到内存的虚拟地址空间,但并没有分配物理页,当访问这个虚拟地址时,产生缺页异常,然后请页异常,然后从空闲的物理页里面找出物理内存单元分配给app。
第二条:来自内核,如kmalloc分配内存,然后slab先有物理内存,然后kmalloc分配内存。
第三条:来自内核,如vmalloc,类似于malloc。
*未完