Linux内核中的内存管理
Linux内核中的内存管理
基本概念
Linux内核管理内存时以物理页(4k)为单位内核会为每一个物理内存页建立一个数据结构
1 | struct page{ |
Linux内核在管理内存时,并不一视同仁
高端内存:物理内存超过896M(大小可调)的空间
动态映射的策略,使用时进行映射,使用完毕之后ima接触映射
低端内存:介于0~896M的内存,映射关系是固定的,虚拟地址 = 0xc0000000 + 物理偏移
内核中动态申请内存的方法
按页申请
方式一
alloc_pages(gfd_mask, order)
order,要申请2^order
void *page_address(const struct page *page)
将page
指定的物理内存进行映射,返回映射后的虚拟地址
方式二
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
要申请2^order个物理内存页
方式三
__get_free_page
申请一页的物理内存
get_zeroed_page(gfp_t gfp_mask)
申请一个内存页,并清零
释放物理内存页:
void free_pages(unsigned long addr, unsigned int order)
按字节申请
方式一
kmalloc / kfree
kmalloc(size, flags)
size要申请的字节个数
flags常用的取值:GFP_KERNEL
、GFP_ATMIC
GFP_KERNEL
申请内存的过程中,可能发生阻塞,所以不能用于中断上下文
GFP_ATMIC
申请内存的过程中,一旦阻塞立即返回,可以用于中断上下文
kmalloc申请的内存是连续的,执行效率高
方式二
vmalloc / vfree
vmalloc(size)
size要申请的字节个数
vmalloc
得到的虚拟地址也是连续的,但是将来对应的物理地址可能不连续
vfree(ptr);
一定不要在中断上下文中使用
映射之后的转换
前提是必须已经建立映射
虚拟地址到物理地址之间的转换virt_to_phys
物理地址到虚拟地址之间的转换static inline void *phys_to_virt(phys_addr_t x)
ioremap
在Linux内核态直接访问特殊功能寄存器
统一编址:将内存和外设(特殊功能寄存器)使用一套编号,ARM、mips中使用的是统一编址
独立编制:使用不同编号
将外设称为IO端口
使用步骤
申请I/O内存(告诉linux内核我要使用)
reques_mem_region(start,n,name)
start
要申请的起始物理地址
n
要连续申请的字节数
name
名称
static inline void __iomem *ioremap(phys_addr_t offset, unsigned long size)
offset
要映射的起始物理地址
size
要连续映射的字节数
返回值是映射后的虚拟地址
通过映射后的虚拟地址访问I/O内存
1 | writeb *((volatile unsigned char *) ptr) = 0x100; |
解除映射
static inline void iounmap(void __iomem *addr)
释放IO内存(告诉Linux内核不用了)
release_mem_region
release_region(start,n)
mmap
实际编程中有两种mmap
的使用场景
- 映射普通文件
- 映射设备文件
映射LED设备,在LED设备中实现mmap
的功能,该函数将控制LED的特殊功能寄存器对应的物理地址映射到0~3G的虚拟地址空间,达到在用户空间直接操作硬件的效果
硬件操作函数集合中
mmp
,可以将硬件的物理地址映射到用户空间,从而在用户空间直接空着硬件
可以使用mmap函数直接将LCD显存映射到用户空间,这样做的好处在于可以减小数据的拷贝的过程
g-sensor驱动在Linux中的实现
IIC控制器的驱动在Linux中已经实现了
1 | struct bus_type i2c_bus_type = { |
设备
1 | struct i2c_client { |
驱动
1 | struct i2c_driver { |
实验步骤
1234