IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> kzalloc-vzalloc -> 正文阅读

[系统运维]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);
}
  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-11-15 16:13:57  更:2021-11-15 16:14:57 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 23:45:42-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码