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

文档信息

前言

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

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

2.  Oracle Solaris 内核和设备树

3.  多线程

锁定原语

驱动程序数据的存储类

互斥锁

设置互斥锁

使用互斥锁

读取器/写入器锁

信号

线程同步

线程同步中的条件变量

初始化条件变量

等待条件

发出条件信号

cv_wait()cv_timedwait() 函数

cv_wait_sig() 函数

cv_timedwait_sig() 函数

选择锁定方案

潜在的锁定缺点

线程无法接收信号

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.  推荐的编码方法

第 4 部分附录

A.  硬件概述

B.  Solaris DDI/DKI 服务汇总

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

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

E.  pci.conf 文件

索引

线程同步

除了保护共享数据外,驱动程序通常需要在多个线程之间同步执行。

线程同步中的条件变量

条件变量是线程同步的标准形式。这些变量专门用于互斥锁。关联互斥锁可以确保条件的检查是原子操作,并且线程可以基于关联的条件变量阻塞,同时不会忽略对条件的更改或条件已更改的信号。

condvar(9F) 函数包括:

cv_broadcast(9F)

向基于条件变量等待的所有线程发出信号。

cv_destroy(9F)

销毁条件变量。

cv_init(9F)

初始化条件变量。

cv_signal(9F)

向基于条件变量等待的一个线程发出信号。

cv_timedwait(9F)

等待条件、超时或信号。请参见线程无法接收信号

cv_timedwait_sig(9F)

等待条件或超时。

cv_wait(9F)

等待条件。

cv_wait_sig(9F)

等待条件或在收到信号时返回零。请参见线程无法接收信号

初始化条件变量

针对每个条件声明一个 kcondvar_t 类型的条件变量。通常,条件变量是驱动程序定义的数据结构中的一个变量。使用 cv_init(9F) 可初始化每个条件变量。与互斥锁类似,条件变量通常在执行 attach(9E) 时初始化。以下是一个初始化条件变量的典型示例:

cv_init(&xsp->cv, NULL, CV_DRIVER, NULL);

有关条件变量初始化的较完整示例,请参见第 6 章

等待条件

要使用条件变量,请在等待条件的代码路径中执行以下步骤:

  1. 获取用于保护条件的互斥锁。

  2. 测试条件。

  3. 如果测试结果表明不允许线程继续执行,请使用 cv_wait(9F) 根据条件阻塞当前线程。cv_wait(9F) 函数将在阻塞线程之前释放互斥锁,并在返回之前重新获取互斥锁。从 cv_wait(9F) 返回时,重复该测试。

  4. 测试表明允许线程继续执行后,请将条件设置为其新值。例如,将设备标志设置为繁忙。

  5. 释放互斥锁。

发出条件信号

请在代码路径中执行以下步骤以发出条件信号:

  1. 获取用于保护条件的互斥锁。

  2. 设置条件。

  3. 使用 cv_broadcast(9F) 向阻塞的线程发出信号。

  4. 释放互斥锁。

以下示例使用繁忙标志以及互斥锁和条件变量来强制 read(9E) 例程进行等待,直到设备不再繁忙时为止,然后开始传送。

示例 3-1 使用互斥锁和条件变量

static int
xxread(dev_t dev, struct uio *uiop, cred_t *credp)
{
        struct xxstate *xsp;
        /* ... */
        mutex_enter(&xsp->mu);
        while (xsp->busy)
                cv_wait(&xsp->cv, &xsp->mu);
        xsp->busy = 1;
        mutex_exit(&xsp->mu);
        /* perform the data access */
}

static uint_t
xxintr(caddr_t arg)
{
        struct xxstate *xsp = (struct xxstate *)arg;
        mutex_enter(&xsp->mu);
        xsp->busy = 0;
        cv_broadcast(&xsp->cv);
        mutex_exit(&xsp->mu);
}

cv_wait()cv_timedwait() 函数

如果使用 cv_wait(9F) 根据某个条件将线程阻塞,但该条件不发生,则该线程将永远等待。要避免这种情况,请使用 cv_timedwait(9F),它取决于执行唤醒的其他线程。cv_timedwait() 采取绝对等待时间作为参数。如果时间已到但未发生事件,则 cv_timedwait() 将返回 -1。如果满足条件,则 cv_timedwait() 将返回一个正值。

cv_timedwait(9F) 要求自上次重新引导系统以来的绝对等待时间(以时钟周期表示)。通过使用 ddi_get_lbolt(9F) 检索当前值可确定该等待时间。驱动程序通常具有的是最大等待秒数或微秒数,因此需要使用 drv_usectohz(9F) 将该值转换为时钟周期,然后与 ddi_get_lbolt(9F) 的值相加。

以下示例说明如何使用 cv_timedwait(9F) 最多等待五秒钟便访问设备,然后向调用方返回 EIO

示例 3-2 使用 cv_timedwait()

clock_t            cur_ticks, to;
mutex_enter(&xsp->mu);
while (xsp->busy) {
        cur_ticks = ddi_get_lbolt();
        to = cur_ticks + drv_usectohz(5000000); /* 5 seconds from now */
        if (cv_timedwait(&xsp->cv, &xsp->mu, to) == -1) {
                /*
                 * The timeout time 'to' was reached without the
                 * condition being signaled.
                 */
                /* tidy up and exit */
                mutex_exit(&xsp->mu);
                return (EIO);
        }
}
xsp->busy = 1;
mutex_exit(&xsp->mu);

虽然设备驱动程序写入器通常首选使用 cv_timedwait(9F) 而不是 cv_wait(9F),但是有时选用 cv_wait(9F) 会更好。例如,如果驱动程序基于以下条件等待,则使用 cv_wait(9F) 更合适:

cv_wait_sig() 函数

驱动程序可能正在等待不会产生或长时间不会发生的条件。在此类情况下,用户可发送信号中止该线程。根据驱动程序设计,信号可能无法将驱动程序唤醒。

cv_wait_sig(9F) 允许使用信号来解除阻塞线程。借助此功能,用户可以通过使用 kill(1) 向线程发送信号或键入中断字符,从而免于可能的长时间等待。如果 cv_wait_sig(9F) 由于收到信号而返回,则会返回零;如果条件发生,则返回非零值。但是,对于可能未收到信号的情况,请参见线程无法接收信号

以下示例说明如何使用 cv_wait_sig(9F) 以允许使用信号解除阻塞线程。

示例 3-3 使用 cv_wait_sig()

mutex_enter(&xsp->mu);
while (xsp->busy) {
        if (cv_wait_sig(&xsp->cv, &xsp->mu) == 0) {
        /* Signaled while waiting for the condition */
                /* tidy up and exit */
                mutex_exit(&xsp->mu);
                return (EINTR);
        }
}
xsp->busy = 1;
mutex_exit(&xsp->mu);

cv_timedwait_sig() 函数

cv_timedwait_sig(9F)cv_timedwait(9F)cv_wait_sig(9F) 相似,不同之处在于,达到超时后,如果没有发出条件信号,则 cv_timedwait_sig() 将返回 -1,如果向线程发送了信号(例如 kill(2)),则将返回 0

对于 cv_timedwait(9F)cv_timedwait_sig(9F),系统将使用上次重新引导系统以来的绝对时钟周期度量时间。