• uio用户态驱动



    1.1为什么出现了UIO?
       硬件设备可以根据功能分为网络设备,块设备,字符设备,或者根据与CPU相连的方式分为PCI设备,USB设备等。它们被不同的内核子系统支持。这些标准的设备的驱动编写较为容易而且容易维护。很容易加入主内核源码树。但是,又有很多设备难以划分到这些子系统中,比如I/O卡,现场总线接口或者定制的FPGA。通常这些非标准设备的驱动被实现为字符驱动。这些驱动使用了很多内核内部函数和宏。而这些内部函数和宏是变化的。这样驱动的编写者必须编写一个完全的内核驱动,而且一直维护这些代码。而且这些驱动进不了主内核源码。于是就出现了用户空间I/O框架(Userspace I/O framework)。

    1.2 dpdk的uio使用

    在基于kernel的IO模型中,所有的设备IO都要经过内核处理,在高并发的网络数据包收发的情况下,大量硬件中断会降低内核数据包处理能力,内核和用户空间的数据拷贝也会造成大量的计算资源浪费。所以,作为高并发大流量网络开发框架的DPDK,必须要找到一个能够避免内核中断爆炸和大量数据拷贝的方法,在用户空间能够直接和硬件进行交互。Linux的UIO就是这样一个将硬件操作映射到用户空间的kernel bypass方案。

    下图展示了UIO驱动的内核部分,用户空间部分,和UIO框架以及内核的关系:

    1.3 dpdk的网口绑定干了啥?

    绑定操作:

    ./dpdk-devbind.py -b igb_uio 0000:00:07.0

    既然是设备驱动程序一定会创建设备,/dev下查看:多出了uio0设备;

    vm65:/dev # ll |grep uio
    crw-------  1 root root    246,   0 Feb 18 07:41 uio0
    vm65:/dev #

    查看/sys/class/uio/uio:

    vm65:/sys/class/uio/uio0 # ll
    total 0
    -r--r--r-- 1 root root 4096 Feb 18 06:42 dev
    lrwxrwxrwx 1 root root    0 Feb 18 06:42 device -> ../../../0000:00:07.0
    -r--r--r-- 1 root root 4096 Feb 18 06:42 event
    drwxr-xr-x 4 root root    0 Feb 18 06:42 maps
    -r--r--r-- 1 root root 4096 Feb 18 06:42 name
    drwxr-xr-x 2 root root    0 Feb 18 06:42 power
    lrwxrwxrwx 1 root root    0 Feb 18 06:42 subsystem -> ../../../../../class/uio
    -rw-r--r-- 1 root root 4096 Feb 18 06:41 uevent
    -r--r--r-- 1 root root 4096 Feb 18 06:42 version

    同时,查看dmesg,多出了以下几行信息:

    <6>[171282.211083] igb_uio 0000:00:07.0: mapping 1K dma=0x123fb9b000 host=ffff88123fb9b000
    <6>[171282.211088] igb_uio 0000:00:07.0: unmapping 1K dma=0x123fb9b000 host=ffff88123fb9b000

    可以看到。绑定过程中的kernel part会通过uio_register_device 注册对应的uio设备,获取pcie的配置空间BAR信息包括addr、name、size、offset填充到map/map0中,这些在用户态会将其读出,并mmap至用户态进程空间,这样用户态便可直接操作设备的内存空间。

    除了内存管理,第二个任务,对于设备中断的应答必须在内核空间进行。所以在内核空间有一小部分代码用来应答和禁止中断,中断的其他任务由用户态完成;

    假设用户空间要等待一个设备中断,它仅仅须要简单的堵塞在对 /dev/uioX的read()操作上。 当设备产生中断时,read()操作马上返回。

    UIO 也实现了poll()系统调用。你能够使用 select()来等待中断的发生。select()有一个超时參数能够用来实现有限时间内等待中断。

    1.4 用户态驱动

    常用的用户态驱动开发有两种方式可选:

    1、打开/dev/mem 后,使用MMAP映射出芯片物理地址对应的虚拟地址,在用户态访问虚拟地址加偏移就能访问到芯片的寄存器;

    2、采用UIO方式,在内核态映射地址后,在用户态通过打开/dev/uioxxx 方式,再使用MMAP映射一次,就可采用跟方式1 相同的方式访问芯片reg或是memory;

    这两种方式各有优缺点:

    方式1:优点是实现简单,如果不需要用到中断,可以不用管内核态的内容,关注用户态代码就好。缺点是代码运行的进程要有root权限,不然无法访问/dev/mem,如果要使用中断还是会涉及到内核态的驱动;

    方式2:优点是无需要root权限,中断可以直接在用户态获取。缺点是必须要在内核态做初始化;即需要在内核态通过uio_register_device注册对应的uio设备,这样user space就可以通过/sys/class/uio/uioX/mapsX来访问这个uio设备;

    关键结构体&函数:

    注册uio设备前需要初始化对应的uio_info结构体:

    struct uio_info {  
        struct uio_device   *uio_dev; // 在__uio_register_device中初始化 
        const char      *name; // 调用__uio_register_device之前必须初始化 
        const char      *version; //调用__uio_register_device之前必须初始化 
        struct uio_mem      mem[MAX_UIO_MAPS]; 
        struct uio_port     port[MAX_UIO_PORT_REGIONS]; 
        long            irq; //分配给uio设备的中断号,调用__uio_register_device之前必须初始化 
        unsigned long       irq_flags;// 调用__uio_register_device之前必须初始化 
        void            *priv; // 
        irqreturn_t (*handler)(int irq, struct uio_info *dev_info); //uio_interrupt中调用,用于中断处理 
                                                                                                       // 调用__uio_register_device之前必须初始化 
        int (*mmap)(struct uio_info *info, struct vm_area_struct *vma); //在uio_mmap中被调用, 
                                                                                                                    // 执行设备打开特定操作 
        int (*open)(struct uio_info *info, struct inode *inode);//在uio_open中被调用,执行设备打开特定操作 
        int (*release)(struct uio_info *info, struct inode *inode);//在uio_device中被调用,执行设备打开特定操作 
        int (*irqcontrol)(struct uio_info *info, s32 irq_on);//在uio_write方法中被调用,执行用户驱动的 
                                                                                           //特定操作。 
    }; 

    设备注册函数:

    #define uio_register_device(parent, info) __uio_register_device(THIS_MODULE, parent, info)
    int __uio_register_device(struct module *owner,
                  struct device *parent,
                  struct uio_info *info)

    用户态驱动示例:

    https://blog.csdn.net/ganggexiongqi/article/details/6751798

    https://blog.csdn.net/ganggexiongqi/article/details/6748103

  • 相关阅读:
    Javascript、C#、php、asp、python 等语言的链式操作的实现
    根据C# 事件思想来实现 php 事件
    initerrlog: 无法打开错误日志文件 'D:Program FilesMicrosoft SQL ServerMSSQL10_50.MSSQLSERVERMSSQLLog 解决办法
    64位直接加载个img 标签的src
    各种脚本语言变量作用域总结
    数据库设计14个技巧【转】
    基于Jquery 简单实用的弹出提示框
    C# dll 事件执行 js 回调函数
    php 配置xdebug
    sqlserver 构架与性能优化
  • 原文地址:https://www.cnblogs.com/rex-2018-cloud/p/10399184.html
Copyright © 2020-2023  润新知