kmalloc 介绍
kmalloc和malloc相似,可以被阻塞,不对所获取的内存空间清零,它所分配的区域在物理内存中也是连续的。
函数原型
/*
* size: 要分配块的大小
* flags: 分配的标志,能以多种方式控制kmalloc的行为
*/
void *kmalloc(size_t size, int flags);
/* 释放 */
void kfree(void* ptr);
flags种类说明
GFP_ATOMIC
:用于在中断处理例程或其他运行于进程上下文之外的代码中分配内存,不会休眠GFP_KERNEL
:内核内存的通常分配方法,可能引起休眠GFP_USER
:用于为用户空间页分配内存,可能会休眠GFP_HIGHUSER
:类似于GFP_USER,不过如果有高端内存的话就从那里分配GFP_NOIO
和GFG_NOFS
:功能类似于GFP_KERNEL,但是为内核分配内存的工作方式添加了一些限制。具有GFG_NOFS标志的分配不允许执行任何文件系统调用,而GFP_NOIO禁止任何I/O的初始化。这两个标志主要在文件系统和虚拟内存代码中使用,这些代码中的内存分配可休眠,但是不应该发生递归的文件系统调用
flags可以与下面标志“或”起来使用,下面的这些标志控制着如何进行分配。
__GFP_DMA
:该标志请求分配发生在可进行DMA的内存区段中__GFP_HIGHMEM
:这个标志表明要分配的内存可位于高端内存__GFP_COLD
:对于用DMA读取的页面分配,可使用这个标志__GFP_NOWARN
:很少使用,它可以避免内核在无法满足分配请求时产生警告__GFP_HIGH
:这个标志标识了一个高优先级请求, 它被允许来消耗甚至被内核保留给紧急状况的最后的内存页__GFP_REPEAT
:表示“努力再尝试一次”,它会重新尝试分配,但是仍有可能失败__GFP_NOFAIL
:告诉分配器始终不返回失败,它会努力满足分配请求。不鼓励使用__GFP_NORETRY
:如果请求的内存不可获得,就立即返回
其中__GFP_REPEAT
、__GFP_NOFAIL
和__GFP_NORETRY
这三个标志告诉分配器在满足分配需求遇到困难时该采取何种行为。
size参数说明
内核只能分配一些预定义的,固定大小的字节组,所以申请任意数量的内存的时候,可能会多一些,最多会得到申请数量两倍的内存。
kmalloc能申请的最小的内存是32或者64,具体取决于当前系统所使用的的页面的大小。
示例
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#define CDEV_IOC_MAGIC 'x'
struct ioctl_transfer {
int type;
int value;
};
#define X2_MAGNIFICATION 0x00F1
#define X10_MAGNIFICATION 0x00F2
static unsigned int major = 222;
static unsigned int minor = 0;
static dev_t dev_no;
static struct class* cls = NULL;
static struct device* cls_dev = NULL;
static long hello_ioctl(struct file* filep, unsigned int cmd, unsigned long arg) {
int result;
struct ioctl_transfer* arg_data = NULL;
arg_data = (struct ioctl_transfer *)kmalloc(sizeof(struct ioctl_transfer), GFP_KERNEL);
if (arg_data == NULL) {
printk("alloc error!\n");
return -ENODEV;
}
printk("hello_ioctl cmd:%u\n", cmd);
if (_IOC_TYPE(cmd) != CDEV_IOC_MAGIC) {
printk("magic num error!\n");
goto err;
}
if (!access_ok((void __user *)arg, _IOC_SIZE(cmd))) {
printk("size error!\n");
goto err;
}
if (copy_from_user(arg_data, (void __user *)arg, sizeof(struct ioctl_transfer))) {
printk("get arg data error!\n");
goto err;
}
printk("arg.type: 0x%x, arg.value: %d\n", arg_data->type, arg_data->value);
switch (arg_data->type) {
case X2_MAGNIFICATION: {
arg_data->value *= 2;
result = copy_to_user((void __user *)arg, arg_data, sizeof(struct ioctl_transfer));
} break;
case X10_MAGNIFICATION: {
arg_data->value *= 10;
result = copy_to_user((void __user *)arg, arg_data, sizeof(struct ioctl_transfer));
} break;
default: {
goto err;
} break;
}
kfree(arg_data);
return result;
err:
kfree(arg_data);
return -ENODEV;
}
static struct file_operations hello_ops = {
.unlocked_ioctl = hello_ioctl,
};
static int hello_init(void) {
int result = 0;
printk("hello init enter!");
dev_no = MKDEV(major, minor);
result = register_chrdev(major, "hello", &hello_ops);
if (result < 0) {
printk("register chrdev failed! result: %d\n", result);
return result;
}
cls = class_create(THIS_MODULE, "hello_cls");
if (IS_ERR(cls) != 0) {
printk("class create failed!");
result = PTR_ERR(cls);
goto err_1;
}
cls_dev = device_create(cls, NULL, dev_no, NULL, "hello_dev");
if (IS_ERR(cls_dev) != 0) {
printk("device create failed!");
result = PTR_ERR(cls_dev);
goto err_2;
}
return 0;
err_2:
class_destroy(cls);
err_1:
unregister_chrdev(major, "hello");
return result;
}
static void hello_exit(void) {
printk("hello exit enter!");
device_destroy(cls, dev_no);
class_destroy(cls);
unregister_chrdev(major, "hello");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");