kzalloc
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/printk.h>
#define MY_PAGE_SIZE (1UL << 12)
static void *virtual_addr_start;
void *my_zeroed_page(void)
{
// return (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
return kzalloc(MY_PAGE_SIZE, GFP_KERNEL);
}
static int __init my_demo_init(void)
{
unsigned long long int phy_addr_start;
virtual_addr_start = my_zeroed_page();
if (!virtual_addr_start) {
pr_err("%s, memory allocation failed\n", __func__);
return -ENOMEM;
}
// 虚拟地址和物理地址的映射
phy_addr_start = virt_to_phys(virtual_addr_start);
pr_info("%s, virtual start addr 0x%x\n", __func__, virt_to_phys);
pr_info("%s, phy start addr 0x%x\n", __func__, phy_addr_start);
// 测试中间某个地址
pr_info("%s, virtual addr 0x%x\n", __func__, virt_to_phys + MY_PAGE_SIZE/2);
pr_info("%s, phy addr 0x%x\n", __func__, phy_addr_start + MY_PAGE_SIZE/2);
// 测试结束地址
pr_info("%s, virtual end addr 0x%x\n", __func__, virt_to_phys + MY_PAGE_SIZE);
pr_info("%s, phy end addr 0x%x\n", __func__, phy_addr_start + MY_PAGE_SIZE);
return 0;
}
static void my_demo_exit(void)
{
// free_page(virtual_addr_start); // 此时形参是 unsigned long 类型
if (!IS_ERR_OR_NULL(virtual_addr_start)) {
kfree((void *)virtual_addr_start);
virtual_addr_start= NULL;
pr_info("%s, memory free success\n", __func__);
} else {
pr_err("%s, memory free failed\n", __func__);
}
}
module_init(my_demo_init);
module_exit(my_demo_exit);
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("kiss1994");
MODULE_DESCRIPTION ("kmalloc and vmalloc");
[ 1584.935084] my_demo_init, virtual start addr 0xc06b5000
[ 1584.935089] my_demo_init, phy start addr 0x393cd000
[ 1584.935091] my_demo_init, virtual addr 0xc06b5800
[ 1584.935093] my_demo_init, phy addr 0x393cd800
[ 1584.935094] my_demo_init, virtual end addr 0xc06b6000
[ 1584.935096] my_demo_init, phy end addr 0x393ce000
[ 1601.537419] my_demo_exit, memory free success
地址确实是连续的
//==================================================================
vzalloc
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/printk.h>
#include <linux/vmalloc.h>
#define MY_PAGE_SIZE (1UL << 12)
static void *virtual_addr_start;
void *my_zeroed_page(void)
{
return vzalloc(MY_PAGE_SIZE);
}
static int __init my_demo_init(void)
{
unsigned long long int phy_addr_start;
virtual_addr_start = my_zeroed_page();
if (!virtual_addr_start) {
pr_err("%s, memory allocation failed\n", __func__);
return -ENOMEM;
}
// 虚拟地址和物理地址的映射
phy_addr_start = virt_to_phys(virtual_addr_start);
pr_info("%s, virtual start addr 0x%x\n", __func__, virt_to_phys);
pr_info("%s, phy start addr 0x%x\n", __func__, phy_addr_start);
// 测试中间某个地址
pr_info("%s, virtual addr 0x%x\n", __func__, virt_to_phys + MY_PAGE_SIZE/2);
pr_info("%s, phy addr 0x%x\n", __func__, phy_addr_start + MY_PAGE_SIZE/2);
// 测试结束地址
pr_info("%s, virtual end addr 0x%x\n", __func__, virt_to_phys + MY_PAGE_SIZE);
pr_info("%s, phy end addr 0x%x\n", __func__, phy_addr_start + MY_PAGE_SIZE);
return 0;
}
static void my_demo_exit(void)
{
// free_page(virtual_addr_start); // 此时形参是 unsigned long 类型
if (!IS_ERR_OR_NULL(virtual_addr_start)) {
vfree((void *)virtual_addr_start);
virtual_addr_start= NULL;
pr_info("%s, memory free success\n", __func__);
} else {
pr_err("%s, memory free failed\n", __func__);
}
}
module_init(my_demo_init);
module_exit(my_demo_exit);
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("kiss1994");
MODULE_DESCRIPTION ("kmalloc and vmalloc");
[ 2142.149619] my_demo_init, virtual start addr 0xc06ba000
[ 2142.149625] my_demo_init, phy start addr 0x2c5000
[ 2142.149627] my_demo_init, virtual addr 0xc06ba800
[ 2142.149628] my_demo_init, phy addr 0x2c5800
[ 2142.149630] my_demo_init, virtual end addr 0xc06bb000
[ 2142.149632] my_demo_init, phy end addr 0x2c6000
这里碰巧是连续的,可能是我的电脑内存不紧张,要么就是Linux对PAGE大小的内存申请做了优化
vmalloc 和 __vmalloc
Allocate enough pages to cover @size from the page level allocator with @gfp_mask flags. Map them into contiguous kernel virtual space. For tight control over page level allocator and protection flags use __vmalloc() instead.
两者区别:
void *vmalloc(unsigned long size)
{
return __vmalloc_node(size, 1, GFP_KERNEL, NUMA_NO_NODE, __builtin_return_address(0));
}
void *__vmalloc(unsigned long size, gfp_t gfp_mask)
{
return __vmalloc_node(size, 1, gfp_mask, NUMA_NO_NODE, __builtin_return_address(0));
}
参考博客中的区别
https://www.cnblogs.com/eleclsc/p/11531589.html 1).kmalloc/__get_free_pages申请的内存块都在物理内存映射区,即在(PAGE_OFFSET,HIGH_MEMORY)之间, 处理的都是物理地址,且保证在物理地址空间上是连续的; 二者返回的都是虚拟地址,如果需要得到正确的物理地址,需要使用virt_to_phys()进行转换; 但是,kmalloc和vmalloc都是以字节为单位进行申请, 而__get_free_pages()则是以页为单位进行申请; 2).vmalloc函数申请的内存块位于虚拟内存映射区,即在(VMALLOC_START,VMALLOC_END)之间, 处理的都是虚拟内存,且保证在虚拟地址空间上是连续的,但是在物理地址空间上不要求连续; 一般作为交换区、模块的内存使用; 3).kmalloc和vmalloc都是基于slab机制实现的,但是kmalloc的速度比vmalloc的速度快; __get_free_pages是基于buddy机制实现的,速度也较快; 4).kmalloc用于小块内存的申请,通常,一次所能申请的内存块的大小在(32/64字节,128KB-16)之间; 而vmalloc可以用于分配大块内存的场合; 5).kmalloc申请的内存块在物理地址空间上是连续的,所以它申请的内存块可以直接用于DMA传输; vmalloc申请的内存块在虚拟地址空间上连续,但是在物理地址空间上不要求连续, 所以它申请的内存块不能直接用于DMA传输; 6).kmalloc申请的内存块用kfree释放;vmalloc申请的内存块用vfree释放; __get_free_pages申请的内存页用__free_pages释放; 7).kmalloc申请得到的地址称为内核逻辑地址,vmalloc申请得到的地址称为内核虚拟地址;
kernel中两者的结合
先用 kzalloc ,不行的话再用 __vmalloc
/* Trying kmalloc first, falling back to vmalloc.
* GFP_NOIO, as this is called while drbd IO is "suspended",
* and during resize or attach on diskless Primary,
* we must not block on IO to ourselves.
* Context is receiver thread or dmsetup. */
new_pages = kzalloc(bytes, GFP_NOIO | __GFP_NOWARN);
if (!new_pages) {
new_pages = __vmalloc(bytes, GFP_NOIO | __GFP_ZERO);
if (!new_pages)
return NULL;
}
附录
常用的内存申请 gfp_mask 如下:
#define GFP_ATOMIC (__GFP_HIGH|__GFP_ATOMIC|__GFP_KSWAPD_RECLAIM)
#define GFP_KERNEL (__GFP_RECLAIM | __GFP_IO | __GFP_FS)
#define GFP_KERNEL_ACCOUNT (GFP_KERNEL | __GFP_ACCOUNT)
#define GFP_NOWAIT (__GFP_KSWAPD_RECLAIM)
#define GFP_NOIO (__GFP_RECLAIM)
#define GFP_NOFS (__GFP_RECLAIM | __GFP_IO)
#define GFP_USER (__GFP_RECLAIM | __GFP_IO | __GFP_FS | __GFP_HARDWALL)
#define GFP_DMA __GFP_DMA
#define GFP_DMA32 __GFP_DMA32
#define GFP_HIGHUSER (GFP_USER | __GFP_HIGHMEM)
#define GFP_HIGHUSER_MOVABLE (GFP_HIGHUSER | __GFP_MOVABLE | \
__GFP_SKIP_KASAN_POISON)
#define GFP_TRANSHUGE_LIGHT ((GFP_HIGHUSER_MOVABLE | __GFP_COMP | \
__GFP_NOMEMALLOC | __GFP_NOWARN) & ~__GFP_RECLAIM)
#define GFP_TRANSHUGE (GFP_TRANSHUGE_LIGHT | __GFP_DIRECT_RECLAIM)
kcalloc
申请一个内存数组,这应该是方便指针的累进吧
/**
* kcalloc - allocate memory for an array. The memory is set to zero.
* @n: number of elements.
* @size: element size.
* @flags: the type of memory to allocate (see kmalloc).
*/
static inline void *kcalloc(size_t n, size_t size, gfp_t flags)
{
return kmalloc_array(n, size, flags | __GFP_ZERO);
}
|