本文共 4756 字,大约阅读时间需要 15 分钟。
UIO技术详解
UIO(Userspace I/O)是一种运行在用户空间的I/O技术,主要用于编写大型驱动程序。UIO能够有效地避免频繁的内核模块切换与重启,同时在某些情况下其性能甚至优于传统的内核驱动。UIO技术常见于DPDK等大型驱动程序中,同时也适合在虚拟化环境中实现设备透传,成为衡量性能的不错选择之一。
编写设备驱动程序通常需要处理两大核心任务:设备内存的读写和中断的响应处理。UIO通过支持mmap功能,将物理内存映射到用户空间,从而实现了对设备内存的用户态读写。这一机制极大地简化了用户态程序对设备内存的访问。
尽管中断响应在UIO架构中需在内核层处理,但用户态层只需简单地在对对应的设备文件(如"/dev/uioX")的read()操作上进行阻塞等待。当设备触发中断时,read()函数会立即返回。此外,UIO还提供了poll()系统调用,使得用户态程序可以利用select()等机制来以超时方式等待中断事件的发生。为了实现设备控制,UIO采用了通过/sys/class/uio目录下的文件进行操作的方式。用户程序可以通过对应的设备文件读写来控制和访问设备功能。
要使用UIO技术,通常需要遵循以下步骤:
编写内核UIO驱动需要遵循标准的驱动开发流程。在本节中,我们将详细探讨一个简单的内核UIO驱动实现示例。该示例使用了虚拟设备的方式代替真实设备,从而简化了开发流程——模块插入时立即可以初始化UIO驱动,让用户态程序可以直接进行操作。
以下是一个典型的内核UIO驱动实现代码示例:
#include#include #include #include #include #include #include static struct uio_info uio_virtual_device_info = { .name = "myuio", .version = "1.0", .irq = UIO_IRQ_NONE,};static int uio_virtual_device_drv_probe(struct platform_device *pdev) { printk("uio_virtual_device_drv_probe( %p)\n", pdev); uio_virtual_device_info.mem[0].addr = (unsigned long)kmalloc(1024, GFP_KERNEL); if (uio_virtual_device_info.mem[0].addr == 0) { return -ENOMEM; } uio_virtual_device_info.mem[0].memtype = UIO_MEM_LOGICAL; uio_virtual_device_info.mem[0].size = 1024; printk("[__func__:%d] uio_virtual_device_info.mem[0].addr: 0x%x, .size: %lu\n", __func__, __LINE__, uio_virtual_device_info.mem[0].addr, uio_virtual_device_info.mem[0].size); if (uio_register_device(&pdev->dev, &uio_virtual_device_info)) { return -ENODEV; } return 0;}static int uio_virtual_device_drv_remove(struct platform_device *pdev) { uio_unregister_device(&uio_virtual_device_info); return 0;}static struct platform_driver virtual_device_drv = { .probe = uio_virtual_device_drv_probe, .remove = uio_virtual_device_drv_remove, .driver = { .name = "VIRTUAL_DEVICE", .owner = THIS_MODULE, }};static void virtual_device_remove(struct device *dev) {}static struct platform_device virtual_device = { .name = "VIRTUAL_DEVICE", .id = -1, .dev = { .release = virtual_device_remove, },};static int __init uio_virtual_device_init(void) { printk("virtual_device init ok!\n"); platform_device_register(&virtual_device); printk("virtual_device_drv init ok!\n"); return platform_driver_register(&virtual_device_drv);}static void __exit uio_virtual_device_exit(void) { printk("Virtual_device remove ok!\n"); platform_device_unregister(&virtual_device); printk("virtual_device_drv remove ok!\n"); platform_driver_unregister(&virtual_device_drv);}module_init(uio_virtual_device_init);module_exit(uio_virtual_device_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("ZP1015");MODULE_DESCRIPTION("Demon of UIO");
该示例实现了一个虚拟的UIO设备,可用的方式是将真实设备驱动卸载后,将设备绑定到UIO驱动。通过这种方式,可以在不具备真实设备的情况下测试和运行UIO应用程序。
在实际应用中,UIO驱动需要绑定真实设备。在sys下,用户可以通过查看PCI设备目录来理解设备ID。以下是常见的路径:
/sys/bus/pci/devices//vendor/sys/bus/pci/devices//device/sys/bus/pci/devices//subsystem_vendor/sys/bus/pci/devices//subsystem_device
通过匹配设备ID,可以实现对应的设备绑定和解绑操作。对于真正的设备绑定,可能需要使用或 categorie等更高级的绑定机制。在本示例中,我们暂且不考虑真实设备的工作流程,因为使用虚拟设备简化了这一过程。
随着内核UIO驱动的实现,用户态程序可以通过相应的系统调用和文件操作来实现与UIO设备的通信。在用户态驱动中,开发者需要对设备的内存区域进行映射,以便进行读写操作。
以下是一个用户态UIO驱动的示例代码:
#include#include #include #include #include #include #include #define UIO_DEV "/dev/uio0"#define UIO_ADDR "/sys/class/uio/uio0/maps/map0/addr"#define UIO_SIZE "/sys/class/uio/uio0/maps/map0/size"static char uio_addr_buf[16], uio_size_buf[16];int main() { int uio_fd, addr_fd, size_fd; int uio_size; void* uio_addr, *access_address; uio_fd = open(UIO_DEV, O_RDWR); addr_fd = open(UIO_ADDR, O_RDONLY); size_fd = open(UIO_SIZE, O_RDONLY); if (addr_fd < 0 || size_fd < 0 || uio_fd < 0) { fprintf(stderr, "mmap: %s\n", strerror(errno)); exit(EXIT_FAILURE); } if (read(addr_fd, uio_addr_buf, sizeof(uio_addr_buf)) != sizeof(uio_addr_buf)) { fprintf(stderr, "read: %s\n", strerror(errno)); exit(EXIT_FAILURE); } if (read(size_fd, uio_size_buf, sizeof(uio_size_buf)) != sizeof(uio_size_buf)) { fprintf(stderr, "read: %s\n", strerror(errno)); exit(EXIT_FAILURE); } uio_addr = (void*)strtol(uio_addr_buf, NULL, 0); uio_size = (int)strtol(uio_size_buf, NULL, 0); access_address = mmap(NULL, uio_size, PROT_READ | PROT_WRITE, MAP_SHARED, uio_fd, 0); if (access_address == (void*) -1) { fprintf(stderr, "mmap: %s\n", strerror(errno)); exit(EXIT_FAILURE); } printf("The device address %p (length %d) can be accessed over logical address %p\n", uio_addr, uio_size, access_address); return EXIT_SUCCESS;}
上述用户态程序完成了以下操作:
通过这个例子,可以清晰地看到UIO技术在用户态程序中实现的核心功能。这类程序可以有效地利用UIO架构进行I/O操作,同时充分发挥用户态空间的优势。
转载地址:http://qvzhz.baihongyu.com/