当前位置: 首页 > article >正文

Linux内核 -- UIO (User-space I/O) 简介与使用笔记

UIO (User-space I/O) 简介

UIO (User-space I/O) 是 Linux 内核提供的一种机制,用于简化设备驱动的开发。它将设备的硬件资源(如内存映射、中断等)通过简单的接口暴露给用户空间程序,从而使用户可以在用户空间编写复杂的设备逻辑,而内核只需完成基础的资源管理工作。


内核态作用与实现

UIO 内核态的作用
  1. 硬件资源管理
    • 提供设备内存映射支持,将设备寄存器或内存映射到用户空间。
    • 注册和处理硬件中断(可选)。
  2. 中断通知
    • 将设备硬件中断转发给用户空间程序。
UIO 内核态的实现步骤
  1. 定义设备信息结构
    使用 struct uio_info 定义设备信息,包括中断号、内存地址等。

    static struct uio_info my_uio_info = {
        .name = "my_device",
        .version = "1.0",
        .irq = 42,                      // 中断号(若不需要中断,可设置为 UIO_IRQ_NONE)
        .irq_flags = IRQF_SHARED,       // 中断标志
        .handler = my_irq_handler,      // 中断处理函数(可选)
        .mem[0].addr = 0x40000000,      // 物理地址
        .mem[0].size = 0x1000,          // 内存大小
        .mem[0].memtype = UIO_MEM_PHYS, // 内存类型
    };
    
  2. 注册设备
    使用 uio_register_device 注册设备信息:

    static int __init my_uio_init(void) {
        return uio_register_device(NULL, &my_uio_info);
    }
    
    static void __exit my_uio_exit(void) {
        uio_unregister_device(&my_uio_info);
    }
    
    module_init(my_uio_init);
    module_exit(my_uio_exit);
    MODULE_LICENSE("GPL");
    
  3. 实现中断处理函数(可选)
    如果需要支持硬件中断,需要实现中断处理逻辑:

    static irqreturn_t my_irq_handler(int irq, struct uio_info *dev_info) {
        return IRQ_HANDLED; // 通知内核中断已处理
    }
    
注意事项
  • 如果设备不需要中断功能,可以将 irq 设置为 UIO_IRQ_NONE,并省略 handler
  • 内核态代码只需要完成资源管理工作,具体设备逻辑由用户空间程序完成。

用户态作用与实现

UIO 用户态的作用
  1. 设备访问:通过 /dev/uioX 文件与设备交互。
  2. 内存映射:使用 mmap 将设备内存映射到用户空间,直接操作设备寄存器。
  3. 中断处理:通过 read 调用等待中断事件。
用户态的实现步骤
  1. 打开设备文件

    int fd = open("/dev/uio0", O_RDWR);
    if (fd < 0) {
        perror("Failed to open /dev/uio0");
        return -1;
    }
    
  2. 内存映射
    使用 mmap 将设备内存映射到用户空间:

    void *reg_base = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (reg_base == MAP_FAILED) {
        perror("Failed to mmap");
        close(fd);
        return -1;
    }
    
  3. 处理中断
    通过 read 等待中断事件:

    unsigned int irq_count;
    while (1) {
        read(fd, &irq_count, sizeof(irq_count));
        printf("Interrupt received! Count: %d\n", irq_count);
    }
    
  4. 释放资源
    在程序结束时,释放映射的内存并关闭设备:

    munmap(reg_base, 0x1000);
    close(fd);
    

示例代码

内核态代码

以下是一个完整的内核态代码示例:

#include <linux/module.h>
#include <linux/uio_driver.h>

static irqreturn_t my_irq_handler(int irq, struct uio_info *dev_info) {
    return IRQ_HANDLED;
}

static struct uio_info my_uio_info = {
    .name = "my_device",
    .version = "1.0",
    .irq = 42,
    .irq_flags = IRQF_SHARED,
    .handler = my_irq_handler,
    .mem[0].addr = 0x40000000,
    .mem[0].size = 0x1000,
    .mem[0].memtype = UIO_MEM_PHYS,
};

static int __init my_uio_init(void) {
    return uio_register_device(NULL, &my_uio_info);
}

static void __exit my_uio_exit(void) {
    uio_unregister_device(&my_uio_info);
}

module_init(my_uio_init);
module_exit(my_uio_exit);
MODULE_LICENSE("GPL");
用户态代码

以下是对应的用户态代码示例:

#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>

int main() {
    int fd = open("/dev/uio0", O_RDWR);
    if (fd < 0) {
        perror("Failed to open /dev/uio0");
        return -1;
    }

    void *reg_base = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (reg_base == MAP_FAILED) {
        perror("Failed to mmap");
        close(fd);
        return -1;
    }

    unsigned int irq_count;
    while (1) {
        read(fd, &irq_count, sizeof(irq_count));
        printf("Interrupt received! Count: %d\n", irq_count);
    }

    munmap(reg_base, 0x1000);
    close(fd);
    return 0;
}

注意事项

  1. 中断功能
    • 如果不需要中断功能,可以省略中断处理函数,并将 irq 设置为 UIO_IRQ_NONE
  2. 性能问题
    • 使用 UIO 时,中断和用户空间之间的切换会增加延迟,不适合高实时性场景。
  3. 安全性
    • UIO 将设备的硬件资源直接映射到用户空间,需确保用户空间程序的安全性,以防止资源滥用。

总结

UIO 是一种简化设备驱动开发的高效工具,适合处理简单设备或快速原型开发。在内核态中,开发者只需完成资源注册和中断处理(可选);在用户态中,程序通过 /dev/uioX 文件与设备交互,完成具体逻辑。


http://www.kler.cn/a/453931.html

相关文章:

  • 如何快速找到合适的科学问题
  • 基于SpringBoot的“房产销售平台”的设计与实现(源码+数据库+文档+PPT)
  • 【再谈设计模式】享元模式~对象共享的优化妙手
  • VirtualBox下ubuntu23.04使用主机串口以及使用 minicom 进行串口调试
  • 如何检查交叉编译器gcc工具链里是否有某个库(以zlib库和libpng库为例)
  • RK3588在Android13/14如何查看GPU,NPU,DDR,RGA数据
  • 使用Grafana中按钮插件实现收发HTTP请求
  • 【C语言】矩阵乘法
  • 如何查看个人电脑ip和修改ip
  • FFmpeg 的常用API
  • 【机器学习】探索机器学习与人工智能:驱动未来创新的关键技术
  • 归并排序:JAVA
  • IntelliJ IDEA 中 Editor > General > Appearance 设置:编辑器的视觉外观和行为
  • C++--------------树
  • RK3576 Android14编译OTA包提示java.lang.UnsupportedClassVersionError问题
  • STM32学习之 蜂鸣器
  • mac远程控制另一台mac怎么操作?
  • 电脑ip地址会变化吗?电脑ip地址如何固定
  • Postman接口测试01|接口测试基础概念、http协议、RESTful风格、接口文档
  • ELM回归-单隐层前馈神经网络(Single Hidden Layer Feedforward Neural Network)
  • STM32基于标准库如何查看时钟主频,100%简单
  • 使用 ECharts 与 Vue 构建数据可视化组件
  • 在linux系统中使用jdbc访问sqlite数据库时报错“java.lang.UnsatisfiedLinkError”
  • 一文流:Mysql my.cnf配置完整示例
  • 精选9个自动化任务的Python脚本精选
  • docker仓库用户认证