用户空间 I/O HOWTO¶
- 作者:
Hans-Jürgen Koch,Linux 开发人员,Linutronix
- 日期:
2006-12-11
关于本文档¶
翻译¶
如果您知道本文档的任何翻译版本,或者您有兴趣翻译它,请给我发送电子邮件 hjk@hansjkoch.de。
前言¶
对于许多类型的设备来说,创建 Linux 内核驱动程序有些过度。真正需要的是某种处理中断并提供对设备内存空间访问的方法。控制设备的逻辑不一定必须在内核中,因为设备不需要利用内核提供的任何其他资源。像这样的一个常见设备类别是工业 I/O 卡。
为了解决这种情况,设计了用户空间 I/O 系统 (UIO)。对于典型的工业 I/O 卡,只需要一个非常小的内核模块。驱动程序的主要部分将在用户空间中运行。这简化了开发,并降低了内核模块中出现严重错误的风险。
请注意,UIO 不是通用的驱动程序接口。已经由其他内核子系统(如网络、串行或 USB)很好地处理的设备不是 UIO 驱动程序的候选者。理想情况下适用于 UIO 驱动程序的硬件符合以下所有条件
设备具有可以映射的内存。可以通过写入此内存来完全控制设备。
设备通常会生成中断。
设备不适合任何标准内核子系统。
致谢¶
我要感谢 Linutronix 的 Thomas Gleixner 和 Benedikt Spranger,他们不仅编写了大部分 UIO 代码,而且还通过向我提供各种背景信息,极大地帮助编写了本 HOWTO。
反馈¶
发现本文档有任何错误吗?(或者也许是正确的内容?)我很乐意收到您的来信。请发送电子邮件至 hjk@hansjkoch.de。
关于 UIO¶
如果您使用 UIO 作为您的卡驱动程序,您将获得以下好处
只需编写和维护一个小的内核模块。
使用您习惯的所有工具和库,在用户空间中开发驱动程序的主要部分。
驱动程序中的错误不会导致内核崩溃。
驱动程序的更新可以在无需重新编译内核的情况下进行。
UIO 的工作原理¶
每个 UIO 设备都通过设备文件和几个 sysfs 属性文件进行访问。第一个设备的设备文件将称为 /dev/uio0
,后续设备将称为 /dev/uio1
、/dev/uio2
等。
/dev/uioX
用于访问卡的地址空间。只需使用 mmap()
即可访问您的卡的寄存器或 RAM 位置。
中断通过读取 /dev/uioX
来处理。从 /dev/uioX
进行阻塞 read()
将在中断发生时立即返回。您还可以对 /dev/uioX
使用 select()
以等待中断。从 /dev/uioX
读取的整数值表示中断总数。您可以使用此数字来确定是否错过了某些中断。
对于某些内部具有多个中断源,但没有单独的 IRQ 掩码和状态寄存器的硬件,如果内核处理程序通过写入芯片的 IRQ 寄存器禁用它们,则可能会出现用户空间无法确定中断源的情况。在这种情况下,内核必须完全禁用 IRQ,以保持芯片的寄存器不受影响。现在,用户空间部分可以确定中断的原因,但它不能重新启用中断。另一个极端情况是,重新启用中断是对组合的 IRQ 状态/确认寄存器进行读-修改-写操作的芯片。如果同时发生新的中断,这将是竞争的。
为了解决这些问题,UIO 还实现了 write() 函数。它通常不使用,对于只有一个中断源或具有单独 IRQ 掩码和状态寄存器的硬件,可以忽略它。但是,如果您需要它,则写入 /dev/uioX
将调用驱动程序实现的 irqcontrol()
函数。您必须写入一个 32 位值,该值通常为 0 或 1 以禁用或启用中断。如果驱动程序未实现 irqcontrol()
,write()
将返回 -ENOSYS
。
为了正确处理中断,您的自定义内核模块可以提供自己的中断处理程序。它将由内置处理程序自动调用。
对于不生成中断但需要轮询的卡,可以设置一个定时器,该定时器以可配置的时间间隔触发中断处理程序。这种中断模拟是通过从定时器的事件处理程序调用 uio_event_notify()
来完成的。
每个驱动程序都提供用于读取或写入变量的属性。这些属性可以通过 sysfs 文件访问。自定义内核驱动程序模块可以将自己的属性添加到 uio 驱动程序拥有的设备,但此时不会添加到 UIO 设备本身。如果发现有用,将来可能会发生变化。
UIO 框架提供以下标准属性
name
:您设备的名称。建议为此使用您的内核模块的名称。version
:由您的驱动程序定义的版本字符串。这允许驱动程序的用户空间部分处理不同版本的内核模块。event
:自上次读取设备节点以来,驱动程序处理的中断总数。
这些属性出现在 /sys/class/uio/uioX
目录下。请注意,此目录可能是符号链接,而不是真实目录。访问它的任何用户空间代码都必须能够处理这种情况。
每个 UIO 设备可以提供一个或多个内存区域以进行内存映射。这是必要的,因为一些工业 I/O 卡需要在驱动程序中访问多个 PCI 内存区域。
每个映射在 sysfs 中都有自己的目录,第一个映射显示为 /sys/class/uio/uioX/maps/map0/
。后续映射创建目录 map1/
、map2/
等。只有当映射的大小不为 0 时,这些目录才会出现。
每个 mapX/
目录都包含四个只读文件,这些文件显示内存的属性
name
:此映射的字符串标识符。这是可选的,该字符串可以为空。驱动程序可以设置此值,以方便用户空间查找正确的映射。addr
:可以映射的内存地址。size
:addr 指向的内存大小(以字节为单位)。offset
:必须添加到mmap()
返回的指针以获取实际设备内存的偏移量(以字节为单位)。如果设备的内存未按页面对齐,则此值很重要。请记住,mmap()
返回的指针始终是按页面对齐的,因此始终添加此偏移量是一种良好的风格。
从用户空间,通过调整 mmap()
调用的 offset
参数来区分不同的映射。要映射映射 N 的内存,您必须使用 N 倍的页面大小作为偏移量
offset = N * getpagesize();
有时,有些硬件具有类似内存的区域,无法使用此处描述的技术进行映射,但仍然可以通过用户空间访问它们。最常见的例子是 x86 ioports。在 x86 系统上,用户空间可以使用 ioperm()
、iopl()
、inb()
、outb()
和类似的函数来访问这些 ioport。
由于这些 ioport 区域无法映射,因此它们不会像上面描述的普通内存一样出现在 /sys/class/uio/uioX/maps/
下。如果没有关于硬件必须提供的端口区域的信息,则驱动程序的用户空间部分很难找出哪些端口属于哪个 UIO 设备。
为了解决这种情况,添加了新的目录 /sys/class/uio/uioX/portio/
。它仅在驱动程序想要将有关一个或多个端口区域的信息传递给用户空间时才存在。如果是这种情况,则在 /sys/class/uio/uioX/portio/
下面将出现名为 port0
、port1
等的子目录。
每个 portX/
目录都包含四个只读文件,这些文件显示端口区域的名称、开始、大小和类型
name
: 此端口区域的字符串标识符。该字符串是可选的,可以为空。驱动程序可以设置它,以便用户空间更容易找到特定的端口区域。start
: 此区域的第一个端口。size
: 此区域中的端口数量。porttype
: 描述端口类型的字符串。
编写你自己的内核模块¶
请查看 uio_cif.c
作为示例。以下段落解释了此文件的不同部分。
struct uio_info¶
此结构体向框架告知你的驱动程序的详细信息。某些成员是必需的,其他成员是可选的。
const char *name
: 必需。你的驱动程序的名称,它将出现在 sysfs 中。我建议使用你的模块的名称作为此名称。const char *version
: 必需。此字符串出现在/sys/class/uio/uioX/version
中。struct uio_mem mem[ MAX_UIO_MAPS ]
: 如果你有可以使用mmap()
映射的内存,则为必需。对于每个映射,你需要填写一个uio_mem
结构。有关详细信息,请参见下面的描述。struct uio_port port[ MAX_UIO_PORTS_REGIONS ]
: 如果你想将有关 ioport 的信息传递给用户空间,则为必需。对于每个端口区域,你需要填写一个uio_port
结构。有关详细信息,请参见下面的描述。long irq
: 必需。如果你的硬件生成中断,则你的模块的任务是在初始化期间确定 irq 号。如果你没有硬件生成的中断,但想以其他方式触发中断处理程序,请将irq
设置为UIO_IRQ_CUSTOM
。如果你根本没有中断,则可以将irq
设置为UIO_IRQ_NONE
,尽管这很少有意义。unsigned long irq_flags
: 如果你已将irq
设置为硬件中断号,则为必需。此处给出的标志将用于调用request_irq()
。int (*mmap)(struct uio_info *info, struct vm_area_struct *vma)
: 可选。如果你需要一个特殊的mmap()
函数,则可以在此处设置它。如果此指针不为 NULL,则将调用你的mmap()
而不是内置的mmap()
。int (*open)(struct uio_info *info, struct inode *inode)
: 可选。你可能希望有自己的open()
,例如,仅当实际使用你的设备时才启用中断。int (*release)(struct uio_info *info, struct inode *inode)
: 可选。如果你定义了自己的open()
,则可能还需要自定义的release()
函数。int (*irqcontrol)(struct uio_info *info, s32 irq_on)
: 可选。如果你需要能够通过写入/dev/uioX
从用户空间启用或禁用中断,则可以实现此函数。参数irq_on
将为 0 以禁用中断,为 1 以启用中断。
通常,你的设备将具有一个或多个可以映射到用户空间的内存区域。对于每个区域,你必须在 mem[]
数组中设置一个 struct uio_mem
。以下是 struct uio_mem
字段的描述
const char *name
: 可选。设置此项以帮助识别内存区域,它将显示在相应的 sysfs 节点中。int memtype
: 如果使用映射,则为必需。如果你有物理内存要在卡上映射,请将其设置为UIO_MEM_PHYS
。对于逻辑内存(例如,使用__get_free_pages()
分配的但不是kmalloc()
分配的),请使用UIO_MEM_LOGICAL
。还有用于虚拟内存的UIO_MEM_VIRTUAL
。phys_addr_t addr
: 如果使用映射,则为必需。填写你的内存块的地址。此地址是出现在 sysfs 中的地址。resource_size_t size
: 填写addr
指向的内存块的大小。如果size
为零,则认为映射未使用。请注意,你必须为所有未使用的映射将size
初始化为零。void *internal_addr
: 如果你必须从你的内核模块中访问此内存区域,你将需要使用诸如ioremap()
之类的东西在内部映射它。此函数返回的地址不能映射到用户空间,因此你不得将其存储在addr
中。请改用internal_addr
来记住这样的地址。
请不要触碰 struct uio_mem
的 map
元素!UIO 框架使用它为此映射设置 sysfs 文件。请保持原样。
有时,你的设备可能具有一个或多个无法映射到用户空间的端口区域。但是,如果用户空间有其他访问这些端口的可能性,那么在 sysfs 中提供有关端口的信息是有意义的。对于每个区域,你必须在 port[]
数组中设置一个 struct uio_port
。以下是 struct uio_port
字段的描述
char *porttype
: 必需。将其设置为预定义的常量之一。对于在 x86 架构中找到的 ioport,请使用UIO_PORT_X86
。unsigned long start
: 如果使用端口区域,则为必需。填写此区域的第一个端口的编号。unsigned long size
: 填写此区域中的端口数。如果size
为零,则认为该区域未使用。请注意,你必须为所有未使用的区域将size
初始化为零。
请不要触碰 struct uio_port
的 portio
元素!UIO 框架在内部使用它为此区域设置 sysfs 文件。请保持原样。
添加中断处理程序¶
你在中断处理程序中需要执行的操作取决于你的硬件以及你想要如何处理它。你应该尽量减少内核中断处理程序中的代码量。如果你的硬件不需要在每次中断后执行任何你必须执行的操作,那么你的处理程序可以为空。
另一方面,如果你的硬件需要在每次中断后执行某些操作,那么你必须在你的内核模块中执行它。请注意,你不能依赖于你的驱动程序的用户空间部分。你的用户空间程序可以随时终止,可能会使你的硬件处于仍需要正确中断处理的状态。
可能还存在一些应用程序,你希望在每次中断时从硬件读取数据,并将其缓存在你为此目的分配的一块内核内存中。使用此技术,如果你的用户空间程序错过了中断,则可以避免数据丢失。
关于共享中断的说明:你的驱动程序应尽可能支持中断共享。当且仅当你的驱动程序可以检测到你的硬件是否触发了中断时,才有可能进行中断共享。这通常通过查看中断状态寄存器来完成。如果你的驱动程序看到 IRQ 位实际上已设置,它将执行其操作,并且处理程序返回 IRQ_HANDLED。如果驱动程序检测到不是你的硬件导致了中断,它将不执行任何操作并返回 IRQ_NONE,从而允许内核调用下一个可能的中断处理程序。
如果你决定不支持共享中断,你的卡在没有空闲中断的计算机上将无法工作。由于这在 PC 平台上经常发生,因此支持中断共享可以为你省去很多麻烦。
为平台设备使用 uio_pdrv¶
在许多情况下,平台设备的 UIO 驱动程序可以以通用方式处理。在您定义 struct platform_device
的同一位置,您只需实现中断处理程序并填写 struct uio_info
。然后,将指向此 struct uio_info
的指针用作平台设备的 platform_data
。
您还需要设置一个 struct resource
数组,其中包含内存映射的地址和大小。此信息使用 struct platform_device
的 .resource
和 .num_resources
元素传递给驱动程序。
现在,您必须将 struct platform_device
的 .name
元素设置为 "uio_pdrv"
,以使用通用的 UIO 平台设备驱动程序。此驱动程序将根据给定的资源填充 mem[]
数组,并注册设备。
这种方法的优点是,您只需编辑无论如何都需要编辑的文件。您无需创建额外的驱动程序。
为平台设备使用 uio_pdrv_genirq¶
尤其是在嵌入式设备中,您经常会发现芯片的 IRQ 引脚与其自身的专用中断线相连。在这种情况下,您可以非常确定中断不是共享的,我们可以将 uio_pdrv
的概念更进一步,并使用通用的中断处理程序。这就是 uio_pdrv_genirq
的作用。
此驱动程序的设置与上面为 uio_pdrv
描述的相同,只是您不实现中断处理程序。struct uio_info
的 .handler
元素必须保持 NULL
。.irq_flags
元素不得包含 IRQF_SHARED
。
您将 struct platform_device
的 .name
元素设置为 "uio_pdrv_genirq"
以使用此驱动程序。
uio_pdrv_genirq
的通用中断处理程序将只使用 disable_irq_nosync()
禁用中断线。完成其工作后,用户空间可以通过向 UIO 设备文件写入 0x00000001 来重新启用中断。驱动程序已经实现了一个 irq_control()
来实现此目的,您不得实现自己的 irq_control()
。
使用 uio_pdrv_genirq
不仅可以节省几行中断处理程序代码。您也不需要了解芯片的内部寄存器来创建驱动程序的内核部分。您需要知道的只是芯片连接到的引脚的 IRQ 号。
在设备树启用的系统中使用时,驱动程序需要使用设置为驱动程序应处理的节点的 "compatible"
字符串的 "of_id"
模块参数进行探测。默认情况下,节点的名称(不带单元地址)将作为用户空间中 UIO 设备的名称公开。要设置自定义名称,可以在 DT 节点中指定名为 "linux,uio-name"
的属性。
为平台设备使用 uio_dmem_genirq¶
除了静态分配的内存范围外,可能还需要在用户空间驱动程序中使用动态分配的区域。特别是,能够访问通过 dma-mapping API 提供的内存可能特别有用。uio_dmem_genirq
驱动程序提供了一种实现此目的的方法。
此驱动程序的使用方式类似于 "uio_pdrv_genirq"
驱动程序,在中断配置和处理方面。
将 struct platform_device
的 .name
元素设置为 "uio_dmem_genirq"
以使用此驱动程序。
使用此驱动程序时,请填写 struct platform_device
的 .platform_data
元素,该元素的类型为 struct uio_dmem_genirq_pdata
,其中包含以下元素
struct uio_info uioinfo
:与uio_pdrv_genirq
平台数据相同的结构unsigned int *dynamic_region_sizes
:指向要映射到用户空间的动态内存区域大小列表的指针。unsigned int num_dynamic_regions
:dynamic_region_sizes
数组中的元素数。
平台数据中定义的动态区域将附加到 `` mem[] `` 数组中,在平台设备资源之后,这意味着静态和动态内存区域的总数不能超过 MAX_UIO_MAPS
。
动态内存区域将在打开 UIO 设备文件 /dev/uioX
时分配。与静态内存资源类似,动态区域的内存区域信息随后可通过 sysfs 在 /sys/class/uio/uioX/maps/mapY/*
中查看。动态内存区域将在 UIO 设备文件关闭时释放。当没有进程保持设备文件打开时,返回给用户空间的地址为 ~0。
在用户空间编写驱动程序¶
一旦您为硬件开发了一个可用的内核模块,您就可以编写驱动程序的用户空间部分。您不需要任何特殊的库,您的驱动程序可以用任何合理的语言编写,您可以使用浮点数等等。简而言之,您可以使用通常用于编写用户空间应用程序的所有工具和库。
获取有关 UIO 设备的信息¶
有关所有 UIO 设备的信息都可在 sysfs 中找到。您在驱动程序中应做的第一件事是检查 name
和 version
,以确保您正在与正确的设备通信,并且其内核驱动程序的版本符合您的预期。
您还应确保所需的内存映射存在且具有您预期的大小。
有一个名为 lsuio
的工具,它列出了 UIO 设备及其属性。它可在此处获取
http://www.osadl.org/projects/downloads/UIO/user/
使用 lsuio
,您可以快速检查内核模块是否已加载及其导出的属性。有关详细信息,请参阅手册页。
lsuio
的源代码可以用作获取有关 UIO 设备信息的示例。uio_helper.c
文件包含许多您可以在用户空间驱动程序代码中使用的函数。
mmap() 设备内存¶
在您确保获得了具有所需内存映射的正确设备后,您只需调用 mmap()
将设备的内存映射到用户空间。
mmap()
调用的参数 offset
对于 UIO 设备具有特殊含义:它用于选择要映射的设备的哪个映射。要映射映射 N 的内存,您必须使用 N 次页面大小作为偏移量
offset = N * getpagesize();
N 从零开始,因此如果您只有一个内存范围要映射,请设置 offset = 0
。此技术的缺点是内存始终从其起始地址开始映射。
等待中断¶
在您成功映射了设备的内存后,您可以像访问普通数组一样访问它。通常,您将执行一些初始化。之后,您的硬件开始工作,并且一旦完成、有可用数据或由于发生错误而需要您注意时,将生成中断。
/dev/uioX
是一个只读文件。read()
将始终阻塞,直到发生中断。对于 read()
的 count
参数只有一个合法值,即带符号 32 位整数 (4) 的大小。任何其他 count
值都会导致 read()
失败。读取的带符号 32 位整数是您设备的中断计数。如果该值比上次读取的值大 1,则一切正常。如果差值大于 1,则您错过了中断。
您还可以在 /dev/uioX
上使用 select()
。
通用 PCI UIO 驱动程序¶
通用驱动程序是一个名为 uio_pci_generic 的内核模块。它可以与符合 PCI 2.3(大约 2002 年)的任何设备以及任何符合 PCI Express 的设备一起使用。使用此驱动程序,您只需编写用户空间驱动程序,从而无需编写特定于硬件的内核模块。
使驱动程序识别设备¶
由于该驱动程序没有声明任何设备 ID,它不会被自动加载,也不会自动绑定到任何设备。您必须手动加载它并为驱动程序分配 ID。例如:
modprobe uio_pci_generic
echo "8086 10f5" > /sys/bus/pci/drivers/uio_pci_generic/new_id
如果您的设备已经有一个硬件特定的内核驱动程序,通用驱动程序仍然不会绑定到它。在这种情况下,如果您想使用通用驱动程序(为什么要这样做?),您必须手动解除绑定硬件特定的驱动程序,然后绑定通用驱动程序,就像这样:
echo -n 0000:00:19.0 > /sys/bus/pci/drivers/e1000e/unbind
echo -n 0000:00:19.0 > /sys/bus/pci/drivers/uio_pci_generic/bind
您可以通过在 sysfs 中查找来验证设备是否已绑定到驱动程序,例如像下面这样:
ls -l /sys/bus/pci/devices/0000:00:19.0/driver
如果成功,应该会打印出:
.../0000:00:19.0/driver -> ../../../bus/pci/drivers/uio_pci_generic
请注意,通用驱动程序不会绑定到旧的 PCI 2.2 设备。如果绑定设备失败,请运行以下命令:
dmesg
并在输出中查找失败原因。
关于 uio_pci_generic 的须知¶
中断处理使用 PCI 命令寄存器中的“中断禁用”位和 PCI 状态寄存器中的“中断状态”位。所有符合 PCI 2.3 标准(大约 2002 年)和所有符合 PCI Express 标准的设备都应该支持这些位。uio_pci_generic 检测到此支持,并且不会绑定到不支持命令寄存器中的“中断禁用”位的设备。
每次中断时,uio_pci_generic 都会设置“中断禁用”位。这会阻止设备生成进一步的中断,直到清除该位。用户空间驱动程序应在阻塞并等待更多中断之前清除此位。
使用 uio_pci_generic 编写用户空间驱动程序¶
用户空间驱动程序可以使用 pci sysfs 接口,或者封装它的 libpci 库,与设备通信,并通过写入命令寄存器来重新启用中断。
使用 uio_pci_generic 的示例代码¶
以下是一些使用 uio_pci_generic 的示例用户空间驱动程序代码:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main()
{
int uiofd;
int configfd;
int err;
int i;
unsigned icount;
unsigned char command_high;
uiofd = open("/dev/uio0", O_RDONLY);
if (uiofd < 0) {
perror("uio open:");
return errno;
}
configfd = open("/sys/class/uio/uio0/device/config", O_RDWR);
if (configfd < 0) {
perror("config open:");
return errno;
}
/* Read and cache command value */
err = pread(configfd, &command_high, 1, 5);
if (err != 1) {
perror("command config read:");
return errno;
}
command_high &= ~0x4;
for(i = 0;; ++i) {
/* Print out a message, for debugging. */
if (i == 0)
fprintf(stderr, "Started uio test driver.\n");
else
fprintf(stderr, "Interrupts: %d\n", icount);
/****************************************/
/* Here we got an interrupt from the
device. Do something to it. */
/****************************************/
/* Re-enable interrupts. */
err = pwrite(configfd, &command_high, 1, 5);
if (err != 1) {
perror("config write:");
break;
}
/* Wait for next interrupt. */
err = read(uiofd, &icount, 4);
if (err != 4) {
perror("uio read:");
break;
}
}
return errno;
}
通用 Hyper-V UIO 驱动程序¶
通用驱动程序是一个名为 uio_hv_generic 的内核模块。它支持 Hyper-V VMBus 上的设备,类似于 PCI 总线上的 uio_pci_generic。
使驱动程序识别设备¶
由于该驱动程序没有声明任何设备 GUID,它不会被自动加载,也不会自动绑定到任何设备。您必须手动加载它并为驱动程序分配 ID。例如,要使用网络设备类 GUID:
modprobe uio_hv_generic
echo "f8615163-df3e-46c5-913f-f2d2f965ed0e" > /sys/bus/vmbus/drivers/uio_hv_generic/new_id
如果设备已经有一个硬件特定的内核驱动程序,通用驱动程序仍然不会绑定到它。在这种情况下,如果您想为用户空间库使用通用驱动程序,则必须手动解除绑定硬件特定的驱动程序,并使用特定设备的 GUID 绑定通用驱动程序,如下所示:
echo -n ed963694-e847-4b2a-85af-bc9cfc11d6f3 > /sys/bus/vmbus/drivers/hv_netvsc/unbind
echo -n ed963694-e847-4b2a-85af-bc9cfc11d6f3 > /sys/bus/vmbus/drivers/uio_hv_generic/bind
您可以通过在 sysfs 中查找来验证设备是否已绑定到驱动程序,例如像下面这样:
ls -l /sys/bus/vmbus/devices/ed963694-e847-4b2a-85af-bc9cfc11d6f3/driver
如果成功,应该会打印出:
.../ed963694-e847-4b2a-85af-bc9cfc11d6f3/driver -> ../../../bus/vmbus/drivers/uio_hv_generic
关于 uio_hv_generic 的须知¶
每次中断时,uio_hv_generic 都会设置“中断禁用”位。这会阻止设备生成进一步的中断,直到清除该位。用户空间驱动程序应在阻塞并等待更多中断之前清除此位。
当主机撤销设备时,中断文件描述符将被标记为关闭,并且对中断文件描述符的任何读取都将返回 -EIO。类似于关闭的套接字或断开连接的串行设备。
- vmbus 设备区域映射到 uio 设备资源中:
通道环形缓冲区:访客到主机和主机到访客
访客到主机中断信号页面
访客到主机监视页面
网络接收缓冲区区域
网络发送缓冲区区域
如果通过向主机请求创建子通道,则 uio_hv_generic 设备驱动程序将为每个通道环形缓冲区创建一个 sysfs 二进制文件。例如:
/sys/bus/vmbus/devices/3811fe4d-0fa0-4b62-981a-74fc1084c757/channels/21/ring