linux内核驱动开发视频课程

Linux内核驱动开发---789it.top/14260/

Linux 内核驱动开发:精通 file_operations 结构体

课程概述

本课程将深入讲解 Linux 内核驱动开发中的核心概念 - file_operations 结构体,帮助您掌握设备文件与用户空间交互的关键技术。

1. file_operations 结构体基础

1.1 什么是 file_operations?

file_operations 是 Linux 内核中定义的一个关键数据结构,它包含了指向各种文件操作函数的指针。当用户空间程序对设备文件进行系统调用时,内核会调用相应的函数指针。

c 复制代码
// 主要结构定义(Linux 5.x+ 版本)
struct file_operations {
    struct module *owner;
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    int (*open) (struct inode *, struct file *);
    int (*release) (struct inode *, struct file *);
    // ... 其他成员
};

1.2 结构体成员详解

成员 描述 对应系统调用
open 打开设备文件 open()
release 关闭设备文件 close()
read 从设备读取数据 read()
write 向设备写入数据 write()
llseek 改变文件位置 lseek()
unlocked_ioctl 设备控制命令 ioctl()
mmap 内存映射 mmap()
poll 轮询设备状态 select(), poll()

2. 实战:实现一个简单的字符设备驱动

2.1 基础驱动框架

c 复制代码
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>

#define DEVICE_NAME "my_device"
#define CLASS_NAME "my_class"

static int major_number;
static struct class* dev_class = NULL;
static struct cdev my_cdev;

// 设备缓冲区
static char device_buffer[256];
static int buffer_index = 0;

// file_operations 函数实现
static int device_open(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "MyDevice: Device opened\n");
    return 0;
}

static int device_release(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "MyDevice: Device closed\n");
    return 0;
}

static ssize_t device_read(struct file *filp, char __user *buf, 
                          size_t len, loff_t *offset)
{
    int bytes_to_copy;
    int retval;
    
    // 计算要拷贝的字节数
    bytes_to_copy = min(len, (size_t)buffer_index);
    
    if (bytes_to_copy == 0) {
        return 0; // EOF
    }
    
    // 将数据从内核空间拷贝到用户空间
    retval = copy_to_user(buf, device_buffer, bytes_to_copy);
    if (retval) {
        return -EFAULT; // 拷贝失败
    }
    
    // 更新偏移量
    *offset += bytes_to_copy;
    
    printk(KERN_INFO "MyDevice: Read %d bytes\n", bytes_to_copy);
    return bytes_to_copy;
}

static ssize_t device_write(struct file *filp, const char __user *buf,
                           size_t len, loff_t *offset)
{
    int bytes_to_copy;
    int retval;
    
    // 确保不超出缓冲区大小
    bytes_to_copy = min(len, sizeof(device_buffer) - 1);
    
    if (bytes_to_copy == 0) {
        return -ENOMEM; // 缓冲区满
    }
    
    // 从用户空间拷贝数据到内核空间
    retval = copy_from_user(device_buffer, buf, bytes_to_copy);
    if (retval) {
        return -EFAULT;
    }
    
    device_buffer[bytes_to_copy] = '\0'; // 添加字符串结束符
    buffer_index = bytes_to_copy;
    
    printk(KERN_INFO "MyDevice: Write %d bytes: %s\n", bytes_to_copy, device_buffer);
    return bytes_to_copy;
}

// 定义 file_operations 结构体
static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = device_open,
    .release = device_release,
    .read = device_read,
    .write = device_write,
};

// 模块初始化
static int __init my_device_init(void)
{
    int retval;
    dev_t dev = 0;
    
    printk(KERN_INFO "MyDevice: Initializing device driver\n");
    
    // 动态分配主设备号
    retval = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME);
    if (retval < 0) {
        printk(KERN_ALERT "MyDevice: Failed to allocate device number\n");
        return retval;
    }
    
    major_number = MAJOR(dev);
    
    // 初始化 cdev 结构
    cdev_init(&my_cdev, &fops);
    my_cdev.owner = THIS_MODULE;
    
    // 添加字符设备到系统
    retval = cdev_add(&my_cdev, dev, 1);
    if (retval < 0) {
        unregister_chrdev_region(dev, 1);
        printk(KERN_ALERT "MyDevice: Failed to add cdev\n");
        return retval;
    }
    
    // 创建设备类
    dev_class = class_create(THIS_MODULE, CLASS_NAME);
    if (IS_ERR(dev_class)) {
        cdev_del(&my_cdev);
        unregister_chrdev_region(dev, 1);
        printk(KERN_ALERT "MyDevice: Failed to create class\n");
        return PTR_ERR(dev_class);
    }
    
    // 创建设备文件
    device_create(dev_class, NULL, dev, NULL, DEVICE_NAME);
    
    printk(KERN_INFO "MyDevice: Device driver loaded successfully\n");
    printk(KERN_INFO "MyDevice: Major number is %d\n", major_number);
    
    return 0;
}

// 模块退出
static void __exit my_device_exit(void)
{
    dev_t dev = MKDEV(major_number, 0);
    
    // 清理资源
    device_destroy(dev_class, dev);
    class_destroy(dev_class);
    cdev_del(&my_cdev);
    unregister_chrdev_region(dev, 1);
    
    printk(KERN_INFO "MyDevice: Device driver unloaded\n");
}

module_init(my_device_init);
module_exit(my_device_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple character device driver");

2.2 测试应用程序

c 复制代码
// test_device.c - 用户空间测试程序
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define DEVICE_PATH "/dev/my_device"

int main()
{
    int fd;
    char buffer[256];
    ssize_t ret;
    
    // 打开设备
    fd = open(DEVICE_PATH, O_RDWR);
    if (fd < 0) {
        perror("Failed to open device");
        return -1;
    }
    
    // 写入数据
    char *message = "Hello, Linux Driver!";
    ret = write(fd, message, strlen(message));
    if (ret < 0) {
        perror("Failed to write to device");
        close(fd);
        return -1;
    }
    printf("Written %zd bytes to device\n", ret);
    
    // 读取数据
    ret = read(fd, buffer, sizeof(buffer) - 1);
    if (ret < 0) {
        perror("Failed to read from device");
        close(fd);
        return -1;
    }
    
    buffer[ret] = '\0'; // 添加字符串结束符
    printf("Read %zd bytes from device: %s\n", ret, buffer);
    
    // 关闭设备
    close(fd);
    return 0;
}

3. 高级功能实现

3.1 ioctl 命令实现

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

// 定义 ioctl 命令
#define MYDEVICE_IOCTL_MAGIC 'k'
#define MYDEVICE_RESET _IO(MYDEVICE_IOCTL_MAGIC, 0)
#define MYDEVICE_GET_SIZE _IOR(MYDEVICE_IOCTL_MAGIC, 1, int)
#define MYDEVICE_SET_SIZE _IOW(MYDEVICE_IOCTL_MAGIC, 2, int)

static long device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    int retval = 0;
    
    // 检查命令类型和权限
    if (_IOC_TYPE(cmd) != MYDEVICE_IOCTL_MAGIC) {
        return -ENOTTY; // 不是我们的命令
    }
    
    if (_IOC_NR(cmd) > 2) {
        return -ENOTTY; // 命令编号无效
    }
    
    switch (cmd) {
    case MYDEVICE_RESET:
        buffer_index = 0;
        memset(device_buffer, 0, sizeof(device_buffer));
        printk(KERN_INFO "MyDevice: Buffer reset\n");
        break;
        
    case MYDEVICE_GET_SIZE:
        retval = put_user(buffer_index, (int __user *)arg);
        if (retval) {
            return -EFAULT;
        }
        printk(KERN_INFO "MyDevice: Get size: %d\n", buffer_index);
        break;
        
    case MYDEVICE_SET_SIZE:
        // 实际应用中需要更复杂的边界检查
        retval = get_user(buffer_index, (int __user *)arg);
        if (retval) {
            return -EFAULT;
        }
        printk(KERN_INFO "MyDevice: Set size to: %d\n", buffer_index);
        break;
        
    default:
        return -ENOTTY;
    }
    
    return retval;
}

// 更新 file_operations
static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = device_open,
    .release = device_release,
    .read = device_read,
    .write = device_write,
    .unlocked_ioctl = device_ioctl,
};

3.2 poll 操作实现

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

static unsigned int device_poll(struct file *filp, poll_table *wait)
{
    unsigned int mask = 0;
    
    // 将等待队列添加到 poll_table
    poll_wait(filp, &my_wait_queue, wait);
    
    // 检查设备状态
    if (buffer_index > 0) {
        mask |= POLLIN | POLLRDNORM;  // 可读
    }
    
    if (buffer_index < sizeof(device_buffer) - 1) {
        mask |= POLLOUT | POLLWRNORM; // 可写
    }
    
    return mask;
}

// 更新 file_operations
static struct file_operations fops = {
    // ... 其他成员
    .poll = device_poll,
};

4. 最佳实践和注意事项

4.1 错误处理要点

c 复制代码
// 正确的错误处理示例
static ssize_t safe_device_read(struct file *filp, char __user *buf, 
                               size_t len, loff_t *offset)
{
    // 参数验证
    if (!buf) {
        return -EINVAL;
    }
    
    if (len == 0) {
        return 0;
    }
    
    // 边界检查
    if (*offset >= buffer_index) {
        return 0; // EOF
    }
    
    // 实际读取操作...
}

4.2 并发控制

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

static DEFINE_MUTEX(device_mutex);

static ssize_t thread_safe_write(struct file *filp, const char __user *buf,
                                size_t len, loff_t *offset)
{
    int retval;
    
    // 获取互斥锁
    if (mutex_lock_interruptible(&device_mutex)) {
        return -ERESTARTSYS;
    }
    
    // 临界区操作
    retval = copy_from_user(device_buffer, buf, 
                           min(len, sizeof(device_buffer) - 1));
    if (!retval) {
        // 成功处理
        buffer_index = min(len, sizeof(device_buffer) - 1);
        device_buffer[buffer_index] = '\0';
    }
    
    // 释放互斥锁
    mutex_unlock(&device_mutex);
    
    return retval ? -EFAULT : len;
}

5. 调试技巧

5.1 使用 printk 调试

c 复制代码
// 不同级别的调试信息
#define DEBUG_LEVEL 3

#if DEBUG_LEVEL >= 1
#define dbg_info(fmt, args...) printk(KERN_INFO "MyDevice: " fmt, ##args)
#else
#define dbg_info(fmt, args...)
#endif

#if DEBUG_LEVEL >= 2
#define dbg_debug(fmt, args...) printk(KERN_DEBUG "MyDevice: " fmt, ##args)
#else
#define dbg_debug(fmt, args...)
#endif

5.2 动态调试

c 复制代码
// 在模块参数中控制调试级别
static int debug_level = 1;
module_param(debug_level, int, S_IRUGO | S_IWUSR);

// 条件调试输出
if (debug_level > 0) {
    printk(KERN_DEBUG "MyDevice: Debug information\n");
}

总结

通过本课程的学习,您应该能够:

  1. 理解 file_operations 结构体的作用和重要性
  2. 实现 完整的字符设备驱动程序
  3. 掌握 用户空间与内核空间的数据交换
  4. 应用 高级功能如 ioctl、poll 等
  5. 遵循 内核驱动开发的最佳实践

继续深入学习建议:

  • 研究内核源码中的实际驱动实现
  • 学习设备树(Device Tree)的使用
  • 掌握中断处理和 DMA 操作
  • 了解内核同步机制和内存管理

这个框架为您提供了 Linux 内核驱动开发的核心知识,特别是 file_operations 结构体的深入理解。实际开发中请根据具体需求和内核版本进行适当调整。

相关推荐
泉城老铁2 小时前
除了群机器人,如何通过钉钉工作通知API给指定用户发消息?
spring boot·后端
泉城老铁2 小时前
springboot 对接钉钉发送消息
spring boot·后端
用户203735549812 小时前
51CTO-Linux内核驱动开发视频课程
后端
shallwe小威2 小时前
SpringBoot集成Kafka
spring boot·后端·kafka
databook3 小时前
Manim实现气泡特效
后端·python·动效
梵得儿SHI3 小时前
Java 运算符全解析:从基础用法到优先级避坑,一文吃透 5 大类运算符
后端
xuejianxinokok3 小时前
透明的多级并发(行) 方式
后端
235163 小时前
【Redis】缓存击穿、缓存穿透、缓存雪崩的解决方案
java·数据库·redis·分布式·后端·缓存·中间件