Linux内核-idr/ida
一、简介
1. idr.rst
注:翻译自 msm-5.4/Documentation/core-api/idr.rst
1.1 概述
要解决的一个常见问题是分配标识符 (ID); 通常用很小的数字来标识一个事物。 示例包括文件描述符、进程 ID、网络协议中的数据包标识符、SCSI 标签和设备实例号。 IDR 和 IDA 为该问题提供了合理的解决方案,以避免每个人都发明自己的解决方案。 IDR 提供将 ID 映射到指针的功能,而 IDA 只提供 ID 分配,因此内存效率更高。
1.2 IDR使用
首先初始化 IDR,使用 DEFINE_IDR() 宏(对于静态分配的 IDR)或使用 idr_init()(对于动态分配的 IDR)。
您可以调用 idr_alloc() 来分配未使用的 ID。 通过调用 idr_find() 查找与 ID 关联的指针,并通过调用 idr_remove() 释放 ID。
如果需要更改与 ID 关联的指针,可以调用 idr_replace() 。 这样做的一个常见原因是通过向分配函数传递“NULL”指针来保留 ID; 使用保留的ID初始化对象,最后将初始化的对象插入到IDR中。
有些用户需要分配大于 INT_MAX 的ID。 到目前为止,所有这些用户都满足于 UINT_MAX 限制,并且他们使用 idr_alloc_u32()。 如果您需要的 ID 不适合 u32,我们将与您合作来满足您的需求。I: 即目前idr中的id只支持32bit的。
如果需要顺序分配 ID,可以使用 idr_alloc_cyclic()。 当处理较大的 ID 时,IDR 的效率会降低,因此使用此功能会产生轻微的成本。
要对 IDR 使用的所有指针执行操作,您可以使用基于回调的 idr_for_each() 或迭代器样式 idr_for_each_entry()。 您可能需要使用 idr_for_each_entry_continue() 来继续迭代。 如果迭代器不满足您的需求,您还可以使用 idr_get_next() 。
当您使用完 IDR 后,可以调用 idr_destroy() 来释放 IDR 使用的内存。 这不会释放 IDR 指向的对象; 如果您想这样做,请使用其中一个迭代器来完成。
您可以使用 idr_is_empty() 来查看当前是否有分配的 ID。
如果您在从 IDR 分配新 ID 时需要持锁,则可能需要传递一组限制性的 GFP 标志,这可能会导致 IDR 无法分配内存。 要解决此问题,您可以在持锁之前调用 idr_preload() ,然后在分配之后调用 idr_preload_end() 。
.. 内核文档:: include/linux/idr.h
:doc: IDR 同步
1.3 IDA使用
.. 内核文档:: lib/idr.c
:doc: IDA 描述
1.4 函数与数据结构
.. 内核文档:: include/linux/idr.h
.. 内核文档:: lib/idr.c
二、idr使用实验
1. idr_test.c
#define pr_fmt(fmt) "idr_test: " fmt #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/idr.h> #include <linux/mutex.h> #include <linux/uaccess.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> struct idr_test_data { int id; int i; }; static DEFINE_MUTEX(test_lock); static DEFINE_IDR(test_idr); int idr_liter_callback(int id, void *p, void *data) { struct idr_test_data *itd = (struct idr_test_data *)p; unsigned long tag = (unsigned long)data; pr_info("idr id=%d liter callback, tag=%lu, data: id=%d, i=%d, p=%p\n", id, tag, itd->id, itd->i, itd); return 0; } int idr_free_callback(int id, void *p, void *data) { struct idr_test_data *itd = (struct idr_test_data *)p; unsigned long tag = (unsigned long)data; pr_info("idr id=%d free callback, tag=%lu, data: id=%d, i=%d, p=%p\n", id, tag, itd->id, itd->i, itd); idr_remove(&test_idr, id); kfree(itd); return 0; } static int idr_test_func(int type, int value) { int i, id; /* (1) 分配idr */ if (type == 1) { for (i = 0; i < value; i++) { struct idr_test_data *itd = kzalloc(sizeof(struct idr_test_data), GFP_KERNEL); if (!itd) { pr_info("no memory!\n"); return -ENOMEM; } mutex_lock(&test_lock); id = idr_alloc(&test_idr, itd, 1, INT_MAX, GFP_KERNEL); mutex_unlock(&test_lock); if (id < 0) { pr_info("couldn't get idr, idr=%d\n", id); return id; } itd->id = id; itd->i = i; pr_info("id=%d, i=%d, p=%p\n", itd->id, itd->i, itd); } } /* (2) 查找idr */ if (type == 2) { struct idr_test_data *itd; mutex_lock(&test_lock); itd = idr_find(&test_idr, value); mutex_unlock(&test_lock); if (!itd) { pr_info("id=%d not found\n", value); return -1; } pr_info("id=%d found: id=%d, i=%d, p=%p\n", value, itd->id, itd->i, itd); } /* (3) 遍历操作 */ if (type == 3) { mutex_lock(&test_lock); idr_for_each(&test_idr, idr_liter_callback, (void *)type); mutex_unlock(&test_lock); } /* (4) 遍历的另一种方式 */ if (type == 4) { struct idr_test_data *entry; mutex_lock(&test_lock); idr_for_each_entry(&test_idr, entry, id) { pr_info("id=%d, data: id=%d, i=%d, p=%p\n", id, entry->id, entry->i, entry); //实测OK,即使上面id从1开始 } mutex_unlock(&test_lock); } /* (5) 移除idr */ if (type == 5) { struct idr_test_data *entry; mutex_lock(&test_lock); entry = idr_remove(&test_idr, value); mutex_unlock(&test_lock); if (!entry) { pr_info("id=%d associated entry not found\n", value); return -1; } pr_info("id=%d removed, data: id=%d, i=%d, p=%p\n", value, entry->id, entry->i, entry); kfree(entry); } /* (6) 移除所有idr元素 */ if (type == 6) { mutex_lock(&test_lock); idr_for_each(&test_idr, idr_free_callback, (void *)type); mutex_unlock(&test_lock); } /* (7) 销毁 idr */ if (type == 7) { idr_destroy(&test_idr); } return 0; } static ssize_t idr_ida_test_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { char buffer[64] = {0}; int ret, type, value; struct test_struct *ts, *pos, *n; if (count > sizeof(buffer) - 1) count = sizeof(buffer) - 1; if (copy_from_user(buffer, buf, count)) return -EFAULT; ret = sscanf(buffer, "%d %d", &type, &value); if (ret <= 0) { pr_err("sscanf failed\n"); return -EINVAL;; } pr_info("set param: type=%d, value=%d\n", type, value); idr_test_func(type, value); return count; } static ssize_t idr_ida_test_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct test_struct *pos; char buffer[256] = { "\t echo <type> <value> > /proc/idr_test\n\ \t 1 value: add value entrys\n\ \t 2 value: find id=value entry\n\ \t 3 value: liter print entry\n\ \t ...\n" }; return simple_read_from_buffer(buf, count, ppos, buffer, strlen(buffer)); } static const struct file_operations proc_idr_ida_test_fops = { .write = idr_ida_test_write, .read = idr_ida_test_read, }; static int __init idr_ida_test_init(void) { pr_info("init.\n"); proc_create("idr_test", 0666, NULL, &proc_idr_ida_test_fops); return 0; } module_init(idr_ida_test_init);
(1) 分配idr
# echo 1 5 > /proc/idr_test # dmesg -c | grep idr_test: [ 90.418712] (2)[9154:sh]idr_test: set param: type=1, value=5 [ 90.419026] (2)[9154:sh]idr_test: id=1, i=0, p=ffffff8178427c80 [ 90.419227] (2)[9154:sh]idr_test: id=2, i=1, p=ffffff8193e8c080 [ 90.419410] (2)[9154:sh]idr_test: id=3, i=2, p=ffffff8137ac4a80 [ 90.419588] (2)[9154:sh]idr_test: id=4, i=3, p=ffffff8137ab2b00 [ 90.419822] (2)[9154:sh]idr_test: id=5, i=4, p=ffffff8137ab1480
(2) 查找idr
# echo 2 3 > /proc/idr_test # dmesg -c | grep idr_test: [ 115.535971] (3)[9154:sh]idr_test: set param: type=2, value=3 [ 115.536276] (3)[9154:sh]idr_test: id=3 found: id=3, i=2, p=ffffff8137ac4a80
(3) 遍历操作
# echo 3 0 > /proc/idr_test # dmesg -c | grep idr_test: [ 133.693945] (0)[9154:sh]idr_test: set param: type=3, value=0 [ 133.694187] (0)[9154:sh]idr_test: idr id=1 liter callback, tag=3, data: id=1, i=0, p=ffffff8178427c80 [ 133.694458] (0)[9154:sh]idr_test: idr id=2 liter callback, tag=3, data: id=2, i=1, p=ffffff8193e8c080 [ 133.694785] (0)[9154:sh]idr_test: idr id=3 liter callback, tag=3, data: id=3, i=2, p=ffffff8137ac4a80 [ 133.694970] (0)[9154:sh]idr_test: idr id=4 liter callback, tag=3, data: id=4, i=3, p=ffffff8137ab2b00 [ 133.695203] (0)[9154:sh]idr_test: idr id=5 liter callback, tag=3, data: id=5, i=4, p=ffffff8137ab1480
(4) 遍历的另一种方式
# echo 4 0 > /proc/idr_test # dmesg -c | grep idr_test: [ 151.228481] (2)[9154:sh]idr_test: set param: type=4, value=0 [ 151.228733] (2)[9154:sh]idr_test: id=1, data: id=1, i=0, p=ffffff8178427c80 [ 151.228988] (2)[9154:sh]idr_test: id=2, data: id=2, i=1, p=ffffff8193e8c080 [ 151.229208] (2)[9154:sh]idr_test: id=3, data: id=3, i=2, p=ffffff8137ac4a80 [ 151.229435] (2)[9154:sh]idr_test: id=4, data: id=4, i=3, p=ffffff8137ab2b00 [ 151.229651] (2)[9154:sh]idr_test: id=5, data: id=5, i=4, p=ffffff8137ab1480
(5) 移除idr
# echo 5 3 > /proc/idr_test # dmesg -c | grep idr_test: [ 174.396929] (1)[9154:sh]idr_test: set param: type=5, value=3 [ 174.397598] (1)[9154:sh]idr_test: id=3 removed, data: id=3, i=2, p=ffffff8137ac4a80
(6) 移除所有idr元素
# echo 6 0 > /proc/idr_test # dmesg -c | grep idr_test: [ 220.358150] (3)[9154:sh]idr_test: set param: type=6, value=0 [ 220.358401] (3)[9154:sh]idr_test: idr id=1 free callback, tag=6, data: id=1, i=0, p=ffffff8178427c80 [ 220.358668] (3)[9154:sh]idr_test: idr id=2 free callback, tag=6, data: id=2, i=1, p=ffffff8193e8c080 [ 220.358939] (3)[9154:sh]idr_test: idr id=4 free callback, tag=6, data: id=4, i=3, p=ffffff8137ab2b00 [ 220.359201] (3)[9154:sh]idr_test: idr id=5 free callback, tag=6, data: id=5, i=4, p=ffffff8137ab1480 //移除后再看: # echo 3 0 > /proc/idr_test # dmesg -c | grep idr_test: [ 261.827079] (2)[9195:sh]idr_test: set param: type=3, value=0
(7) 销毁 idr
# echo 7 0 > /proc/idr_test # dmesg -c | grep idr_test: [ 293.143420] (3)[9154:sh]idr_test: set param: type=7, value=0
注: 销毁后重新 "echo 1 X" 又重新能正常分配了,没有经历初始化步骤也行。
posted on 2023-10-23 15:06 Hello-World3 阅读(198) 评论(0) 收藏 举报
浙公网安备 33010602011771号