在 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.用户空间如何访问内核模块


驱动开发虽然初学门槛略高,但当你掌握框架、理解内核接口之后,将打开更广阔的高阶技术之门。