linux内核驱动开发视频课程

Linux内核驱动开发入门:从环境搭建到第一个字符设备驱动

Linux内核驱动开发是嵌入式系统、操作系统内核及硬件交互领域的核心技术之一。对于初学者而言,从环境搭建到完成第一个字符设备驱动的开发,既是技术挑战,也是快速掌握内核开发流程的绝佳路径。本文将以Ubuntu 22.04 LTS为开发环境,结合Linux 6.x内核,详细讲解从开发环境配置、内核模块基础到字符设备驱动实现的全流程。

一、开发环境搭建:工具链与内核源码准备

1. 安装基础开发工具

bash 复制代码
# 更新软件包列表
sudo apt update

# 安装编译工具链(GCC、Make、GDB等)
sudo apt install build-essential libncurses-dev bison flex libssl-dev

# 安装内核开发包(头文件与文档)
sudo apt install linux-headers-$(uname -r) linux-doc

2. 获取Linux内核源码

  • 方法1:从官方仓库安装(适用于当前运行内核的开发)

    bash 复制代码
    sudo apt install linux-source
    tar -xvf /usr/src/linux-source-*.tar.bz2 -C ~/
    cd ~/linux-source-*
  • 方法2:下载稳定版内核源码(推荐学习最新特性)

    bash 复制代码
    wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.x.y.tar.xz
    tar -xvf linux-6.x.y.tar.xz -C ~/
    cd ~/linux-6.x.y

3. 配置内核编译选项

生成默认配置(基于当前内核):

bash 复制代码
make defconfig

或手动配置(推荐初学者使用menuconfig):

bash 复制代码
make menuconfig

重点配置项:

  • Device Drivers → Character devices:启用字符设备支持
  • Kernel hacking → Memory Debugging:开启内存调试(便于排查问题)

4. 编译内核与模块(可选)

若需完整编译内核(测试驱动时可能需要):

bash 复制代码
make -j$(nproc)  # 使用所有CPU核心加速编译
sudo make modules_install install

二、内核模块基础:Hello World驱动

1. 创建第一个内核模块

~/drivers/hello目录下创建以下文件:

hello.c(模块入口)

c 复制代码
#include <linux/module.h>
#include <linux/kernel.h>

static int __init hello_init(void) {
    printk(KERN_INFO "Hello, Linux Kernel!\n");
    return 0;
}

static void __exit hello_exit(void) {
    printk(KERN_INFO "Goodbye, Linux Kernel!\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A Simple Hello World Kernel Module");

Makefile(编译规则)

makefile 复制代码
obj-m := hello.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
    make -C $(KDIR) M=$(PWD) modules

clean:
    make -C $(KDIR) M=$(PWD) clean

2. 编译与加载模块

bash 复制代码
make
sudo insmod hello.ko  # 加载模块
dmesg | tail          # 查看内核日志(输出"Hello, Linux Kernel!")
sudo rmmod hello      # 卸载模块
dmesg | tail          # 查看卸载日志

3. 关键点解析

  • module_init/module_exit:定义模块的加载和卸载函数。
  • printk :内核态打印函数,KERN_INFO为日志级别。
  • MODULE_LICENSE:必须声明许可证(GPL避免污染内核)。

三、字符设备驱动开发:从理论到实践

1. 字符设备驱动核心概念

  • 设备号 :主设备号(分类) + 次设备号(实例),通过register_chrdev_regionalloc_chrdev_region分配。
  • 文件操作集(struct file_operations :定义openreadwrite等操作。
  • 设备节点 :通过mknodudev自动创建(如/dev/mychardev)。

2. 实现第一个字符设备驱动

步骤1:创建驱动框架~/drivers/chardev目录下创建:

chardev.c

c 复制代码
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>  // 用户空间与内核空间数据拷贝

#define DEVICE_NAME "mychardev"
#define BUF_SIZE 1024

static int major_num;
static char kernel_buf[BUF_SIZE];
static int buf_len = 0;

// 文件操作集
static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = chardev_open,
    .read = chardev_read,
    .write = chardev_write,
    .release = chardev_release,
};

// 打开设备
static int chardev_open(struct inode *inode, struct file *file) {
    printk(KERN_INFO "chardev: Device opened\n");
    return 0;
}

// 读取数据
static ssize_t chardev_read(struct file *file, char __user *buf, size_t len, loff_t *offset) {
    int bytes_read = 0;
    if (*offset >= buf_len || buf_len == 0)
        return 0;

    if (*offset + len > buf_len)
        len = buf_len - *offset;

    if (copy_to_user(buf, kernel_buf + *offset, len))
        return -EFAULT;

    *offset += len;
    bytes_read = len;
    return bytes_read;
}

// 写入数据
static ssize_t chardev_write(struct file *file, const char __user *buf, size_t len, loff_t *offset) {
    if (len > BUF_SIZE - buf_len)
        len = BUF_SIZE - buf_len;

    if (copy_from_user(kernel_buf + buf_len, buf, len))
        return -EFAULT;

    buf_len += len;
    return len;
}

// 释放设备
static int chardev_release(struct inode *inode, struct file *file) {
    printk(KERN_INFO "chardev: Device closed\n");
    return 0;
}

// 模块初始化
static int __init chardev_init(void) {
    // 动态分配设备号
    major_num = register_chrdev(0, DEVICE_NAME, &fops);
    if (major_num < 0) {
        printk(KERN_ALERT "chardev: Failed to register device\n");
        return major_num;
    }
    printk(KERN_INFO "chardev: Registered with major number %d\n", major_num);
    return 0;
}

// 模块退出
static void __exit chardev_exit(void) {
    unregister_chrdev(major_num, DEVICE_NAME);
    printk(KERN_INFO "chardev: Unregistered device\n");
}

module_init(chardev_init);
module_exit(chardev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A Simple Character Device Driver");

Makefile

makefile 复制代码
obj-m := chardev.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
    make -C $(KDIR) M=$(PWD) modules

clean:
    make -C $(KDIR) M=$(PWD) clean

3. 编译、加载与测试

bash 复制代码
make
sudo insmod chardev.ko

# 查看设备号
dmesg | tail
# 输出示例:chardev: Registered with major number 240

# 创建设备节点(手动方式)
sudo mknod /dev/mychardev c 240 0  # 240替换为实际主设备号

# 测试读写
echo "Hello Kernel" | sudo tee /dev/mychardev
sudo cat /dev/mychardev

# 卸载模块
sudo rmmod chardev

4. 关键代码解析

  • register_chrdev:动态分配设备号(主设备号0表示自动分配)。
  • copy_to_user/copy_from_user:安全地在用户空间和内核空间之间拷贝数据。
  • loff_t *offset:跟踪读写位置,支持多次调用。

四、调试与优化技巧

1. 内核日志查看

bash 复制代码
dmesg -w  # 实时监控内核日志

2. 使用GDB调试内核模块

  • 编译时启用调试信息:

    makefile 复制代码
    EXTRA_CFLAGS := -g
  • 使用kgdbqemu模拟调试(需配置内核启动参数)。

3. 常见问题排查

  • 权限问题 :确保设备节点可读写(sudo chmod 666 /dev/mychardev)。
  • 内存越界 :检查BUF_SIZEbuf_len的边界条件。
  • 模块未卸载干净 :使用lsmod | grep chardev确认。

五、进阶学习路径

  1. 深入理解内核机制

    • 进程调度与中断处理
    • 内存管理(kmalloc/vmalloc
    • 同步机制(自旋锁、信号量)
  2. 扩展驱动功能

    • 实现ioctl接口(设备控制)
    • 添加阻塞I/O支持(wait_queue
    • 支持多线程安全(mutex
  3. 实战项目

    • 开发LED、按键等GPIO驱动
    • 实现串口通信驱动
    • 编写网络协议栈模块

六、总结与资源推荐

从环境搭建到完成第一个字符设备驱动,开发者需掌握内核模块开发流程、字符设备核心机制及调试技巧。推荐学习资源:

  • 书籍:《Linux设备驱动开发(第3版)》(宋宝华译)
  • 文档 :Linux内核源码中的Documentation/driver-api/
  • 社区:Linux Kernel Mailing List(LKML)、Stack Overflow内核标签

通过持续实践与深入学习,开发者将逐步掌握Linux内核驱动开发的核心能力,为嵌入式系统、操作系统内核等领域的职业发展奠定坚实基础。

相关推荐
Clownseven3 小时前
如何用Fail2ban保护Linux服务器?防止SSH暴力破解教程
linux·服务器·ssh
源码部署23 小时前
linux内核驱动开发视频课程
linux
无敌最俊朗@4 小时前
Linux 进程创建与控制详解
linux·运维·服务器
张红尘4 小时前
龙蜥OS8.10配置repo源使用RPM安装Redis8.2
linux·redis·操作系统
编程点滴4 小时前
前端项目从 Windows 到 Linux:构建失败的陷阱
linux·前端
小白银子4 小时前
零基础从头教学Linux(Day 43)
linux·运维·服务器·nginx
安审若无6 小时前
PMON failed to acquire latch 的报错及sqlplus / as sysdba 无法连接
linux·运维·数据库
9毫米的幻想6 小时前
【Linux系统】—— 环境变量
linux·服务器·c语言·c++
DARLING Zero two♡6 小时前
【Linux操作系统】简学深悟启示录:动静态库
linux·运维·服务器