六。内存管理机制--MMU
来源:互联网 发布:淘宝网金丝绒太极服 编辑:程序博客网 时间:2024/05/21 19:37
大家发现什么村务一定要告诉我,大家一起学习:
1、#define abc(n) do{xxx;yyy;zzz;}while(0)
#define abc(n) {xxx;yyy;zzz;}
加上do while 和不加可能执行结果可能一样,但是内核一般都加
是怕在执行函数的时候出现错误
int a ;
if(a > 0)
abc(100);//若用下面的宏定义,加上分号编译出错,是因为在后面加了分号,多加的分号就分割了if / else
else
abc(200);
return 0;
################# 内存管理 #################
1、概念:
MPU 内存保护单元< 有操作系统的一般不用,用 MMU >
会写一个页表类似的东西,但不是页表,记录下面内存空间的只读、同步等属性
每一段内存空间的权限:ro sync ex rw nx ...
示意将内存分为:[0, 0x1000)[0x1000, 0x2000)[0x2000, 0x3000)[0x3000, ...)
MMU 内存管理单元 MMU = MPU + (查找页表)
arm9、arm11、armA系列内有 MMU;一般面向应用的电子带有。但是并不是说有操作系统就一定有 MMU
如果做工控,控制器一般有 MPU 或没有,eg:cortex-M 和 cortex-R 系列单片机用的就是 MPU;只有面向想飞电子的高端芯片才用 MMU,安装有操作系统的控制器,要实现多任务,多任务会用到虚拟地址,所以需要 MMU。但并使有操作系统就一定有 MMU
2、linux内核管理机制
bootargs = "mem = 128M" 当初写这个原因就是告诉内核内存有多大,内核就可以计算出用分配多少页表
内核用的是小页映射 page = 4K
每个页对应一个结构体 struct page ---> 描述一页的物理内存
virtual 如果分配过存虚拟地址,没有的就是NULL
page 结构体在内存中以连续、有序数组存放
struct zone //标记的都是物理地址;标准的linux/unix X86 上这样分配
DMA [0 - 16M] //分这16M是历史问题,以前的DMA只有24位,最大访问16M,所以留下这块给DMA用
NORMAL[16M - 896M]
HIGH [896M - ...]
嵌入式
2.6.28 DMA[0x50000000, 0x58000000] 此内核只有这一个区
3.4.24 NORMAL[0x50000000, 0x58000000] 此内核只有这一个区
只要用到分配内存,不管三七二十一,先包含 <linux/slab.h> 这个头文件
3、MMU:<笔记>
MMU
—————————————————
| | ADDR | TLB | |
| ARM core |====== | 缓存 | | 32位,物理地址
| | |———--| |=============
|————— | | | ADDR
| CP15 | 查页表 | |
| | | | |
| ---> c2(TTBR) | | |
| | | | |
| | | | |
———|—————————————
|-----------------------------> 页表基地址
a>.从arm出来的地址总线上,肯定是物理地址;虚拟地址一定不会出现在地址总线上,
用到虚拟地址的时候一定是 MMU 打开的情况,MMU拿到虚拟地址后取查页表,找到对应的物理地址,将其放到总线上;如果关闭 MMU,从ARM core中出来的地址就直接上总线了。前期在学裸板的时候,MMU 是关闭的,我们直接访问的就是物理地址。
b>.MMU
1.MMU 内有一段缓存,存放的是用过的页表的,
2.页表的每一个条目(32位,armA8以前的都是,armA9、armA15等为了使用大内存扩宽了)都在一定范围内对应一个虚拟地址和物理地址的对应关系
MMU 是通过物理地址访问页表的位置的(即找到基地址)
CP15 可用于开关 MMU,其中的 C2 寄存器内存放着页表的基地之
访问虚拟地址的时候, MMU 根据 C2里的基地址找到页表的位置,再根据基地址找到相应的条目,找到虚拟地址和物理地址的对应关系
c>.arm1176 手册的 464 页的图就是一个页表的放大图,此图的上面是低地址,下面是高地址
这些条目就最后两位的不同,有 4 中情况:
<1>.00 --- 非法的;此类条目是不能用的,每个进程都会有 4G 的虚拟内存,但不是全部的都会被映射,没映射的内存就是这一类,主要访问这类内存就会出现段错误,
<2>.01 --- 页映射;对应有二级页表;通过一级页表的31 ~ 20 位找到找到最后两位是 01 的条目,通过一级页表的 31 ~ 10 位找到二级页表的基地址,再根据 19 ~ 12 位作为二级页表的偏移,找到二级页表中对应的相应的条目,二级页表的条目根据最后两位也有 4 种情况:
(1)00 --- 非法的。与一级页表的相同
(2)01 --- 64K 大页映射。通过二级页表的31 ~ 16 位作为基地址,知道某个大页的基地址,然后 15 ~ 0 位做偏移找到相对应的条目,得到物理地址
(3)1x --- 4K 小页映射。通过二级页表的31 ~ 12 位作为基地址,知道某个大页的基地址,然后 11 ~ 0 位做偏移找到相对应的条目,得到物理地址
<3>.10 --- (bit(18) = 0)普通段映射;普通段映射,1M物理内存; 使用虚拟地址的前12位,做偏移。若找到段映射条目,把页表的31 ~ 20 位拿出,以段对齐,后20 位补 0,找到段,再将虚拟地址的后20位在段内做偏移,找到一个字,就是对应的物理地址;
<4>.10 --- (bit(18) = 1)超级段映射,16M物理内存;超级段映射方法相同与上面相同
d>.例子:根据映射关系举个例子,普通段映射eg:虚拟地址:0x12345678,物理地址:0x56000000 ~ 0x57000000
通过 123 做偏移找到的条目中前 12 位放 560, 映射到物理地址前12位560,后面以 0 补齐,就找到段的起始位置;
后20位做段内偏移,找到对应的字
e>.例子:二级页表;例如虚拟地址是0x12345000 找到0x56001000;
一级页表从51000000开始
二级页表从50000000开始
则在二级页表的第0x45个位置 尾部写10,然后把0x56001放到前31到10位上,二级页表完成
把一级页表的第123位,尾部写01,写上二级页表起始地址0x50000000的前20位写进去,一级页表完成
4、
虚拟内存 物理内存
4G ———————— ————————————
间接/动态 映射区 HIGH
—————————
直接/静态 映射区 ———————————— 896M
3G ———————— 0xc00000000 NORMAL
———————————— 16M
DMA
0 ————————
———————————— 0M
内核一启动,只会映射直接区,直接映射区是映射到DMA 和 NORMAL 内存处,即低896M内存了;这种映射叫做平坦映射
内核刚启动的时候 HIGH 是不能访问的
直接映射区会根据你的物理内存的大小变化,最大是 896M;剩下的都是间接映射区
HIGH内存一般通过 ioremap 映射到间接映射区;因为间接映射区地址有限,映射完一定要通过 iounmap 释放
5、buddy 内存管理子系统:
buddy上挂载了55条链表,分成11组(0 ~ 10),每组5条,每条链表上挂载的是5种不同的内存,5种类型的页:
MIGRATE_UNMOVABLE 0 -- 不可移动的页;启动规定好的,系统工作相关的
MIGRATE_RECLAIMABLE 1 -- 启动规定好的;系统工作相关的
MIGRATE_MOVABLE 2 -- 可移动的页;映射文件的都是可移动的,当内核想得到连续的内存,需要移动文件所占的物理内存,内核自动更改页表位置和映射关系,对程序不会产生影响
MIGRATE_PCPTYPES 3 -- 没用
MIGRATE_RESERVE 3 -- 没用
MIGRATE_ISOLATE 4 -- 没用
MIGRATE_TYPES 5 -- 没用
buddy子系统是基于MMU的,是管理内存子系统最底层的一层,每一组所挂的链表的节点大小等于 2 的组号次幂 乘以 4K(页的大小),即 2^(组号) * 4K;
由上可知, buddy 分配连续的最大物理空间是 4M,可以通过修改结构体 struct zone 中的 MAX_ARDER 的值增加组个数
buddy子系统挂载的永远都是空闲的内存;申请内存时,按照大小是层0组开始一层层的向上的,不够向上层借;释放内层时一样,若是连续内存,大小达到上层要求,就会挂载到上层,一层层的往上
优点:能最大限度的保证有连续的内存
缺点:分配方法粗糙,操作范围最小为 4K,会造成内存浪费
buddy子系统常用函数:
alloc_pages() 申请连续的页,物理地址连续;
释放用 __free_pages()
alloc_page() 申请一页;
释放用 __free_page()
__get_free_pages() 与 alloc_pages() 函数没什么区别;
释放用 free_pages()
__get_free_page() 与 alloc_page() 函数没什么区别;
释放用 free_page()
注:不建议在 buddy 中拿内存
6、slab 子系统
基于 buddy 子系统的一层子系统
slab 从buddy子系统中申请的内存分成两类,通用内存、专用内存:
通用内存:
可以访问到字节
申请内存函数:
< 一下函数带 z 只是会把申请的内存刷成 0 >
kmalloc() / kzalloc():所申请的内存虚拟地址和物理地址都是连续的,一般最大 4M
释放用 kfree()
vmalloc() / vzalloc():申请大内存,虚拟地址一定连续,物理地址可能不连续
释放用 vfree()
vmalloc / vzalloc 需要自己的头文件 <linux/vmalloc.h>
申请优先从高端内存(HIGH)分配内存,高端内存没有时再取低端内存
专用内存:
可用于进程、网络、...
用于进程:会提前创建好进程结构体 task_struct ,用就拿,不用送回
用于网络:会提前创建号缓存 sk_buff,用就拿,不用送回
创建高速缓存:kmem_cache_create()
创建一次会有 n 个,大于 n 时,内核会自动再分配 n 个(不同的内核 n 的大小不定)
拿从高速缓存:kmem_cache_alloc()
放回高速缓存:kmem_cache_free()
销毁高速缓存:kmem_cache_destory()
优先把高速缓存放到硬件cache中
每一个高速缓存对应于一个结构体 struct kmem_cache,自己创建页需要先创建这么一个结构体,可以通过 kmem_cache 的个数来判断高速缓存的个数
kmem_cache_creat("name", size,align,flags, ctor)
name:你的高速缓存的名字
size:每个元素的大小
align:0;无用是就填 0
flags:对其方式;因为高速缓存会被缓存到硬件cache中,所以以硬件cache行的方式对其最快
ctor:函数指针;会产生 n 个结构体,这个函数会被调用n次,这个函数可自己写,可以用于初始化结构体内某些有规律的成员
7.dma 一致性内存
注意与 SCU(一致性硬件)的区分
一致性硬件:
SCU可以保证一级cash的一致性
cache分为数据cache和指令cache,
ddr把东西放到cache,arm再从cache拿
声卡放音乐的时候,cpu需要把数据写道cache,然后cache数据拿到ddr,dma再从ddr中取数据放到pcmdata,再声卡播放
要求实时性很强,arm 中的数据一到 cache 中,就立刻需要同步到 DDR(内存)中,DMA会不断从内存中读取数据,这就叫一致性内存
分配一致性内存:dma_alloc_coherent() / dam_zalloc_coherent() < z 清零 >
包含在头文件<linux/dma-mapping.h>
释放一致性内存:dma_free_coherent()
DMA 使用的一般都是物理地址,%99.9的可能是这样的;只有很少很少的一部分,会使用虚拟地址,DMA内部就会带有一个硬件,能读取页表
v = dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)
参数1:NULL先不需关心
参数2:申请大小
参数3:引用回一个物理地址,这个物理地址可以在配置dma通道的时候用
参数4:GFP_KERNEL
这种方式是放在动态内存区(间接映射区)这里就相当于是映射了一个设备dma,还有ioremap也是
8、修改页表:
外设的地址也叫设备内存,一般不会映射到直接映射区
通过函数 ioremap 将物理地址映射到间接映射区,用完就要用 iounmap 函数释放
包含头文件 <asm/io.h>
ioremap(0x7f008000, 0x1000)
分配地址要以页对齐,因为底层实现一次是一个页(4K)
9、综述:
映射 修改页表
——————————————————————————————————————————————
dma 一致性内存
——————————————————————————————————————————————
slab
1.物理连续 2.物理不连续 3.高速缓存
——————————————————————————————————————————————
buddy
——————————————————————————————————————————————
MMU
——————————————————————————————————————————————
buddy子系统是内核分配内存的基础,slab从buddy申请内存,然后把内存告诉通用缓存或者专用缓存(slab就是联系buddy和cache的桥梁),在这里能调用kmalloc和zmalloc和创建高速缓存,如果cache需要和内存同步,就要创建一致性内存dma_alloc系列函数,如果在知道物理地址但是不知道虚拟地址的情况下,就需要修改页表来让物理地址映射到间接映射区,这些所有的申请空间函数和都映射到动态映射区,其中dma和ioremap相当于添加了一个设备,即把外部的dma和led等等设备的物理地址映射到动态内存区(间接映射区)
10、函数解析:
kmalloc/kzalloc(20, GPL_KERNEL);
GPL_KERNEL:允许函数睡眠
GPL_ATOMIC:不允许函数睡眠,被打断就返回出错
vmalloc(20)
默认传的就是 GPL_KERNEL,在不允许睡眠的情况下不能使用这个函数
11、不同层次申请、释放内存方法:
代码参考 <path>/code/04mm/*
从buffy子系统里拿
方法1:
创建页表
alloc_pages可以申请若干个连续的页
__free_pages()
alloc_page可以申请一个页
__free_page()
方法2:
创建页表
__get_free_pages() 申请若干页
free_pages()
__get_free_page()申请一个页
free_page()
从slab里拿
方法3:
创建高速缓存:
kmem_cache_create()创建高速缓存
kmem_cache_alloc()申请高速缓存空间(拿东西)
kmem_cache_free()释放缓存空间(放回去)
kmem_cache_destroy()销毁高速缓存
为dma申请
方法4:
创建一致性内存
dma_alloc_coherent()分配
dma_zalloc_coherent()//把内存里所有东西都清0
dma_free_coherent()
为映射创建
方法5:
例如知道物理地址 0x72000000 然后用ioremap()做映射(映射到间接映射区)用iounmap()释放
因为在代码里只能访问虚拟地址,所以需要把物理地址映射到间接映射区,然后为了不混淆,用的时候映射,不用的时候释放
1、#define abc(n) do{xxx;yyy;zzz;}while(0)
#define abc(n) {xxx;yyy;zzz;}
加上do while 和不加可能执行结果可能一样,但是内核一般都加
是怕在执行函数的时候出现错误
int a ;
if(a > 0)
abc(100);//若用下面的宏定义,加上分号编译出错,是因为在后面加了分号,多加的分号就分割了if / else
else
abc(200);
return 0;
################# 内存管理 #################
1、概念:
MPU 内存保护单元< 有操作系统的一般不用,用 MMU >
会写一个页表类似的东西,但不是页表,记录下面内存空间的只读、同步等属性
每一段内存空间的权限:ro sync ex rw nx ...
示意将内存分为:[0, 0x1000)[0x1000, 0x2000)[0x2000, 0x3000)[0x3000, ...)
MMU 内存管理单元 MMU = MPU + (查找页表)
arm9、arm11、armA系列内有 MMU;一般面向应用的电子带有。但是并不是说有操作系统就一定有 MMU
如果做工控,控制器一般有 MPU 或没有,eg:cortex-M 和 cortex-R 系列单片机用的就是 MPU;只有面向想飞电子的高端芯片才用 MMU,安装有操作系统的控制器,要实现多任务,多任务会用到虚拟地址,所以需要 MMU。但并使有操作系统就一定有 MMU
2、linux内核管理机制
bootargs = "mem = 128M" 当初写这个原因就是告诉内核内存有多大,内核就可以计算出用分配多少页表
内核用的是小页映射 page = 4K
每个页对应一个结构体 struct page ---> 描述一页的物理内存
virtual 如果分配过存虚拟地址,没有的就是NULL
page 结构体在内存中以连续、有序数组存放
struct zone //标记的都是物理地址;标准的linux/unix X86 上这样分配
DMA [0 - 16M] //分这16M是历史问题,以前的DMA只有24位,最大访问16M,所以留下这块给DMA用
NORMAL[16M - 896M]
HIGH [896M - ...]
嵌入式
2.6.28 DMA[0x50000000, 0x58000000] 此内核只有这一个区
3.4.24 NORMAL[0x50000000, 0x58000000] 此内核只有这一个区
只要用到分配内存,不管三七二十一,先包含 <linux/slab.h> 这个头文件
3、MMU:<笔记>
MMU
—————————————————
| | ADDR | TLB | |
| ARM core |====== | 缓存 | | 32位,物理地址
| | |———--| |=============
|————— | | | ADDR
| CP15 | 查页表 | |
| | | | |
| ---> c2(TTBR) | | |
| | | | |
| | | | |
———|—————————————
|-----------------------------> 页表基地址
a>.从arm出来的地址总线上,肯定是物理地址;虚拟地址一定不会出现在地址总线上,
用到虚拟地址的时候一定是 MMU 打开的情况,MMU拿到虚拟地址后取查页表,找到对应的物理地址,将其放到总线上;如果关闭 MMU,从ARM core中出来的地址就直接上总线了。前期在学裸板的时候,MMU 是关闭的,我们直接访问的就是物理地址。
b>.MMU
1.MMU 内有一段缓存,存放的是用过的页表的,
2.页表的每一个条目(32位,armA8以前的都是,armA9、armA15等为了使用大内存扩宽了)都在一定范围内对应一个虚拟地址和物理地址的对应关系
MMU 是通过物理地址访问页表的位置的(即找到基地址)
CP15 可用于开关 MMU,其中的 C2 寄存器内存放着页表的基地之
访问虚拟地址的时候, MMU 根据 C2里的基地址找到页表的位置,再根据基地址找到相应的条目,找到虚拟地址和物理地址的对应关系
c>.arm1176 手册的 464 页的图就是一个页表的放大图,此图的上面是低地址,下面是高地址
这些条目就最后两位的不同,有 4 中情况:
<1>.00 --- 非法的;此类条目是不能用的,每个进程都会有 4G 的虚拟内存,但不是全部的都会被映射,没映射的内存就是这一类,主要访问这类内存就会出现段错误,
<2>.01 --- 页映射;对应有二级页表;通过一级页表的31 ~ 20 位找到找到最后两位是 01 的条目,通过一级页表的 31 ~ 10 位找到二级页表的基地址,再根据 19 ~ 12 位作为二级页表的偏移,找到二级页表中对应的相应的条目,二级页表的条目根据最后两位也有 4 种情况:
(1)00 --- 非法的。与一级页表的相同
(2)01 --- 64K 大页映射。通过二级页表的31 ~ 16 位作为基地址,知道某个大页的基地址,然后 15 ~ 0 位做偏移找到相对应的条目,得到物理地址
(3)1x --- 4K 小页映射。通过二级页表的31 ~ 12 位作为基地址,知道某个大页的基地址,然后 11 ~ 0 位做偏移找到相对应的条目,得到物理地址
<3>.10 --- (bit(18) = 0)普通段映射;普通段映射,1M物理内存; 使用虚拟地址的前12位,做偏移。若找到段映射条目,把页表的31 ~ 20 位拿出,以段对齐,后20 位补 0,找到段,再将虚拟地址的后20位在段内做偏移,找到一个字,就是对应的物理地址;
<4>.10 --- (bit(18) = 1)超级段映射,16M物理内存;超级段映射方法相同与上面相同
d>.例子:根据映射关系举个例子,普通段映射eg:虚拟地址:0x12345678,物理地址:0x56000000 ~ 0x57000000
通过 123 做偏移找到的条目中前 12 位放 560, 映射到物理地址前12位560,后面以 0 补齐,就找到段的起始位置;
后20位做段内偏移,找到对应的字
e>.例子:二级页表;例如虚拟地址是0x12345000 找到0x56001000;
一级页表从51000000开始
二级页表从50000000开始
则在二级页表的第0x45个位置 尾部写10,然后把0x56001放到前31到10位上,二级页表完成
把一级页表的第123位,尾部写01,写上二级页表起始地址0x50000000的前20位写进去,一级页表完成
4、
虚拟内存 物理内存
4G ———————— ————————————
间接/动态 映射区 HIGH
—————————
直接/静态 映射区 ———————————— 896M
3G ———————— 0xc00000000 NORMAL
———————————— 16M
DMA
0 ————————
———————————— 0M
内核一启动,只会映射直接区,直接映射区是映射到DMA 和 NORMAL 内存处,即低896M内存了;这种映射叫做平坦映射
内核刚启动的时候 HIGH 是不能访问的
直接映射区会根据你的物理内存的大小变化,最大是 896M;剩下的都是间接映射区
HIGH内存一般通过 ioremap 映射到间接映射区;因为间接映射区地址有限,映射完一定要通过 iounmap 释放
5、buddy 内存管理子系统:
buddy上挂载了55条链表,分成11组(0 ~ 10),每组5条,每条链表上挂载的是5种不同的内存,5种类型的页:
MIGRATE_UNMOVABLE 0 -- 不可移动的页;启动规定好的,系统工作相关的
MIGRATE_RECLAIMABLE 1 -- 启动规定好的;系统工作相关的
MIGRATE_MOVABLE 2 -- 可移动的页;映射文件的都是可移动的,当内核想得到连续的内存,需要移动文件所占的物理内存,内核自动更改页表位置和映射关系,对程序不会产生影响
MIGRATE_PCPTYPES 3 -- 没用
MIGRATE_RESERVE 3 -- 没用
MIGRATE_ISOLATE 4 -- 没用
MIGRATE_TYPES 5 -- 没用
buddy子系统是基于MMU的,是管理内存子系统最底层的一层,每一组所挂的链表的节点大小等于 2 的组号次幂 乘以 4K(页的大小),即 2^(组号) * 4K;
由上可知, buddy 分配连续的最大物理空间是 4M,可以通过修改结构体 struct zone 中的 MAX_ARDER 的值增加组个数
buddy子系统挂载的永远都是空闲的内存;申请内存时,按照大小是层0组开始一层层的向上的,不够向上层借;释放内层时一样,若是连续内存,大小达到上层要求,就会挂载到上层,一层层的往上
优点:能最大限度的保证有连续的内存
缺点:分配方法粗糙,操作范围最小为 4K,会造成内存浪费
buddy子系统常用函数:
alloc_pages() 申请连续的页,物理地址连续;
释放用 __free_pages()
alloc_page() 申请一页;
释放用 __free_page()
__get_free_pages() 与 alloc_pages() 函数没什么区别;
释放用 free_pages()
__get_free_page() 与 alloc_page() 函数没什么区别;
释放用 free_page()
注:不建议在 buddy 中拿内存
6、slab 子系统
基于 buddy 子系统的一层子系统
slab 从buddy子系统中申请的内存分成两类,通用内存、专用内存:
通用内存:
可以访问到字节
申请内存函数:
< 一下函数带 z 只是会把申请的内存刷成 0 >
kmalloc() / kzalloc():所申请的内存虚拟地址和物理地址都是连续的,一般最大 4M
释放用 kfree()
vmalloc() / vzalloc():申请大内存,虚拟地址一定连续,物理地址可能不连续
释放用 vfree()
vmalloc / vzalloc 需要自己的头文件 <linux/vmalloc.h>
申请优先从高端内存(HIGH)分配内存,高端内存没有时再取低端内存
专用内存:
可用于进程、网络、...
用于进程:会提前创建好进程结构体 task_struct ,用就拿,不用送回
用于网络:会提前创建号缓存 sk_buff,用就拿,不用送回
创建高速缓存:kmem_cache_create()
创建一次会有 n 个,大于 n 时,内核会自动再分配 n 个(不同的内核 n 的大小不定)
拿从高速缓存:kmem_cache_alloc()
放回高速缓存:kmem_cache_free()
销毁高速缓存:kmem_cache_destory()
优先把高速缓存放到硬件cache中
每一个高速缓存对应于一个结构体 struct kmem_cache,自己创建页需要先创建这么一个结构体,可以通过 kmem_cache 的个数来判断高速缓存的个数
kmem_cache_creat("name", size,align,flags, ctor)
name:你的高速缓存的名字
size:每个元素的大小
align:0;无用是就填 0
flags:对其方式;因为高速缓存会被缓存到硬件cache中,所以以硬件cache行的方式对其最快
ctor:函数指针;会产生 n 个结构体,这个函数会被调用n次,这个函数可自己写,可以用于初始化结构体内某些有规律的成员
7.dma 一致性内存
注意与 SCU(一致性硬件)的区分
一致性硬件:
SCU可以保证一级cash的一致性
cache分为数据cache和指令cache,
ddr把东西放到cache,arm再从cache拿
声卡放音乐的时候,cpu需要把数据写道cache,然后cache数据拿到ddr,dma再从ddr中取数据放到pcmdata,再声卡播放
要求实时性很强,arm 中的数据一到 cache 中,就立刻需要同步到 DDR(内存)中,DMA会不断从内存中读取数据,这就叫一致性内存
分配一致性内存:dma_alloc_coherent() / dam_zalloc_coherent() < z 清零 >
包含在头文件<linux/dma-mapping.h>
释放一致性内存:dma_free_coherent()
DMA 使用的一般都是物理地址,%99.9的可能是这样的;只有很少很少的一部分,会使用虚拟地址,DMA内部就会带有一个硬件,能读取页表
v = dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)
参数1:NULL先不需关心
参数2:申请大小
参数3:引用回一个物理地址,这个物理地址可以在配置dma通道的时候用
参数4:GFP_KERNEL
这种方式是放在动态内存区(间接映射区)这里就相当于是映射了一个设备dma,还有ioremap也是
8、修改页表:
外设的地址也叫设备内存,一般不会映射到直接映射区
通过函数 ioremap 将物理地址映射到间接映射区,用完就要用 iounmap 函数释放
包含头文件 <asm/io.h>
ioremap(0x7f008000, 0x1000)
分配地址要以页对齐,因为底层实现一次是一个页(4K)
9、综述:
映射 修改页表
——————————————————————————————————————————————
dma 一致性内存
——————————————————————————————————————————————
slab
1.物理连续 2.物理不连续 3.高速缓存
——————————————————————————————————————————————
buddy
——————————————————————————————————————————————
MMU
——————————————————————————————————————————————
buddy子系统是内核分配内存的基础,slab从buddy申请内存,然后把内存告诉通用缓存或者专用缓存(slab就是联系buddy和cache的桥梁),在这里能调用kmalloc和zmalloc和创建高速缓存,如果cache需要和内存同步,就要创建一致性内存dma_alloc系列函数,如果在知道物理地址但是不知道虚拟地址的情况下,就需要修改页表来让物理地址映射到间接映射区,这些所有的申请空间函数和都映射到动态映射区,其中dma和ioremap相当于添加了一个设备,即把外部的dma和led等等设备的物理地址映射到动态内存区(间接映射区)
10、函数解析:
kmalloc/kzalloc(20, GPL_KERNEL);
GPL_KERNEL:允许函数睡眠
GPL_ATOMIC:不允许函数睡眠,被打断就返回出错
vmalloc(20)
默认传的就是 GPL_KERNEL,在不允许睡眠的情况下不能使用这个函数
11、不同层次申请、释放内存方法:
代码参考 <path>/code/04mm/*
从buffy子系统里拿
方法1:
创建页表
alloc_pages可以申请若干个连续的页
__free_pages()
alloc_page可以申请一个页
__free_page()
方法2:
创建页表
__get_free_pages() 申请若干页
free_pages()
__get_free_page()申请一个页
free_page()
从slab里拿
方法3:
创建高速缓存:
kmem_cache_create()创建高速缓存
kmem_cache_alloc()申请高速缓存空间(拿东西)
kmem_cache_free()释放缓存空间(放回去)
kmem_cache_destroy()销毁高速缓存
为dma申请
方法4:
创建一致性内存
dma_alloc_coherent()分配
dma_zalloc_coherent()//把内存里所有东西都清0
dma_free_coherent()
为映射创建
方法5:
例如知道物理地址 0x72000000 然后用ioremap()做映射(映射到间接映射区)用iounmap()释放
因为在代码里只能访问虚拟地址,所以需要把物理地址映射到间接映射区,然后为了不混淆,用的时候映射,不用的时候释放
- 六。内存管理机制--MMU
- MMU管理机制
- 内存管理机制
- 内存管理机制
- 内存管理机制
- 内存管理机制
- 内存管理机制
- 内存管理机制
- 内存管理机制
- 控制器中如何设计MMU--虚拟内存管理机制
- 全面介绍Windows内存管理机制及C++内存分配实例(六):堆栈
- Windows内存管理机制及C++内存分配实例(六):堆栈
- 全面介绍Windows内存管理机制及C++内存分配实例(六):堆栈
- 全面介绍Windows内存管理机制及C++内存分配实例(六):堆栈
- 全面介绍Windows内存管理机制及C++内存分配实例(六):堆栈
- 内存优化:内存管理机制
- 内存管理单元 MMU
- 内存管理单元mmu
- 机器学习中的数学(1)-回归(regression)、梯度下降(gradient descent)
- datastage 是什么
- 2013.11.08
- ubuntu 12.10中MyEclipse 10.6+下载+安装+破解
- 当构造自定义的key类型时注意事项
- 六。内存管理机制--MMU
- 正则表达式语法
- Oracle中last_day()函数的用法
- IntelliJ IDEA 12 创建Web项目教程
- eclipse中乱码解决及注释模板导入及linux下eclipse导入源码导致源码编译报错
- 共享游标的弊端
- ubuntu java tomcat 环境变量配置
- hdu4771Stealing Harry Potter's Precious
- 杭电1212之大数