Linux中内存相关概念与内存申请的几种方式
来源:互联网 发布:91.v6p.co index.php 编辑:程序博客网 时间:2024/06/03 13:52
1 物理地址,MMU相关概念
Intel X86存在IO空间,相对于内存空间,通过IN OUT指令访问。大多数ARM PowerPC仅有内存空间。内存空间通过地址,指针访问。程序,程序运行中使用的变量都在内存空间。
物理地址
unsigned char *p = (unsigned char*) 0xF000FF00;*p = 1;
0xF000FF00这个地址对于x86是16bit段地址+16bit偏移地址,即,0xF000 * 16 + 0xFF00 = 0xF0000 + 0xFF00 = 0xFFF00
地址对于ARM等为采用段地址的处理器,就是空间0xF000FF00。
x86处理器用实际地址做第一步跳转(软重启):
typedef void (*lpFunction) ();//define a function pointer typelpFunction lpReset = (lpFunction)0xF000FFF0; //get a pointer that point to the addrlpRest(); //go to the function at addr
MMU
memory management unit,辅助内存管理,提供虚拟和物理地址映射、内存访问权限保护、Cache缓存控制。
Kernel借助MMU让用户感觉可以使用很大的内存空间,而让开发者在写程序的时候可以不考虑物理实际容量。
TLB:Translation Lookaside Buffer,转换旁路缓存。是MMU的核心部件,缓存少量的虚拟–物理关系,是转换表的Cache,也称为“快表”。
TTW:Translation Table walk,转换表漫游。当TLB没有需要的转换对,通过内存中的转换表(常常是多级页表,从页表记地址寄存器找到页表,一层一层直到代码页。)访问得到虚拟–物理关系,TTW成功就写入TLB。
写入之后如果权限正确将访问Cache或者内存找到相应的数据。如果不允许,MMU会向ARM发送一个存储器异常。
Linux三级页表
- PGD,Page Global Directory (页目录);
- PMD,Page Middle Directory (页目录);
*前两者内部的成为PDE,页目录项,Page Directory Entry。 - PTE,Page Table Entry (页表项,每一个表项对应一个物理页)。
相关宏可见下图:
一般由虚拟地址三级查询得到PTE的页表的过程(page table walk):
- 有描述进程占有资源的
struct mm_sturct mm
和需要访问的虚拟地址unsigned long addr
- 通过
pgd_offset(mm, addr)
得到一级页表入口 - 通过
pmd_offset(pgd, addr)
得到二级页表入口 - 通过
pte_offset_map(pmd, addr)
得到目标页表项
更多详细可查看reference [1]
注:Linux 2.6支持不带MMU的处理器。其为了兼容嵌入式系统,融合了uClinux,来支持MMU-Less系统。
2 Linux内存管理
包含MMU的处理器可以使进程的访问空间达到4G,0-3G是User Space,3-4G是Kernel Space。PAGE_OFFSET
为3G末尾,即0x86的0xC000 0000
。
每个进程有自己的页表,相互独立。内核空间由内核负责映射,固定不随进程变化。而1G的Kernel Space划分为:
- 物理内存映射区(0-896MB),线性映射,常规内存。当物理内存大于896MB,超出部分称为高端内存。
- 896MB之后的区域:
- vmalloc分配器区(前后有隔离带,地址VMALLOC_START ~ VMALLOC_END)
- 高端内存映射区(高端内存只能以映射在这里)(PKMAP_BASE) 更多关于 高端内存
- 专用页面映射区(实际中FIXADDR_START ~ FIXADDR_TOP)这部分需要配置。
- 保留区域(实际中FIXADDR_TOP ~ 4G区域)
当内存超过4G,需要使用CPU扩展分页(PAE)模式提供的64bit也目录项才能访问到更高物理内存,需要CPU支持。
3 内存存取
内存申请
下面会提到的内存申请:
- malloc - free: 用户空间
- kmalloc - kfree: 内核空间,物理连续
- __get_free_pages - free_pages: 内核空间,物理连续
- vmalloc - vfree: 内核空间,物理不连续,虚拟连续
- slab: kmem_cache_create - kmem_cache_destory
1 用户空间内存动态申请 malloc()
申请的空间在heap上,需要申请者用free()
释放。注意尽量成对出现,避免内存泄漏。注:C Linux的malloc常用brk()
和mmap()
系统调用实现。
2 内核空间内存动态申请
kmalloc()
申请内存位于物理内存映射区,物理上也连续。和真实物理地址只有一个固定的offset。 void *kmalloc(size_t size, int flags);
size是大小,flag是标志,GFP_KERNEL表示在内核空间进程中申请内存。其底层依赖__get_free_pages()
实现。使用这个flag后,如果不能满足,进程会睡眠等待页,可能会引起阻塞。因此不能在++中断上下文,spin lock++中使用GFP_KERNEL申请内存。
在++中断处理函数,tasklet,内核定时器++非进程上下文不能阻塞,应该用GFP_ATOMIC申请内存,不存在空闲会直接返回。
相应其他标志位定义于:include/linux/gfp.h。 kfree()
释放空间。
__get_free_pages()
Linux Kernel最底层使用的获取空间的方法。底层以page的2^n为单位管理空闲内存,所以内存页的申请是以page为单位。 get_zeroed_page(unsigned int flags);
指向一个清零的新page。 __get_free_page(unsigned int flags);
指向新页但不清零,实际上是用了order为0的下一个函数。 __get_free_pages(unsigned int flags, unsigned int order);
获取多个pages数量是2^order,不清零。order最大是10或11,硬件相关。
前面三个函数的实现其实是调用了alloc_pages()
,该函数可以在用户空间,也可以在内核空间使用。返回struct page *
。
释放: void free_page(unsigned long addr);
void free_pages(unsigned long addr, unsigned long order);
特别注意order前后要一致。
vmalloc() void *vmalloc(unsigned long size);
在虚拟空间得到一块连续区域,在vmalloc专用区,物理内存不一定连续。用于较大的顺序缓冲区分配内存,开销远大于GFP,新的页表要被建立。如果是用它申请少量内存,是不妥的。
释放void vfree(void *addr)
slab
以page为单位容易产生内部碎片(internal fragmentation)。同时设想如果能让前后两次相同对象的分配在同一块内存,而且已经保留的数据结构,就能提高效率。得到slab概念,驻留任意数目,同样大小的后背缓存。
创建:
struct kmem_cache *kmem_cache_create( const char *name, size_t size, //size为每个等大结构的大小byte size_t align, unsigned long flags, viod (*ctor)(void*, struct kmem_cache *, unsigned long), void (*dtor)(void*, struct kmem_cache *, unsigned long));`
分配slab缓存:
void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags);//在之前分配的slab中分出一块并返回首指针
释放slab缓存:
void kmem_cache_free(struct kmem_cache *cachep, void *objp);
回收整个slab:
int kmem_cache_destroy(struct kmem_cache *cachep);
cat /proc/slabinfo
可以获知slab的分配使用情况。
注意slab底层也依赖于__get_free_pges()
,只是分割了小单元减少内部碎片方便管理。
内存池
也是用与分配大量小对象的后背缓存技术。
相关函数有mempool_create
,mempool_alloc
,mempool_free
,mempool_destory
。
虚拟地址和物理地址
使用virt_to_phys()
实现内核虚拟向物理地址的转化,函数实现和体系结构相关。phys_to_virt()
物理向虚拟。仅仅适用于常规内存区域。
reference
[1] 三级页表,http://blog.csdn.net/myarrow/article/details/8624687
[2] 高端内存,http://ilinuxkernel.com/?p=1013
notification
source: 《Linux设备驱动开发详解》(第二版),内容为读书笔记和网络资料,有些资料原始来源不详,分享为了方便自己和他人查阅。如有侵权请及时告知,对于带来的不便非常抱歉。转载请注明来源。Terrence Zhou.
- Linux中内存相关概念与内存申请的几种方式
- 内存分配的几种方式与野指针
- 内核的几种内存分配与线性映射方式
- android中可能造成内存泄露的几种方式
- 内存的申请与释放
- 内存的申请与释放
- 共享内存的相关概念
- 共享内存申请方式
- linux中高端内存和低端内存的概念
- linux驱动开发--内核空间中内存的申请与释放
- 几种内存的分配方式
- C++ 申请动态内存的三种方式
- C++内存申请、实例化的方式
- C++中指针的内存申请与释放问题
- C语言中 内存的申请与释放
- 【Linux】在内核中申请内存
- 内存中与进程相关的信息
- 栈内存申请与堆内存申请
- Java Collection
- 和尚特烦恼2——第几个素数
- 黑马程序员——模板
- AVL树笔记(二):maintain,delete
- 文件的读写与创建简单例子
- Linux中内存相关概念与内存申请的几种方式
- 20151117《Unix环境高级编程》文件apue.h的获取与使用
- 数据结构--Chapter1(绪论)
- 使用influxdb+cadvisor+grafana的docker镜像搭建一个实时监控系统的环境
- 和尚特烦恼3——何时能下山
- android studio gradle初步理解
- 南大软院21天大神养成计划第2天
- CPU Ranking
- Visual Studio 写自己的动态链接库(DLL)