聊聊Linux用户态驱动设计

序言

设备驱动可以运行在内核态,也可以运行在用户态,用户态驱动的利弊网上有很多的讨论,而且有些还上升到政治性上,这里不再多做讨论。不管用户态驱动还是内核态驱动,他们都有各自的缺点。内核态驱动的问题是:系统调用开销大;学习曲线陡峭;接口稳定性差;调试困难;bug致命;编程语言选择受限;而用户态驱动面临的挑战是:如何中断处理;如何DMA;如何管理设备的依赖关系;无法使用内核服务等。对此,《User-Space Device Drivers in Linux: A First Look》 一文有较详细描述。

可能是为了性能优化,或者为了故障隔离,或者为了逃避开源许可证的约束,不管是基于何种目的,linux已有多种用户态驱动的实现,如UIO,VFIO、USB用户态驱动等。它们在处理中断、DMA、设备依赖管理等方面的设计方案和实现细节上各有千秋,这是《User-Space Device Drivers in Linux: A First Look》 没有提到的,也是本文的重点。

UIO

UIO 框架导出sysfs和/dev/uioX 2套用户态接口,用户对设备节点/dev/uioX进行设备控制,mmap()接口用于映射设备寄存器空间,write()接口用于控制中断关闭/打开,read()接口用于等待一个设备中断。

因为对于设备中断的应答必须在内核空间进行,所以在内核空间有一小部分代码用来应答中断和禁止中断,其余的工作全部留给用户空间处理。如果用户空间要等待一个设备中断,它只需要简单的阻塞在对 /dev/uioX的read()操作上。 当设备产生中断时,read()操作立即返回。UIO 也实现了poll()系统调用,你可以使用 select()来等待中断的发生。select()有一个超时参数可以用来实现有限时间内等待中断。

UIO的几个特点:

  • 一个UIO设备最多支持5个mem和portio空间mmap映射。
  • UIO设备的中断用户态通信机制基于wait_queue实现。
  • 一个UIO设备只支持一个中断号注册,支持中断共享。

总的来说,UIO框架适用于简单设备的驱动,因为它不支持DMA,不能支持多个中断线,缺乏逻辑设备抽象能力。

VFIO

上文提到,UIO不支持DMA,所以通过DMA传输大流量数据的IO设备,如网卡、显卡等设备,无法使用UIO框架,VFIO做为UIO的升级版,主要就是解决了这个问题。通过用户态配置IOMMU接口,可以将DMA地址空间映射限制在进程虚拟空间中。这对高性能驱动和虚拟化场景device passthrough尤其重要。

在VFIO框架中,有几个核心概念或对象:IOMMU、/dev/vfio、container、iommu_group。

  • IOMMU是一个硬件单元,它可以把设备的IO地址映射成虚拟地址,为设备提供页表映射,设备通过IOMMU将数据直接DMA写到用户空间。之所以不共用MMU单元,是为了保证和进程的页表相互独立,防止设备访问进程的任意地址空间。所以VFIO的IOMMU功能保障了安全的非特权级别的用户态设备驱动机制。
  • /dev/vfio是一个设备文件,作为一个IOMMU设备的用户态呈现。
  • container是内核对象,表示一个IOMMU设备,是一个IOMMU设备的内核态呈现。所以在VFIO中,container是IOMMU操作的最小对象。
  • 在虚拟化场景下,一个物理网卡可能要虚拟成几个虚拟网卡,或者说虚拟功能设备(VF),这几个VF共用一个IOMMU,所以VFIO模型增加一个iommu_group的概念,用来表示共享同一个IOMMU的一组device。

VFIO的几个特点:

  • VFIO设备支持多中断号注册。
  • 设备的中断用户态通信机制基于eventfd/irqfd实现。用户通过/dev/vfio设备select/poll/epoll,从而实现中断从内核态到用户态的异步事件通知。
  • 支持对物理设备进行逻辑抽象。
  • 仅支持pci intx中断共享,其他类型中断不支持共享。
  • VFIO仅支持特定IOMMU设备,如x86与PowerPC平台的PCI设备和ARM平台的platform设备。

USB用户态驱动

usbfs(USB file system)提供了一些在用户空间下操作USB设备的函数接口和数据结构, 在开发用户态驱动时, 可以直接利用这些函数接口来实现对 USB 设备的控制和数据传输。
libusb对usb file system提供的函数接口和数据结构进行了封装, 可以有效减少程序中函数和数据结构使用不当造成的错误。Libusb 对 USB 设备的访问提供了两种机制, 同步访问和异步访问。
khubd是内核后台线程,用来管理监视USB HUB的状态, 一旦发生热插拔就会唤醒这个线程, 进而添加或者移除设备,并发送netlink消息传递给用户态空间。

USB 用户态驱动框架的几个特点:

  • 设备的中断用户态通信机制基于netlink实现。
  • USB设备以usbdevfs (后改名为usbfs)文件系统的形式(如/proc/bus/usb/BBB/DDD)对用户态呈现。
  • 对usb的所有访问通过usbfs ioctl实现,如control transfer, bulk transfer, reset等。
  • 仅支持USB设备

参考资料

Linux Kernel v4.4

User-Space Device Drivers in Linux: A First Look

User-Mode Driver Framework

VFIO Introduction

Linux iommu和vfio概念空间解构

原文地址:https://www.cnblogs.com/wahaha02/p/8569074.html

时间: 2024-10-03 17:25:51

聊聊Linux用户态驱动设计的相关文章

[国嵌攻略][155][I2C用户态驱动设计]

用户态驱动模型 用户态驱动模型首先是一个应用程序,其次是在这个用户程序中通过内核调用来驱动设备. IIC通用驱动代码 IIC通用驱动程序的代码在/drivers/i2c/i2c-dev.c中.一次读操作或者一次写操作就是一条消息. EEPROM用户态驱动 IIC通用设备对应/dev/i2c-0设备文件. 1.打开通用设备驱动 2.构造写数据到eeprom的消息 3.使用ioctl写入数据 4.构造从eeprom读数据的消息 5.使用ioctl读出数据 6.关闭设备 配置IIC驱动 make me

Linux 设备驱动之 UIO 用户态驱动优缺点分析

[摘要]linux用户态的设备驱动开发:并不是所有的设备驱动程序都要在内核编写,有些情况下,在用户空间编写驱动程序能够更好地解决遇到的问题.本文对用户态驱动优缺点进行分析. 1.用户空间驱动程序的优点 1.可以和整个C库链接. 2.在驱动中可以使用浮点数,在某些特殊的硬件中,可能需要使用浮点数,而linux内核并不提供浮点数的支持.如果能在用户态实现驱动,就可以轻松解决这一问题. 3.驱动问题不会导致整个系统挂起.内核态驱动的一些错误常常导致整个系统挂起. 4.用户态的驱动调试方便. 5.可以给

Linux I2C驱动--用户态驱动简单示例

1. Linux内核支持I2C通用设备驱动(用户态驱动:由应用层实现对硬件的控制可以称之为用户态驱动),实现文件位于drivers/i2c/i2c-dev.c,设备文件为/dev/i2c-0 2. I2C通用设备驱动以字符设备注册进内核的 static const struct file_operations i2cdev_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = i2cdev_read, .write = i2cde

Linux用户态程序计时方式详解

前言 良好的计时器可帮助程序开发人员确定程序的性能瓶颈,或对不同算法进行性能比较.但要精确测量程序的运行时间并不容易,因为进程切换.中断.共享的多用户.网络流量.高速缓存访问及转移预测等因素都会对程序计时产生影响. 本文将不考虑这些影响因素(相关资料可参考<深入理解计算机系统>一书),而仅仅关注Linux系统中用户态程序执行时间的计算方式.除本文所述计时方式外,还可借助外部工具统计耗时,如<Linux调试分析诊断利器——strace>一文中介绍的strace. 本文示例代码的运行环

linux用户态和内核态通信之netlink机制【转】

本文转载自:http://blog.csdn.net/zcabcd123/article/details/8272360 这是一篇学习笔记,主要是对<Linux 系统内核空间与用户空间通信的实现与分析>中的源码imp2的分析.其中的源码,可以到以下URL下载: http://www-128.ibm.com/developerworks/cn/Linux/l-netlink/imp2.tar.gz [size=3]参考文档[/size] <linux 系统内核空间与用户空间通信的实现与分析

Linux 用户态与内核态的交互【转载】

Linux 用户态与内核态的交互  在 Linux 2.4 版以后版本的内核中,几乎全部的中断过程与用户态进程的通信都是使用 netlink 套接字实现的,例如iprote2网络管理工具,它与内核的交互就全部使用了netlink,著名的内核包过滤框架Netfilter在与用户空间的通 读,也在最新版本中改变为netlink,无疑,它将是Linux用户态与内核态交流的主要方法之一.它的通信依据是一个对应于进程的标识,一般定为该进 程的 ID.当通信的一端处于中断过程时,该标识为 0.当使用 net

Linux用户态程序计时方式详解[转]

转自: http://www.cnblogs.com/clover-toeic/p/3845210.html 前言 良好的计时器可帮助程序开发人员确定程序的性能瓶颈,或对不同算法进行性能比较.但要精确测量程序的运行时间并不容易,因为进程切换.中断.共享的多用户.网络流量.高速缓存访问及转移预测等因素都会对程序计时产生影响. 本文将不考虑这些影响因素(相关资料可参考<深入理解计算机系统>一书),而仅仅关注Linux系统中用户态程序执行时间的计算方式.除本文所述计时方式外,还可借助外部工具统计耗时

linux 用户态 内核态

http://blog.chinaunix.net/uid-1829236-id-3182279.html 究竟什么是用户态,什么是内核态,这两个基本概念以前一直理解得不是很清楚,根本原因个人觉得是在于因为大部分时候我们在写程序时关注的重点和着眼的角度放在了实现的功能和代码的逻辑性上,先看一个例子: 1)例子 C代码 1.     void testfork(){   2.     if(0 = = fork()){   3.     printf(“create new process suc

linux用户态与内核态

系统调用:如何中用户态切换到内核态 在linux中,系统调用是通过0x86体系结构中的软件中断实现的.这个软件中断与通常说的硬件中断不同之处在于,它是通过软件指令触发,而不是外部设备,这是程序员可以触发的一种异常,调用int 0x80汇编指令就可以产生向量号为128的异常. 进程与线程的区别与联系 操作系统教科书上讲的理论东西就不说了,这里说说linux中复制进程和复制线程的异同. 我们知道,Linux的线程实现是在核外进行的,核内提供的是创建进程的接口do_fork().内核提供了两个系统调用