Heng30的博客
搜索 分类 关于 订阅

我的第一个内核驱动程序?

2025-02-09

学习Linux内核相关知识从什么角度入手不好把握。有的人喜欢从文档开始看,了解整个内核的体系结构,再看源代码。有的人喜欢一开始就看源代码,不过内核源码有900M,就算是去除驱动相关代码也有400M。如果想通过看代码来学习内核,不出几天热情就会消退。内核源码的结构复杂,代码难度也大。新手上来就看源码不合适。后来发现从内核驱动开始入手学习内核源码是一个不错的选择。一方面可以直接上手写代码,完成一个内核驱动也是比较有成就感的事情。另一方面能通过编写内核驱动了解到内核的一些机制和子系统。这样对内核有了一个初步的认识会比较容易坚持下来。

环境配置

  • sudo apt install build-essential
  • sudo apt install device-tree-compiler
  • sudo apt install linux-headers-$(uname -r)

hello.c驱动源码

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

static int __init hello_init(void) {
    printk(KERN_INFO "Hi, hello!\n");
    return 0;
}

static void __exit hello_exit(void) {
    printk(KERN_INFO "Goodbye, hello!\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("your-name");
MODULE_VERSION("v0.0.1");
MODULE_DESCRIPTION("A simple hello kernel module");

首先是引入头文件

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

接着定义驱动加载和退出时调用的函数。在执行insmod hello.ko命令加载驱动后会调用static int __init hello_init(void) 在执行rmmod hello.ko命令卸载驱动后会调用static void __exit hello_exit(void)module_initmodule_exit分别向内核注册驱动初始化卸载函数,这样驱动才能知道该调用哪个函数。

最后的是一些驱动相关的元信息,包括:协议,作者,版本和描述信息。

MODULE_LICENSE("GPL");
MODULE_AUTHOR("your-name");
MODULE_VERSION("v0.0.1");
MODULE_DESCRIPTION("A simple hello kernel module");

Makefile编译脚本

#!/bin/sh

arch = x86
top-dir = $(shell pwd)
kernel-version = $(shell uname -r)
kernel-dir ?= /lib/modules/$(kernel-version)/build

obj-m = hello.o

all:
	make -C $(kernel-dir) modules M=$(top-dir)

clean:
	rm -f *.o *.ko *.mod *.mod.c *.order *.symvers
	make -C $(kernel-dir) clean m=$(top-dir)

kernel-dir是获取内核驱动的构建目录。在编译的过程中会先进入到内核驱动目录进行构建,再在当前hello.c目录进行构建hello.ko驱动。

obj-m指明编译为一个外部的内核驱动,需要使用insmod命令进行驱动加载。

最后执行sudo make命令编译hello.ko驱动。运行sudo insmod hello.ko加载驱动。 运行sudo dmesg查看驱动加载信息,可以在输出的日志看到Hi, hello!信息。

以上就是一个最简单的内核驱动的实现过程。希望对你有所帮助。