kzalloc-vzalloc

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);
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值