本篇文章是学习了《linux内核设计与实现》和《linux设备驱动开发详解》关于linux 内存部分的记录。
MMU
内存管理单元,提供虚拟地址和物理地址映射、内存访问权限、cache缓存控制
TLB
缓存部分虚拟地址和物理地址的映射关系。
TTW
当TLB没有的时候,经过TTW转换后报错到TLB中;
页:
内核把物理页作为内存管理的基本单位,MMC通常都是以页为单位进行虚拟地址和物理地址的转换的;页的大小根据不同的体系结构而不同;32bit的为4K,64bit的为8k;使用struct page表示。
区:
对具有相似功能的页进行分组;一下会根据不同的体系结构有差异
ZONE_DMA 执行DMA操作
ZONE_NORMAL 证访问映射的页
ZONE_HIGHEM 高端内存 不能永久映射到内核地址空间
Linux的内存管理
通过MMU管理,每个进程的地址空间可以达到4GB,其中0-3GB为用户空间使用;3-4GB为kernel空间使用;用于空间不能访问ernel空间数据 只能通过系统调用进行访问;
1G的kernel空间又被划分为物理内存映射区、vmalloc虚拟内存映射区、高端页面映射区、专用页面映射区、系统保留区
内核接口
页操作:
字节操作
Kmalloc 分配指定的内存字节大小,在物理地址上是连续的;
Kmallco(size_t size,gfp_t flags);kfree 释放分配的空间
Size 申请的字节数
Flags 通常使用
GFP_KERNEL 这个会引起睡眠,再分配不出来的时候回等待,因此不能在中断上下文中使用
GFP_ATOMIC 不会睡眠,分配不到立即返回,可以在中断中使用;
Vmalloc 分配指定内存字节大小,在虚拟地址上是连续的 物理地址上是不连续的;需要对物理地址和虚拟地址进行映射,因此效率不高;
会引起睡眠,不能在中断上下文中使用
Vmallc(unsigned long size) vfree
栈上的静态分配
用户空间栈—空间大 可以动态增长
内核空间栈—空间小而且固定 通常是2个页或者2个页 可以通过配置设定;如果设置为1个页,中断栈就会有独立栈空间
局部变量总空间不能超过几百字节;
栈溢出会造成系统自己火星莫名破坏其他数据,导致异常问题;
Malloc
Malloc 并不一定每次都进行系统调用;比如在执行free前执行mallopt,free就不会把内存还给kernel,地址由C库保存。
Malloc返回成功后,实际上并没有申请空间只有开始写入后才开始分配;
I/O端口和I/O内存
I/O端口是在X86上的概念这里并不描述
I/O内存 就是UART USB等控制器地址;访问这个地址通过ioremap函数映射成虚拟地址访问;通过ioumap释放;devm_ioremap不需要释放
访问由2种方式
C指针直接操作寄存器
Kernel提供的接口:readb readw readl writeb writew writel
设备地址映射到用户空间
1、显示设备的kernel地址空间数据直接映射到用户空间,防止在copy一次;
2、应用程序直接访问某段地址;
两者都是通过应用空间的mmap函数和驱动中对应的实现函数完成;mmap函数必须已页为单位进行映射
比如直接访问地址
Mmap的fd是打开的/dev/mem节点
static int drv_mmap(struct file*file, struct vm_area_struct *vma){
struct drv_test *devp = file->private_data; /*获得设备结构体指针*/
vma->vm_flags |= VM_IO;
vma->vm_flags |= VM_RESERVED;
if (remap_pfn_range(vma,vma->vm_start,virt_to_phys(devp->mem)>>PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot))
return -EAGAIN;
return 0;
}
剩余补充:
如果频繁申请和释放内存,可以考虑如下方式
Slab 如果需要频繁申请和释放,并且希望前后2次申请在同一块内存;
内存池
分配大量小对象 后备缓存技术
Mempool_create
Mempoll_alloc
Mempoll_free
Mempoll_destroy
DMA
DMA编程待补充
DMA 和cache
注意dma的目的地址和cache对应的ddr地址是否有重叠;方式dma把ddr地址修改但是cache没有更新的问题;
每个CPU接口
内核提供接口为每个cpu提供需要的内存
申请内存Alloc_percpu
操作内存 get_cpu_var put_cpu_var