【Linux】设备驱动开发:hello world实战

参考书籍:《Linux设备驱动程序(第3版)》

次实例演示如何构建一个简单的内核驱动文件,然后加载此内核驱动文件到Linux内核中,最后从内核卸载此驱动。

源码 hello.c

#include <linux/init.h>
#include <linux/module.h>

MODULE_LICENSE("Dual BSD/GPL");

static int hello_init(void)
{
    printk(KERN_ALERT "Hello, world\n"); // 用空格分割参数?
    return 0;
}

static void hello_exit(void)
{
    printk(KERN_ALERT "Goodbye, cruel world\n"); // 用空格分割参数?
}

module_init(hello_init);
module_exit(hello_exit);

构建文件 Makefile

Makefile文件中各行含义解析见【附录A】 

ifeq ($(KERNELRELEASE),)
	KERNELDIR ?= /lib/modules/$(shell uname -r)/build
	PWD := $(shell pwd)

modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clean:
	rm -rf *.o *~core .depend .*.cmd *.ko *.mod.c .tmp_versions

.PHONY: modules modules_install clean

else
    obj-m := hello.o
endif

构建方法

首先介绍我目录结构:

$ tree
.
├── hello.c
└── Makefile

构建

$ make
make -C /lib/modules/5.4.0-100-generic/build M=/home/dog/Dev/Linux/driver/hello modules
make[1]: 进入目录“/usr/src/linux-headers-5.4.0-100-generic”
  CC [M]  /home/dog/Dev/Linux/driver/hello/hello.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC [M]  /home/dog/Dev/Linux/driver/hello/hello.mod.o
  LD [M]  /home/dog/Dev/Linux/driver/hello/hello.ko
make[1]: 离开目录“/usr/src/linux-headers-5.4.0-100-generic”

构建后的目录结构(不可见文件省略)

$ tree
.
├── hello.c
├── hello.ko
├── hello.mod
├── hello.mod.c
├── hello.mod.o
├── hello.o
├── Makefile
├── modules.order
└── Module.symvers

测试驱动文件

$ sudo insmod hello.ko  #需要root权限;加载驱动文件到内核
$ sudo rmmod hello      #从内核卸载驱动模块

查看相关操作的系统日志

【附录A】Makefile解析

可以阅读《Linux设备驱动程序(第3版)》第28页“编译和装载”,对此类型的Makefile有详细说明。

以下为我个人在实践中总结而来:

先上Makefile文件

从 make 操作开始分析:

$ make
"--------->Entery"  # 第1次进入
make -C /lib/modules/5.4.0-100-generic/build M=/home/dog/Dev/Linux/driver/hello modules
make[1]: 进入目录“/usr/src/linux-headers-5.4.0-100-generic”
"--------->Entery"  # 第2次进入
  CC [M]  /home/dog/Dev/Linux/driver/hello/hello.o
"--------->Entery"  # 第3次进入
  Building modules, stage 2.
  MODPOST 1 modules
  CC [M]  /home/dog/Dev/Linux/driver/hello/hello.mod.o
  LD [M]  /home/dog/Dev/Linux/driver/hello/hello.ko
make[1]: 离开目录“/usr/src/linux-headers-5.4.0-100-generic”

 第1次进入:执行第3~8行(第8行仅执行到$(MAKE) -C $(KERNELDIR))

第2次进入:执行第18~19行

第3次进入:执行第8行的“M=$(PWD) modules”部分

如上分析自认为不太正确,但大概是这样的流程,且进入Makefile3次是确定无疑。请自行看“$make”的输出信息也能明白是怎样的流程。

ifeq ($(KERNELRELEASE),)

驱动编译要用到kernel的Makefile文件——也就是源码树的编译系统(kbuild构建体系)。开发者的Makefile会被多次执行,因此需要判断是从哪个入口跳转(是从kernel的Makefile跳转,还是直接make开发者的Makefile)过来执行。因此需要逻辑判断以区分。

$(KERNELRELEASE)被定义在$(KERNELDIR)变量表示目录下的某个Makefile中。在开发者目录下执行“make”命令时,$(KERNELDIR)下的Makefile尚未执行,因此ifeq ($(KERNELRELEASE),)判断为真,即执行

	KERNELDIR ?= /lib/modules/$(shell uname -r)/build
	PWD := $(shell pwd)

第一行:获取当前系统kernel的build目录

为什么要在目录中使用$(shell uname -r)变量来组合目录?因为一般Linux系统下会有多个kernel目录,我们只选取当前系统正在使用的版本(即uname -r输出的那个),如Ubuntu18.04下有如下kernel目录

第二行:获取make开始执行的目录

-C $(KERNELDIR) 

切换到$(KERNELDIR)目录下执行(即make执行此目录下的Makefile文件,此处为内核构建体系的Makefile文件) 

M=$(PWD)

返回到开发者构建目录下执行(即make执行M=$(PWD)后面的命令modules)。

M是kbuild体系中的makefile脚本中定义的一个变量

# Use make M=dir to specify directory of external module to build
# Old syntax make ... SUBDIRS=$PWD is still supported
# Setting the environment variable KBUILD_EXTMOD take precedence
ifdef SUBDIRS
    KBUILD_EXTMOD ?= $(SUBDIRS)
endif

ifdef M //如果没有定义或赋值M,此处M未定义(undefined)
    ifeq ("$(origin M)", "command line") //如果定义了,此句用来判断M是否从命令行来
        KBUILD_EXTMOD := $(M)
    endif
endif

else
    obj-m := hello.o 

obj-m是kbuild构建系统需要生成的目标文件。kbuild构建系统会根据obj-m中对应目标文件的文件名找到对应的.c文件来生成对应的.o文件。

如果hello.o由多个源文件生成(比如file1.c,file2.c),则需要使用“module-objs”,例如:

obj-m := hello.o

module-objs := file1.o file2.o

.PHONY: modules modules_install clean

目标的具体意思是如果在Makefile的工作目录中有名如:modules,modules_install,clean等文件时命令会出错。

参考:

make -C $(LINUX_KERNEL_PATH) M=$(PWD) modules中的M选项-qinzhong-ChinaUnix博客

编译内核模块的Makefile中的($(KERNELRELEASE)_在水_的博客-CSDN博客

Linux设备驱动程序(第三版)第一个Hello world程序的实现_追求理想,不再犹豫~的技术博客_51CTO博客

linux设备驱动程序--hello-world - 牧野星辰 - 博客园

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值