如同ID Allocation的Overview提到的, kernel提供了對應的一些API, 用以產生與維護identifiers (IDs), 舉凡file descriptor, process IDs, device instance number等等. IDR主要多了ID與pointer的對用能力, 而IDA就是單純的分配ID, 本章透過簡單的程式碼讓大家能瞭解與使用IDA.
首先, 該範例是個簡易的kernel module, 透過DEFINE_IDA(my_ida)宣告一個my_ida變數, 這是我們, 並透過read file operation去取得一個新的ID(ida_simple_get), 在write file operation中透過寫入特定ID移除該ID(ida_simple_remove), 最後在移除kernel module時, 使用ida_destroy(struct ida * ida)把所有的IDA resource都釋放, 不然會造成memory leak.
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/idr.h> #include <linux/moduleparam.h> #include <linux/fs.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/uaccess.h> // Required for copy_from_user MODULE_LICENSE("GPL"); MODULE_AUTHOR("Brook"); MODULE_DESCRIPTION("Kernel module to demo IDA"); MODULE_VERSION("0.1"); static DEFINE_IDA(my_ida); static ssize_t ida_demo_read(struct file *file, char *buf, size_t count, loff_t *ppos) { int id, len; char tmp_buf[10]; id = ida_simple_get(&my_ida, 0, 0, GFP_KERNEL); if (id >= 0) { printk(KERN_INFO "IDR Demo: Successfully allocated ID: %d\n", id); } else { printk(KERN_ERR "IDR Demo: Failed to allocate ID\n"); return -ENOMEM; } len = snprintf(tmp_buf, sizeof(tmp_buf), "%d", id); if (len < 0) { return -EINVAL; } if (copy_to_user(buf, tmp_buf, len)) { return -EFAULT; } return 0; } static ssize_t ida_demo_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { char tmp_buf[20]; int id; if (count >= sizeof(tmp_buf)) return -EINVAL; if (copy_from_user(tmp_buf, buf, count)) return -EFAULT; tmp_buf[count] = '\0'; // Convert the input string to an integer if (kstrtoint(tmp_buf, 10, &id)) { printk(KERN_ERR "invalid ID: %s\n", tmp_buf); return -EINVAL; } printk(KERN_INFO "remove ID %d\n", id); ida_simple_remove(&my_ida, id); return count; } // Define a file operation structure for IDA access static struct file_operations ida_fops = { .open = simple_open, .read = ida_demo_read, .write = ida_demo_write, .llseek = default_llseek, }; static int __init ida_demo_init(void) { struct proc_dir_entry *proc_entry; printk(KERN_INFO "IDR Demo: Initializing module\n"); // Create a file entry to invoke 'ida' proc_entry = proc_create("ida", S_IRUGO, NULL, &ida_fops); if (!proc_entry) { printk(KERN_ERR "Failed to create sysfs entry for 'ida'\n"); return -ENOMEM; } return 0; } static void __exit ida_demo_exit(void) { printk(KERN_INFO "IDR Demo: Exiting module\n"); ida_destroy(&my_ida); } module_init(ida_demo_init); module_exit(ida_demo_exit);
簡易的Makefile
KDIR ?= /build/brook/Projects/qemu/linux/ # Modules which are included in the kernel are installed in the # directory: # /lib/modules/$(KERNELRELEASE)/kernel/ # And external modules are installed in: # /lib/modules/$(KERNELRELEASE)/extra/ # # INSTALL_MOD_PATH # A prefix can be added to the # installation path using the variable INSTALL_MOD_PATH: # # $ make INSTALL_MOD_PATH=/frodo modules_install # => Install dir: /frodo/lib/modules/$(KERNELRELEASE)/kernel/ export INSTALL_MOD_PATH=/build/brook/Projects/qemu/initrd-arm obj-m := ida_demo.o ida_demo-y := ida_main.o modules modules_install clean: $(MAKE) -C $(KDIR) M=$$PWD $@
這是在QEMU下執行的結果, 會先產生ID 0/1/2, 然後移除1, 接著再產生的ID就會把1生出來, 再來就是3了, 所以透過IDA, 可以幫user管理ID(唯一的編號)
/ # uname -a Linux (none) 5.4.0+ #6 SMP Tue Jan 3 08:39:24 CST 2023 armv7l GNU/Linux / # insmod /lib/modules/5.4.0+/extra/ida_demo.ko ida_demo: loading out-of-tree module taints kernel. IDR Demo: Initializing module / # cat /proc/ida IDR Demo: Successfully allocated ID: 0 / # cat /proc/ida IDR Demo: Successfully allocated ID: 1 / # cat /proc/ida IDR Demo: Successfully allocated ID: 2 / # echo 1 > /proc/ida remove ID 1 / # cat /proc/ida IDR Demo: Successfully allocated ID: 1 / # cat /proc/ida IDR Demo: Successfully allocated ID: 3
- https://www.kernel.org/doc/html/v5.4/core-api/idr.html, ID Allocation