【6】内存分配

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_NOIOGFG_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");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值