Linux驱动开发—— IS_ENABLED

本文详细解释了Linux内核中常见的几个配置宏(IS_ENABLED、IS_BUILTIN、IS_MODULE、IS_REACHABLE)的定义与作用,并通过示例程序演示这些宏如何在预编译阶段展开。

 

在閱讀Linux內核代碼的時候,會經常遇到下面的幾個宏函數:

IS_ENABLED  這個宏最爲常見

IS_BUILTIN

IS_MODULE

IS_REACHABLE

這幾個宏函數是在文件include/linux/kconfig.h中定義的,如下:

/*
 * Helper macros to use CONFIG_ options in C/CPP expressions. Note that
 * these only work with boolean and tristate options.
 */

/*
 * Getting something that works in C and CPP for an arg that may or may
 * not be defined is tricky.  Here, if we have "#define CONFIG_BOOGER 1"
 * we match on the placeholder define, insert the "0," for arg1 and generate
 * the triplet (0, 1, 0).  Then the last step cherry picks the 2nd arg (a one).
 * When CONFIG_BOOGER is not defined, we generate a (... 1, 0) pair, and when
 * the last step cherry picks the 2nd arg, we get a zero.
 */
#define __ARG_PLACEHOLDER_1 0,
#define config_enabled(cfg) _config_enabled(cfg)
#define _config_enabled(value) __config_enabled(__ARG_PLACEHOLDER_##value)
#define __config_enabled(arg1_or_junk) ___config_enabled(arg1_or_junk 1, 0)
#define ___config_enabled(__ignored, val, ...) val

/*
 * IS_ENABLED(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'y' or 'm',
 * 0 otherwise.
 *
 */
#define IS_ENABLED(option) \
    (config_enabled(option) || config_enabled(option##_MODULE))

/*
 * IS_BUILTIN(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'y', 0
 * otherwise. For boolean options, this is equivalent to
 * IS_ENABLED(CONFIG_FOO).
 */
#define IS_BUILTIN(option) config_enabled(option)

/*
 * IS_MODULE(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'm', 0
 * otherwise.
 */
#define IS_MODULE(option) config_enabled(option##_MODULE)

/*
 * IS_REACHABLE(CONFIG_FOO) evaluates to 1 if the currently compiled
 * code can call a function defined in code compiled based on CONFIG_FOO.
 * This is similar to IS_ENABLED(), but returns false when invoked from
 * built-in code when CONFIG_FOO is set to 'm'.
 */
#define IS_REACHABLE(option) (config_enabled(option) || \
         (config_enabled(option##_MODULE) && config_enabled(MODULE)))

 

首先需要有一些基礎知識.

我們知道,在進行內核配置的時候,內核配置若是boolean類型,那麼對應的配置有y和n兩種狀態,如果是tristate,那麼對應的配置有y,n和m三種狀態.

然後,在編譯內核的時候會生成include/generated/autoconf.h

如果某個配置被設置爲了y,那麼在.config文件中:

 CONFIG_SND_DUMMY=y

在autoconf.h中會看到:

 #define CONFIG_SND_DUMMY 1 

如果被設置爲了m,表示這個部分代碼會編譯成內核模塊,那麼在.config中會看到:

 CONFIG_SND_DUMMY=m 

在autoconf.h中會看到:

 #define CONFIG_SND_DUMMY_MODULE 1 

可以看到,在宏的末尾多了一個_MODULE的後綴.

如果被設置爲了n,那麼在.config中會看到:

 # CONFIG_SND_DUMMY is not set 

而在autoconf.h中看不到這個宏的任何定義.

對於靜態編譯到kernel中的文件, 在編譯的時候會宏MODULE不會被定義

而對於編譯到模塊中的文件,在編譯的時候,編譯系統會定義MODULE宏.

 

下面解釋一下這幾個宏函數的作用

  • IS_ENABLED

如果option或者option_MODULE所表示的宏定義在當前上下文被設置爲了1, 那麼這個宏函數會返回1;如果沒有定義或者被定義爲了0, 那麼就返回0.

無論CONFIG_SND_DUMMY被設置爲了y還是m,IS_ENABLED(CONFIG_SND_DUMMY)都會返回1.

  • IS_BUILTIN

如果option所表示的宏定義在當前上下文被設置爲了1, 那麼這個宏函數會返回1;如果沒有定義或者被定義爲了0, 那麼就返回0.

只有CONFIG_SND_DUMMY被設置爲了y,IS_BUILTIN(CONFIG_SND_DUMMY)才會返回1.

  • IS_MODULE

如果option_MODULE所表示的宏定義在當前上下文被設置爲了1, 那麼這個宏函數會返回1;如果沒有定義或者被定義爲了0, 那麼就返回0.

只有CONFIG_SND_DUMMY被設置爲了m,IS_MODULE(CONFIG_SND_DUMMY)才會返回1.

  • IS_REACHABLE

這個宏函數跟IS_ENABLED的類似, 不同之處是,如果CONFIG_SND_DUMMY被設置爲了m,那麼IS_REACHABLE(CONFIG_SND_DUMMY)在靜態編譯到kernel中的文件中被預處理的時候會返回0,而在編譯到模塊的文件中預處理時會返回0.原因是MODULE宏只有在編譯模塊代碼期間纔會被定義.

 

下面我們將config_enabled(CONFIG_SND_DUMMY)展開:

如果CONFIG_SND_DUMMYautoconf.h中被設置爲了1, 那麼展開如下:

___config_enabled(__ARG_PLACEHOLDER_1 1, 0)

再進一步展開

___config_enabled(0, 1, 0) 

最後展開如下:

  1 

 

如果CONFIG_SND_DUMMY在autoconf.h中沒有被設置, 那麼展開如下:

 ___config_enabled(__ARG_PLACEHOLDER_CONFIG_SND_DUMMY 1, 0) 

進一步展開如下:

  0 

 

下面用一個簡單的測試程序試試:

a.c

 1 #define __ARG_PLACEHOLDER_1 0,
 2 #define config_enabled(cfg) _config_enabled(cfg)
 3 #define _config_enabled(value) __config_enabled(__ARG_PLACEHOLDER_##value)
 4 #define __config_enabled(arg1_or_junk) ___config_enabled(arg1_or_junk 1, 0)
 5 #define ___config_enabled(__ignored, val, ...) val
 6 
 7 #define ONE 2
 8 #define TWO 1
 9 #define THREE 0
10 #define FOUR
11 
12 int main(int argc, char *argv[])
13 {
14 
15     int a;
16 
17     if (config_enabled(ONE)) {
18          a = 1;
19     }
20 
21     if (config_enabled(TWO)) {
22          a = 1;
23     }
24 
25     if (config_enabled(THREE)) {
26          a = 1;
27     }
28 
29     if (config_enabled(FOUR)) {
30          a = 1;
31     }
32 
33     if (config_enabled(FIVE)) {
34          a = 1;
35     }
36 
37     return 0;
38 }

 

 然後使用gcc的預編譯命令 gcc -E a.c -o a.i 編譯出文件a.i如下:

 1 int main(int argc, char *argv[])
 2 {
 3 
 4  int a;
 5 
 6  if (0) {
 7    a = 1;
 8  }
 9 
10  if (1) {
11    a = 1;
12  }
13 
14  if (0) {
15    a = 1;
16  }
17 
18  if (0) {
19    a = 1;
20  }
21 
22  if (0) {
23    a = 1;
24  }
25 
26  return 0;
27 }

 

 

完.

转载于:https://www.cnblogs.com/pengdonglin137/p/5960412.html

我新建了文件夹并在其中仅放入my_char_dev.c和Makefile文件分别如下:#include <linux/module.h> // 内核模块基础支持 #include <linux/kernel.h> // 内核打印函数printk #include <linux/fs.h> // 文件操作结构体file_operations #include <linux/cdev.h> // 字符设备结构体cdev #include <linux/uaccess.h> // 用户空间内存访问函数 #include <linux/device.h> // 设备类支持 #define DEVICE_NAME "my_char_dev" // 设备名称 #define CLASS_NAME "my_class" // 设备类名称 MODULE_LICENSE("GPL"); // 模块许可证 MODULE_AUTHOR("Your Name"); // 模块作者 MODULE_DESCRIPTION("Simple Char Driver with Case Control"); // 模块描述 // 设备结构体包含所有设备相关信息 struct my_device { struct cdev cdev; // 字符设备结构 struct class *class; // 设备类指针 struct device *device; // 设备实例 int cap_enabled; // 大写转换标志 (1=启用) } my_dev; // 模块参数声明 static int cap_param = 0; // 默认关闭大写转换 module_param(cap_param, int, S_IRUGO); // 定义模块参数 MODULE_PARM_DESC(cap_param, "Enable uppercase conversion (1=enable)"); // 参数描述 // 设备打开函数 static int dev_open(struct inode *inode, struct file *file) { printk(KERN_INFO "my_char_dev: Device opened\n"); return 0; } // 设备释放函数 static int dev_release(struct inode *inode, struct file *file) { printk(KERN_INFO "my_char_dev: Device closed\n"); return 0; } // 设备写入函数 (核心功能) static ssize_t dev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) { char kernel_buf[256]; // 内核缓冲区 int i; // 检查写入长度是否超过缓冲区大小 if (count > sizeof(kernel_buf) - 1) { printk(KERN_WARNING "my_char_dev: Write size too large\n"); return -EFAULT; } // 从用户空间复制数据到内核空间 if (copy_from_user(kernel_buf, buf, count)) { printk(KERN_ERR "my_char_dev: Failed to copy from user\n"); return -EFAULT; } kernel_buf[count] = '\0'; // 确保字符串终止 // 根据cap_enabled标志进行大写转换 if (my_dev.cap_enabled) { for (i = 0; kernel_buf[i]; i++) { if (kernel_buf[i] >= 'a' && kernel_buf[i] <= 'z') { kernel_buf[i] -= 32; // 小写转大写 } } } // 打印处理后的内容 printk(KERN_INFO "my_char_dev: Received: %s\n", kernel_buf); return count; // 返回实际写入的字节数 } // 文件操作结构体定义 static struct file_operations fops = { .owner = THIS_MODULE, .open = dev_open, .release = dev_release, .write = dev_write, }; // 模块初始化函数 static int __init char_dev_init(void) { dev_t devno; int result; printk(KERN_INFO "my_char_dev: Initializing module\n"); // 1. 分配设备号 if ((result = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME)) < 0) { printk(KERN_ERR "my_char_dev: Failed to allocate device number\n"); return result; } // 2. 初始化字符设备 cdev_init(&my_dev.cdev, &fops); my_dev.cdev.owner = THIS_MODULE; // 3. 添加字符设备到系统 if ((result = cdev_add(&my_dev.cdev, devno, 1)) < 0) { unregister_chrdev_region(devno, 1); printk(KERN_ERR "my_char_dev: Failed to add cdev\n"); return result; } // 4. 创建设备类 my_dev.class = class_create(THIS_MODULE, CLASS_NAME); if (IS_ERR(my_dev.class)) { cdev_del(&my_dev.cdev); unregister_chrdev_region(devno, 1); printk(KERN_ERR "my_char_dev: Failed to create class\n"); return PTR_ERR(my_dev.class); } // 5. 创建设备节点 (/dev/my_char_dev) my_dev.device = device_create(my_dev.class, NULL, devno, NULL, DEVICE_NAME); if (IS_ERR(my_dev.device)) { class_destroy(my_dev.class); cdev_del(&my_dev.cdev); unregister_chrdev_region(devno, 1); printk(KERN_ERR "my_char_dev: Failed to create device\n"); return PTR_ERR(my_dev.device); } // 6. 应用模块参数 my_dev.cap_enabled = cap_param; printk(KERN_INFO "my_char_dev: Module loaded. Uppercase conversion: %s\n", my_dev.cap_enabled ? "ENABLED" : "DISABLED"); return 0; } // 模块退出函数 static void __exit char_dev_exit(void) { dev_t devno = my_dev.cdev.dev; // 1. 销毁设备节点 device_destroy(my_dev.class, devno); // 2. 销毁设备类 class_destroy(my_dev.class); // 3. 删除字符设备 cdev_del(&my_dev.cdev); // 4. 释放设备号 unregister_chrdev_region(devno, 1); printk(KERN_INFO "my_char_dev: Module unloaded\n"); } // 注册初始化和退出函数 module_init(char_dev_init); module_exit(char_dev_exit); obj-m := my_char_dev.o # 使用 := 替代 += 避免污染 all: $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean rm -f *.o *.ko *.mod* .*.cmd .*.d modules.order Module.symvers——运行时报错如下:linzihao@linzihao-virtual-machine:~/Code/test/coursework2/work4$ make clean make: *** 没有规则可制作目标“clean”。 停止。——这是什么原因
最新发布
08-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值