内核模块实验1 lx_hello.ko

实验环境

  • 完整的可以编译的内核源码,这里选取 RK3568_Android11 源码中的kernel部分

添加HELLO WORLD字符设备驱动

  • 在RK3568_Android11/kernel/drivers/char/中新建文件夹lx_hello
  • 文件夹中增加lx_hello.c Makefile Kconfig 文件(Makefile 和Kconfig 为编译规则文件,这里仿照其他文件内容填写)

编写驱动文件lx_hello.c

内核模块头文件

  • #include <linux/module.h>:包含内核模块信息声明的相关函数
  • #include <linux/init.h>:包含了 module_init()和 module_exit()函数的声明
  • #include <linux/kernel.h>:包含内核提供的各种函数,如printk

驱动文件的入口和出口

  • 模块的出口和入口函数是内核模块驱动所必须的
    • module_init(XXXX_init); //固定格式模块入口,加载模块的时候调用
    • module_exit(XXXX_exit); //固定格式模块出口,卸载模块的时候调用
  • 入口函数实现
    static int __init hello_init(void)
      {
          printk(KERN_INFO "module init success\n");
          return 0;
      }
    
  • 出口函数实现
  static void __exit hello_exit(void) //模块的退出
    {
        printk(KERN_INFO "module exit success\n");
    }
  • 入口函数中__init 启修饰函数节省内存的作用,因为加载驱动模块的初始化程序只会调用一次,在调用完成后不会再次调用,所以该函数占用的内存空间应该被释放掉,函数名前边加__init 可以达到此目的。

    • __init 标记的函数会在编译后放在ELF文件的特定代码段,在模块加载时单独分配内存,函数调用成功后,模块的加载程序会释放掉该部分内存空间。
    • __exit 标记用于修饰清除函数,作用和__init 类似,但是用于模块的卸载,如果模块不用卸载,则这段代码不会被加载到内存中。
  • prink 区别于用户层c调用的printf函数,printf为glibc库函数,内核无法调用。内核自己实现了一套打印机制。

  • printk 与printf的使用区别是,prink需要增加打印等级,数字越小级别越高。

    • #define KERN_EMERG "<0>" 通常是系统崩溃前的信息
    • #define KERN_ALERT "<1>" 需要立即处理的消息
    • #define KERN_CRIT "<2>" 严重情况
    • #define KERN_ERR "<3>" 错误情况
    • #define KERN_WARNING "<4>" 有问题的情况
    • #define KERN_NOTICE "<5>" 注意信息
    • #define KERN_INFO "<6>" 普通消息
    • #define KERN_DEBUG "<7>" 调试信息
  • 当printk 等级比控制台等级高时,会打印在终端上,可以通过cat /proc/sys/kernel/printk查看系统控制台等级,出来的四个参数依次为

    • 当前控制台日志级别
    • 默认消息日志级别
    • 最小的控制台级别
    • 默认控制台日志级别

打印内核所有打印信息:dmesg

  • 内核log缓冲区大小有限制,缓冲区数据可能被冲掉

声明文件

MODULE_AUTHOR("lx");   //声明作者
MODULE_DESCRIPTION("功能描述");     //功能描述
MODULE_VERSION("1.0");      //版本
MODULE_LICENSE("GPL v2");////告诉内核该模块具有GNU公共许可证
  • MODULE_LICENSE():表示模块代码接受的软件许可协议,Linux内核遵循GPL V2开源协议,内核模块与linux内核保持一致即可。
  • MODULE_AUTHOR():描述模块的作者信息
  • MODULE_DESCRIPTION():对模块的简单介绍
  • MODULE_ALIAS():给模块设置一个别名
  • 声明文件为宏定义,最后编译器会将这些信息放在编译生成的ELF文件的一个特殊的段中。

编译生成库文件

  • 修改上层(kernel/drivers/char/)Kconfig 添加source "drivers/char/lx_hello/Kconfig"
  • 修改上层(kernel/drivers/char/)Makefiel 添加obj-$(CONFIG_LX_HELLO) += lx_hello/
  • 修改内核配置文件 (kernel/arch/arm64/configs/rockchip_defconfig)添加CONFIG_LX_HELLO=m
    • m 代表编译成模块 y 代表直接编译进内核 n 表示内核不支持该模块
    • 选择m编译后生成ko文件会放在当前目录下

内核模块加载/卸载

  • 使用insmod命令加载

  • 使用rmmod命令卸载

posted @ 2022-08-24 00:05  lxblog  阅读(103)  评论(0编辑  收藏  举报