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

【摘要】linux用户态的设备驱动开发:并不是所有的设备驱动程序都要在内核编写,有些情况下,在用户空间编写驱动程序能够更好地解决遇到的问题。本文对用户态驱动优缺点进行分析。

1、用户空间驱动程序的优点

1、可以和整个C库链接。

2、在驱动中可以使用浮点数,在某些特殊的硬件中,可能需要使用浮点数,而linux内核并不提供浮点数的支持。如果能在用户态实现驱动,就可以轻松解决这一问题。

3、驱动问题不会导致整个系统挂起。内核态驱动的一些错误常常导致整个系统挂起。

4、用户态的驱动调试方便。

5、可以给出封闭源码的驱动程序,不必采用GPL,更为灵活

2、用户空间驱动程序的缺点

1、无法使用中断。中断在用户空间不可用,最新的UIO接口已经解决了这一问题。

有些硬件厂商只提供和某些linux开发版(常常早就过时了)相匹配的用户空间驱动。尽管对用户空间驱动存在争议,但内核还是选择对其进行支持。最新的接口称为UIO(以前的内核也有,但新版本做了很多修改),是在2.6.22版本的一个补丁中出现的,并且在2.6.23中正式合并到了内核的代码树中。和以前相比,该接口有了一些改变。和以前的版本一样,UIO并没有完全取消内核空间代码。在内核中有一个很小的模块用于建立连接到PCI总线的设备(device)或者接口(interface),并提供中断处理程序。这一点(中断处理程序)很重要,尽管有更多的事情可以在用户空间完成,但还是需要有一个内核中的中断处理程序来通知设备停止发送中断。

该内核模块需要包括< linux/uio_driver.h>。如果他是一个PCI设备的驱动,需要按照统一设备模型的要求注册PCI驱动。当需要连接设备的时候(可能在PCI的probe()函数中),该驱动需要填写一个uio_info结构体:

struct uio_info {
  char *name;
  char  *version;
  struct uio_mem  mem[MAX_UIO_MAPS];
  long irq;
  unsigned long irq_flags;
  void  *priv;
  irqreturn_t (*handler)(int irq, struct uio_info *dev_info);
  int (*mmap)(struct uio_info *info, struct vm_area_struct *vma);
  int (*open)(struct uio_info *info, struct inode *inode);
  int (*release)(struct uio_info *info, struct inode *inode);
/* Internal stuff omitted */
 };

上面结构体中,name是设备的名字;version是驱动的版本号(将显示在sysfs中);irp是设备所使用的IRQ号;irq_flags是中断调用标志,将传递给request_irq();handler()是中断处理程序,除了负责应答硬件中断,一般不再做别的工作;mmap()/open()/release()由file_operations中的对应成员调用

结构体中的mem数组用于描述任何可以被映射到用户空间的内存区域。uio_mem结构体的主要成员如下:

struct uio_mem {
  unsigned long addr;
  unsigned long size;
  int memtype;
  void __iomem *internal_addr;
  /* ... */
};

对于每个可以映射的区域,addr是该区域的地址(物理地址);size是该区域的大小;internal_addr是由ioremap()返回的该区域地址(虚拟地址)。

memtype用于描述该区域的属性,包括:

*UIO_MEM_PHYS

表明addr是一个物理地址,通常用于I/O内存区

*UIO_MEM_LOGICAL

该区域处于内核的逻辑地址空间,例如那些通过kmalloc()获得的空间

*UIO_MEM_VIRTUAL

该区域处于内核的虚拟地址空间,这些区域由vmalloc_user()使用

一旦uio_info结构体填写完毕,驱动会将其传给如下函数:

int uio_register_device(struct device *parent, struct uio_info *info);

parent指针告诉内核该UIO设备是和哪个”real”的设备相关联,如果驱动是针对PCI设备,则parent指向pci_dev->dev。

内核空间的UIO基本上就这些API了,当设备被拔除,驱动需要调用:

void uio_unregister_device(struct uio_info *info);

最后一个和通知(note)相关的函数是:

void uio_event_notify(struct uio_info *info);

该函数的目的是通知UIO核心,发生了一个事件(典型情况是一个中断)。当真正的中断发生时,stub驱动不必调用uio_event_notify(),但这个函数可以在其他的情况下模拟中断。

在用户空间,第一个UIO处理的设备将显示为/dev/uio0(假设udev已启动)。用户空间的驱动将会打开该设备。对该设备的读操作返回一个int值,该值保存了事件计数(发生了多少次中断)。如果从上次读设备以来还没有产生中断,则读操作会阻塞直到一个中断发生(当然,支持非阻塞的操作)。文件描述符可以被发送给poll()。

在内核空间驱动中所描述的内存区域可以通过mmap()调用映射到用户空间中。传递给mmap()的参数有一些奇怪,内核的第N个区域,其offset参数的值将是当前页大小的第N倍。也就是说,如果当前系统的页大小是4096个字节,映射第一个内存区的offset值为0,而第二个内存区就是4096,第三个就是8192,以此类推。

当然了,使用UIO会受到一些限制。首先,UIO驱动是字符型的驱动,目前没有提供创建用户空间块设备或网络设备驱动的接口。另外,在用户空间也不可能建立DMA操作。但是,对于那些只包括I/O内存访问以及简单的中断处理程序的驱动,UIO接口完全可以胜任。

在UIO接口的patch中,除了UIO核心程序,还有一个示例程序。根据作者ThomasGleixner的实验,如果采用完全装入内核的方式实现驱动,则需要实现68个不同的ioctl()命令,整个驱动的长度超过5000行,对应的用户空间代码超过3000行;而如果采用UIO模式的驱动,内核代码只有156行,而用户空间代码也下降到3000行以内。

不过,linux内核的重要维护者Andrew Morton也对UIO的使用表达了一些保留意见:

“我对UIO的整个想法有一些不确定,我感觉我们应该更鼓励人们在GPL许可证下开发内核驱动,而不是鼓励他们开发不公开源代码的用户空间驱动。这些驱动往往更慢,缺乏可靠性,并且无法跨平台使用。”

在2.6.23中,和UIO相关的主要有以下文件:

(1)/drivers/uio/uio.c

uio子系统的核心文件

(2)/drivers/uio/uio_cif.c

Hilscher CIF DeviceNet以及Profibus card的驱动。

3、从dpdk分析uio驱动

uio提供了用户态驱动开发的框架,主要是由于驱动依赖的内核函数和宏因内核版本变化,导致驱动可能也需要改,所以改为用户态驱动来完成任务,这也算是类似windows的HAL的机制,当然HAL是硬件抽象层。

dpdk是如何与硬件交互的?

dpdk中的驱动需要定期去poll/select /dev/uioX 检查设备是否有中断产生,并不是用来收发数据,另外通过mmap来操作设备的设备内存。uio框架本身要处理设备的中断,中断只能在内核态处理,uio的中断处理函数也只是增加中断的计数而已。

mmap可以处理物理内存映射,逻辑内存,内核虚拟内存映射。 uio也是通过mmap将设备的内存映射到用户空间,当然用户态也可以通过/sys/class/uio/uioX/maps/mapX来实现对设备内存的访问,所以发送和接受数据是通过操作设备的内存来完成的。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-31 04:40:57

Linux 设备驱动之 UIO 用户态驱动优缺点分析的相关文章

聊聊Linux用户态驱动设计

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

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

[国嵌攻略][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内核与应用数据通信(四):映射设备内核空间到用户态

[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet.文章仅供学习交流,请勿用于商业用途] 一个进程的内存映象由以下几部分组成:代码段.数据段.BSS段和堆栈段.以及内存映射的区域等部分,内存映射函数mmap(), 负责把文件内容映射到进程的虚拟内存空间, 通过对这段内存的读取和改动.来实现对文件的读取和改动,而文件能够是设备驱动文件节点. 通过把内核驱动的内存空间映射到应用层.能够实现应用和内核空间的数据交换. linux设备分三种,字符设备.块设备.网络接口设备

Linux中的栈:用户态栈/内核栈/中断栈

http://blog.chinaunix.net/uid-14528823-id-4136760.html Linux中有多种栈,很容易弄晕,简单说明一下: 1.用户态栈:在进程用户态地址空间底部,跟平时我们简单和理解的一样,就是虚拟地址空间中的一段,不多说~ 2.内核栈:     跟用户态栈是独立的,在用户态和内核态切换时,需要进行切换.     默认8k,可以通过内核配置项修改     跟thread_info结构放在一起,公用一个union:thread_union, 点击(此处)折叠或

用户空间驱动

一个第一次涉及内核问题的 Unix 程序猿, 可能会紧张写一个模块. 编写一个用户程序来 直接读写设备port可能easy些. 确实, 有几个论据倾向于用户空间编程, 有时编写一个所谓的用户空间设备驱动对照钻研 内核是一个明智的选择. 在本节, 我们讨论几个理由, 为什么你可能在用户空间编写驱动. 本书是关于内核空间驱动的, 可是, 所以我们不超越这个介绍性的讨论. 用户空间驱动的优点在于: ? 完整的 C 库能够连接. 驱动能够进行很多奇怪的任务, 不用依靠外面的程序(实现 使用策略的工具程序

Linux 设备驱动之 UIO 机制(一)

[摘要]什么是UIO?UIO是运行在用户空间的I/O,那为什么要把I/O放在用户空间呢? 1.UIO出现的原因 第一,硬件设备可以根据功能分为网络设备,块设备,字符设备,或者根据与CPU相连的方式分为PCI设备,USB设备等.它们被不同的内核子系统支持.这些标准的设备的驱动编写较为容易而且容易维护.很容易加入主内核源码树.但是,又有很多设备难以划分到这些子系统中,比如I/O卡,现场总线接口或者定制的FPGA.通常这些非标准设备的驱动被实现为字符驱动.这些驱动使用了很多内核内部函数和宏.而这些内部

Linux 设备驱动之 UIO 机制

一个设备驱动的主要任务有两个: 1. 存取设备的内存 2. 处理设备产生的中断 对于第一个任务.UIO 核心实现了mmap()能够处理物理内存(physical memory),逻辑内存(logical memory), 虚拟内存(virtual memory).UIO驱动的编写是就不须要再考虑这些繁琐的细节. 第二个任务,对于设备中断的应答必须在内核空间进行.所以在内核空间有一小部分代码 用来应答中断和禁止中断,可是其余的工作所有留给用户空间处理. 假设用户空间要等待一个设备中断,它仅仅须要简

linux设备驱动归纳总结(四):5.SMP下的竞态和并发

linux设备驱动归纳总结(四):5.多处理器下的竞态和并发 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 这节将在上一节的基础上介绍支持多处理器和内核抢占的内核如何避免并发.除了内核抢占和中断外,由于多处理起的缘故,它可以做到多个程序同时执行.所以,进程除了要防自己的处理器外,还要防别的处理器,这个就是这节要介绍的内容. xxxxxxxxxxxxxxxxxxxxxxx