在 Linux OS 下开发一个驱动程序的完整流程与实战步骤(含代码)
随着 Linux 系统在服务器、嵌入式和工业控制等场景的广泛应用,掌握 Linux 驱动开发已经成为高级开发人员的必备技能。本文将带你完整走一遍「Linux 驱动程序」从开发到部署的全过程。
一、什么是 Linux 驱动程序?
Linux 驱动程序本质上是一组运行在内核态的软件模块,它们为用户空间的程序提供与硬件交互的接口。例如:USB、I2C、LED、网卡、GPIO 等。
二、准备工作
1. 系统环境
- 操作系统:Ubuntu 20.04 或 Debian 系列
- 内核版本:5.x(与目标平台一致)
- 编译工具:make、gcc、build-essential、linux-headers
sudo apt update
sudo apt install build-essential linux-headers-$(uname -r)
三、驱动开发基础结构
一个典型的字符设备驱动包括以下几个要素:
- 模块初始化函数:module_init
- 模块退出函数:module_exit
- 文件操作结构体:file_operations
- 设备注册与释放:register_chrdev_region / unregister_chrdev_region
四、编写你的第一个 Linux 驱动(HelloDriver)
1. 源码结构如下:
hellodriver/
├── Makefile
└── hello.c
2. hello.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#define DEVICE_NAME "hellodrv"
static int major;
static int hello_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "hello_open called\n");
return 0;
}
static ssize_t hello_read(struct file *file, char __user *buf, size_t len, loff_t *offset)
{
char msg[] = "Hello from kernel space\n";
int bytes = sizeof(msg);
if (*offset >= bytes)
return 0;
if (copy_to_user(buf, msg, bytes))
return -EFAULT;
*offset += bytes;
return bytes;
}
static int hello_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "hello_release called\n");
return 0;
}
static struct file_operations fops = {
.open = hello_open,
.read = hello_read,
.release = hello_release,
};
static int __init hello_init(void)
{
major = register_chrdev(0, DEVICE_NAME, &fops);
if (major < 0) {
printk(KERN_ALERT "Registering char device failed\n");
return major;
}
printk(KERN_INFO "HelloDriver loaded: major=%d\n", major);
return 0;
}
static void __exit hello_exit(void)
{
unregister_chrdev(major, DEVICE_NAME);
printk(KERN_INFO "HelloDriver unloaded\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jack Zhang");
MODULE_DESCRIPTION("A simple Hello World Linux driver");
3. 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 日志
dmesg | tail
# 创建设备节点(假设 major 为 240)
sudo mknod /dev/hellodrv c 240 0
sudo chmod 666 /dev/hellodrv
# 使用 cat 测试
cat /dev/hellodrv
七、卸载驱动
sudo rmmod hello
dmesg | tail
八、扩展建议
你可以在此基础上做以下进阶实践:
- 实现 write() 与 ioctl() 支持
- 支持异步通知 poll/select
- 编写 udev 脚本自动创建设备节点
- 面向具体硬件进行 MMIO 或 GPIO 控制
九、实战部署场景举例
- 嵌入式 Linux 中控制 GPIO 灯
- 在内核空间与用户态通信(日志采集)
- 开发 USB 或 PCI 设备驱动
十、总结
通过本文你已经掌握了:
编写一个简单 Linux 字符驱动
1.使用 Makefile 编译模块
2. 如何加载 / 卸载模块及创建设备节点
3.用户空间如何访问内核模块
驱动开发虽然初学门槛略高,但当你掌握框架、理解内核接口之后,将打开更广阔的高阶技术之门。