内核空间----是由内核负责映射,它并不会跟着进程改变,是固定的。
高端内存--896M以上的部分称之为高端内存。
3G--------------------------------------------------------------------4G
直接映射区---8M-动态映射区-8K---kmap区---固定映射区-4K
896M-------120M----------------------4M--------------4M---------
直接映射区--从3G开始最大896M的线性地址区间,该区域的线性地址和物理地址之间存在线性转换关系。线性地址=3G+物理地址。
动态映射区---地址由内核函数vmalloc来进行分配,特点是线性空间连续,但对应的物理空间不一定连续。vmalloc分配的线性地址所对应的物理页可能处于低端内存,也可能处于高端内存。
永久内存映射区----对于896M以上的高端内存,可使用该区域来访问,访问方法:
1-使用alloc_page(__GFP_HIGHMEM)分配高端内存页。
2使用kmap函数将分配到的高端内存映射到该区域。
固定映射区--固定映射区中每个地址都服务于特定的用途,如ACPI_BASE等。
虚拟内存---
linux操作系统采用虚拟内存管理技术,使得每个进程都有独立的进程地址空间,该空间是大小为3G,用户看到和接触的都是虚拟地址,无法看到实际的物理地址。利用这种虚拟地址不但能起到保护操作系统的作用,而且更重要的是用户程序可使用比实际物理内存更大的地址空间。
linux将4G的虚拟地址空间划分为两个部分------用户空间和内核空间。用户空间从0----0xbfffffff,内核空间从3G----4G。用户进程通常情况下只能访问用户空间的虚拟地址,不能访问内核空间。例外情况是用户进程通过系统调用访问内核空间。
进程空间----
用户空间对应进程,所以每当进程切换,用户空间就会跟着变化。
每个进程的用户空间都是完全独立,互不相干的。把同一个程序同时运行10次,会看到10个进程使用的线性地址一摸一样。cat /proc/<pid>maps
创建进程fork(),程序载入execve(),动态内存分配malloc()等进程相关操作都需要分配内存给进程。这时进程申请和获得的不是物理地址,仅仅是虚拟地址。
实际的物理内存只有当进程真的去访问新获取的虚拟地址时,才会由请页机制产生缺页异常,从而进入分配实际页框的程序。该异常是虚拟内存机制赖以存在的基本保证------它会告诉内核去为进程分配物理页,并建立对应的页表,这之后虚拟地址才实实在在的映射到了物理地址上。
在应用程序中,常用malloc函数进行动态内存分配,而在linux内核中,通常使用kmalloc来动态分配内存。
#include <linux/slab.h>
void *kmalloc(size_t size,int flags)
size--要分配的内存大小
flags--分配标志,它控制kmalloc的行为。
最常用的标志是GFP_KERNEL。它的意思是该内存分配是由运行在内核态的进程调用的。也就是说,调用它的函数属于某个进程的。当空闲内存太少时,kmalloc函数会使当前进程进入睡眠,等待空闲的页出现。如果kmalloc是在进程上下文之外调用,比如在中断处理,任务队列处理,和内核定时器处理中。这些情况属于中断上下文,不能进入睡眠,这时应该使用优先权GFP_ATOMIC
GFP_ATOMIC--在进程上下文之外的代码中分配内存,从不睡眠。
GFP_KERNEL--进程上下文中的分配,可能睡眠。(16M---896M)
__GFP_DMA-----表示分配能够DMA的内存区(物理地址在16M以下的页帧)
__GFP_HIGHMEM----表示分配的内存位于高端内存(896M以上)
按页分配--------------
若模块需要分配大块的内存,那使用面向页的分配技术会更好。
get_zeroed_page(unsigned int flags)
返回指向新页面的指针,并将页面清零。
__get_free_page(unsigned int flags)
不清0 页面
__get_free_pages(unsigned int flags,unsigned int order)
分配若干个连续的页面,返回指向该内存区域的指针,但也不清0这段内存区域。
释放---
当程序用完这些页,需要释放。
void free_page(unsigned long addr)
void free_pages(unsigned long addr,unsigned long order)
如果释放的和先前分配数目不等的页面,会导致系统错误。