前言
一、环境搭建
不同系统不一样,需要安装内核的开发包,下面是kali linux的方式,里面的版本号使用uname -r确认
sudo apt install linux-image-6.11.2-amd64-dbg linux-headers-6.11.2-amd64
二、开发指导
1. 基本模板
1.1. Makefile编写
- Makefile第一个M要大写,不然也会报错
NAME := hello
obj-m := $(NAME).o
ifeq ($(KERNDIR), )
KDIR := /lib/modules/$(shell uname -r)/build
else
KDIR := $(KERNDIR)
endif
PWD := $(shell pwd)
$(NAME)-obj += hello_main.o
# $(NAME)-y +=
# $(NAME)-n +=
ifeq ($(DEBUG), 1)
ccflags-y += -g -O0
endif
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean :
$(MAKE) -C $(KDIR) M=$(PWD) modules clean
1) 命令解释
- linux驱动编译最核心的命令是
make -C <kernel-source-dir> M=$(PWD) modules
-C <dir>
: 指定内核源码目录,里面存放最高级的makefileM=<dir>
: 代表我们模块的makefile在这里
2) Makefile内容解释
obj-m := ${MODULE_NAME}.o
: 指定要编译的ko文件,${MODULE_NAME}.o
会生成${MODULE_NAME}.ko
文件,写多个会生成多个${MODULE_NAME}-obj += xxx.o
: 指定要编译的多个c文件,每个xxx.o
会找xxx.c
文件去编译,注意不能和${MODULE_NAME}
同名,同名会循环引用错误- 如果编译
hello.ko
有两个源文件hello.c
和file1.c
,需要指定obj-m := hello.o
,而obj需要将hello.c
重命名为hello_main.c
,然后设置hello-objs := hello_main.o file1.o
- 如果只有一个源文件
hello.c
,那么不用指定hello-objs
,会找hello.c
文件 - 经过测试,此变量不能等于变量,需要显式指定文件
- 如果编译
${MODULE_NAME}-y += xxx.o
: 和-n
对应,某些文件是否编译进去,一般设定变量来决定是y
还是n
1.2. 源文件结构
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
static int hello_init(void) {
printk(KERN_ALERT "hello, world\n");
return 0;
}
static void hello_exit(void) {
nf_unregister_net_hook(&init_net, &nfho);
printk(KERN_ALERT "Goodby, cruel world\n");
}
module_init(hello_init);
module_exit(hello_exit);
- 上面这样的结构,需要用
module_init
和module_exit
定义insmod和rmmod对应的时间点的操作 - 打印只能使用
printk
,会输出到dmesg
命令的输出中 MODULE_LICENSE
定义开源协议
2. 传参
- 源文件中这样写可以传参
static int mode = 0; // 这里定义默认值
module_param(mode, int, S_IRUGO); // 第三个参数是权限,定义在include/linux/stat.h,这里是0444
传参示例
# 驱动传参必须指定参数名称
insmod ./aaa.ko mode=1
权限的作用
- 所有挂载的驱动,在
/sys/module/<mod_name>/parameters/
下以参数名命名
=> ls -l /sys/module/test/parameters/
total 0
-r--r--r-- 1 admin root 4096 Mar 26 11:21 mode
=> cat /sys/module/test/parameters/mode
1
- 权限设置为可写的话,其实可以使用vi进行修改此选项。但是内核没有方式通知驱动此参数值发生了变化,所以要么自己写机制保障,要么就权限设置为只读
三、小工具代码
1. 日志
#include <linux/printk.h>
#define tag "xxx"
#define xxx_log_info(fmt, ...) \
pr_info("[%s] "fmt, tag, ##__VA_ARGS__)
2. 编译的内核版本输出
- 这个只输出针对编译内核的版本
#include <linux/version.h>
static char *get_kernel_version(void) {
static char buf[16] = {0};
short a, b, c;
if (buf[0] != 0) return buf;
a = LINUX_VERSION_CODE >> 16;
b = (LINUX_VERSION_CODE >> 8) & 0xff;
c = LINUX_VERSION_CODE & 0xff;
sprintf(buf, "%d.%d.%d", a, b, c);
return buf;
}
3. 添加git commitid
makefile中对于编译添加选项
ifeq ($(GIT_COMMITID), )
GIT_COMMITID := $(shell git rev-parse HEAD)
endif
ccflags-y += -DGIT_COMMITID=\"$(GIT_COMMITID)\"
代码中定义到comment里面
#ifndef GIT_COMMITID
#define GIT_COMMITID "unknown"
#endif
// 显示在modinfo xxx.ko中
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("xxxx, Git: "GIT_COMMITID);
标签:NAME,框架,xxx,MODULE,COMMITID,module,linux,驱动,hello
From: https://blog.csdn.net/qq_26124425/article/details/143821512