编写设备驱动程序

用户接口

LDI 中包括用户级库和命令接口,用于报告设备分层和使用信息。设备信息库接口介绍了用于报告设备分层信息的 libdevinfo(3LIB) 接口。列显系统配置命令接口介绍了用于报告内核设备使用信息的 prtconf(1M) 接口。设备用户命令接口介绍了用于报告设备使用方信息的 fuser(1M) 接口。

设备信息库接口

LDI 中包括用于报告设备分层信息快照的 libdevinfo(3LIB) 接口。如果系统中的一个设备是同一系统中另一个设备的使用方,则会发生设备分层。仅当使用方和目标都绑定到快照中包含的设备节点时,才会报告设备分层信息。

libdevinfo(3LIB) 接口以有向图的形式报告设备分层信息。lnode 是一个抽象术语,在图中表示顶点,并被绑定到设备节点。可以使用 libdevinfo(3LIB) 接口来访问 lnode 的属性,如节点的名称和设备编号。

图中的边表示链接。链接既有表示设备使用方的源 lnode,也有表示目标设备的目标 lnode。

下面介绍了 libdevinfo(3LIB) 设备分层信息接口:

DINFOLYR

通过它来捕获设备分层信息的快照标志。

di_link_t

两个端点之间的有向链接。每个端点都是一个 di_lnode_t。属于不透明结构。

di_lnode_t

链接的端点。属于不透明结构。di_lnode_t 绑定到 di_node_t

di_node_t

表示设备节点。属于不透明结构。di_node_t 不一定绑定到 di_lnode_t

di_walk_link(3DEVINFO)

遍历快照中的所有链接。

di_walk_lnode(3DEVINFO)

遍历快照中的所有 lnode。

di_link_next_by_node(3DEVINFO)

获取下一个其中指定的 di_node_t 节点是源节点或目标节点的链接的句柄。

di_link_next_by_lnode(3DEVINFO)

获取下一个其中指定的 di_lnode_t lnode 是源 lnode 或目标 lnode 的链接的句柄。

di_link_to_lnode(3DEVINFO)

获取与 di_link_t 链接的指定端点对应的 lnode。

di_link_spectype(3DEVINFO)

获取链接的规范类型。规范类型指示如何访问目标设备。目标设备由目标 lnode 表示。

di_lnode_next(3DEVINFO)

获取下一个与指定的 di_node_t 设备节点关联的指定 di_lnode_t lnode 的句柄。

di_lnode_name(3DEVINFO)

获取与指定 lnode 关联的名称。

di_lnode_devinfo(3DEVINFO)

获取与指定 lnode 关联的设备节点的句柄。

di_lnode_devt(3DEVINFO)

获取与指定 lnode 关联的设备节点的设备编号。

LDI 返回的设备分层信息可能十分复杂。因此,LDI 提供了一些接口来协助遍历设备树和设备使用情况图。通过这些接口,设备树快照的使用方可以将自定义数据指针与快照中的不同结构关联。例如,应用程序遍历 lnode 时,它可以更新与每个 lnode 关联的自定义指针,以标记已经识别的 lnode。

下面介绍了 libdevinfo(3LIB) 节点和链接标记接口:

di_lnode_private_set(3DEVINFO)

将指定的数据与指定的 lnode 关联。通过此关联,可以遍历快照中的 lnode。

di_lnode_private_get(3DEVINFO)

检索指向通过调用 di_lnode_private_set(3DEVINFO) 而与 lnode 关联的数据的指针。

di_link_private_set(3DEVINFO)

将指定的数据与指定的链接关联。通过此关联,可以遍历快照中的链接。

di_link_private_get(3DEVINFO)

检索指向通过调用 di_link_private_set(3DEVINFO) 而与链接关联的数据的指针。

列显系统配置命令接口

prtconf(1M) 命令已得到增强,可以显示内核设备使用信息。缺省的 prtconf( 1M) 输出没有变化。如果在 prtconf(1M) 命令中指定详细选项 (-v),则会显示设备使用信息。如果在 prtconf(1M) 命令行上指定特定设备的路径,则会显示有关该设备的使用信息。

prtconf -v

显示设备次要节点和设备使用信息。显示内核使用方和每个内核使用方当前打开的次要节点。

prtconf path

显示由 path 指定的设备的设备使用信息。

prtconf -a path

显示由 path 指定的设备的设备使用信息,以及作为 path 的祖先的所有设备节点。

prtconf -c path

显示由 path 指定的设备的设备使用信息,以及作为 path 的子节点的所有设备节点。


示例 14–6 设备使用信息

如果需要有关特定设备的使用信息,path 参数的值可以是任何有效的设备路径。


% prtconf /dev/cfg/c0
SUNW,isptwo, instance #0


示例 14–7 祖先节点使用信息

要显示有关特定设备的使用信息以及作为其祖先的所有设备节点,请在 prtconf(1M) 命令中指定 -a 标志。祖先包括直到设备树的根的所有节点。如果在 prtconf(1M) 命令中指定 -a 标志,则还必须指定设备的 path 名称。


% prtconf -a /dev/cfg/c0
SUNW,Sun-Fire
    ssm, instance #0
        pci, instance #0
            pci, instance #0
                SUNW,isptwo, instance #0


示例 14–8 子节点使用信息

要显示有关特定设备的使用信息以及作为其子节点的所有设备节点,请在 prtconf(1M) 命令中指定 -c 标志。如果在 prtconf(1M) 命令中指定 -c 标志,则还必须指定设备的 path 名称。


% prtconf -c /dev/cfg/c0
SUNW,isptwo, instance #0
    sd (driver not attached)
    st (driver not attached)
    sd, instance #1
    sd, instance #0
    sd, instance #6
    st, instance #1 (driver not attached)
    st, instance #0 (driver not attached)
    st, instance #2 (driver not attached)
    st, instance #3 (driver not attached)
    st, instance #4 (driver not attached)
    st, instance #5 (driver not attached)
    st, instance #6 (driver not attached)
    ses, instance #0 (driver not attached)
    ...


示例 14–9 分层和设备次要节点信息-键盘

要显示有关特定设备的设备分层和设备次要节点信息,请在 prtconf(1M) 命令中指定 -v 标志。


% prtconf -v /dev/kbd
conskbd, instance #0
    System properties:
        ...
    Device Layered Over:
        mod=kb8042 dev=(101,0)
            dev_path=/isa/i8042@1,60/keyboard@0
    Device Minor Nodes:
        dev=(103,0)
            dev_path=/pseudo/conskbd@0:kbd
                spectype=chr type=minor
                dev_link=/dev/kbd
        dev=(103,1)
            dev_path=/pseudo/conskbd@0:conskbd
                spectype=chr type=internal
            Device Minor Layered Under:
                mod=wc accesstype=chr
                    dev_path=/pseudo/wc@0

本示例中,/dev/kbd 设备所在层位于硬件键盘设备 (/isa/i8042@1,60/keyboard@0) 之上。另外,本示例中,/dev/kbd 设备具有两个设备次要节点。第一个次要节点具有可用于访问该节点的 /dev 链接。第二个次要节点是一个无法通过文件系统访问的内部节点。wc 驱动程序(即工作站控制台)已经打开了第二个次要节点。请将本示例的输出与示例 14–12 的输出进行比较。



示例 14–10 分层和设备次要节点信息-网络设备

本示例说明哪些设备正在使用当前检测到的网络设备。


% prtconf -v /dev/iprb0
pci1028,145, instance #0
    Hardware properties:
        ...
    Interrupt Specifications:
        ...
    Device Minor Nodes:
        dev=(27,1)
            dev_path=/pci@0,0/pci8086,244e@1e/pci1028,145@c:iprb0
                spectype=chr type=minor
                alias=/dev/iprb0
        dev=(27,4098)
            dev_path=<clone>
            Device Minor Layered Under:
                mod=udp6 accesstype=chr
                    dev_path=/pseudo/udp6@0
        dev=(27,4097)
            dev_path=<clone>
            Device Minor Layered Under:
                mod=udp accesstype=chr
                    dev_path=/pseudo/udp@0
        dev=(27,4096)
            dev_path=<clone>
            Device Minor Layered Under:
                mod=udp accesstype=chr
                    dev_path=/pseudo/udp@0

本示例中,在采用 udpudp6 的情况下链接了 iprb0 设备。请注意,此处并未显示指向采用 udpudp6 的次要节点的任何路径。本示例中未显示任何路径是因为次要节点是通过对 iprb 驱动程序执行 clone 打开操作创建的,因此不存在可以访问这些节点的文件系统路径。请将本示例的输出与示例 14–11 的输出进行比较。


设备用户命令接口

fuser(1M) 命令已得到增强,可以显示设备使用信息。仅当 path 表示设备次要节点时,fuser(1M) 命令才会显示设备使用信息。仅当指定了表示设备次要节点的 path 时,在 fuser(1M) 命令中使用 -d 标志才会有效。

fuser path

显示有关应用程序设备使用方和内核设备使用方的信息(如果 path 表示设备次要节点)。

fuser -d path

显示与 path 表示的设备次要节点关联的基础设备的所有用户。

报告内核设备使用方时采用以下四种格式之一。内核设备使用方始终用方括号 ([]) 括起来。


        [kernel_module_name]
        [kernel_module_name,dev_path=path]
        [kernel_module_name,dev=(major,minor)]
        [kernel_module_name,dev=(major,minor),dev_path=path]

如果 fuser(1M) 命令显示的是文件或设备用户,则输出由 stdout 中的进程 ID 后跟 stderr 中的字符组成。stderr 中的字符描述如何使用文件或设备。stderr 中会显示所有内核使用方信息。而 stdout 中不会显示任何内核使用方信息。

如果不使用 -d 标志,则 fuser(1M) 命令仅报告由 path 指定的设备次要节点的使用方。如果使用 -d 标志,则 fuser(1M) 命令会报告由 path 指定的次要节点的基础设备节点的使用方。以下示例说明了这两种情况下报告输出的差别。


示例 14–11 基础设备节点的使用方

大多数网络设备在打开时都会克隆其次要节点。如果请求克隆次要节点的设备使用信息,则该使用信息可能会表明没有任何进程在使用该设备。而如果请求基础设备节点的设备使用信息,则该使用信息可能会表明某个进程正在使用该设备。在本示例中,如果仅将设备 path 传递到 fuser(1M) 命令,则不会报告任何设备使用方。如果使用 -d 标志,则输出表明正在采用 udpudp6 \uc2\u26469 来访问该设备。


% fuser /dev/iprb0
/dev/iprb0:
% fuser -d /dev/iprb0
/dev/iprb0:  [udp,dev_path=/pseudo/udp@0] [udp6,dev_path=/pseudo/udp6@0]

请将本示例的输出与示例 14–10 的输出进行比较。



示例 14–12 键盘设备的使用方

在本示例中,某个内核使用方正在访问 /dev/kbd。正在访问 /dev/kbd 设备的内核使用方是工作站控制台驱动程序。


% fuser -d /dev/kbd
/dev/kbd:  [genunix] [wc,dev_path=/pseudo/wc@0]

请将本示例的输出与示例 14–9 的输出进行比较。