要求:通过网址生成任务,提交生成内容的实践过程。https://play.mdflow.run/share?id=6bf459c8b3dc3ecfaf23a6d1eb01d530
Linux 驱动程序
课程目标:
了解驱动程序在操作系统中的角色。
理解内核模块的基本概念。
亲手编写、编译、加载和卸载一个最简单的内核模块。
对后续深入学习的方向有初步认识。
第一部分:引言与概念铺垫
什么是驱动程序?(5分钟)
想象一下,硬件就像是各种不同的"乐器"——显卡是钢琴,声卡是小提琴,网卡是鼓。操作系统呢,就是乐队的"指挥"。但指挥看不懂每个乐器自己的语言,这时候就需要"乐手"——也就是驱动程序。
驱动程序就是那个能看懂乐谱(操作系统指令)并让乐器发声的"乐手"。
它的核心作用就是当翻译官和桥梁:
把操作系统的统一指令翻译成硬件能听懂的具体操作
把硬件的状态和数据处理成操作系统能理解的信息
最终目标就是让所有应用程序都能用统一的方式(比如read、write这些系统调用)来访问五花八门的硬件
为什么要在Linux上学习驱动编程?(5分钟)
你可能想问:为什么偏偏是Linux?我给你三个很实在的理由:
开源精神:Linux内核源码完全开放,这就像给你一本最权威的教科书,随时可以翻看学习
实践价值:现在物联网、嵌入式、自动驾驶这些热门领域,对驱动开发人才的需求特别大
加深理解:学会了驱动编程,你才能真正理解操作系统底层是怎么工作的
内核空间 vs 用户空间 (5分钟)
这是驱动编程与传统应用编程最大的不同点,你一定要搞清楚:
用户空间:
就是你平时写的那些C/C++/Python程序运行的地方
受到CPU保护,不能直接访问硬件
内存是隔离的、虚拟的,一个程序崩溃不会影响整个系统
内核空间:
操作系统核心和驱动程序运行的地方
拥有最高权限,可以直接操控硬件和所有内存
一旦出错(比如非法内存访问),会导致整个系统崩溃
交互方式:应用程序通过"系统调用"陷入内核,请求驱动提供服务。
第二部分:我们的第一个驱动——“Hello, Kernel!”
代码实现
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple hello world kernel module");
static int __init hello_init(void)
{
printk(KERN_INFO "Hello, Kernel! Module loaded successfully.\n");
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_INFO "Goodbye, Kernel! Module unloaded.\n");
}
module_init(hello_init);
module_exit(hello_exit);

编译Makefile
obj-m += hello.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

运行过程
编译模块:
make

你会看到生成了hello.ko文件

加载模块:
sudo insmod hello.ko
查看加载结果:
dmesg | tail -5
你应该能看到:“Hello, Kernel! Module loaded successfully.”

查看已加载模块:
lsmod | grep hello

卸载模块:
sudo rmmod hello
确认卸载:
dmesg | tail -5
你会看到:“Goodbye, Kernel! Module unloaded.”

第三部分:更多实用示例
示例1:带参数的驱动模块
代码实现
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
static char *mystring = "default";
static int myint = 100;
module_param(myint, int, S_IRUGO);
module_param(mystring, charp, S_IRUGO);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Module with parameters");
static int __init param_init(void)
{
printk(KERN_INFO "Parameter module loaded\n");
printk(KERN_INFO "myint = %d, mystring = %s\n", myint, mystring);
return 0;
}
static void __exit param_exit(void)
{
printk(KERN_INFO "Parameter module unloaded\n");
}
module_init(param_init);
module_exit(param_exit);



运行过程
正常加载:
sudo insmod param_module.ko
dmesg | tail -3
输出默认值:myint = 100, mystring = default

带参数加载:
sudo insmod param_module.ko myint=200 mystring="custom_value"
dmesg | tail -3
输出:myint = 200, mystring = custom_value

示例2:简单的字符设备驱动
代码实现
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#define DEVICE_NAME "simple_char"
#define BUFFER_SIZE 1024
static char device_buffer[BUFFER_SIZE];
static int buffer_pointer = 0;
MODULE_LICENSE("GPL");
static int device_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "Simple char device opened\n");
return 0;
}
static int device_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "Simple char device closed\n");
return 0;
}
static ssize_t device_read(struct file *filp, char __user *buffer, size_t length, loff_t *offset)
{
int bytes_read = 0;
if (buffer_pointer <= 0) {
return 0;
}
if (copy_to_user(buffer, device_buffer, buffer_pointer)) {
return -EFAULT;
}
bytes_read = buffer_pointer;
buffer_pointer = 0;
printk(KERN_INFO "Read %d bytes from device\n", bytes_read);
return bytes_read;
}
static ssize_t device_write(struct file *filp, const char __user *buffer, size_t length, loff_t *offset)
{
if (length > BUFFER_SIZE) {
return -EINVAL;
}
if (copy_from_user(device_buffer, buffer, length)) {
return -EFAULT;
}
buffer_pointer = length;
printk(KERN_INFO "Write %zu bytes to device: %s\n", length, device_buffer);
return length;
}
static struct file_operations fops = {
.open = device_open,
.release = device_release,
.read = device_read,
.write = device_write,
};
static int __init char_init(void)
{
int ret;
int major = 0;
ret = register_chrdev(major, DEVICE_NAME, &fops);
if (ret < 0) {
printk(KERN_ALERT "Failed to register char device\n");
return ret;
}
printk(KERN_INFO "Simple char device registered with major number %d\n", ret);
return 0;
}
static void __exit char_exit(void)
{
unregister_chrdev(0, DEVICE_NAME);
printk(KERN_INFO "Simple char device unregistered\n");
}
module_init(char_init);
module_exit(char_exit);


运行过程
编译加载:
make
sudo insmod char_device.ko

查看设备ID:
dmesg | tail -n 10

创建设备节点:
sudo mknod /dev/simple_char c 497 0
sudo chmod 666 /dev/simple_char

测试写入:
echo "Hello from userspace" > /dev/simple_char
dmesg | tail -3
你会看到:“Write 21 bytes to device: Hello from userspace”

测试读取:
cat /dev/simple_char
dmesg | tail -3
你会看到读取的内容和字节数

卸载清理:
sudo rmmod char_device
sudo rm /dev/simple_char

通过这三个例子,你已经掌握了Linux驱动开发的基本流程和核心概念。从最简单的Hello World到带参数的模块,再到有实际读写功能的字符设备,这是一个循序渐进的学习过程。
32

被折叠的 条评论
为什么被折叠?



