JavaScript is required to for searching.
跳过导航链接
退出打印视图
编写设备驱动程序     Oracle Solaris 11.1 Information Library (简体中文)
为本文档评分
search filter icon
search icon

文档信息

前言

第 1 部分针对 Oracle Solaris 平台设计设备驱动程序

1.  Oracle Solaris 设备驱动程序概述

2.  Oracle Solaris 内核和设备树

3.  多线程

4.  属性

5.  管理事件和排队任务

6.  驱动程序自动配置

7.  设备访问:程控 I/O

8.  中断处理程序

9.  直接内存访问 (Direct Memory Access, DMA)

10.  映射设备和内核内存

11.  设备上下文管理

12.  电源管理

13.  强化 Oracle Solaris 驱动程序

14.  分层驱动程序接口 (Layered Driver Interface, LDI)

第 2 部分设计特定种类的设备驱动程序

15.  字符设备驱动程序

16.  块设备驱动程序

17.  SCSI 目标驱动程序

18.  SCSI 主机总线适配器驱动程序

19.  网络设备驱动程序

20.  USB 驱动程序

21.  SR-IOV 驱动程序

第 3 部分生成设备驱动程序

22.  编译、装入、打包和测试驱动程序

23.  调试、测试和调优设备驱动程序

24.  推荐的编码方法

调试准备方法

使用唯一前缀来避免内核符号冲突

使用 cmn_err() 记录驱动程序活动

使用 ASSERT() 捕捉无效假设

使用 mutex_owned() 验证和记录锁定要求

使用条件编译在开销较大的调试功能之间切换

将变量声明为可变变量

可维护性

定期运行状况检查

第 4 部分附录

A.  硬件概述

B.  Oracle Solaris DDI/DKI 服务汇总

C.  使设备驱动程序支持 64 位

D.  控制台帧缓存器驱动程序

E.  pci.conf 文件

索引

请告诉我们如何提高我们的文档:
过于简略
不易阅读或难以理解
重要信息缺失
错误的内容
需要翻译的版本
其他
Your rating has been updated
感谢您的反馈!

您的反馈将非常有助于我们提供更好的文档。 您是否愿意参与我们的内容改进并提供进一步的意见?

调试准备方法

由于以下原因,驱动程序代码比用户程序更难调试:

请确保您的驱动程序可以支持调试。此支持便于进行维护工作和未来的开发工作。

使用唯一前缀来避免内核符号冲突

每个函数、数据元素和驱动程序预处理程序定义的名称必须对每个驱动程序都唯一。

驱动程序模块将链接到内核。对特定驱动程序唯一的每个符号名称不得与其他内核符号冲突。为避免这种冲突,特定驱动程序的每个函数和数据元素的名称必须带有该驱动程序共有的前缀。该前缀必须足以让每个驱动程序符号的名称保持唯一。通常,该前缀是驱动程序的名称,或者是驱动程序名称的缩写。例如,xx_open() 是驱动程序 xxopen(9E) 例程的名称。

在构建驱动程序时,驱动程序一定包含许多系统头文件。这些头文件中的全局可见名称无法预测。为避免与这些名称产生冲突,必须使用一个标识前缀为每个驱动程序预处理程序定义指定唯一的名称。

在进行错误诊断时,还可以借助唯一的驱动程序符号前缀来解读系统日志和故障消息。您看到的将是与 xx_attach() 有关的错误消息,而不是与二义性 attach() 函数有关的错误。

使用 cmn_err() 记录驱动程序活动

使用 cmn_err(9F) 函数可以将来自设备驱动程序的消息列显到系统日志中。用于内核模块的 cmn_err (9F) 函数与用于应用程序的 printf(3C) 函数类似。cmn_err(9F) 函数可提供其他格式字符,如用于列显设备寄存器位的 %b 格式。cmn_err(9F) 函数可以将消息写入系统日志中。使用 tail(1) 命令可以监视 /var/adm/messages 中的这些消息。

% tail -f /var/adm/messages

使用 ASSERT() 捕捉无效假设

断言是活动文档一种极有价值的形式。ASSERT(9F) 的语法如下所示:

void ASSERT(EXPRESSION)

如果预期为 true 的条件实际为 false,则 ASSERT() 宏会停止执行内核。ASSERT() 为程序员提供了对某段代码所做的假设进行验证的方法。

仅当定义了 DEBUG 编译符号时,才会定义 ASSERT() 宏。如果未定义 DEBUGASSERT() 宏将无效。

以下示例断言将测试特定指针值不是 NULL 的假设:

ASSERT(ptr != NULL);

如果驱动程序已使用 DEBUG 进行编译,并且执行至此时 ptr 的值为 NULL,则在控制台上会列显以下故障消息:

panic: assertion failed: ptr != NULL, file: driver.c, line: 56

注 - 由于 ASSERT(9F) 使用 DEBUG 编译符号,因此任何条件调试代码也应使用 DEBUG


使用 mutex_owned() 验证和记录锁定要求

mutex_owned(9F) 的语法如下:

int mutex_owned(kmutex_t *mp);

驱动程序开发过程中的一项重要工作涉及正确处理多个线程。获得了互斥锁时,应始终使用注释。在获得明确需要的互斥锁时,注释将更有用。要确定线程是否持有互斥锁,请在 ASSERT(9F) 中使用 mutex_owned()

void helper(void)
{
    /* this routine should always be called with xsp's mutex held */
    ASSERT(mutex_owned(&xsp->mu));
    /* ... */
}

注 - mutex_owned() 只在 ASSERT() 宏内有效。应该使用 mutex_owned() 控制驱动程序的行为。


使用条件编译在开销较大的调试功能之间切换

可以使用预处理程序符号(例如 DEBUG)或全局变量,借助条件编译将用于调试的代码插入驱动程序中。使用条件编译,可在生产驱动程序中删除不必要的代码。使用变量可以在运行时设置调试输出量。在运行时使用 ioctl 或调试器设置调试级别可以指定输出。通常,可以组合使用这两种方法。

以下示例依赖编译器来删除无法访问的代码(在本例中是跟在始终为 false 的零测试之后的代码)。此示例还提供了可在 /etc/system 中设置或由调试器修补的局部变量。

#ifdef DEBUG
/* comments on values of xxdebug and what they do */
static int xxdebug;
#define dcmn_err if (xxdebug) cmn_err
#else
#define dcmn_err if (0) cmn_err
#endif
/* ... */
    dcmn_err(CE_NOTE, "Error!\n");

此方法可处理 cmn_err(9F) 具有可变数量参数的情况。另一种方法依赖于宏只有一个参数的情况,即 cmn_err(9F) 的用括号括起的参数列表。宏可以删除此参数。此宏还可在未定义 DEBUG 的情况下,通过将宏扩展为不包含任何内容来消除对优化程序的依赖性。

#ifdef DEBUG
/* comments on values of xxdebug and what they do */
static int xxdebug;
#define dcmn_err(X) if (xxdebug) cmn_err X
#else
#define dcmn_err(X) /* nothing */
#endif
/* ... */
/* Note:double parentheses are required when using dcmn_err. */
    dcmn_err((CE_NOTE, "Error!"));

可以采用多种方法扩展此技术。一种方法是根据 xxdebug 的值指定来自 cmn_err(9F) 的不同消息。但在此类情况下,必须注意不要用大量的调试信息使代码变得晦涩难懂。

另一种常见方案是编写 xxlog() 函数,以便使用 vsprintf(9F)vcmn_err(9F) 来处理变量参数列表。