1.基本概念
1) linux内核管理内存时以页(page)为单位
通常一个物理内存页的大小为4KB
内核会为每个物理内存页建立一个数据结构
include/linux/mm_types.h
struct page {
//保存该内存被引用的次数 为0 代表空闲页
atomic_t _refcount;
};
2) linux内核在管理内存时,并不是一视同仁
高端内存: 物理内存超过896M(大小可调)的空间
动态映射的策略
使用时进行映射,使用完毕后马上解除映射
动态内存映射区
永久内存映射区
固定内存映射区
低端内存: 介于0896M的内存称为低端内存
映射关系是固定的
虚拟地址= 0xc0000000 + 物理偏移
2.内核中动态申请内存的方法
2.1 按页申请
方式一:
alloc_pages(gfp_t gfp_mask, unsigned int order);
order,要申请 2^order 个物理内存页
static inline void *page_address(const struct page *page);
将page指定的物理内存进行映射
返回映射后的起始虚拟地址
方式二:
__get_free_pages(gfp_t gfp_mask, unsigned int order);
order,要申请 2^order 个物理内存页
方式三:
unsigned long __get_free_page(unsigned int flags);//申请一个物理内存页
unsigned long get_zeroed_page(gfp_t gfp_mask);//申请一个物理内存页,并清0
free_pages(unsigned long addr, unsigned int order);
2.2 按字节申请
方式一:
kmalloc/kfree
void *kmalloc(size_t size, gfp_t flags);
size,要申请的字节数
flags,
常用的取值: GFP_KERNEL,申请内存过程中 可能发生阻塞
不能用于中断上下文
GFP_ATOMIC,申请不成功 直接返回错误 不会阻塞
可以用于中断上下文
分配得到的物理内存是连续的 (执行效率高)
kfree(void *p)
p, kmalloc返回的虚拟地址
方式二:
vmalloc/vfree
void *vmalloc(unsigned long size);
size, 要申请的字节数
得到的虚拟地址是连续的
但是其对应的物理地址可能不连续
void vfree(const void *addr);//一定不要在中断上下文中使用
2.3 映射之后的相互转换
使用的前提是已经建立了映射
//虚拟地址转物理地址
phys_addr_t virt_to_phys(const volatile void *x);
//物理地址转虚拟地址
void *phys_to_virt(phys_addr_t x);
#include <linux/init.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
MODULE_LICENSE("GPL");
unsigned long pages_addr = 0;
void * kmalloc_addr = 0;
void * vmalloc_addr = 0;
int __init kernelspace_init(void)
{
int err = 0;
/*申请8个空闲物理内存页*/
pages_addr = __get_free_pages (GFP_KERNEL, 3);
if (!pages_addr)
{
err = -ENOMEM;
goto failure_get_free_pages;
}
printk ("pages virt addr = %p phy addr = %p", pages_addr, virt_to_phys(pages_addr));
kmalloc_addr = kmalloc(100, GFP_KERNEL);
if (!kmalloc_addr)
{
err = -ENOMEM;
//return err;//不能在这里直接返回,会发生内存泄漏
goto failure_kmalloc;
}
printk ("kmalloc vir addr = %p, phy addr = %p\n", kmalloc_addr, virt_to_phy(kmalloc_addr));
vmalloc_addr = vmalloc(10*1024);
if (!vmalloc_addr)
{
err = -ENOMEM;
goto failure_vmalloc;
}
return 0;
failure_vmalloc:
kfree(kmalloc_addr);
failure_kmalloc:
free_pages (pages_addr, 3);
failure_get_free_pages:
return err;
}
void __exit kernelspace_exit(void)
{
vfree(vmalloc_addr);
kfree(kmalloc_addr);
free_pages(pages_addr,3);
}
module_init(kernelspace_init);
module_exit(kernelspace_exit);