Linux 驱动开发入门:从 Hello World 到内核交互
Linux 驱动开发是操作系统内核与硬件设备之间的桥梁,它允许应用程序通过标准接口访问硬件资源,本文将以一个经典的 “Hello World” 驱动为例,逐步介绍驱动开发的基本流程、核心概念及实现细节,帮助读者快速入门 Linux 内核模块编程。

Linux 驱动基础概念
Linux 驱动主要分为字符设备、块设备和网络设备三大类,字符设备以字节流为单位进行数据传输(如串口、键盘),块设备则通过固定大小的块进行读写(如硬盘、U 盘),本文聚焦于字符设备驱动,因其结构简单,适合初学者理解驱动开发的核心逻辑。
驱动程序在内核空间运行,与用户空间的应用程序通过系统调用(如 open()、read()、write())进行交互,内核模块(Kernel Module)是动态加载的驱动程序,无需重启系统即可扩展内核功能,这也是 “Hello World” 驱动的常见实现形式。
开发环境准备
在开始编写驱动之前,需确保系统已安装必要的工具和内核头文件,以 Ubuntu 为例,可通过以下命令安装依赖:
sudo apt update sudo apt install build-essential linux-headers-$(uname -r)
build-essential 包含编译工具(如 gcc、make),linux-headers 提供当前内核版本的头文件,用于驱动与内核的接口定义。
“Hello World” 驱动代码实现
以下是一个简单的字符设备驱动代码,实现模块加载时打印 “Hello, World!”,卸载时打印 “Goodbye, World!”,并支持基本的读写操作。

1 驱动代码 (hello_drv.c)
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#define DEVICE_NAME "hello_drv"
#define BUF_SIZE 1024
static char msg_buf[BUF_SIZE] = {0};
static int msg_len = 0;
static int major_num;
// 打开设备
static int hello_open(struct inode *inode, struct file *file) {
printk(KERN_INFO "hello_drv: Device opened\n");
return 0;
}
// 读取设备数据
static ssize_t hello_read(struct file *file, char __user *buf, size_t len, loff_t *offset) {
if (*offset >= msg_len) {
return 0; // 读取完成
}
if (copy_to_user(buf, msg_buf + *offset, len)) {
return -EFAULT; // 拷贝失败
}
*offset += len;
return len;
}
// 写入设备数据
static ssize_t hello_write(struct file *file, const char __user *buf, size_t len, loff_t *offset) {
if (len > BUF_SIZE) {
len = BUF_SIZE;
}
if (copy_from_user(msg_buf, buf, len)) {
return -EFAULT; // 拷贝失败
}
msg_len = len;
*offset = len;
return len;
}
// 关闭设备
static int hello_release(struct inode *inode, struct file *file) {
printk(KERN_INFO "hello_drv: Device closed\n");
return 0;
}
// 文件操作结构体
static struct file_operations fops = {
.open = hello_open,
.read = hello_read,
.write = hello_write,
.release = hello_release,
};
// 模块加载函数
static int __init hello_init(void) {
major_num = register_chrdev(0, DEVICE_NAME, &fops);
if (major_num < 0) {
printk(KERN_ALERT "hello_drv: Failed to register device\n");
return major_num;
}
printk(KERN_INFO "hello_drv: Registered with major number %d\n", major_num);
printk(KERN_INFO "Hello, World!\n");
return 0;
}
// 模块卸载函数
static void __exit hello_exit(void) {
unregister_chrdev(major_num, DEVICE_NAME);
printk(KERN_INFO "Goodbye, World!\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Hello World driver");
2 Makefile 编译脚本
obj-m += hello_drv.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
驱动编译与加载
1 编译模块
在终端执行 make 命令,生成 hello_drv.ko 内核模块文件:
make
2 加载与卸载模块
使用 insmod 和 rmmod 命令管理模块:
# 加载模块 sudo insmod hello_drv.ko # 查看模块信息 lsmod | grep hello_drv # 查看内核日志(dmesg) dmesg | tail # 卸载模块 sudo rmmod hello_drv # 再次查看内核日志 dmesg | tail
加载模块后,内核日志会打印 “Hello, World!”;卸载时则打印 “Goodbye, World!”。
设备文件与用户空间交互
驱动加载后,需创建设备文件才能被用户程序访问。
1 创建设备文件
# 查看主设备号
dmesg | grep "hello_drv: Registered" | awk '{print $NF}'
# 假设主设备号为 247,创建设备文件
sudo mknod /dev/hello_drv c 247 0
sudo chmod 666 /dev/hello_drv
2 用户空间测试程序 (test_hello.c)
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#define DEVICE_PATH "/dev/hello_drv"
int main() {
int fd = open(DEVICE_PATH, O_RDWR);
if (fd < 0) {
perror("Failed to open device");
return -1;
}
// 写入测试数据
char write_buf[] = "Hello from user space!";
write(fd, write_buf, sizeof(write_buf));
// 读取数据
char read_buf[1024] = {0};
read(fd, read_buf, sizeof(read_buf));
printf("Driver read: %s\n", read_buf);
close(fd);
return 0;
}
编译并运行测试程序:

gcc test_hello.c -o test_hello ./test_hello
程序会通过驱动读取并打印写入的数据,验证驱动与用户空间的交互功能。
驱动调试与常见问题
1 调试技巧
printk日志:通过dmesg查看驱动运行时的调试信息。Dynamic Debug:启用内核动态调试功能,实时打印驱动日志。kgdb:使用内核调试器进行断点调试。
2 常见问题与解决方案
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
insmod 提示 “Invalid module format” |
内核头文件版本不匹配 | 安装当前内核版本的 linux-headers |
dmesg 无日志输出 |
日志级别过高 | 修改 printk 的日志级别(如 KERN_INFO) |
| 用户程序无法打开设备 | 设备文件权限错误 | 使用 chmod 修改设备文件权限 |
本文通过一个简单的 “Hello World” 驱动,展示了 Linux 驱动开发的基本流程,包括环境搭建、代码编写、模块加载、设备文件创建及用户空间交互,驱动开发是 Linux 内核编程的重要分支,掌握其核心概念和实践方法,有助于进一步探索复杂的硬件驱动开发,后续可学习设备树、中断处理、DMA 等高级主题,逐步提升驱动开发能力。










