驱动简介

因为Linux是宏内核(Monolithic Kernel),所有的驱动程序作为内核的一部分一起运行。这大概也是普通程序员提交内核代码最简单的一个途径了。

内核模块

但是编译Linux内核是一件麻烦而且费时的事情,为了解决这个问题,内核提供了模块机制(Loadable Kernel Module(LKM))。既能独立编译驱动程序,又能将其动态加载进内核。

  • 模块代码入口
    内核模块接口在include\linux\module.h。可以看到每个模块其实是一个initcall。
/* Each module must use one module_init(). */
#define module_init(initfn)                    \
    static inline initcall_t __maybe_unused __inittest(void)        \
    { return initfn; }                  \
    int init_module(void) __copy(initfn)            \
        __attribute__((alias(#initfn)));        \
    ___ADDRESSABLE(init_module, __initdata);
/* This is only required if you want to be unloadable. */
#define module_exit(exitfn)                    \
    static inline exitcall_t __maybe_unused __exittest(void)        \
    { return exitfn; }                  \
    void cleanup_module(void) __copy(exitfn)        \
        __attribute__((alias(#exitfn)));        \
    ___ADDRESSABLE(cleanup_module, __exitdata);

可以参考cdrom的代码drivers\cdrom\cdrom.c

module_init(cdrom_init);
module_exit(cdrom_exit);
MODULE_DESCRIPTION("Uniform CD-ROM driver");
MODULE_LICENSE("GPL");
  • 独立编译驱动模块
    编译的时候会自动生成一个module结构(设置好了init和exit等入口函数),编译最终结果是一个ELF格式的.ko文件。
    ELF是Linux使用的代码文件格式,常见的.o、.so、可执行程序都是这种格式,详情见https://refspecs.linuxfoundation.org/elf/elf.pdf
  • 动态加载模块
    使用程序/bin/kmod加载模块会触发系统调用init_module或者finit_module,最终都会调用到load_module。代码在kernel\module\main.c
    load_module函数里其实是做了可执行程序加载so类似的过程(link、relocate等)。

手写驱动

  • 样例代码
    在Linux的源码目录./drivers下新建一个hello目录,hello.c代码如下
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
static int __init hello_init(void)
{
    printk(KERN_EMERG "[ KERN_EMERG ] Hello Module Init\n");
    printk( "[ default ] Hello Module Init\n");
    return 0;
}
static void __exit hello_exit(void)
{
    printk("[ default ]  Hello Module Exit\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL2");
MODULE_AUTHOR("matthewliu");
MODULE_DESCRIPTION("hello module");
MODULE_ALIAS("test_module");
  • Makefile
    Makefile代码如下
# SPDX-License-Identifier: GPL-2.0
obj-m += hello.o #不支持配置,如果要配置参见make menuconfig

编译驱动

  • 开始编译
    在Linux源码跟目录下执行
cp /boot/config-6.6.34-9.oc9.x86_64 .
make //第一次要全编译一遍,不然编译模块会报Module.symvers is missing.
make M=./drivers/hello
  • 编译依赖安装
    编译过程有什么依赖错误就安装对应的库
yum install openssl-devel //模块有签名校验
yum install elfutils-devel.x86_64 //模块是elf文件格式

安装驱动

记得用一个虚拟机什么的来尝试。。。

insmod hello.ko
rmmod hello.ko

dmesg查看模块打印的日志是否ok。



微信扫描下方的二维码阅读本文

上一篇: Kernel源码浅析-协议栈

下一篇: Kernel源码浅析-系统调用

Categories: Linux

0 Comments

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注