obj-m += chrdev_node.o 将当前目录下的 chrdev_node.c 源文件编译为可加载的内核模块

obj-m += chrdev_node.o 是 Linux 内核模块 Makefile 中的核心指令,其作用是将 chrdev_node.c 源文件编译为内核模块(.ko 文件)。以下是分步解释:


1. 语法解析

  • obj-m:内核构建系统变量,表示要编译为可加载模块(Loadable Kernel Module, LKM) 的目标文件列表。
  • +=:追加操作符,将右侧的值添加到 obj-m 列表中。
  • chrdev_node.o:目标文件名(.o 为对象文件扩展名),对应源文件 chrdev_node.c

2. 工作流程

  1. 源文件匹配
    • 内核构建系统会自动查找与 .o 文件同名的 .c 源文件(即 chrdev_node.c)。
  2. 编译为对象文件
    • 将 chrdev_node.c 编译为 chrdev_node.o
  3. 链接为模块
    • 将 chrdev_node.o 链接为最终的内核模块 chrdev_node.ko

3. 关键规则

  • 文件名必须一致
    • .o 文件名必须与源文件 .c 名称一致(扩展名不同)。
    • 错误示例
      
      

      obj-m += my_driver.o # 若源文件为 chrdev_node.c,则编译失败!

  • 多文件模块
    • 若模块由多个源文件组成,需显式列出所有 .o 文件:
      
      

      obj-m += my_module.o
      my_module-objs := file1.o file2.o # 指定组成模块的对象文件


4. 典型 Makefile 结构

# 指定内核源码目录(通常自动检测)
KDIR ?= /lib/modules/$(shell uname -r)/build

# 指定当前目录为模块源码目录
PWD := $(shell pwd)

# 定义模块目标(关键行!)
obj-m += chrdev_node.o

# 默认目标:编译模块
all:
    $(MAKE) -C $(KDIR) M=$(PWD) modules

# 清理编译产物
clean:
    $(MAKE) -C $(KDIR) M=$(PWD) clean

5. 验证编译结果

  • 生成 .ko 文件
    
    

    make
    ls *.ko # 应看到 chrdev_node.ko

  • 加载模块
    
    

    sudo insmod chrdev_node.ko
    dmesg | tail # 查看内核日志


6. 常见问题

  • 文件名不匹配
    
    

    obj-m += driver.o # 若源文件为 chrdev_node.c,则报错:No rule to make target 'driver.o'

  • 源文件不在当前目录
    • 需通过 obj-m += ../path/to/chrdev_node.o 指定路径(但通常建议将源文件放在模块目录下)。

7. 扩展知识

  • obj-y vs obj-m
    • obj-y:将代码编译为内核内建部分(不可卸载)。
    • obj-m:将代码编译为可加载模块(通过 insmod/modprobe 动态加载)。
  • 条件编译

    # 根据配置选择编译方式
    obj-$(CONFIG_MY_DRIVER) += chrdev_node.o


总结

obj-m += chrdev_node.o 的核心作用是:将当前目录下的 chrdev_node.c 源文件编译为可加载的内核模块。务必确保 .o 文件名与源文件 .c 名称一致,这是内核构建系统自动匹配源文件的关键约定。

### 配置 `obj-m += hello_world.o` 的方法 在 Linux 内核模块编译过程中,`obj-m` 变量用于指定哪些源文件需要被编译为独立的内核模块(`.ko` 文件)。为了正确配置 `hello_world.o` 并将其作为模块编译,需遵循以下规则和结构。 --- #### 1. 修改 Makefile 添加 `hello_world.o` 在一个标准的内核模块项目中,Makefile 应该包含以下内容: ```makefile # 指定要编译成模块的目标文件 obj-m += hello_world.o # 定义内核源码目录 KDIR ?= /lib/modules/$(shell uname -r)/build # 获取当前工作目录 PWD := $(shell pwd) # 编译目标 all: $(MAKE) -C $(KDIR) M=$(PWD) modules # 清理目标 clean: rm -f *.o *.ko *.mod.c *.mod.o *.symvers *.order ``` - **`obj-m += hello_world.o`**: 表明 `hello_world.c` 将被编译为一个可加载内核模块- **`KDIR` 和 `PWD`**: 分别指定了内核源码路径以及当前项目的根目录。 - **`all` 目标**: 调用了内核构建系统的 `modules` 目标来实际编译模块。 - **`clean` 目标**: 提供清理功能以移除生成的中间文件和最终产物。 --- #### 2. 确保源文件存在 确保当前目录下有名为 `hello_world.c` 的 C 源文件。例如,下面是一个简单的 `hello_world.c` 实现: ```c #include <linux/module.h> /* 所有模块都需要 */ #include <linux/kernel.h> /* printk() 函数 */ // 初始化函数,在模块加载时调用 static int __init hello_world_init(void) { printk(KERN_INFO "Hello, World! This is the init function.\n"); return 0; // 返回 0 表示成功初始化 } // 退出函数,在模块卸载时调用 static void __exit hello_world_exit(void) { printk(KERN_INFO "Goodbye, World! This is the exit function.\n"); } module_init(hello_world_init); // 注册初始化函数 module_exit(hello_world_exit); // 注册退出函数 MODULE_LICENSE("GPL"); // 设置许可证类型 MODULE_AUTHOR("Your Name"); // 设置作者信息 MODULE_DESCRIPTION("A Simple Hello World Kernel Module."); // 描述模块 ``` --- #### 3. 解决常见问题 ##### (1) 头文件缺失导致 `implicit declaration of function 'printk'` 如果出现此类错误,则表明缺少必要的头文件声明。解决办法是在源代码顶部加入以下两行: ```c #include <linux/module.h> #include <linux/kernel.h> ``` ##### (2) `No rule to make target 'modules'. Stop.` 错误 这种错误通常是由于 `KDIR` 没有正确指向内核源码或构建目录引起的。可以通过以下方式修复: - 确认 `/lib/modules/$(uname -r)/build` 是否有效。 - 如果无效,请手动设置 `KDIR` 指向正确的内核源码路径[^2]。 ##### (3) `Invalid module format` 错误 当编译后的模块与运行中的内核版本不匹配时会出现此错误。确保编译所用的内核版本与当前运行的内核版本一致[^3]。 --- #### 4. 测试编译结果 完成以上步骤后,可以执行以下命令进行测试: ```bash # 编译模块 make # 加载模块 sudo insmod hello_world.ko # 查看日志确认模块是否正常加载 dmesg | tail # 卸载模块 sudo rmmod hello_world ``` 如果一切顺利,应该能在 `dmesg` 输出中看到类似以下的日志消息: ``` [XXXXX.XXX] Hello, World! This is the init function. [YYYYY.YYY] Goodbye, World! This is the exit function. ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值