> For the complete documentation index, see [llms.txt](https://book.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://book.bsdcn.org/4.3-bsd-unix-cao-zuo-xi-tong-she-ji-yu-shi-xian/di-san-bu-fen-io-xi-tong/8.7.-massbus-she-bei.md).

# 8.7 MASSBUS 设备

DEC 设计 MASSBUS 作为比 UNIBUS 所能支持的速度更高的大容量存储外设的 I/O 总线。MASSBUS 带宽比 UNIBUS 大，结构也更死板：一条 MASSBUS 至多支持八台设备，同时只有一台传输数据，可用设备只有磁盘和磁带。每台磁带格式器至多支持八台磁带机。VAX 上的每条 MASSBUS 经一个 MASSBUS 适配器接系统总线。既然 MASSBUS 设计支持的传输率高于 UNIBUS，一些厂商设计了直接接系统总线、仿真 MASSBUS 适配器的设备控制器。

MASSBUS 的死板结构允许每个 MASSBUS 设备驱动的相当一部分隔离成一组公共的 MASSBUS 适配器支持例程。这些例程管理 I/O 请求排队的大部分工作，并在设备与设备驱动之间插入自己的中断服务例程。MASSBUS 设备驱动与 UNIBUS 设备驱动的差别在于为每次 DMA 操作调度适配器资源的方式：UNIBUS 适配器所需的复杂资源调度这里不需要，因为 MASSBUS 一次只允许一台设备传输数据。单一数据通路简化了 I/O 调度，但可能增加排队延迟。

**自动配置**

MASSBUS 设备的探测与附接方式与 UNIBUS 设备类似，主要差别是 MASSBUS 设备可以按设备无关的方式探测——MASSBUS 适配器中某些控制器状态寄存器定义了标准位分配。这意味着可以写一个单一的 MASSBUS 探测例程，识别 MASSBUS 适配器上八台可能设备中的每一台；只有磁带机的识别须逐设备处理。于是，MASSBUS 设备驱动的自动配置支持缩减为一个附接例程，设备若为磁带控制器，再加一个从属例程。

**I/O 策略**

MASSBUS 设备只有磁带和磁盘控制器。这些设备只提供裸接口和面向块的接口，全部 I/O 请求最终都经设备驱动的 strategy 例程。与 `up` 设备驱动中的调度不同，MASSBUS 设备的 I/O 调度大部分在 MASSBUS 适配器支持例程中完成。例如 `ht` 磁带设备的 `htstrategy()` 例程（见图 8.16）很简单：把每个请求放到磁带机请求队列的末尾；磁带机当前不活跃，就调用 MASSBUS 适配器支持例程 `mbustart()` 启动该部件的活动。

```c
htstrategy(bp)
	buffer *bp;
{
	提升优先级到 splbio;
	if (本磁带机的请求队列为空)
		把 bp 放在队列头;
	else
		把 bp 放在队列尾;
	if (磁带机不活跃)
		调用 mbustart 启动磁带机上的操作;
	降低优先级;
}
```

图 8.16 ht 驱动策略的算法

I/O 请求调度算法的结构与 `up` 驱动所用算法颇为相似。一个或多个操作放上设备请求队列后，设备驱动调用 MASSBUS 适配器的部件启动例程 `mbustart()`。`mbustart()` 例程取设备请求队列头部的操作，回调（call back）设备驱动的部件启动例程（unit start routine）发起操作。部件启动例程为指定请求做一切所需的事，然后返回表明发生了下列情况之一的指示：

* 请求立即完成（例如磁带机上的 sense 操作）。
* 设备忙，请求无法完成（例如双端口磁盘机当时正在服务另一端口）。
* 请求已启动（非数据传输操作）。
* 请求涉及数据传输，设备应被调度使用 MASSBUS 适配器上的单一数据通路。

若是数据传输，设备被放上 MASSBUS 适配器的数据传输请求队列；适配器数据通路空闲时，部件启动例程调用 `mbstart()` 例程立即启动传输。

`mbstart()` 例程在功能上与上一节的 `ubago()` 例程相似。不过与 UNIBUS 适配器上的操作不同，I/O 操作在 `mbstart()` 例程中发起而不是在设备驱动的启动例程中——这做得到，因为所有 MASSBUS 设备的寄存器组都在已知位置有一个命令寄存器。开始一次传输时，先调用驱动的启动例程填写驱动器专用寄存器、确定要放进设备命令寄存器的值；然后把数据缓冲映射进 MASSBUS 地址空间，使设备能访问该缓冲；最后把驱动启动例程给出的命令写进命令寄存器，发起操作。

**中断处理**

MASSBUS 设备的中断对 CPU 产生单一的 MASSBUS 适配器中断。MASSBUS 适配器中断服务例程带着中断适配器的标识被调用，必须读适配器的状态寄存器查明哪些设备需要服务。MASSBUS 适配器的中断处理算法见图 8.17。每个设备驱动可提供两个中断服务例程：一个用于涉及数据传输的操作，一个用于不涉及数据传输的操作。两类例程都必须返回状态码，表明操作已完成还是因错误需要重试（另有与我们的讨论无关的状态码）。MASSBUS 中断服务例程用这些状态码驱动一个控制操作流经适配器的有限状态机。

```c
mbintr(number)
	massbus adapter number;
{
	从 massbus 适配器取回设备状态;
	if (适配器正活跃地做数据传输) {
		标识正在传数据的设备;
		调用设备的数据传输完成中断
			例程;
		switch (数据传输的状态) {
		case 传输完成:
			从设备请求队列摘下请求;
			通知发起操作的进程;
			/* 落入下一分支…… */
		case 因错误重试传输:
			从适配器请求队列摘下请求;
			标记适配器不再活跃地做数据传输;
			if (设备的请求队列非空)
				调用 mbustart 启动设备的
					下一个操作;
			break;
		}
	}
	for (每台需要照应的设备) {
		if (设备有非数据传输中断例程) {
			调用设备的非数据传输中断例程;
			switch (非数据传输的状态) {
			case 操作完成:
				从设备请求队列摘下请求;
				通知发起操作的进程;
				/* 落入下一分支…… */
			case 因错误重试操作:
				if (设备的请求队列非空)
					调用 mbustart 启动设备的
						下一个操作;
				break;
			}
		} else {
			调用 mbustart 启动设备的下一个操作;
		}
	}
	if ((适配器请求队列非空) &&
	    (适配器未标记为正在做数据传输))
		调用 mbstart 发起下一个 I/O 操作;
}
```

图 8.17 MASSBUS 适配器中断处理的算法

## 习题

8.1 你预期盒式磁带机是块接口还是字符接口？为什么？

8.2 **fsck** 等文件系统工具为什么通常用字符设备接口访问磁盘机？

8.3 内核下半部运行的中断服务例程为什么不得调用 `sleep()` 例程？

8.4 系统启动的自动配置阶段，设备探测例程有哪些职责？

8.5 解释 4.3BSD 安装在中断向量表中的胶合例程的用途与运作。

8.6 `disksort()` 例程为磁盘机排序 I/O 请求时为什么用两条链？

8.7 发往 UNIBUS 或 MASSBUS 设备的 I/O 请求在适配器排队，可能毁掉设备驱动所做的磁盘机调度。在设备驱动中做寻道优化与在磁盘控制器中做寻道优化，各有什么利弊？

8.8 多数硬件设备一次只允许一个 I/O 操作。4.3BSD 如何强制这一限制？

8.9 设想你要为能处理多个同时 I/O 请求的磁盘控制器修改 `up` 设备驱动。驱动 I/O 排队结构中的哪些假设必须改变？

8.10 设备的 `open()` 例程每次 `open` 系统调用都被调用，`close()` 例程却只在设备的最后一次关闭时调用。为什么选这样的语义？举一个这些语义会引起问题的设备例子。

8.11 UNIBUS 适配器为支持在 VAX 上接入 UNIBUS 提供什么功能？

8.12 图 8.3 的 `upprobe()` 例程等 10 毫秒让设备中断。这个延迟你会怎么实现？CPU 的性能如何影响你所选的实现？

8.13 4.3BSD 为什么把次设备号解释为逻辑而非物理设备部件号？

8.14 磁盘驱动要支持重叠寻道必须做什么？什么是 ECC 纠错？什么是坏扇区转发？什么是滑移扇区？

8.15 解释 `up` 磁盘驱动中如何使用 `sdist` 和 `rdist` 参数。

8.16 4.3BSD 为什么要求所有 UNIBUS 设备驱动经 UNIBUS 适配器例程分派 I/O 操作？

8.17 MASSBUS 适配器例程为什么能比 UNIBUS 适配器例程做更多工作？

8.18 图 8.1 的 `physio()` 例程在使用缓冲头之前把处理器优先级提升到 `splbio`。为什么必须提升处理器优先级？

\*8.19 图 8.1 的 `physio()` 例程把处理器优先级提升又降低了好几次。能不能改为进入时提升一次、退出时降低？解释你的答案。

\*8.20 含驱动器几何和逻辑分区的磁盘标号，在磁盘第一分区打开时由磁盘驱动读入。磁盘标号必须放在哪里？为什么必须在那个位置？

\*8.21 有些操作系统允许把设备驱动装入运行中的系统。这一方案的利弊何在？

\*8.22 假设某设备不用向量中断，而是把中断信号一直保持到被叫停。这对 4.3BSD 在 VAX 上的中断处理有什么影响？对自动配置方案有什么影响？

\*8.23 描述文件系统在磁盘机上摆放数据的算法与 `disksort()` 例程所体现的 I/O 调度算法之间的互动。

**8.24** 构造一个磁盘机调度模型，用来为 `up` 驱动所用的 `sdist` 和 `rdist` 参数算出准确值。CPU 的速度如何影响你的模型？

**8.25** 描述总线复位期间管理 UNIBUS 资源的一种替代方案。

**8.26** 考虑所有设备在所有处理器上都可访问的共享内存多处理机体系结构。要允许 I/O 操作由任一处理器管理，I/O 系统和设备驱动需要哪些改动？


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://book.bsdcn.org/4.3-bsd-unix-cao-zuo-xi-tong-she-ji-yu-shi-xian/di-san-bu-fen-io-xi-tong/8.7.-massbus-she-bei.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
