2025-02-09
学习Linux内核相关知识从什么角度入手不好把握。有的人喜欢从文档开始看,了解整个内核的体系结构,再看源代码。有的人喜欢一开始就看源代码,不过内核源码有900M,就算是去除驱动相关代码也有400M。如果想通过看代码来学习内核,不出几天热情就会消退。内核源码的结构复杂,代码难度也大。新手上来就看源码不合适。后来发现从内核驱动开始入手学习内核源码是一个不错的选择。一方面可以直接上手写代码,完成一个内核驱动也是比较有成就感的事情。另一方面能通过编写内核驱动了解到内核的一些机制和子系统。这样对内核有了一个初步的认识会比较容易坚持下来。
sudo apt install build-essentialsudo apt install device-tree-compilersudo 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_init和module_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!信息。
以上就是一个最简单的内核驱动的实现过程。希望对你有所帮助。