aboutsummaryrefslogtreecommitdiff
path: root/zh_CN.UTF-8/books
diff options
context:
space:
mode:
authorXin LI <delphij@FreeBSD.org>2013-11-14 00:20:51 +0000
committerXin LI <delphij@FreeBSD.org>2013-11-14 00:20:51 +0000
commit797a990cf709e43fdcfa0f1b0b8b8e0127076002 (patch)
tree38ebab2823385023d1fa54018fceb14262c4cf93 /zh_CN.UTF-8/books
parent8ff93b9827e6bfbbae625ff0d45eb2ce00ebe029 (diff)
downloaddoc-797a990cf709e43fdcfa0f1b0b8b8e0127076002.tar.gz
doc-797a990cf709e43fdcfa0f1b0b8b8e0127076002.zip
Convert zh_CN from GB2312 to UTF-8. While I'm there, also
fix a bunch of font issues when rendering PDF.
Notes
Notes: svn path=/head/; revision=43188
Diffstat (limited to 'zh_CN.UTF-8/books')
-rw-r--r--zh_CN.UTF-8/books/Makefile14
-rw-r--r--zh_CN.UTF-8/books/Makefile.inc6
-rw-r--r--zh_CN.UTF-8/books/arch-handbook/Makefile58
-rw-r--r--zh_CN.UTF-8/books/arch-handbook/book.xml169
-rw-r--r--zh_CN.UTF-8/books/arch-handbook/boot/chapter.xml915
-rw-r--r--zh_CN.UTF-8/books/arch-handbook/chapters.ent35
-rw-r--r--zh_CN.UTF-8/books/arch-handbook/driverbasics/chapter.xml555
-rw-r--r--zh_CN.UTF-8/books/arch-handbook/isa/chapter.xml2146
-rw-r--r--zh_CN.UTF-8/books/arch-handbook/jail/chapter.xml636
-rw-r--r--zh_CN.UTF-8/books/arch-handbook/kobj/chapter.xml284
-rw-r--r--zh_CN.UTF-8/books/arch-handbook/locking/chapter.xml334
-rw-r--r--zh_CN.UTF-8/books/arch-handbook/mac.ent98
-rw-r--r--zh_CN.UTF-8/books/arch-handbook/mac/chapter.xml7233
-rw-r--r--zh_CN.UTF-8/books/arch-handbook/newbus/chapter.xml328
-rw-r--r--zh_CN.UTF-8/books/arch-handbook/pccard/chapter.xml302
-rw-r--r--zh_CN.UTF-8/books/arch-handbook/pci/chapter.xml438
-rw-r--r--zh_CN.UTF-8/books/arch-handbook/scsi/chapter.xml1815
-rw-r--r--zh_CN.UTF-8/books/arch-handbook/smp/chapter.xml929
-rw-r--r--zh_CN.UTF-8/books/arch-handbook/sound/chapter.xml623
-rw-r--r--zh_CN.UTF-8/books/arch-handbook/sysinit/chapter.xml185
-rw-r--r--zh_CN.UTF-8/books/arch-handbook/usb/chapter.xml448
-rw-r--r--zh_CN.UTF-8/books/arch-handbook/vm/chapter.xml234
-rw-r--r--zh_CN.UTF-8/books/handbook/Makefile356
-rw-r--r--zh_CN.UTF-8/books/handbook/advanced-networking/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/advanced-networking/chapter.xml5519
-rw-r--r--zh_CN.UTF-8/books/handbook/audit/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/audit/chapter.xml602
-rw-r--r--zh_CN.UTF-8/books/handbook/basics/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/basics/chapter.xml2476
-rw-r--r--zh_CN.UTF-8/books/handbook/bibliography/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/bibliography/chapter.xml636
-rw-r--r--zh_CN.UTF-8/books/handbook/book.xml308
-rw-r--r--zh_CN.UTF-8/books/handbook/boot/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/boot/chapter.xml849
-rw-r--r--zh_CN.UTF-8/books/handbook/bsdinstall/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/bsdinstall/chapter.xml2340
-rw-r--r--zh_CN.UTF-8/books/handbook/chapters.ent72
-rw-r--r--zh_CN.UTF-8/books/handbook/colophon.xml20
-rw-r--r--zh_CN.UTF-8/books/handbook/config/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/config/chapter.xml2807
-rw-r--r--zh_CN.UTF-8/books/handbook/cutting-edge/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/cutting-edge/chapter.xml2678
-rw-r--r--zh_CN.UTF-8/books/handbook/desktop/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/desktop/chapter.xml1154
-rw-r--r--zh_CN.UTF-8/books/handbook/disks/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/disks/chapter.xml4016
-rw-r--r--zh_CN.UTF-8/books/handbook/dtrace/Makefile18
-rw-r--r--zh_CN.UTF-8/books/handbook/dtrace/chapter.xml364
-rw-r--r--zh_CN.UTF-8/books/handbook/eresources/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/eresources/chapter.xml1840
-rw-r--r--zh_CN.UTF-8/books/handbook/filesystems/Makefile18
-rw-r--r--zh_CN.UTF-8/books/handbook/filesystems/chapter.xml570
-rw-r--r--zh_CN.UTF-8/books/handbook/firewalls/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/firewalls/chapter.xml2956
-rw-r--r--zh_CN.UTF-8/books/handbook/geom/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/geom/chapter.xml850
-rw-r--r--zh_CN.UTF-8/books/handbook/install/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/install/chapter.xml4460
-rw-r--r--zh_CN.UTF-8/books/handbook/introduction/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/introduction/chapter.xml931
-rw-r--r--zh_CN.UTF-8/books/handbook/jails/chapter.xml804
-rw-r--r--zh_CN.UTF-8/books/handbook/kernelconfig/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/kernelconfig/chapter.xml1352
-rw-r--r--zh_CN.UTF-8/books/handbook/l10n/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/l10n/chapter.xml822
-rw-r--r--zh_CN.UTF-8/books/handbook/linuxemu/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/linuxemu/chapter.xml1137
-rw-r--r--zh_CN.UTF-8/books/handbook/mac/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/mac/chapter.xml1757
-rw-r--r--zh_CN.UTF-8/books/handbook/mail/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/mail/chapter.xml1971
-rw-r--r--zh_CN.UTF-8/books/handbook/mirrors/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/mirrors/chapter.xml3144
-rw-r--r--zh_CN.UTF-8/books/handbook/multimedia/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/multimedia/chapter.xml1609
-rw-r--r--zh_CN.UTF-8/books/handbook/network-servers/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/network-servers/chapter.xml4707
-rw-r--r--zh_CN.UTF-8/books/handbook/pgpkeys/Makefile20
-rw-r--r--zh_CN.UTF-8/books/handbook/pgpkeys/chapter.xml42
-rw-r--r--zh_CN.UTF-8/books/handbook/ports/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/ports/chapter.xml1328
-rw-r--r--zh_CN.UTF-8/books/handbook/ppp-and-slip/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/ppp-and-slip/chapter.xml2763
-rw-r--r--zh_CN.UTF-8/books/handbook/preface/preface.xml583
-rw-r--r--zh_CN.UTF-8/books/handbook/printing/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/printing/chapter.xml4616
-rw-r--r--zh_CN.UTF-8/books/handbook/security/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/security/chapter.xml3445
-rw-r--r--zh_CN.UTF-8/books/handbook/serialcomms/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/serialcomms/chapter.xml2553
-rw-r--r--zh_CN.UTF-8/books/handbook/txtfiles.ent73
-rw-r--r--zh_CN.UTF-8/books/handbook/users/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/users/chapter.xml895
-rw-r--r--zh_CN.UTF-8/books/handbook/vinum/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/vinum/chapter.xml1135
-rw-r--r--zh_CN.UTF-8/books/handbook/virtualization/Makefile18
-rw-r--r--zh_CN.UTF-8/books/handbook/virtualization/chapter.xml1009
-rw-r--r--zh_CN.UTF-8/books/handbook/x11/Makefile16
-rw-r--r--zh_CN.UTF-8/books/handbook/x11/chapter.xml1536
-rw-r--r--zh_CN.UTF-8/books/porters-handbook/Makefile54
-rw-r--r--zh_CN.UTF-8/books/porters-handbook/book.xml14227
101 files changed, 105721 insertions, 0 deletions
diff --git a/zh_CN.UTF-8/books/Makefile b/zh_CN.UTF-8/books/Makefile
new file mode 100644
index 0000000000..4297aff018
--- /dev/null
+++ b/zh_CN.UTF-8/books/Makefile
@@ -0,0 +1,14 @@
+#
+# The FreeBSD Simplified Chinese Project
+#
+# Original Revision: 1.14
+# $FreeBSD$
+
+SUBDIR = arch-handbook
+SUBDIR+= handbook
+SUBDIR+= porters-handbook
+
+ROOT_SYMLINKS= handbook
+
+DOC_PREFIX?= ${.CURDIR}/../..
+.include "${DOC_PREFIX}/share/mk/doc.project.mk"
diff --git a/zh_CN.UTF-8/books/Makefile.inc b/zh_CN.UTF-8/books/Makefile.inc
new file mode 100644
index 0000000000..e7b5457951
--- /dev/null
+++ b/zh_CN.UTF-8/books/Makefile.inc
@@ -0,0 +1,6 @@
+#
+# Original Revision: 1.4
+# $FreeBSD$
+#
+
+DESTDIR?= ${DOCDIR}/zh_CN.UTF-8/books/${.CURDIR:T}
diff --git a/zh_CN.UTF-8/books/arch-handbook/Makefile b/zh_CN.UTF-8/books/arch-handbook/Makefile
new file mode 100644
index 0000000000..d9a4337a92
--- /dev/null
+++ b/zh_CN.UTF-8/books/arch-handbook/Makefile
@@ -0,0 +1,58 @@
+#
+# Original Revision: 1.25
+# $FreeBSD$
+#
+# Build the FreeBSD Architecture Handbook.
+#
+
+MAINTAINER=doc@FreeBSD.org
+
+DOC?= book
+
+FORMATS?= html-split
+
+INSTALL_COMPRESSED?= gz
+INSTALL_ONLY_COMPRESSED?=
+
+#
+# SRCS lists the individual XML files that make up the document. Changes
+# to any of these files will force a rebuild
+#
+
+# XML content
+SRCS= book.xml
+SRCS+= boot/chapter.xml
+SRCS+= driverbasics/chapter.xml
+SRCS+= isa/chapter.xml
+SRCS+= jail/chapter.xml
+SRCS+= kobj/chapter.xml
+SRCS+= locking/chapter.xml
+SRCS+= mac/chapter.xml
+SRCS+= newbus/chapter.xml
+SRCS+= pci/chapter.xml
+SRCS+= scsi/chapter.xml
+SRCS+= smp/chapter.xml
+SRCS+= sound/chapter.xml
+SRCS+= pccard/chapter.xml
+SRCS+= sysinit/chapter.xml
+SRCS+= usb/chapter.xml
+SRCS+= vm/chapter.xml
+
+# Images from the cross-document image library
+IMAGES_LIB= callouts/1.png
+IMAGES_LIB+= callouts/2.png
+IMAGES_LIB+= callouts/3.png
+IMAGES_LIB+= callouts/4.png
+IMAGES_LIB+= callouts/5.png
+IMAGES_LIB+= callouts/6.png
+IMAGES_LIB+= callouts/7.png
+IMAGES_LIB+= callouts/8.png
+IMAGES_LIB+= callouts/9.png
+IMAGES_LIB+= callouts/10.png
+
+# Entities
+
+URL_RELPREFIX?= ../../../..
+DOC_PREFIX?= ${.CURDIR}/../../..
+
+.include "${DOC_PREFIX}/share/mk/doc.project.mk"
diff --git a/zh_CN.UTF-8/books/arch-handbook/book.xml b/zh_CN.UTF-8/books/arch-handbook/book.xml
new file mode 100644
index 0000000000..685341953b
--- /dev/null
+++ b/zh_CN.UTF-8/books/arch-handbook/book.xml
@@ -0,0 +1,169 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE book PUBLIC "-//FreeBSD//DTD DocBook XML V5.0-Based Extension//EN"
+ "http://www.FreeBSD.org/XML/share/xml/freebsd50.dtd" [
+<!ENTITY % chapters SYSTEM "chapters.ent">
+%chapters;
+<!ENTITY % mac-entities SYSTEM "mac.ent">
+%mac-entities;
+]>
+<!--
+ The FreeBSD Documentation Project
+ The FreeBSD Simplified Chinese Project
+
+ Original Revision: 1.51
+ $FreeBSD$
+-->
+<book xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" xml:lang="zh_cn">
+ <info><title>&os; 系统结构手册</title>
+
+
+ <author><orgname>The FreeBSD Documentation Project</orgname></author>
+
+ <pubdate>2000 年 8 月</pubdate>
+
+ <copyright>
+ <year>2000</year>
+ <year>2001</year>
+ <year>2002</year>
+ <year>2003</year>
+ <year>2004</year>
+ <year>2005</year>
+ <year>2006</year>
+ <holder>The FreeBSD Documentation Project</holder>
+ </copyright>
+
+ <author><orgname>&cnproj.freebsd.org;</orgname></author>
+
+ <pubdate>2005 年 12 月</pubdate>
+
+ <copyright>
+ <year>2004</year>
+ <year>2005</year>
+ <year>2006</year>
+ <holder>&cnproj.freebsd.org;</holder>
+ </copyright>
+
+ &trademarks;
+ &legalnotice;
+
+ <releaseinfo>$FreeBSD$</releaseinfo>
+
+ <abstract>
+
+ <!--
+ The following two entities "contributing.to.freebsd.doc" and
+ "getting.freebsd.doc" are defined in zh_CN.UTF-8/share/xml/l10n.ent
+ -->
+
+ &cnproj.contributing.to.freebsd.doc;
+
+ &cnproj.getting.freebsd.doc;
+ </abstract>
+ </info>
+
+ <part xml:id="kernel">
+ <title>内核</title>
+
+ &chap.boot;
+ &chap.locking;
+ &chap.kobj;
+ &chap.jail;
+ &chap.sysinit;
+ &chap.mac;
+ &chap.vm;
+ &chap.smp;
+
+ </part>
+
+ <part xml:id="devicedrivers">
+ <title>设备驱动程序</title>
+
+ &chap.driverbasics;
+ &chap.isa;
+ &chap.pci;
+ &chap.scsi;
+ &chap.usb;
+ &chap.newbus;
+
+ &chap.snd;
+ &chap.pccard;
+
+ </part>
+
+<!-- XXX - finish me
+ <part id="architectures">
+ <title>Architectures</title>
+
+ <chapter id="i386">
+ <title>* I386</title>
+
+ <para>Talk about <literal>i386</literal> specific &os;
+ architecture.</para>
+ </chapter>
+
+ <chapter id="alpha">
+ <title>* Alpha</title>
+
+ <para>Talk about the architectural specifics of
+ FreeBSD/alpha.</para>
+ </chapter>
+
+ <chapter id="ia64">
+ <title>* IA-64</title>
+
+ <para>Talk about the architectural specifics of
+ FreeBSD/ia64.</para>
+
+ </chapter>
+
+ <chapter id="sparc64">
+ <title>* SPARC64</title>
+
+ <para>Talk about <literal>SPARC64</literal> specific &os;
+ architecture.</para>
+ </chapter>
+
+ <chapter id="amd64">
+ <title>* AMD64</title>
+
+ <para>Talk about <literal>AMD64</literal> specific &os;
+ architecture.</para>
+ </chapter>
+
+ <chapter id="powerpc">
+ <title>* PowerPC</title>
+
+ <para>Talk about <literal>PowerPC</literal> specific &os;
+ architecture.</para>
+ </chapter>
+ </part>
+-->
+
+ <part xml:id="appendices">
+ <title>附录</title>
+
+ <bibliography>
+
+ <biblioentry xreflabel="1">
+ <authorgroup>
+ <author><personname><firstname>Marshall</firstname><othername role="Middle">Kirk</othername><surname>McKusick</surname></personname></author>
+ <author><personname><firstname>Keith</firstname><surname>Bostic</surname></personname></author>
+ <author><personname><firstname>Michael</firstname><othername role="MI">J</othername><surname>Karels</surname></personname></author>
+ <author><personname><firstname>John</firstname><othername role="MI">S</othername><surname>Quarterman</surname></personname></author>
+ </authorgroup>
+ <copyright><year>1996</year><holder>Addison-Wesley Publishing Company,
+ Inc.</holder></copyright>
+ <biblioid class="isbn">0-201-54979-4</biblioid>
+ <publisher>
+ <publishername>Addison-Wesley Publishing Company, Inc.</publishername>
+ </publisher>
+ <citetitle>The Design and Implementation of the 4.4 BSD Operating System</citetitle>
+ <pagenums>1-2</pagenums>
+ </biblioentry>
+
+ </bibliography>
+ </part>
+
+ &chap.index;
+
+</book>
diff --git a/zh_CN.UTF-8/books/arch-handbook/boot/chapter.xml b/zh_CN.UTF-8/books/arch-handbook/boot/chapter.xml
new file mode 100644
index 0000000000..74cf854717
--- /dev/null
+++ b/zh_CN.UTF-8/books/arch-handbook/boot/chapter.xml
@@ -0,0 +1,915 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+The FreeBSD Documentation Project
+The FreeBSD Simplified Chinese Project
+
+Original Revision: 1.28
+
+Copyright (c) 2002 Sergey Lyubka <devnull@uptsoft.com>
+All rights reserved
+$FreeBSD$
+-->
+<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" xml:id="boot">
+ <info><title>引导过程与内核初始化</title>
+ <authorgroup>
+ <author><personname><firstname>Sergey</firstname><surname>Lyubka</surname></personname><contrib>&cnproj.contributed.by;</contrib></author> <!-- devnull@uptsoft.com 12 Jun 2002 -->
+ </authorgroup>
+ <authorgroup>
+ <author><personname/><contrib>&cnproj.translated.by;</contrib></author>
+ </authorgroup>
+ </info>
+
+
+ <sect1 xml:id="boot-synopsis">
+ <title>概述</title>
+
+ <indexterm><primary>BIOS(基本输入输出系统, Basic Input Output System)</primary></indexterm>
+ <indexterm><primary>fireware(固件)</primary></indexterm>
+ <indexterm><primary>POST(加电自检, Power On Self Test)</primary></indexterm>
+ <indexterm><primary>IA-32</primary></indexterm>
+ <indexterm><primary>booting(引导)</primary></indexterm>
+ <indexterm><primary>system initialization(系统初始化)</primary></indexterm>
+ <para>这一章是对引导过程和系统初始化过程的总览。这些过程始于BIOS(固件)POST,
+ 直到第一个用户进程建立。由于系统启动的最初步骤是与硬件结构相关的、是紧配合的,
+ 这里用IA-32(Intel Architecture 32bit)结构作为例子。</para>
+ </sect1>
+
+ <sect1 xml:id="boot-overview">
+ <title>总览</title>
+
+ <para>一台运行FreeBSD的计算机有多种引导方法。这里讨论其中最通常的方法,
+ 也就是从安装了操作系统的硬盘上引导。引导过程分几步完成:</para>
+
+ <itemizedlist>
+ <listitem><para>BIOS POST</para></listitem>
+ <listitem><para><literal>boot0</literal>阶段</para></listitem>
+ <listitem><para><literal>boot2</literal>阶段</para></listitem>
+ <listitem><para>loader阶段</para></listitem>
+ <listitem><para>内核初始化</para></listitem>
+ </itemizedlist>
+
+ <indexterm><primary>BIOS POST</primary></indexterm>
+ <indexterm><primary>boot0</primary></indexterm>
+ <indexterm><primary>boot2</primary></indexterm>
+ <indexterm><primary>loader</primary></indexterm>
+ <para><literal>boot0</literal>和<literal>boot2</literal>阶段在手册
+ &man.boot.8;中被称为<emphasis>bootstrap stages 1 and 2</emphasis>,
+ 是FreeBSD的3阶段引导过程的开始。在每一阶段都有各种各样的信息显示在屏幕上,
+ 你可以参考下表识别出这些步骤。请注意实际的显示内容可能随机器的不同而有一些区别:
+ </para>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="2">
+ <tbody>
+ <row>
+ <entry><para>视不同机器而定</para></entry>
+
+ <entry><para>BIOS(固件)消息</para></entry>
+ </row>
+
+ <row>
+ <entry><para>
+<screen>F1 FreeBSD
+F2 BSD
+F5 Disk 2</screen>
+ </para></entry>
+
+ <entry><para><literal>boot0</literal></para></entry>
+ </row>
+
+ <row>
+ <entry><para>
+<screen>&gt;&gt;FreeBSD/i386 BOOT
+Default: 1:ad(1,a)/boot/loader
+boot:</screen>
+ </para></entry>
+
+ <entry><para><literal>boot2</literal><footnote><para>
+ 这种提示仅在<literal>boot0</literal>阶段用户选择操作系统后
+ 仍按住键盘上某一键时才出现。</para></footnote></para></entry>
+ </row>
+
+ <row>
+ <entry><para>
+<screen>BTX loader 1.0 BTX version is 1.01
+BIOS drive A: is disk0
+BIOS drive C: is disk1
+BIOS 639kB/64512kB available memory
+FreeBSD/i386 bootstrap loader, Revision 0.8
+Console internal video/keyboard
+(jkh@bento.freebsd.org, Mon Nov 20 11:41:23 GMT 2000)
+/kernel text=0x1234 data=0x2345 syms=[0x4+0x3456]
+Hit [Enter] to boot immediately, or any other key for command prompt
+Booting [kernel] in 9 seconds..._</screen>
+ </para></entry>
+
+ <entry><para>loader</para></entry>
+ </row>
+
+ <row>
+ <entry><para>
+ <screen>Copyright (c) 1992-2002 The FreeBSD Project.
+Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994
+ The Regents of the University of California. All rights reserved.
+FreeBSD 4.6-RC #0: Sat May 4 22:49:02 GMT 2002
+ devnull@kukas:/usr/obj/usr/src/sys/DEVNULL
+Timecounter "i8254" frequency 1193182 Hz</screen></para></entry>
+
+ <entry><para>内核</para></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </sect1>
+
+ <sect1 xml:id="boot-bios">
+ <title>BIOS POST</title>
+
+ <para>当PC加电后,处理器的寄存器被设为某些特定值。在这些寄存器中,
+ <emphasis>指令指针</emphasis>寄存器被设为32位值0xfffffff0。
+ 指令指针寄存器指向处理器将要执行的指令代码。<literal>cr1</literal>,
+ 一个32位控制寄存器,在刚启动时值被设为0。cr1的PE(Protected Enabled,
+ 保护模式使能)位用来指示处理器是处于保护模式还是实地址模式。
+ 由于启动时该位被清位,处理器在实地址模式中引导。在实地址模式中,
+ 线性地址与物理地址是等同的。</para>
+
+ <para>值0xfffffff0略小于4G,因此计算机没有4G字节物理内存,
+ 这就不会是一个有效的内存地址。计算机硬件将这个地址转指向BIOS存储块。
+ </para>
+
+ <para>BIOS表示<emphasis>Basic Input Output System</emphasis>
+ (基本输入输出系统)。在主板上,它被固化在一个相对容量较小的
+ 只读存储器(Read-Only Memory, ROM)。BIOS包含各种各样为主板硬件
+ 定制的底层例程。就这样,处理器首先指向常驻BIOS存储器的地址
+ 0xfffffff0。通常这个位置包含一条跳转指令,指向BIOS的POST例程。</para>
+
+ <para>POST表示<emphasis>Power On Self Test</emphasis>(加电自检)。
+ 这套程序包括内存检查,系统总线检查和其它底层工具,
+ 从而使得CPU能够初始化整台计算机。这一阶段中有一个重要步骤,
+ 就是确定引导设备。现在所有的BIOS都允许手工选择引导设备。
+ 你可以从软盘、光盘驱动器、硬盘等设备引导。</para>
+
+ <para>POST的最后一步是执行<literal>INT 0x19</literal>指令。
+ 这个指令从引导设备第一个扇区读取512字节,装入地址0x7c00。
+ <emphasis>第一个扇区</emphasis>的说法最早起源于硬盘的结构,
+ 硬盘面被分为若干圆柱形轨道。给轨道编号,同时又将轨道分为
+ 一定数目(通常是64)的扇形。0号轨道是硬盘的最外圈,1号扇区,
+ 第一个扇区(轨道、柱面都从0开始编号,而扇区从1开始编号)
+ 有着特殊的作用,它又被称为主引导记录(Master Boot Record, MBR)。
+ 第一轨剩余的扇区常常不使用<footnote><para>有些工具如&man.disklabel.8;
+ 会使用这一区域存储信息,主要是在第二扇区里。</para></footnote>。</para>
+ </sect1>
+
+ <sect1 xml:id="boot-boot0">
+ <title><literal>boot0</literal>阶段</title>
+
+ <indexterm><primary>MBR (主引导记录)</primary></indexterm>
+ <para>让我们看一下文件<filename>/boot/boot0</filename>。
+ 这是一个仅512字节的小文件。如果在FreeBSD安装过程中选择
+ <quote>bootmanager</quote>,这个文件中的内容将被写入硬盘MBR</para>
+
+ <para>如前所述, <literal>INT 0x19</literal> 指令装载 MBR,
+ 也就是 <filename>boot0</filename> 的内容至内存地址 0x7c00。
+ 再看文件 <filename>sys/boot/i386/boot0/boot0.S</filename>,
+ 可以猜想这里面发生了什么 - 这是引导管理器,
+ 一段由 Robert Nordier书写的令人起敬的程序片段。</para>
+
+ <para>MBR里,也就是<filename>boot0</filename>里,
+ 从偏移量0x1be开始有一个特殊的结构,称为
+ <emphasis>分区表</emphasis>。其中有4条记录
+ (称为<emphasis>分区记录</emphasis>),每条记录16字节。
+ 分区记录表示硬盘如何被划分,在FreeBSD的术语中,
+ 这被称为slice(d)。16字节中有一个标志字节决定这个分区是否可引导。
+ 有仅只能有一个分区可设定这一标志。否则,
+ <filename>boot0</filename>的代码将拒绝继续执行。</para>
+
+ <para>一个分区记录有如下域:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>1字节 文件系统类型</para>
+ </listitem>
+
+ <listitem>
+ <para>1字节 可引导标志</para>
+ </listitem>
+
+ <listitem>
+ <para>6字节 CHS格式描述符</para>
+ </listitem>
+
+ <listitem>
+ <para>8字节 LBA格式描述符</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>一个分区记录描述符包含某一分区在硬盘上的确切位置信息。
+ LBA和CHS两种描述符指示相同的信息,但是指示方式有所不同:LBA
+ (逻辑块寻址,Logical Block Addressing)指示分区的起始扇区和分区长度,
+ 而CHS(柱面 磁头 扇区)指示首扇区和末扇区</para>
+
+ <para>引导管理器扫描分区表,并在屏幕上显示菜单,以便用户可以
+ 选择用于引导的磁盘和分区。在键盘上按下相应的键后,
+ <filename>boot0</filename>进行如下动作:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>标记选中的分区为可引导,清除以前的可引导标志</para>
+ </listitem>
+
+ <listitem>
+ <para>记住本次选择的分区以备下次引导时作为缺省项</para>
+ </listitem>
+
+ <listitem>
+ <para>装载选中分区的第一个扇区,并跳转执行之</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>什么数据会存在于一个可引导扇区(这里指FreeBSD扇区)的第一扇区里呢?
+ 正如你已经猜到的,那就是<filename>boot2</filename>。</para>
+ </sect1>
+
+ <sect1 xml:id="boot-boot2">
+ <title><literal>boot2</literal>阶段</title>
+
+ <para>也许你想知道,为什么<literal>boot2</literal>是在
+ <literal>boot0</literal>之后,而不是在boot1之后。事实上,
+ 也有一个512字节的文件<filename>boot1</filename>存放在目录
+ <filename>/boot</filename>里,那是用来从一张软盘引导系统的。
+ 从软盘引导时,<filename>boot1</filename>起着
+ <filename>boot0</filename>对硬盘引导相同的作用:它找到
+ <filename>boot2</filename>并运行之。</para>
+
+ <para>你可能已经看到有一文件<filename>/boot/mbr</filename>。
+ 这是<filename>boot0</filename>的简化版本。
+ <filename>mbr</filename>中的代码不会显示菜单让用户选择,
+ 而只是简单的引导被标志的分区。</para>
+
+ <para>实现<filename>boot2</filename>的代码存放在目录
+ <filename>sys/boot/i386/boot2/</filename>里,对应的可执行文件在
+ <filename>/boot</filename>里。在<filename>/boot</filename>里的文件
+ <filename>boot0</filename>和<filename>boot2</filename>不会在引导过程中使用,
+ 只有<application>boot0cfg</application>这样的工具才会使用它们。
+ <filename>boot0</filename>的内容应在MBR中才能生效。
+ <filename>boot2</filename>位于可引导的FreeBSD分区的开始。
+ 这些位置不受文件系统控制,所以它们不可用<application>ls</application>
+ 之类的命令查看。</para>
+
+ <para><literal>boot2</literal>的主要任务是装载文件
+ <filename>/boot/loader</filename>,那是引导过程的第三阶段。
+ 在<literal>boot2</literal>中的代码不能使用诸如
+ <function>open()</function>和<function>read()</function>
+ 之类的例程函数,因为内核还没有被加载。而应当扫描硬盘,
+ 读取文件系统结构,找到文件<filename>/boot/loader</filename>,
+ 用BIOS的功能将它读入内存,然后从其入口点开始执行之。</para>
+
+ <para>除此之外,<literal>boot2</literal>还可提示用户进行选择,
+ loader可以从其它磁盘、系统单元、分区装载。</para>
+
+ <para><literal>boot2</literal> 的二进制代码用特殊的方式产生:</para>
+
+ <programlisting><filename>sys/boot/i386/boot2/Makefile</filename>
+boot2: boot2.ldr boot2.bin ${BTX}/btx/btx
+ btxld -v -E ${ORG2} -f bin -b ${BTX}/btx/btx -l boot2.ldr \
+ -o boot2.ld -P 1 boot2.bin</programlisting>
+
+ <indexterm><primary>BTX</primary></indexterm>
+ <para>这个Makefile片断表明&man.btxld.8;被用来链接二进制代码。
+ BTX表示引导扩展器(BooT eXtender)是给程序(称为客户(client)
+ 提供保护模式环境、并与客户程序相链接的一段代码。所以
+ <literal>boot2</literal>是一个BTX客户,使用BTX提供的服务。</para>
+
+ <indexterm><primary>linker(链接器)</primary></indexterm>
+ <para>工具<application>btxld</application>是链接器,
+ 它将两个二进制代码链接在一起。&man.btxld.8;和&man.ld.1;
+ 的区别是<application>ld</application>通常将两个目标文件
+ 链接成一个动态链接库或可执行文件,而<application>btxld</application>
+ 则将一个目标文件与BTX链接起来,产生适合于放在分区首部的二进制代码,
+ 以实现系统引导。</para>
+
+ <para><literal>boot0</literal>执行跳转至BTX的入口点。
+ 然后,BTX将处理器切换至保护模式,并准备一个简单的环境,
+ 然后调用客户。这个环境包括:</para>
+
+ <indexterm><primary>virtual 8086 mode(虚拟8086模式)</primary></indexterm>
+ <itemizedlist>
+ <listitem><para>虚拟8086模式。这意味着BTX是虚拟8086的监视程序。
+ 实模式指令,如pushf, popf, cli, sti, if,均可被客户调用。</para></listitem>
+
+ <listitem><para>建立中断描述符表(Interrupt Descriptor Table, IDT),
+ 使得所有的硬件中断可被缺省的BIOS程序处理。
+ 建立中断0x30,这是系统调用关口。</para></listitem>
+
+ <listitem><para>两个系统调用<function>exec</function>和
+ <function>exit</function>的定义如下:</para>
+
+ <programlisting><filename>sys/boot/i386/btx/lib/btxsys.s:</filename>
+ .set INT_SYS,0x30 # 中断号
+#
+# System call: exit
+#
+__exit: xorl %eax,%eax # BTX系统调用0x0
+ int $INT_SYS #
+#
+# System call: exec
+#
+__exec: movl $0x1,%eax # BTX系统调用0x1
+ int $INT_SYS # </programlisting></listitem>
+ </itemizedlist>
+
+ <para>BTX建立全局描述符表(Global Descriptor Table, GDT):</para>
+
+ <programlisting><filename>sys/boot/i386/btx/btx/btx.s:</filename>
+gdt: .word 0x0,0x0,0x0,0x0 # 以空为入口
+ .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE
+ .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA
+ .word 0xffff,0x0,0x9a00,0x0 # SEL_RCODE
+ .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA
+ .word 0xffff,MEM_USR,0xfa00,0xcf# SEL_UCODE
+ .word 0xffff,MEM_USR,0xf200,0xcf# SEL_UDATA
+ .word _TSSLM,MEM_TSS,0x8900,0x0 # SEL_TSS</programlisting>
+
+ <para>客户的代码和数据始于地址MEM_USR(0xa000),选择符(selector)
+ SEL_UCODE指向客户的数据段。选择符 SEL_UCODE 拥有第3级描述符权限
+ (Descriptor Privilege Level, DPL),这是最低级权限。但是
+ <literal>INT 0x30</literal> 指令的处理程序存储于另一个段里,
+ 这个段的选择符SEL_SCODE (supervisor code)由有着管理级权限。
+ 正如代码建立IDT(中断描述符表)时进行的操作那样:</para>
+
+ <programlisting> mov $SEL_SCODE,%dh # 段选择符
+init.2: shr %bx # 是否处理这个中断?
+ jnc init.3 # 否
+ mov %ax,(%di) # 设置处理程序偏移量
+ mov %dh,0x2(%di) # 设置处理程序选择符
+ mov %dl,0x5(%di) # 设置 P:DPL:type
+ add $0x4,%ax # 下一个中断处理程序</programlisting>
+
+ <para>所以,当客户调用 <function>__exec()</function>时,代码将被以最高权限执行。
+ 这使得内核可以修改保护模式数据结构,如分页表(page tables)、全局描述符表(GDT)、
+ 中断描述符表(IDT)等。</para>
+
+ <para><literal>boot2</literal> 定义了一个重要的数据结构:
+ <literal>struct bootinfo</literal>。这个结构由
+ <literal>boot2</literal> 初始化,然后被转送到loader,之后又被转入内核。
+ 这个结构的部分项目由<literal>boot2</literal>设定,其余的由loader设定。
+ 这个结构中的信息包括内核文件名、BIOS提供的硬盘柱面/磁头/扇区数目信息、
+ BIOS提供的引导设备的驱动器编号,可用的物理内存大小,<literal>envp</literal>
+ 指针(环境指针)等。定义如下:</para>
+
+ <programlisting><filename>/usr/include/machine/bootinfo.h</filename>
+struct bootinfo {
+ u_int32_t bi_version;
+ u_int32_t bi_kernelname; /* 用一个字节表示 * */
+ u_int32_t bi_nfs_diskless; /* struct nfs_diskless * */
+ /* 以上为常备项 */
+#define bi_endcommon bi_n_bios_used
+ u_int32_t bi_n_bios_used;
+ u_int32_t bi_bios_geom[N_BIOS_GEOM];
+ u_int32_t bi_size;
+ u_int8_t bi_memsizes_valid;
+ u_int8_t bi_bios_dev; /* 引导设备的BIOS单元编号 */
+ u_int8_t bi_pad[2];
+ u_int32_t bi_basemem;
+ u_int32_t bi_extmem;
+ u_int32_t bi_symtab; /* struct symtab * */
+ u_int32_t bi_esymtab; /* struct symtab * */
+ /* 以下项目仅高级bootloader提供 */
+ u_int32_t bi_kernend; /* 内核空间末端 */
+ u_int32_t bi_envp; /* 环境 */
+ u_int32_t bi_modulep; /* 预装载的模块 */
+};</programlisting>
+
+ <para><literal>boot2</literal> 进入一个循环等待用户输入,然后调用
+ <function>load()</function>。如果用户不做任何输入,循环将在一段时间后结束,
+ <function>load()</function> 将会装载缺省文件(<filename>/boot/loader</filename>)。
+ 函数 <function>ino_t lookup(char *filename)</function>和
+ <function>int xfsread(ino_t inode, void *buf, size_t nbyte)</function>
+ 用来将文件内容读入内存。<filename>/boot/loader</filename>是一个ELF格式二进制文件,
+ 不过它的头部被换成了a.out格式中的<literal>struct exec</literal>结构。
+ <function>load()</function>扫描loader的ELF头部,装载<filename>/boot/loader</filename>
+ 至内存,然后跳转至入口执行之:</para>
+
+ <programlisting><filename>sys/boot/i386/boot2/boot2.c:</filename>
+ __exec((caddr_t)addr, RB_BOOTINFO | (opts &amp; RBX_MASK),
+ MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part),
+ 0, 0, 0, VTOP(&amp;bootinfo));</programlisting>
+ </sect1>
+
+ <sect1 xml:id="boot-loader">
+ <title><application>loader</application>阶段</title>
+
+ <para><application>loader</application>也是一个 BTX 客户,在这里不作详述。
+ 已有一部内容全面的手册 &man.loader.8; ,由Mike Smith书写。
+ 比loader更底层的BTX的机理已经在前面讨论过。</para>
+
+ <para>loader 的主要任务是引导内核。当内核被装入内存后,即被loader调用:</para>
+
+ <programlisting><filename>sys/boot/common/boot.c:</filename>
+ /* 从loader中调用内核中对应的exec程序 */
+ module_formats[km-&gt;m_loader]-&gt;l_exec(km);</programlisting>
+ </sect1>
+
+ <sect1 xml:id="boot-kernel">
+ <title>内核初始化</title>
+
+ <para>让我们来看一下链接内核的命令。
+ 这能帮助我们了解 loader 传递给内核的准确位置。
+ 这个位置就是内核真实的入口点。</para>
+
+ <programlisting><filename>sys/conf/Makefile.i386:</filename>
+ld -elf -Bdynamic -T /usr/src/sys/conf/ldscript.i386 -export-dynamic \
+-dynamic-linker /red/herring -o kernel -X locore.o \
+&lt;lots of kernel .o files&gt;</programlisting>
+
+ <indexterm><primary>ELF(可执行可链接格式)</primary></indexterm>
+ <para>在这一行中有一些有趣的东西。首先,内核是一个ELF动态链接二进制文件,
+ 可是动态链接器却是<filename>/red/herring</filename>,一个莫须有的文件。
+ 其次,看一下文件<filename>sys/conf/ldscript.i386</filename>,
+ 可以对理解编译内核时<application>ld</application>的选项有一些启发。
+ 阅读最前几行,字符串</para>
+
+ <programlisting><filename>sys/conf/ldscript.i386:</filename>
+ENTRY(btext)</programlisting>
+
+ <para>表示内核的入口点是符号 `btext'。这个符号在<filename>locore.s</filename>
+ 中定义:</para>
+
+ <programlisting><filename>sys/i386/i386/locore.s:</filename>
+ .text
+/**********************************************************************
+ *
+ * This is where the bootblocks start us, set the ball rolling...
+ * 入口
+ */
+NON_GPROF_ENTRY(btext)</programlisting>
+
+ <para>首先将寄存器EFLAGS设为一个预定义的值0x00000002,
+ 然后初始化所有段寄存器:</para>
+
+ <programlisting><filename>sys/i386/i386/locore.s</filename>
+/* 不要相信BIOS给出的EFLAGS值 */
+ pushl $PSL_KERNEL
+ popfl
+
+/*
+ * 不要相信BIOS给出的%fs、%gs值。相信引导过程中设定的%cs、%ds、%es、%ss值
+ */
+ mov %ds, %ax
+ mov %ax, %fs
+ mov %ax, %gs</programlisting>
+
+ <para>btext调用例程<function>recover_bootinfo()</function>,
+ <function>identify_cpu()</function>,<function>create_pagetables()</function>。
+ 这些例程也定在<filename>locore.s</filename>之中。这些例程的功能如下:</para>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="2" align="left">
+ <tbody>
+ <row>
+ <entry><function>recover_bootinfo</function></entry>
+
+ <entry>这个例程分析由引导程序传送给内核的参数。引导内核有3种方式:
+ 由loader引导(如前所述), 由老式磁盘引导块引导,无盘引导方式。
+ 这个函数决定引导方式,并将结构<literal>struct bootinfo</literal>
+ 存储至内核内存。</entry>
+ </row>
+
+ <row>
+ <entry><function>identify_cpu</function></entry>
+
+ <entry>这个函数侦测CPU类型,将结果存放在变量
+ <varname>_cpu</varname>中。</entry>
+ </row>
+
+ <row>
+ <entry><function>create_pagetables</function></entry>
+
+ <entry>这个函数为分页表在内核内存空间顶部分配一块空间,并填写一定内容</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>下一步是开启VME(如果CPU有这个功能):</para>
+
+ <programlisting> testl $CPUID_VME, R(_cpu_feature)
+ jz 1f
+ movl %cr4, %eax
+ orl $CR4_VME, %eax
+ movl %eax, %cr4</programlisting>
+
+ <para>然后,启动分页模式:</para>
+ <programlisting>/* Now enable paging */
+ movl R(_IdlePTD), %eax
+ movl %eax,%cr3 /* load ptd addr into mmu */
+ movl %cr0,%eax /* get control word */
+ orl $CR0_PE|CR0_PG,%eax /* enable paging */
+ movl %eax,%cr0 /* and let's page NOW! */</programlisting>
+
+ <para>由于分页模式已经启动,原先的实地址寻址方式随即失效。
+ 随后三行代码用来跳转至虚拟地址:</para>
+
+ <programlisting> pushl $begin /* jump to high virtualized address */
+ ret
+
+/* 现在跳转至KERNBASE,那里是操作系统内核被链接后真正的入口 */
+begin:</programlisting>
+
+ <para>函数<function>init386()</function>被调用;随参数传递的是一个指针,
+ 指向第一个空闲物理页。随后执行<function>mi_startup()</function>。
+ <function>init386</function>是一个与硬件系统相关的初始化函数,
+ <function>mi_startup()</function>是个与硬件系统无关的函数
+ (前缀'mi_'表示Machine Independent,不依赖于机器)。
+ 内核不再从<function>mi_startup()</function>里返回;
+ 调用这个函数后,内核完成引导:</para>
+
+ <programlisting><filename>sys/i386/i386/locore.s:</filename>
+ movl physfree, %esi
+ pushl %esi /* 送给init386()的第一个参数 */
+ call _init386 /* 设置386芯片使之适应UNIX工作 */
+ call _mi_startup /* 自动配置硬件,挂接根文件系统,等 */
+ hlt /* 不再返回到这里! */</programlisting>
+
+ <sect2>
+ <title><function>init386()</function></title>
+
+ <para><function>init386()</function>定义在
+ <filename>sys/i386/i386/machdep.c</filename>中,
+ 它针对Intel 386芯片进行低级初始化。loader已将CPU切换至保护模式。
+ loader已经建立了最早的任务。<tip><title>译者注</title>
+ <para>每个"任务"都是与其它“任务”相对独立的执行环境。
+ 任务之间可以分时切换,这为并发进程/线程的实现提供了必要基础。
+ 对于Intel 80x86任务的描述,详见Intel公司关于80386 CPU及后续产品的资料,
+ 或者在<link xlink:href="http://www.lib.tsinghua.edu.cn/">清华大学图书馆</link>
+ 馆藏记录中用"80386"作为关键词所查找到的系统结构方面的书目。</para></tip>
+ 在这个任务中,内核将继续工作。在讨论其代码前,
+ 我将处理器对保护模式必须完成的一系列准备工作一并列出:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>初始化内核的可调整参数,这些参数由引导程序传来</para>
+ </listitem>
+
+ <listitem>
+ <para>准备GDT(全局描述符表)</para>
+ </listitem>
+
+ <listitem>
+ <para>准备IDT(中断描述符表)</para>
+ </listitem>
+
+ <listitem>
+ <para>初始化系统控制台</para>
+ </listitem>
+
+ <listitem>
+ <para>初始化DDB(内核的点调试器),如果它被编译进内核的话</para>
+ </listitem>
+
+ <listitem>
+ <para>初始化TSS(任务状态段)</para>
+ </listitem>
+
+ <listitem>
+ <para>准备LDT(局部描述符表)</para>
+ </listitem>
+
+ <listitem>
+ <para>建立proc0(0号进程,即内核的进程)的pcb(进程控制块)</para>
+ </listitem>
+ </itemizedlist>
+
+ <indexterm><primary>parameters(参数)</primary></indexterm>
+ <para><function>init386()</function>首先初始化内核的可调整参数,
+ 这些参数由引导程序传来。先设置环境指针(environment pointer, envp)调用,
+ 再调用<function>init_param1()</function>。
+ envp指针已由loader存放在结构<literal>bootinfo</literal>中:</para>
+
+ <programlisting><filename>sys/i386/i386/machdep.c:</filename>
+ kern_envp = (caddr_t)bootinfo.bi_envp + KERNBASE;
+
+ /* 初始化基本可调整项,如hz等 */
+ init_param1();</programlisting>
+
+ <para><function>init_param1()</function>定义在
+ <filename>sys/kern/subr_param.c</filename>之中。
+ 这个文件里有一些sysctl项,还有两个函数,
+ <function>init_param1()</function>和<function>init_param2()</function>。
+ 这两个函数从<function>init386()</function>中调用:</para>
+
+ <programlisting><filename>sys/kern/subr_param.c</filename>
+ hz = HZ;
+ TUNABLE_INT_FETCH("kern.hz", &amp;hz);</programlisting>
+
+ <para>TUNABLE_&lt;typename&gt;_FETCH用来获取环境变量的值:</para>
+
+ <programlisting><filename>/usr/src/sys/sys/kernel.h</filename>
+#define TUNABLE_INT_FETCH(path, var) getenv_int((path), (var))
+</programlisting>
+
+ <para>Sysctl<literal>kern.hz</literal>是系统时钟频率。同时,
+ 这些sysctl项被<function>init_param1()</function>设定:
+ <literal>kern.maxswzone,
+ kern.maxbcache, kern.maxtsiz, kern.dfldsiz, kern.maxdsiz, kern.dflssiz,
+ kern.maxssiz, kern.sgrowsiz</literal>。</para>
+
+ <indexterm><primary>Global Descriptors Table (GDT)(全局描述符表)</primary></indexterm>
+ <para>然后<function>init386()</function> 准备全局描述符表
+ (Global Descriptors Table, GDT)。在x86上每个任务都运行在自己的虚拟地址空间里,
+ 这个空间由"段址:偏移量"的数对指定。举个例子,当前将要由处理器执行的指令在
+ CS:EIP,那么这条指令的线性虚拟地址就是<quote>代码段虚拟段地址CS</quote> + EIP。
+ 为了简便,段起始于虚拟地址0,终止于界限4G字节。所以,在这个例子中,
+ 指令的线性虚拟地址正是EIP的值。段寄存器,如CS、DS等是选择符,
+ 即全局描述符表中的索引(更精确的说,索引并非选择符的全部,
+ 而是选择符中的INDEX部分)。<tip><title>译者注</title><para>对于80386,
+ 选择符有16位,INDEX部分是其中的高13位。</para></tip>
+ FreeBSD的全局描述符表为每个CPU保存着15个选择符:</para>
+
+ <programlisting><filename>sys/i386/i386/machdep.c:</filename>
+union descriptor gdt[NGDT * MAXCPU]; /* 全局描述符表 */
+
+<filename>sys/i386/include/segments.h:</filename>
+/*
+ * 全局描述符表(GDT)中的入口
+ */
+#define GNULL_SEL 0 /* 空描述符 */
+#define GCODE_SEL 1 /* 内核代码描述符 */
+#define GDATA_SEL 2 /* 内核数据描述符 */
+#define GPRIV_SEL 3 /* 对称多处理(SMP)每处理器专有数据 */
+#define GPROC0_SEL 4 /* Task state process slot zero and up, 任务状态进程 */
+#define GLDT_SEL 5 /* 每个进程的局部描述符表 */
+#define GUSERLDT_SEL 6 /* 用户自定义的局部描述符表 */
+#define GTGATE_SEL 7 /* 进程任务切换关口 */
+#define GBIOSLOWMEM_SEL 8 /* BIOS低端内存访问(必须是这第8个入口) */
+#define GPANIC_SEL 9 /* 会导致全系统异常中止工作的任务状态 */
+#define GBIOSCODE32_SEL 10 /* BIOS接口(32位代码) */
+#define GBIOSCODE16_SEL 11 /* BIOS接口(16位代码) */
+#define GBIOSDATA_SEL 12 /* BIOS接口(数据) */
+#define GBIOSUTIL_SEL 13 /* BIOS接口(工具) */
+#define GBIOSARGS_SEL 14 /* BIOS接口(自变量,参数) */</programlisting>
+
+ <para>请注意,这些#defines并非选择符本身,而只是选择符中的INDEX域,
+ 因此它们正是全局描述符表中的索引。
+ 例如,内核代码的选择符(GCODE_SEL)的值为0x08。</para>
+
+ <indexterm><primary>Interrupt Descriptor Table (IDT)(中断描述符表)</primary></indexterm>
+ <para>下一步是初始化中断描述符表(Interrupt Descriptor Table, IDT)。
+ 这张表在发生软件或硬件中断时会被处理器引用。例如,执行系统调用时,
+ 用户应用程序提交<literal>INT 0x80</literal> 指令。这是一个软件中断,
+ 处理器用索引值0x80在中断描述符表中查找记录。这个记录指向处理这个中断的例程。
+ 在这个特定情形中,这是内核的系统调用关口。<tip><title>译者注</title>
+ <para>Intel 80386支持“调用门”,可以使得用户程序只通过一条call指令
+ 就调用内核中的例程。可是FreeBSD并未采用这种机制,
+ 也许是因为使用软中断接口可免去动态链接的麻烦吧。另外还有一个附带的好处:
+ 在仿真Linux时,当遇到FreeBSD内核不支持的而又并非关键性的系统调用时,
+ 内核只会显示一些出错信息,这使得程序能够继续运行;
+ 而不是在真正执行程序之前的初始化过程中就因为动态链接失败而不允许程序运行。</para></tip>
+ 中断描述符表最多可以有256 (0x100)条记录。内核分配NIDT条记录的内存给中断描述符表,
+ 这里NIDT=256,是最大值:</para>
+
+ <programlisting><filename>sys/i386/i386/machdep.c:</filename>
+static struct gate_descriptor idt0[NIDT];
+struct gate_descriptor *idt = &amp;idt0[0]; /* 中断描述符表 */
+</programlisting>
+
+ <para>每个中断都被设置一个合适的中断处理程序。
+ 系统调用关口<literal>INT 0x80</literal>也是如此:</para>
+
+ <programlisting><filename>sys/i386/i386/machdep.c:</filename>
+ setidt(0x80, &amp;IDTVEC(int0x80_syscall),
+ SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL));</programlisting>
+
+ <para>所以当一个用户应用程序提交<literal>INT 0x80</literal>指令时,
+ 全系统的控制权会传递给函数<function>_Xint0x80_syscall</function>,
+ 这个函数在内核代码段中,将被以管理员权限执行。</para>
+
+ <para>然后,控制台和DDB(调试器)被初始化:</para>
+ <indexterm><primary>DDB</primary></indexterm>
+
+ <programlisting><filename>sys/i386/i386/machdep.c:</filename>
+ cninit();
+/* 以下代码可能因为未定义宏DDB而被跳过 */
+#ifdef DDB
+ kdb_init();
+ if (boothowto &amp; RB_KDB)
+ Debugger("Boot flags requested debugger");
+#endif</programlisting>
+
+ <para>任务状态段(TSS)是另一个x86保护模式中的数据结构。当发生任务切换时,
+ 任务状态段用来让硬件存储任务现场信息。</para>
+
+ <para>局部描述符表(LDT)用来指向用户代码和数据。系统定义了几个选择符,
+ 指向局部描述符表,它们是系统调用关口和用户代码、用户数据选择符:</para>
+
+ <programlisting><filename>/usr/include/machine/segments.h</filename>
+#define LSYS5CALLS_SEL 0 /* Intel BCS强制要求的 */
+#define LSYS5SIGR_SEL 1
+#define L43BSDCALLS_SEL 2 /* 尚无 */
+#define LUCODE_SEL 3
+#define LSOL26CALLS_SEL 4 /* Solaris &gt;=2.6版系统调用关口 */
+#define LUDATA_SEL 5
+/* separate stack, es,fs,gs sels ? 分别的栈、es、fs、gs选择符? */
+/* #define LPOSIXCALLS_SEL 5*/ /* notyet, 尚无 */
+#define LBSDICALLS_SEL 16 /* BSDI system call gate, BSDI系统调用关口 */
+#define NLDT (LBSDICALLS_SEL + 1)
+</programlisting>
+
+ <para>然后,proc0(0号进程,即内核所处的进程)的进程控制块(Process Control Block)
+ (<literal>struct pcb</literal>)结构被初始化。proc0是一个
+ <literal>struct proc</literal> 结构,描述了一个内核进程。
+ 内核运行时,该进程总是存在,所以这个结构在内核中被定义为全局变量:</para>
+
+ <programlisting><filename>sys/kern/kern_init.c:</filename>
+ struct proc proc0;</programlisting>
+
+ <para>结构<literal>struct pcb</literal>是proc结构的一部分,
+ 它定义在<filename>/usr/include/machine/pcb.h</filename>之中,
+ 内含针对i386硬件结构专有的信息,如寄存器的值。</para>
+ </sect2>
+
+ <sect2>
+ <title><function>mi_startup()</function></title>
+
+ <para>这个函数用冒泡排序算法,将所有系统初始化对象,然后逐个调用每个对象的入口:</para>
+
+ <programlisting><filename>sys/kern/init_main.c:</filename>
+ for (sipp = sysinit; *sipp; sipp++) {
+
+ /* ... 省略 ... */
+
+ /* 调用函数 */
+ (*((*sipp)-&gt;func))((*sipp)-&gt;udata);
+ /* ... 省略 ... */
+ }</programlisting>
+
+ <para>尽管sysinit框架已经在《FreeBSD开发者手册》中有所描述,
+ 我还是在这里讨论一下其内部原理。</para>
+
+ <indexterm><primary>sysinit对象</primary></indexterm>
+ <para>每个系统初始化对象(sysinit对象)通过调用宏建立。
+ 让我们以<literal>announce</literal> sysinit对象为例。
+ 这个对象打印版权信息:</para>
+
+ <programlisting><filename>sys/kern/init_main.c:</filename>
+static void
+print_caddr_t(void *data __unused)
+{
+ printf("%s", (char *)data);
+}
+SYSINIT(announce, SI_SUB_COPYRIGHT, SI_ORDER_FIRST, print_caddr_t, copyright)</programlisting>
+
+ <para>这个对象的子系统标识是SI_SUB_COPYRIGHT(0x0800001),
+ 数值刚好排在SI_SUB_CONSOLE(0x0800000)后面。
+ 所以,版权信息将在控制台初始化之后就被很早的打印出来。</para>
+
+ <para>让我们看一看宏<literal>SYSINIT()</literal>到底做了些什么。
+ 它展开成宏<literal>C_SYSINIT()</literal>。
+ 宏<literal>C_SYSINIT()</literal>然后展开成一个静态结构
+ <literal>struct sysinit</literal>。结构里申明里调用了另一个宏
+ <literal>DATA_SET</literal>:</para>
+ <programlisting><filename>/usr/include/sys/kernel.h:</filename>
+ #define C_SYSINIT(uniquifier, subsystem, order, func, ident) \
+ static struct sysinit uniquifier ## _sys_init = { \ subsystem, \
+ order, \ func, \ ident \ }; \ DATA_SET(sysinit_set,uniquifier ##
+ _sys_init);
+
+#define SYSINIT(uniquifier, subsystem, order, func, ident) \
+ C_SYSINIT(uniquifier, subsystem, order, \
+ (sysinit_cfunc_t)(sysinit_nfunc_t)func, (void *)ident)</programlisting>
+
+ <para>宏<literal>DATA_SET()</literal>展开成<literal>MAKE_SET()</literal>,
+ 宏<literal>MAKE_SET()</literal>指向所有隐含的sysinit幻数:</para>
+
+ <programlisting><filename>/usr/include/linker_set.h</filename>
+#define MAKE_SET(set, sym) \
+ static void const * const __set_##set##_sym_##sym = &amp;sym; \
+ __asm(".section .set." #set ",\"aw\""); \
+ __asm(".long " #sym); \
+ __asm(".previous")
+#endif
+#define TEXT_SET(set, sym) MAKE_SET(set, sym)
+#define DATA_SET(set, sym) MAKE_SET(set, sym)</programlisting>
+
+ <para>回到我们的例子中,经过宏的展开过程,将会产生如下声明:</para>
+
+ <programlisting>static struct sysinit announce_sys_init = {
+ SI_SUB_COPYRIGHT,
+ SI_ORDER_FIRST,
+ (sysinit_cfunc_t)(sysinit_nfunc_t) print_caddr_t,
+ (void *) copyright
+};
+
+static void const *const __set_sysinit_set_sym_announce_sys_init =
+ &amp;announce_sys_init;
+__asm(".section .set.sysinit_set" ",\"aw\"");
+__asm(".long " "announce_sys_init");
+__asm(".previous");</programlisting>
+
+ <para>第一个<literal>__asm</literal>指令在内核可执行文件中建立一个ELF节(section)。
+ 这发生在内核链接的时候。这一节将被命令为<literal>.set.sysinit_set</literal>。
+ 这一节的内容是一个32位值――announce_sys_init结构的地址,这个结构正是第二个
+ <literal>__asm</literal>指令所定义的。第三个<literal>__asm</literal>指令标记节的结束。
+ 如果前面有名字相同的节定义语句,节的内容(那个32位值)将被填加到已存在的节里,
+ 这样就构造出了一个32位指针数组。</para>
+
+ <para>用<application>objdump</application>察看一个内核二进制文件,
+ 也许你会注意到里面有这么几个小的节:</para>
+
+ <screen>&prompt.user; <userinput>objdump -h /kernel</userinput>
+ 7 .set.cons_set 00000014 c03164c0 c03164c0 002154c0 2**2
+ CONTENTS, ALLOC, LOAD, DATA
+ 8 .set.kbddriver_set 00000010 c03164d4 c03164d4 002154d4 2**2
+ CONTENTS, ALLOC, LOAD, DATA
+ 9 .set.scrndr_set 00000024 c03164e4 c03164e4 002154e4 2**2
+ CONTENTS, ALLOC, LOAD, DATA
+ 10 .set.scterm_set 0000000c c0316508 c0316508 00215508 2**2
+ CONTENTS, ALLOC, LOAD, DATA
+ 11 .set.sysctl_set 0000097c c0316514 c0316514 00215514 2**2
+ CONTENTS, ALLOC, LOAD, DATA
+ 12 .set.sysinit_set 00000664 c0316e90 c0316e90 00215e90 2**2
+ CONTENTS, ALLOC, LOAD, DATA</screen>
+
+ <para>这一屏信息显示表明节.set.sysinit_set有0x664字节的大小,
+ 所以<literal>0x664/sizeof(void *)</literal>个sysinit对象被编译进了内核。
+ 其它节,如<literal>.set.sysctl_set</literal>表示其它链接器集合。</para>
+
+ <para>通过定义一个类型为<literal>struct linker_set</literal>的变量,
+ 节<literal>.set.sysinit_set</literal>将被<quote>收集</quote>到那个变量里:</para>
+ <programlisting><filename>sys/kern/init_main.c:</filename>
+ extern struct linker_set sysinit_set; /* XXX */</programlisting>
+
+ <para><literal>struct linker_set</literal>定义如下:</para>
+
+ <programlisting><filename>/usr/include/linker_set.h:</filename>
+ struct linker_set {
+ int ls_length;
+ void *ls_items[1]; /* ls_length个项的数组, 以NULL结尾 */
+};</programlisting>
+
+ <para><tip><title>译者注</title><para>实际上是说,
+ 用C语言结构体linker_set来表达那个ELF节。</para></tip>
+ 第一项是sysinit对象的数量,第二项是一个以NULL结尾的数组,
+ 数组中是指向那些对象的指针。</para>
+
+ <para>回到对<function>mi_startup()</function>的讨论,
+ 我们清楚了sysinit对象是如何被组织起来的。
+ 函数<function>mi_startup()</function>将它们排序,
+ 并调用每一个对象。最后一个对象是系统调度器:</para>
+
+ <programlisting><filename>/usr/include/sys/kernel.h:</filename>
+enum sysinit_sub_id {
+ SI_SUB_DUMMY = 0x0000000, /* 不被执行,仅供链接器使用 */
+ SI_SUB_DONE = 0x0000001, /* 已被处理*/
+ SI_SUB_CONSOLE = 0x0800000, /* 控制台*/
+ SI_SUB_COPYRIGHT = 0x0800001, /* 最早使用控制台的对象 */
+...
+ SI_SUB_RUN_SCHEDULER = 0xfffffff /* 调度器:不返回 */
+};</programlisting>
+
+ <para>系统调度器sysinit对象定义在文件<filename>sys/vm/vm_glue.c</filename>中,
+ 这个对象的入口点是<function>scheduler()</function>。
+ 这个函数实际上是个无限循环,它表示那个进程标识(PID)为0的进程――swapper进程。
+ 前面提到的proc0结构正是用来描述这个进程。</para>
+
+ <para>第一个用户进程是<emphasis>init</emphasis>,
+ 由sysinit对象<literal>init</literal>建立:</para>
+
+ <programlisting><filename>sys/kern/init_main.c:</filename>
+static void
+create_init(const void *udata __unused)
+{
+ int error;
+ int s;
+
+ s = splhigh();
+ error = fork1(&amp;proc0, RFFDG | RFPROC, &amp;initproc);
+ if (error)
+ panic("cannot fork init: %d\n", error);
+ initproc-&gt;p_flag |= P_INMEM | P_SYSTEM;
+ cpu_set_fork_handler(initproc, start_init, NULL);
+ remrunqueue(initproc);
+ splx(s);
+}
+SYSINIT(init,SI_SUB_CREATE_INIT, SI_ORDER_FIRST, create_init, NULL)</programlisting>
+
+ <para><function>create_init()</function>通过调用<function>fork1()</function>
+ 分配一个新的进程,但并不将其标记为可运行。当这个新进程被调度器调度执行时,
+ <function>start_init()</function>将会被调用。
+ 那个函数定义在<filename>init_main.c</filename>中。
+ 它尝试装载并执行二进制代码<filename>init</filename>,
+ 先尝试<filename>/sbin/init</filename>,然后是<filename>/sbin/oinit</filename>,
+ <filename>/sbin/init.bak</filename>,最后是<filename>/stand/sysinstall</filename>:</para>
+
+ <programlisting><filename>sys/kern/init_main.c:</filename>
+static char init_path[MAXPATHLEN] =
+#ifdef INIT_PATH
+ __XSTRING(INIT_PATH);
+#else
+ "/sbin/init:/sbin/oinit:/sbin/init.bak:/stand/sysinstall";
+#endif</programlisting>
+
+ </sect2>
+</sect1>
+
+</chapter>
diff --git a/zh_CN.UTF-8/books/arch-handbook/chapters.ent b/zh_CN.UTF-8/books/arch-handbook/chapters.ent
new file mode 100644
index 0000000000..f914c86067
--- /dev/null
+++ b/zh_CN.UTF-8/books/arch-handbook/chapters.ent
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Creates entities for each chapter in the FreeBSD Architecture
+ Handbook. Each entity is named chap.foo, where foo is the value
+ of the id attribute on that chapter, and corresponds to the name of
+ the directory in which that chapter's .xml file is stored.
+
+ Chapters should be listed in the order in which they are referenced.
+
+ Original Revision: 1.21
+ $FreeBSD$
+-->
+
+<!-- Part one - Kernel -->
+<!ENTITY chap.boot SYSTEM "boot/chapter.xml">
+<!ENTITY chap.kobj SYSTEM "kobj/chapter.xml">
+<!ENTITY chap.sysinit SYSTEM "sysinit/chapter.xml">
+<!ENTITY chap.locking SYSTEM "locking/chapter.xml">
+<!ENTITY chap.vm SYSTEM "vm/chapter.xml">
+<!ENTITY chap.jail SYSTEM "jail/chapter.xml">
+<!ENTITY chap.mac SYSTEM "mac/chapter.xml">
+<!ENTITY chap.smp SYSTEM "smp/chapter.xml">
+
+<!-- Part Two - Device Drivers -->
+<!ENTITY chap.driverbasics SYSTEM "driverbasics/chapter.xml">
+<!ENTITY chap.isa SYSTEM "isa/chapter.xml">
+<!ENTITY chap.pci SYSTEM "pci/chapter.xml">
+<!ENTITY chap.scsi SYSTEM "scsi/chapter.xml">
+<!ENTITY chap.usb SYSTEM "usb/chapter.xml">
+<!ENTITY chap.newbus SYSTEM "newbus/chapter.xml">
+<!ENTITY chap.snd SYSTEM "sound/chapter.xml">
+<!ENTITY chap.pccard SYSTEM "pccard/chapter.xml">
+
+<!-- Part three - Appendices -->
+<!ENTITY chap.index "<index xmlns='http://docbook.org/ns/docbook'/>">
diff --git a/zh_CN.UTF-8/books/arch-handbook/driverbasics/chapter.xml b/zh_CN.UTF-8/books/arch-handbook/driverbasics/chapter.xml
new file mode 100644
index 0000000000..65bed70d1e
--- /dev/null
+++ b/zh_CN.UTF-8/books/arch-handbook/driverbasics/chapter.xml
@@ -0,0 +1,555 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ The FreeBSD Documentation Project
+ The FreeBSD Simplified Chinese Project
+
+ Original Revision: 1.37
+ $FreeBSD$
+-->
+<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" xml:id="driverbasics">
+ <info><title>编写 FreeBSD 设备驱动程序</title>
+ <authorgroup>
+ <author><personname><firstname>Murray</firstname><surname>Stokely</surname></personname><contrib>&cnproj.written.by;</contrib></author>
+ </authorgroup>
+ <authorgroup>
+ <author><personname><firstname>J&ouml;rg</firstname><surname>Wunsch</surname></personname><contrib>基础性手册intro(4):</contrib></author>
+ </authorgroup>
+ <authorgroup>
+ <author><personname/><contrib>&cnproj.translated.by;</contrib></author>
+ </authorgroup>
+ </info>
+
+
+ <sect1 xml:id="driverbasics-intro">
+ <title>简介</title>
+
+ <indexterm><primary>device driver(设备驱动程序)</primary></indexterm>
+ <indexterm><primary>pseudo-device(伪设备)</primary></indexterm>
+ <para>本章简要介绍了如何为FreeBSD编写设备驱动程序。术语设备在
+ 这儿的上下文中多用于指代系统中硬件相关的东西,如磁盘,打印机,
+ 图形显式器及其键盘。设备驱动程序是操作系统中用于控制特定设备的
+ 软件组件。也有所谓的伪设备,即设备驱动程序用软件模拟设备的行为,
+ 而没有特定的底层硬件。设备驱动程序可以被静态地编译进系统,或者
+ 通过动态内核链接工具‘kld’在需要时加载。</para>
+
+ <indexterm><primary>device nodes(设备节点)</primary></indexterm>
+ <indexterm><primary>MAKEDEV</primary></indexterm>
+
+ <para>类&unix;操作系统中的大多数设备都是通过设备节点来访问的,有时也
+ 被称为特殊文件。这些文件在文件系统的层次结构中通常位于
+ <filename>/dev</filename>目录下。在FreeBSD 5.0-RELEASE以前的
+ 发行版中, 对&man.devfs.5;的支持还没有被集成到FreeBSD中,每个设备
+ 节点必须要静态创建,并且独立于相关设备驱动程序的存在。系统中大
+ 多数设备节点是通过运行<command>MAKEDEV</command>创建的。</para>
+
+ <para>设备驱动程序可以粗略地分为两类,字符和网络设备驱动程序。</para>
+
+ </sect1>
+
+ <sect1 xml:id="driverbasics-kld">
+ <title>动态内核链接工具&mdash;KLD</title>
+
+ <indexterm><primary>kernel linking(内核链接)</primary><secondary>dynamic(动态)</secondary></indexterm>
+ <indexterm><primary>kernel loadable modules (KLD, 内核可装载模块)</primary></indexterm>
+ <para>kld接口允许系统管理员从运行的系统中动态地添加和删除功能。
+ 这允许设备驱动程序的编写者将他们的新改动加载到运行的内核中,
+ 而不用为了测试新改动而频繁地重启。</para>
+
+ <para>kld接口通过下面的特权命令使用:
+
+ <indexterm><primary>kernel modules(内核模块)</primary><secondary>loading(装载)</secondary></indexterm>
+ <indexterm><primary>kernel modules(内核模块)</primary><secondary>unloading(卸载)</secondary></indexterm>
+ <indexterm><primary>kernel modules(内核模块)</primary><secondary>listing(列清单)</secondary></indexterm>
+ <itemizedlist>
+ <listitem><simpara><command>kldload</command> - 加载新内核模块
+ </simpara></listitem>
+ <listitem><simpara><command>kldunload</command> - 卸载内核模块
+ </simpara></listitem>
+ <listitem><simpara><command>kldstat</command> - 列举当前加载的模块
+ </simpara></listitem>
+ </itemizedlist>
+ </para>
+
+ <para>内核模块的程序框架</para>
+
+<programlisting>/*
+ * KLD程序框架
+ * 受Andrew Reiter在Daemonnews上的文章所启发
+ */
+
+#include &lt;sys/types.h&gt;
+#include &lt;sys/module.h&gt;
+#include &lt;sys/systm.h&gt; /* uprintf */
+#include &lt;sys/errno.h&gt;
+#include &lt;sys/param.h&gt; /* kernel.h中用到的定义 */
+#include &lt;sys/kernel.h&gt; /* 模块初始化中使用的类型 */
+
+/*
+ * 加载处理函数,负责处理KLD的加载和卸载。
+ */
+
+static int
+skel_loader(struct module *m, int what, void *arg)
+{
+ int err = 0;
+
+ switch (what) {
+ case MOD_LOAD: /* kldload */
+ uprintf("Skeleton KLD loaded.\n");
+ break;
+ case MOD_UNLOAD:
+ uprintf("Skeleton KLD unloaded.\n");
+ break;
+ default:
+ err = EOPNOTSUPP;
+ break;
+ }
+ return(err);
+}
+
+/* 向内核其余部分声明此模块 */
+
+static moduledata_t skel_mod = {
+ "skel",
+ skel_loader,
+ NULL
+};
+
+DECLARE_MODULE(skeleton, skel_mod, SI_SUB_KLD, SI_ORDER_ANY);</programlisting>
+
+
+ <sect2>
+ <title>Makefile</title>
+
+ <para>FreeBSD提供了一个makefile包含文件,利用它你可以快速地编译
+ 你附加到内核的东西。</para>
+
+ <programlisting>SRCS=skeleton.c
+KMOD=skeleton
+
+.include &lt;bsd.kmod.mk&gt;</programlisting>
+
+ <para>简单地用这个makefile运行<command>make</command>就能够创建文件
+ <filename>skeleton.ko</filename>,键入如下命令可以把它加载到内核:
+<screen>&prompt.root; <userinput>kldload -v ./skeleton.ko</userinput></screen>
+ </para>
+ </sect2>
+ </sect1>
+
+ <sect1 xml:id="driverbasics-access">
+ <title>访问设备驱动程序</title>
+
+ <para>&unix; 提供了一套公共的系统调用供用户的应用程序使用。当用户访问
+ 设备节点时,内核的上层将这些调用分发到相应的设备驱动程序。脚本
+ <command>/dev/MAKEDEV</command>为你的系统生成了大多数的设备节点,
+ 但如果你正在开发你自己的驱动程序,可能需要用
+ <command>mknod</command>创建你自己的设备节点。
+ </para>
+
+ <sect2>
+ <title>创建静态设备节点</title>
+
+ <indexterm><primary>device nodes(设备节点)</primary><secondary>static(静态)</secondary></indexterm>
+ <indexterm><primary>mknod</primary></indexterm>
+
+ <para><command>mknod</command>命令需要四个参数来创建设备节点。
+ 你必须指定设备节点的名字,设备的类型,设备的主号码和设备的从号码。
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>动态设备节点</title>
+
+ <indexterm><primary>device nodes(设备节点)</primary><secondary>dynamic(动态)</secondary></indexterm>
+ <indexterm><primary>devfs</primary></indexterm>
+
+ <para>设备文件系统,或者说devfs,在全局文件系统名字空间中提供对
+ 内核设备名字空间的访问。这消除了由于有设备驱动程序而没有静态
+ 设备节点,或者有设备节点而没有安装设备驱动程序而带来的潜在问题。
+ Devfs仍在进展中,但已经能够工作得相当好了。</para>
+ </sect2>
+
+ </sect1>
+
+ <sect1 xml:id="driverbasics-char">
+ <title>字符设备</title>
+
+ <indexterm><primary>character devices(字符设备)</primary></indexterm>
+ <para>字符设备驱动程序直接从用户进程传输数据,或传输数据到用户进程。
+ 这是最普通的一类设备驱动程序,源码树中有大量的简单例子。</para>
+
+ <para>这个简单的伪设备例子会记住你写给它的任何值,并且当你读取它的时候
+ 会将这些值返回给你。下面显示了两个版本,一个适用于&os;&nbsp;4.X,
+ 一个适用于&os;&nbsp;5.X。</para>
+
+ <example>
+ <title>适用于&os;&nbsp;4.X的回显伪设备驱动程序实例</title>
+
+ <programlisting>/*
+ * 简单‘echo’伪设备KLD
+ *
+ * Murray Stokely
+ */
+
+#define MIN(a,b) (((a) &lt; (b)) ? (a) : (b))
+
+#include &lt;sys/types.h&gt;
+#include &lt;sys/module.h&gt;
+#include &lt;sys/systm.h&gt; /* uprintf */
+#include &lt;sys/errno.h&gt;
+#include &lt;sys/param.h&gt; /* kernel.h中用到的定义 */
+#include &lt;sys/kernel.h&gt; /* 模块初始化中使用的类型 */
+#include &lt;sys/conf.h&gt; /* cdevsw结构 */
+#include &lt;sys/uio.h&gt; /* uio结构 */
+#include &lt;sys/malloc.h&gt;
+
+#define BUFFERSIZE 256
+
+/* 函数原型 */
+d_open_t echo_open;
+d_close_t echo_close;
+d_read_t echo_read;
+d_write_t echo_write;
+
+/* 字符设备入口点 */
+static struct cdevsw echo_cdevsw = {
+ echo_open,
+ echo_close,
+ echo_read,
+ echo_write,
+ noioctl,
+ nopoll,
+ nommap,
+ nostrategy,
+ "echo",
+ 33, /* 为lkms保留 - /usr/src/sys/conf/majors */
+ nodump,
+ nopsize,
+ D_TTY,
+ -1
+};
+
+typedef struct s_echo {
+ char msg[BUFFERSIZE];
+ int len;
+} t_echo;
+
+/* 变量 */
+static dev_t sdev;
+static int count;
+static t_echo *echomsg;
+
+MALLOC_DECLARE(M_ECHOBUF);
+MALLOC_DEFINE(M_ECHOBUF, "echobuffer", "buffer for echo module");
+
+/*
+ * 这个函数被kld[un]load(2)系统调用来调用,
+ * 以决定加载和卸载模块时需要采取的动作。
+ */
+
+static int
+echo_loader(struct module *m, int what, void *arg)
+{
+ int err = 0;
+
+ switch (what) {
+ case MOD_LOAD: /* kldload */
+ sdev = make_dev(<literal>&amp;</literal>echo_cdevsw,
+ 0,
+ UID_ROOT,
+ GID_WHEEL,
+ 0600,
+ "echo");
+ /* kmalloc分配供驱动程序使用的内存 */
+ MALLOC(echomsg, t_echo *, sizeof(t_echo), M_ECHOBUF, M_WAITOK);
+ printf("Echo device loaded.\n");
+ break;
+ case MOD_UNLOAD:
+ destroy_dev(sdev);
+ FREE(echomsg,M_ECHOBUF);
+ printf("Echo device unloaded.\n");
+ break;
+ default:
+ err = EOPNOTSUPP;
+ break;
+ }
+ return(err);
+}
+
+int
+echo_open(dev_t dev, int oflags, int devtype, struct proc *p)
+{
+ int err = 0;
+
+ uprintf("Opened device \"echo\" successfully.\n");
+ return(err);
+}
+
+int
+echo_close(dev_t dev, int fflag, int devtype, struct proc *p)
+{
+ uprintf("Closing device \"echo.\"\n");
+ return(0);
+}
+
+/*
+ * read函数接受由echo_write()存储的buf,并将其返回到用户空间,
+ * 以供其他函数访问。
+ * uio(9)
+ */
+
+int
+echo_read(dev_t dev, struct uio *uio, int ioflag)
+{
+ int err = 0;
+ int amt;
+
+ /*
+ * 这个读操作有多大?
+ * 与用户请求的大小一样,或者等于剩余数据的大小。
+ */
+ amt = MIN(uio-&gt;uio_resid, (echomsg-&gt;len - uio-&gt;uio_offset &gt; 0) ?
+ echomsg-&gt;len - uio-&gt;uio_offset : 0);
+ if ((err = uiomove(echomsg-&gt;msg + uio-&gt;uio_offset,amt,uio)) != 0) {
+ uprintf("uiomove failed!\n");
+ }
+ return(err);
+}
+
+/*
+ * echo_write接受一个字符串并将它保存到缓冲区,用于以后的访问。
+ */
+
+int
+echo_write(dev_t dev, struct uio *uio, int ioflag)
+{
+ int err = 0;
+
+ /* 将字符串从用户空间的内存复制到内核空间 */
+ err = copyin(uio-&gt;uio_iov-&gt;iov_base, echomsg-&gt;msg,
+ MIN(uio-&gt;uio_iov-&gt;iov_len, BUFFERSIZE - 1));
+
+ /* 现在需要以null结束字符串,并记录长度 */
+ *(echomsg-&gt;msg + MIN(uio-&gt;uio_iov-&gt;iov_len, BUFFERSIZE - 1)) = 0;
+ echomsg-&gt;len = MIN(uio-&gt;uio_iov-&gt;iov_len, BUFFERSIZE);
+
+ if (err != 0) {
+ uprintf("Write failed: bad address!\n");
+ }
+ count++;
+ return(err);
+}
+
+DEV_MODULE(echo,echo_loader,NULL);</programlisting>
+ </example>
+
+ <example>
+ <title>适用于&os;&nbsp;5.X回显伪设备驱动程序实例</title>
+
+ <programlisting>/*
+ * 简单‘echo’伪设备 KLD
+ *
+ * Murray Stokely
+ *
+ * 此代码由S&oslash;ren (Xride) Straarup转换到5.X
+ */
+
+#include &lt;sys/types.h&gt;
+#include &lt;sys/module.h&gt;
+#include &lt;sys/systm.h&gt; /* uprintf */
+#include &lt;sys/errno.h&gt;
+#include &lt;sys/param.h&gt; /* kernel.h中用到的定义 */
+#include &lt;sys/kernel.h&gt; /* 模块初始化中使用的类型 */
+#include &lt;sys/conf.h&gt; /* cdevsw结构 */
+#include &lt;sys/uio.h&gt; /* uio结构 */
+#include &lt;sys/malloc.h&gt;
+
+#define BUFFERSIZE 256
+
+
+/* 函数原型 */
+static d_open_t echo_open;
+static d_close_t echo_close;
+static d_read_t echo_read;
+static d_write_t echo_write;
+
+/* 字符设备入口点 */
+static struct cdevsw echo_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = echo_open,
+ .d_close = echo_close,
+ .d_read = echo_read,
+ .d_write = echo_write,
+ .d_name = "echo",
+};
+
+typedef struct s_echo {
+ char msg[BUFFERSIZE];
+ int len;
+} t_echo;
+
+/* 变量 */
+static struct cdev *echo_dev;
+static int count;
+static t_echo *echomsg;
+
+MALLOC_DECLARE(M_ECHOBUF);
+MALLOC_DEFINE(M_ECHOBUF, "echobuffer", "buffer for echo module");
+
+/*
+ * 这个函数被kld[un]load(2)系统调用来调用,
+ * 以决定加载和卸载模块时需要采取的动作.
+ */
+
+static int
+echo_loader(struct module *m, int what, void *arg)
+{
+ int err = 0;
+
+ switch (what) {
+ case MOD_LOAD: /* kldload */
+ echo_dev = make_dev(<literal>&amp;</literal>echo_cdevsw,
+ 0,
+ UID_ROOT,
+ GID_WHEEL,
+ 0600,
+ "echo");
+ /* kmalloc分配供驱动程序使用的内存 */
+ echomsg = malloc(sizeof(t_echo), M_ECHOBUF, M_WAITOK);
+ printf("Echo device loaded.\n");
+ break;
+ case MOD_UNLOAD:
+ destroy_dev(echo_dev);
+ free(echomsg, M_ECHOBUF);
+ printf("Echo device unloaded.\n");
+ break;
+ default:
+ err = EOPNOTSUPP;
+ break;
+ }
+ return(err);
+}
+
+static int
+echo_open(struct cdev *dev, int oflags, int devtype, struct thread *p)
+{
+ int err = 0;
+
+ uprintf("Opened device \"echo\" successfully.\n");
+ return(err);
+}
+
+static int
+echo_close(struct cdev *dev, int fflag, int devtype, struct thread *p)
+{
+ uprintf("Closing device \"echo.\"\n");
+ return(0);
+}
+
+/*
+ * read函数接受由echo_write()存储的buf,并将其返回到用户空间,
+ * 以供其他函数访问。
+ * uio(9)
+ */
+
+static int
+echo_read(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ int err = 0;
+ int amt;
+
+ /*
+ * 这个读操作有多大?
+ * 等于用户请求的大小,或者等于剩余数据的大小。
+ */
+ amt = MIN(uio-&gt;uio_resid, (echomsg-&gt;len - uio-&gt;uio_offset &gt; 0) ?
+ echomsg-&gt;len - uio-&gt;uio_offset : 0);
+ if ((err = uiomove(echomsg-&gt;msg + uio-&gt;uio_offset, amt, uio)) != 0) {
+ uprintf("uiomove failed!\n");
+ }
+ return(err);
+}
+
+/*
+ * echo_write接受一个字符串并将它保存到缓冲区, 用于以后的访问.
+ */
+
+static int
+echo_write(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ int err = 0;
+
+ /* 将字符串从用户空间的内存复制到内核空间 */
+ err = copyin(uio-&gt;uio_iov-&gt;iov_base, echomsg-&gt;msg,
+ MIN(uio-&gt;uio_iov-&gt;iov_len, BUFFERSIZE - 1));
+
+ /* 现在需要以null结束字符串,并记录长度 */
+ *(echomsg-&gt;msg + MIN(uio-&gt;uio_iov-&gt;iov_len, BUFFERSIZE - 1)) = 0;
+ echomsg-&gt;len = MIN(uio-&gt;uio_iov-&gt;iov_len, BUFFERSIZE);
+
+ if (err != 0) {
+ uprintf("Write failed: bad address!\n");
+ }
+ count++;
+ return(err);
+}
+
+DEV_MODULE(echo,echo_loader,NULL);</programlisting>
+ </example>
+
+ <para>在&os;&nbsp;4.X上安装此驱动程序,你将首先需要用如下命令在
+ 你的文件系统上创建一个节点:</para>
+
+ <screen>&prompt.root; <userinput>mknod /dev/echo c 33 0</userinput></screen>
+
+ <para>驱动程序被加载后,你应该能够键入一些东西,如:</para>
+
+ <screen>&prompt.root; <userinput>echo -n "Test Data" &gt; /dev/echo</userinput>
+&prompt.root; <userinput>cat /dev/echo</userinput>
+Test Data</screen>
+
+ <para>真正的硬件设备在下一章描述。</para>
+
+ <para>补充资源
+ <itemizedlist>
+ <listitem><simpara><link xlink:href="http://ezine.daemonnews.org/200010/blueprints.html">Dynamic
+ Kernel Linker (KLD) Facility Programming Tutorial</link> -
+ <link xlink:href="http://www.daemonnews.org/">Daemonnews</link> October 2000</simpara></listitem>
+ <listitem><simpara><link xlink:href="http://ezine.daemonnews.org/200007/newbus-intro.html">How
+ to Write Kernel Drivers with NEWBUS</link> - <link xlink:href="http://www.daemonnews.org/">Daemonnews</link> July
+ 2000</simpara></listitem>
+ </itemizedlist>
+ </para>
+ </sect1>
+
+ <sect1 xml:id="driverbasics-block">
+ <title>块设备(消亡中)</title>
+
+ <indexterm><primary>block devices(块设备)</primary></indexterm>
+ <para>其他&unix;系统支持另一类型的磁盘设备,称为块设备。块设备是内核
+ 为它们提供缓冲的磁盘设备。这种缓冲使得块设备几乎没有用,或者说非常
+ 不可靠。缓冲会重新安排写操作的次序,使得应用程序丧失了在任何时刻及时
+ 知道准确的磁盘内容的能力。这导致对磁盘数据结构(文件系统,数据库等)的
+ 可预测的和可靠的崩溃恢复成为不可能。由于写操作被延迟,内核无法向应用
+ 程序报告哪个特定的写操作遇到了写错误,这又进一步增加了一致性问题。
+ 由于这个原因,真正的应用程序从不依赖于块设备,事实上,几乎所有访问
+ 磁盘的应用程序都尽力指定总是使用字符(或<quote>raw</quote>)设备。
+ 由于实现将每个磁盘(分区)同具有不同语义的两个设备混为一谈,从而致使
+ 相关内核代码极大地复杂化,作为推进磁盘I/O基础结构现代化的一部分,&os;
+ 抛弃了对带缓冲的磁盘设备的支持。</para>
+ </sect1>
+
+ <sect1 xml:id="driverbasics-net">
+ <title>网络设备驱动程序</title>
+
+ <indexterm><primary>network devices(网络设备)</primary></indexterm>
+ <para>访问网络设备的驱动程序不需要使用设备节点。选择哪个驱动程序是
+ 基于内核内部的其他决定而不是调用open(),对网络设备的使用通常由
+ 系统调用socket(2)引入。</para>
+
+ <para>更多细节, 请参见 ifnet(9) 联机手册、 回环设备的源代码,
+ 以及 Bill Paul 撰写的网络驱动程序。</para>
+
+ </sect1>
+
+</chapter>
diff --git a/zh_CN.UTF-8/books/arch-handbook/isa/chapter.xml b/zh_CN.UTF-8/books/arch-handbook/isa/chapter.xml
new file mode 100644
index 0000000000..04f91b4bcb
--- /dev/null
+++ b/zh_CN.UTF-8/books/arch-handbook/isa/chapter.xml
@@ -0,0 +1,2146 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ The FreeBSD Documentation Project
+
+ Original Revision: 1.20
+ $FreeBSD$
+-->
+<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" xml:id="isa-driver">
+ <info><title>ISA设备驱动程序</title>
+ <authorgroup>
+ <author><personname><firstname>Sergey</firstname><surname>Babkin</surname></personname><contrib>&cnproj.written.by;</contrib></author>
+ </authorgroup>
+ <authorgroup>
+ <author><personname><firstname>Murray</firstname><surname>Stokely</surname></personname><contrib>&cnproj.modified.for.handbook.by;</contrib></author>
+ <author><personname><firstname>Valentino</firstname><surname>Vaschetto</surname></personname></author>
+ <author><personname><firstname>Wylie</firstname><surname>Stilwell</surname></personname></author>
+ </authorgroup>
+ <authorgroup>
+ <author><personname/><contrib>&cnproj.translated.by;</contrib></author>
+ </authorgroup>
+ </info>
+
+
+
+ <sect1 xml:id="isa-driver-synopsis">
+ <title>概述</title>
+
+ <indexterm><primary>ISA(Industry Standard Architecture, 工业标准结构)</primary></indexterm>
+ <indexterm><primary>device driver(设备驱动程序)</primary><secondary>ISA(Industry Standard Architecture, 工业标准结构)</secondary></indexterm>
+
+ <para>本章介绍了编写ISA设备驱动程序相关的一些问题。这儿展示的伪代码
+ 相当详细,很容易让人联想到真正的代码,不过这依然仅仅是伪代码。
+ 它避免了与所讨论的主题无关的细节。真实的例子可以在实际驱动程序的
+ 源代码中找到。<literal>ep</literal>和<literal>aha</literal>
+ 更是信息的好来源。</para>
+ </sect1>
+
+ <sect1 xml:id="isa-driver-basics">
+ <title>基本信息</title>
+
+ <para>典型的ISA驱动程序需要以下包含文件:</para>
+
+<programlisting>#include &lt;sys/module.h&gt;
+#include &lt;sys/bus.h&gt;
+#include &lt;machine/bus.h&gt;
+#include &lt;machine/resource.h&gt;
+#include &lt;sys/rman.h&gt;
+
+#include &lt;isa/isavar.h&gt;
+#include &lt;isa/pnpvar.h&gt;</programlisting>
+
+ <para>它们描述了针对ISA和通用总线子系统的东西。</para>
+
+ <indexterm><primary>object-oriented(面向对象)</primary></indexterm>
+ <para>总线子系统是以面向对象的方式实现的,其主要结构通过相关联的
+ 方法函数来访问。</para>
+
+ <indexterm><primary>bus methods(总线方法)</primary></indexterm>
+ <para>ISA驱动程序实现的总线方法的列表与任何其他总线的很相似。
+ 对于名字为“xxx”的假想驱动程序,它们将是:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><function>static void xxx_isa_identify (driver_t *,
+ device_t);</function> 通常用于总线驱动程序而不是设备驱动程序。
+ 但对于ISA设备,这个方法有特殊用途:如果设备提供某些设备特定的
+ (非PnP)方法自动侦测设备,这个例程可以实现它。</para>
+ </listitem>
+
+ <listitem>
+ <para><function>static int xxx_isa_probe (device_t
+ dev);</function> 在已知(或PnP)位置探测设备。对于已经部分配置的
+ 设备,这个例程也能够提供设备特定的对某些参数的自动侦测。</para>
+ </listitem>
+
+ <listitem>
+ <para><function>static int xxx_isa_attach (device_t
+ dev);</function> 挂接和初始化设备。</para>
+ </listitem>
+
+ <listitem>
+ <para><function>static int xxx_isa_detach (device_t
+ dev);</function> 卸载设备驱动模块前解挂设备。</para>
+ </listitem>
+
+ <listitem>
+ <para><function>static int xxx_isa_shutdown (device_t
+ dev);</function> 系统关闭前执行设备的关闭。</para>
+ </listitem>
+
+ <listitem>
+ <para><function>static int xxx_isa_suspend (device_t
+ dev);</function> 系统进入节能状态前挂起设备。也可以中止
+ 切换到节能状态。</para>
+ </listitem>
+
+ <listitem>
+ <para><function>static int xxx_isa_resume (device_t
+ dev);</function> 从节能状态返回后恢复设备的活动状态。</para>
+ </listitem>
+
+ </itemizedlist>
+
+ <para><function>xxx_isa_probe()</function>和
+ <function>xxx_isa_attach()</function>是必须提供的,其余例程根据设备的
+ 需要可以有选择地实现。</para>
+
+ <para>使用下面一组描述符将设备驱动链接到系统。</para>
+
+<programlisting> /* 支持的总线方法表 */
+ static device_method_t xxx_isa_methods[] = {
+ /* 列出驱动程序支持的所有总线方法函数 */
+ /* 略去不支持的函数 */
+ DEVMETHOD(device_identify, xxx_isa_identify),
+ DEVMETHOD(device_probe, xxx_isa_probe),
+ DEVMETHOD(device_attach, xxx_isa_attach),
+ DEVMETHOD(device_detach, xxx_isa_detach),
+ DEVMETHOD(device_shutdown, xxx_isa_shutdown),
+ DEVMETHOD(device_suspend, xxx_isa_suspend),
+ DEVMETHOD(device_resume, xxx_isa_resume),
+
+ { 0, 0 }
+ };
+
+ static driver_t xxx_isa_driver = {
+ "xxx",
+ xxx_isa_methods,
+ sizeof(struct xxx_softc),
+ };
+
+
+ static devclass_t xxx_devclass;
+
+ DRIVER_MODULE(xxx, isa, xxx_isa_driver, xxx_devclass,
+ load_function, load_argument);</programlisting>
+
+ <indexterm><primary>softc(硬件设备的软件描述符,相关信息)</primary></indexterm>
+
+ <para>此处的结构<varname remap="structname">xxx_softc</varname>是一个设备
+ 特定的结构,它包含私有的驱动程序数据和驱动程序资源的描述符。
+ 总线代码会自动按需要为每个设备分配一个softc描述符。</para>
+
+ <indexterm><primary>kernel modules(内核模块)</primary></indexterm>
+
+ <para>如果驱动程序作为可加载模块实现,当驱动程序被加载或卸载时,
+ 会调用<function>load_function()</function>函数进行驱动程序特定的
+ 初始化或清理工作,并将load_argument作为函数的一个参量传递进去。
+ 如果驱动程序不支持动态加载(换句话说,它必须被链接到内核中),则
+ 这些值应当被设置为0,最后的定义将看起来如下所示:</para>
+
+ <programlisting> DRIVER_MODULE(xxx, isa, xxx_isa_driver,
+ xxx_devclass, 0, 0);</programlisting>
+
+ <indexterm><primary>PnP(Plug and Play, 即插即用)</primary></indexterm>
+
+ <para>如果驱动程序是为支持PnP的设备而写的,那么就必须定义一个包含
+ 所有支持的PnP ID的表。这个表由此驱动程序所支持的PnP ID的列表
+ 和以人可读的形式给出的、与这些ID对应的硬件类型和型号的描述
+ 组成。看起来如下:</para>
+
+<programlisting> static struct isa_pnp_id xxx_pnp_ids[] = {
+ /* 每个所支持的PnP ID占一行 */
+ { 0x12345678, "Our device model 1234A" },
+ { 0x12345679, "Our device model 1234B" },
+ { 0, NULL }, /* 表结束 */
+ };</programlisting>
+
+ <para>如果驱动程序不支持PnP设备,它仍然需要一个空的PnP ID表,
+ 如下所示:</para>
+
+<programlisting> static struct isa_pnp_id xxx_pnp_ids[] = {
+ { 0, NULL }, /* 表结束 */
+ };</programlisting>
+
+ </sect1>
+
+ <sect1 xml:id="isa-driver-device-t">
+ <title>Device_t指针</title>
+
+ <para><varname remap="structname">Device_t</varname>是为设备结构而定义的指针类型,
+ 这里我们只关心从设备驱动程序编写者的角度看感兴趣的方法。下面的方法
+ 用来操纵设备结构中的值:</para>
+
+ <itemizedlist>
+
+ <listitem><para><function>device_t
+ device_get_parent(dev)</function> 获取设备的父总线。
+ </para></listitem>
+
+ <listitem><para><function>driver_t
+ device_get_driver(dev)</function> 获取指向其驱动程序结构的指针。
+ </para></listitem>
+
+ <listitem><para><function>char
+ *device_get_name(dev)</function> 获取驱动程序的名字,在我们的
+ 例子中为<literal>"xxx"</literal>。</para></listitem>
+
+ <listitem><para><function>int device_get_unit(dev)</function>
+ 获取单元号(与每个驱动程序关联的设备从0开始编号)。
+ </para></listitem>
+
+ <listitem><para><function>char
+ *device_get_nameunit(dev)</function> 获取设备名,包括单元号。
+ 例如<quote>xxx0</quote>,<quote>xxx1</quote>
+ 等。</para></listitem>
+
+ <listitem><para><function>char
+ *device_get_desc(dev)</function> 获取设备描述。通常它以人可读的
+ 形式描述设备的确切型号。</para></listitem>
+
+ <listitem><para><function>device_set_desc(dev,
+ desc)</function>设置描述信息。这使得设备描述指向desc字符串,
+ 此后这个字符串就不能被解除分配。</para></listitem>
+
+ <listitem><para><function>device_set_desc_copy(dev,
+ desc)</function> 设置描述信息。描述被拷贝到内部动态分配的
+ 缓冲区,这样desc字符串在以后可以被改变而不会产生有害的结果。
+ </para></listitem>
+
+ <listitem><para><function>void
+ *device_get_softc(dev)</function> 获取指向与设备关联的设备描述符
+ (<varname remap="structname">xxx_softc</varname>结构)的指针。
+ </para></listitem>
+
+ <listitem><para><function>u_int32_t
+ device_get_flags(dev)</function> 获取配置文件中特定于设备的
+ 标志。</para></listitem>
+
+ </itemizedlist>
+
+ <para>可以使用一个很方便的函数<function>device_printf(dev, fmt,
+ ...)</function>从设备驱动程序中打印讯息。它自动在讯息前添加
+ 单元名和冒号。</para>
+
+ <para>device_t的这些方法在文件<filename>kern/bus_subr.c</filename>
+ 中实现。</para>
+
+ </sect1>
+
+ <sect1 xml:id="isa-driver-config">
+ <title>配置文件与自动配置期间识别和探测的顺序</title>
+
+ <indexterm><primary>ISA(Industry Standard Architecture, 工业标准结构)</primary><secondary>probing(探测)</secondary></indexterm>
+
+ <para>ISA设备在内核配置文件中的描述如下:</para>
+
+ <programlisting>device xxx0 at isa? port 0x300 irq 10 drq 5
+ iomem 0xd0000 flags 0x1 sensitive</programlisting>
+
+ <indexterm><primary>IRQ(中断请求)</primary></indexterm>
+
+ <para>端口值、IRQ值和其他值被转换成与设备关联的资源值。根据设备
+ 对自动配置需要和支持程度的不同,这些值是可选的。例如,
+ 某些设备根本不需要读DRQ,而有些则允许设备从设备配置端口读取
+ IRQ设置。如果机器有多个ISA总线,可以在配置文件中明确指定哪条
+ 总线,如<literal>isa0</literal>或<literal>isa1</literal>,
+ 否则将在所有ISA总线上搜索设备。</para>
+
+ <para><literal>敏感(sensitive)</literal>是一种资源请求,它指示
+ 必须在所有非敏感设备之前探测设备。此特性虽被支持,但似乎从未
+ 在目前的任何驱动程序中使用过。</para>
+
+ <para>对于老的ISA设备,很多情况下驱动程序仍然能够侦测配置参数。
+ 但是系统中配置的每个设备必须具有一个配置行。如果系统中装有同一
+ 类型的两个设备,但对应的驱动程序却只有一个配置行,例如:
+ <programlisting>device xxx0 at isa?</programlisting>
+ 那么只有一个设备会被配置。</para>
+
+ <para>但对于支持通过PnP或专有协议进行自动识别的设备,一个配置行
+ 就足够配置系统中的所有设备,如上面的配置行,或者简单地:</para>
+
+ <programlisting>device xxx at isa?</programlisting>
+
+ <para>如果设备驱动程序既支持能自动识别的设备又支持老设备,并且
+ 两类设备同时安装在一台机器上,那么只要在配置文件中描述老设备
+ 就足够了。自动识别的设备将被自动添加。</para>
+
+ <para>如果ISA设备是自动配置的,发生的事件如下:</para>
+
+ <para>所有设备驱动程序的识别例程(包括识别所有PnP设备的PnP识别
+ 例程)以随机顺序被调用。他们识别出设备后就把设备添加到ISA总线
+ 上的列表中。通常驱动程序的识别例程将新设备与它们的驱动
+ 程序关联起来。而PnP识别例程并不知道其他驱动程序,因此不能将
+ 驱动程序与它所添加的新设备关联起来。</para>
+
+ <para>使用PnP协议让PnP设备进入睡眠,以防止它们被探测为老设备。</para>
+
+ <para>被标记为<literal>敏感(sensitive)</literal>的非PnP设备的
+ 探测例程被调用。如果探测设备成功,那么就为其调用挂接(attach)
+ 例程。</para>
+
+ <para>所有非PnP设备的探测和连接例程以同样的方式被调用。</para>
+
+ <para>PnP设备从睡眠中恢复过来,并给它们分配所请求的资源:I/O、
+ 内存地址范围、IRQ和DRQ,所有这些与已连接的老设备不会冲突。</para>
+
+ <para>对于每个PnP设备,所有ISA设备驱动程序的探测例程都会被调用。
+ 第一个要求此设备的驱动程序将被连接。多个驱动程序以不同的优先权
+ 要求一个设备的情况是可能的,这种情况下,具有最高优先权的驱动程序
+ 将获胜。探测例程必须调用<function>ISA_PNP_PROBE()</function>将
+ 真实的PnP ID和驱动程序支持的ID列表作比较,如果ID不在表中则返回
+ 失败。这意味着每个驱动程序,包括不支持任何PnP设备的驱动程序,
+ 都必须对未知的PnP设备无条件调用
+ <function>ISA_PNP_PROBE()</function>,对于未知设备, 至少要用一个
+ 空的PnP ID表调用并返回失败。</para>
+
+ <para>探测例程遇到错误时会返回一个正值(错误码),成功时返回
+ 零或负值。</para>
+
+ <para>负的返回值用于PnP设备支持多个接口的情况。例如,老的兼容接口
+ 和新的高级接口通过不同的驱动程序来提供支持。两个驱动程序
+ 都侦测设备。在探测例程中返回较高值的驱动程序优先(换句话说,
+ 返回0的驱动程序具有最高的优先级,返回-1的其次,返回-2的更在
+ 其后,如此下去)。如果多个驱动程序返回相同的值,那么最先调用的
+ 获胜。因此,如果驱动程序返回0,就基本能够确信它获得优先权仲裁。
+ </para>
+
+ <para>设备特定的识别例程也能够将一类而不是单个驱动程序指派给设备。
+ 就象使用PnP的情况一样,对于某一设备,会探测这一类中所有的驱动程序。
+ 由于这个特性在任何现存的驱动程序中总均未实现,故本文档中不再予以
+ 考虑。</para>
+
+ <para>由于探测老设备的时候PnP设备被禁用,它们不会被连接两次
+ (一次作为老设备,一次作为PnP)。但如果识别例程设备相关的,
+ 这种情况下设备驱动程序有责任确保同一设备不会被设备驱动程序
+ 连接两次:一次作为老的由用户配置的,一次作为自动识别的。</para>
+
+ <para>对于自动识别的设备(包括PnP和设备特定的)的另一个实践结论是,
+ 不能从内核配置文件中向它们传递旗标。因此它们必须要么根本不使用
+ 旗标,要么为所有自动识别的设备使用单元号为0的设备的旗标,或者
+ 使用sysctl接口而不是旗标。</para>
+
+ <para>通过使用函数族<function>resource_query_*()</function>和
+ <function>resource_*_value()</function>直接访问配置资源,
+ 从而可以提供其他不常用的配置。它们的实现位于
+ <filename>kern/subr_bus.c</filename>。老的IDE磁盘驱动器
+ <filename>i386/isa/wd.c</filename>包含这样使用的例子。
+ 但必须优先使用配置的标准方法。将解析配置资源这类事情留给
+ 总线配置代码。</para>
+
+ </sect1>
+
+ <sect1 xml:id="isa-driver-resources">
+ <title>资源</title>
+
+ <indexterm><primary>resources(资源)</primary></indexterm>
+ <indexterm><primary>device driver(设备驱动程序)</primary><secondary>resources(资源)</secondary></indexterm>
+
+ <para>用户写入到内核配置文件中的信息被作为配置资源处理,并传递到内核。
+ 总线配置代码解析这部分信息并将其转换为结构device_t的值和与之
+ 关联的总线资源。对于复杂情况下的配置,驱动程序可以直接使用
+ <function>resource_*</function> 函数访问配置资源。 然而,
+ 通常既不需要也不推荐这样做,
+ 因此这儿不再进一步讨论这个问题。</para>
+
+ <para>总线资源与每个设备相关联。通过类型和类型中的数字标识它们。
+ 对于ISA总线,定义了下面的类型:</para>
+
+ <indexterm><primary>DMA channel(DMA通道)</primary></indexterm>
+
+ <itemizedlist>
+ <listitem>
+ <para><emphasis>SYS_RES_IRQ</emphasis> - 中断号</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>SYS_RES_DRQ</emphasis> - ISA DMA通道号</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>SYS_RES_MEMORY</emphasis> - 映射到系统内存空间
+ 的设备内存的范围
+ </para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>SYS_RES_IOPORT</emphasis> - 设备I/O寄存器的范围
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>类型内的枚举从0开始,因此如果设备有两个内存区域,它的
+ <literal>SYS_RES_MEMORY</literal> 类型的资源编号为0和1。
+ 资源类型与C语言的类型无关,
+ 所有资源值具有C语言 <literal>unsigned long</literal>
+ 类型,并且必要时必须进行类型强制转换 (cast)。资源号不必连续,
+ 尽管对于ISA它们一般是连续的。ISA设备允许的资源编号为:</para>
+
+ <programlisting> IRQ: 0-1
+ DRQ: 0-1
+ MEMORY: 0-3
+ IOPORT: 0-7</programlisting>
+
+ <para>所有资源被表示为带有起始值和计数的范围。对于IRQ和DRQ资源,
+ 计数一般等于1。内存的值引用物理地址。</para>
+
+ <para>对资源能够执行三种类型的动作:</para>
+
+ <itemizedlist>
+ <listitem><para>set/get</para></listitem>
+ <listitem><para>allocate/release</para></listitem>
+ <listitem><para>activate/deactivate</para></listitem>
+ </itemizedlist>
+
+ <para>Set设置资源使用的范围。Allocation保留出请求的范围,使得
+ 其它设备不能再占用(并检查此范围没有被其它设备占用)。
+ Activation执行必要的动作使得驱动程序可以访问资源(例如,对于
+ 内存,它将被映射到内核的虚拟地址空间)。</para>
+
+ <para>操作资源的函数有:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><function>int bus_set_resource(device_t dev, int type,
+ int rid, u_long start, u_long count)</function></para>
+
+ <para>为资源设置范围。成功则返回0,否则返回错误码。
+ 一般此函数只有在<literal>type</literal>,
+ <literal>rid</literal>,<literal>start</literal>或
+ <literal>count</literal>之一的值超出了允许的范围才会
+ 返回错误。</para>
+
+ <itemizedlist>
+ <listitem>
+ <para> dev - 驱动程序的设备</para>
+ </listitem>
+ <listitem>
+ <para> type - 资源类型,SYS_RES_* </para>
+ </listitem>
+ <listitem>
+ <para> rid - 类型内部的资源号(ID)</para>
+ </listitem>
+ <listitem>
+ <para> start, count - 资源范围 </para>
+ </listitem>
+ </itemizedlist>
+ </listitem>
+
+ <listitem>
+ <para><function>int bus_get_resource(device_t dev, int type,
+ int rid, u_long *startp, u_long *countp)</function></para>
+
+ <para>取得资源范围。成功则返回0,如果资源尚未定义则返回错误码。
+ </para>
+ </listitem>
+
+ <listitem>
+ <para><function>u_long bus_get_resource_start(device_t dev,
+ int type, int rid) u_long bus_get_resource_count (device_t
+ dev, int type, int rid)</function></para>
+
+ <para>便捷函数,只用来获取start或count。出错的情况下返回0,
+ 因此如果0是资源的start合法值之一,将无法区分返回
+ 的0是否指示错误。幸运的是,对于附加驱动程序,没有ISA资源的
+ start值从0开始。</para>
+ </listitem>
+
+ <listitem>
+ <para><function>void bus_delete_resource(device_t dev, int
+ type, int rid)</function></para>
+ <para>删除资源,令其未定义。</para>
+ </listitem>
+
+ <listitem>
+ <para><function>struct resource *
+ bus_alloc_resource(device_t dev, int type, int *rid,
+ u_long start, u_long end, u_long count, u_int
+ flags)</function></para>
+
+ <para>在start和end之间没有被其它设备占用的地方按count值
+ 的范围分配一个资源。不过,不支持对齐。如果资源尚未被设置,
+ 则自动创建它。start为0,end为~0(全1)的这对特殊值
+ 意味着必须使用以前通过
+ <function>bus_set_resource()</function>设置的固定值:
+ start和count就是它们自己,end=(start+count),这种情况下,
+ 如果以前资源没有定义,则返回错误。尽管rid通过引用传递,
+ 但它并不被ISA总线的资源分配代码设置(其它总线可能使用不同的
+ 方法并可能修改它)。</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>旗标是一个位映射,调用者感兴趣的有:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><emphasis>RF_ACTIVE</emphasis> - 使得资源分配后
+ 被自动激活。</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>RF_SHAREABLE</emphasis> - 资源可以同时
+ 被多个驱动程序共享。</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>RF_TIMESHARE</emphasis> - 资源可以被多个驱动
+ 程序分时共享,也就是说,被多个驱动程序同时分配,但任何
+ 给定时间只能被其中一个激活。</para>
+ </listitem>
+<!-- XXXDONT KNOW IT THESE SHOULD BE TWO SEPARATE LISTS OR NOT -->
+ <listitem>
+ <para>出错返回0。被分配的值可以使用
+ <function>rhand_*()</function>从返回的句柄获得。</para>
+ </listitem>
+ <listitem>
+ <para><function>int bus_release_resource(device_t dev, int
+ type, int rid, struct resource *r)</function></para>
+ </listitem>
+
+ <listitem>
+ <para>释放资源,r为<function>bus_alloc_resource()</function>
+ 返回的句柄。成功则返回0,否则返回错误码。</para>
+ </listitem>
+
+ <listitem>
+ <para><function>int bus_activate_resource(device_t dev, int
+ type, int rid, struct resource *r)</function>
+ <function>int bus_deactivate_resource(device_t dev, int
+ type, int rid, struct resource *r)</function></para>
+ </listitem>
+
+ <listitem>
+ <para>激活或禁用资源。成功则返回0,否则返回错误码。如果
+ 资源被分时共享且当前被另一驱动程序激活,则返回 <literal>EBUSY</literal>。
+ </para>
+ </listitem>
+
+ <listitem>
+ <para><function>int bus_setup_intr(device_t dev, struct
+ resource *r, int flags, driver_intr_t *handler, void *arg,
+ void **cookiep)</function> <function>int
+ bus_teardown_intr(device_t dev, struct resource *r, void
+ *cookie)</function></para>
+ </listitem>
+
+ <listitem>
+ <para>关联/分离中断处理程序与设备。成功则返回0,否则
+ 返回错误码。</para>
+ </listitem>
+
+ <listitem>
+ <para>r - 被激活的描述IRQ的资源句柄。</para>
+ <para>flags - 中断优先级,如下之一:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><function>INTR_TYPE_TTY</function> - 终端和其它
+ 类似的字符类型设备。使用
+ <function>spltty()</function>屏蔽它们。</para>
+ </listitem>
+ <listitem>
+ <para><function>(INTR_TYPE_TTY |
+ INTR_TYPE_FAST)</function> - 输入缓冲较小的终端类型
+ 设备,而且输入上的数据丢失很关键(例如老式串口)。
+ 使用<function>spltty()</function>屏蔽它们。</para>
+ </listitem>
+ <listitem>
+ <para><function>INTR_TYPE_BIO</function> - 块类型设备,
+ 不包括CAM控制器上的。使用
+ <function>splbio()</function>屏蔽它们。</para>
+ </listitem>
+ <listitem>
+ <para><function>INTR_TYPE_CAM</function> - CAM (通用访问
+ 方法Common Access Method)总线控制器。使用
+ <function>splcam()</function>屏蔽它们。</para>
+ </listitem>
+ <listitem>
+ <para><function>INTR_TYPE_NET</function> - 网络接口
+ 控制器。使用
+ <function>splimp()</function>屏蔽它们。</para>
+ </listitem>
+ <listitem>
+ <para><function>INTR_TYPE_MISC</function> -
+ 各种其它设备。除了通过
+ <function>splhigh()</function>没有其它方法屏蔽它们。
+ <function>splhigh()</function>屏蔽所有中断。
+ </para>
+ </listitem>
+ </itemizedlist>
+ </listitem>
+ </itemizedlist>
+
+ <para>当中断处理程序执行时,匹配其优先级的所有其它中断都被屏蔽,
+ 唯一的例外是MISC级别,它不会屏蔽其它中断,也不会被其它中断
+ 屏蔽。</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><emphasis>handler</emphasis> - 指向处理程序的指针,
+ 类型driver_intr_t被定义为<function>void
+ driver_intr_t(void *)</function></para>
+ </listitem>
+ <listitem>
+ <para><emphasis>arg</emphasis> - 传递给处理程序的参量,标识
+ 特定设备。由处理程序将它从void*转换为任何实际类型。ISA
+ 中断处理程序的旧约定是使用单元号作为参量,新约定(推荐)
+ 使用指向设备softc结构的指针。</para>
+ </listitem>
+ <listitem>
+ <para><emphasis>cookie[p]</emphasis> - 从
+ <function>setup()</function>接收的值,当传递给
+ <function>teardown()</function>
+ 时用于标识处理程序。</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>定义了若干方法来操作资源句柄(struct resource *)。设备驱动
+ 程序编写者感兴趣的有:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><function>u_long rman_get_start(r) u_long
+ rman_get_end(r)</function> 取得被分配的资源范围的起始和结束。
+ </para>
+ </listitem>
+ <listitem>
+ <para><function>void *rman_get_virtual(r)</function> 取得
+ 被激活的内存资源的虚地址。</para>
+ </listitem>
+ </itemizedlist>
+
+ </sect1>
+
+ <sect1 xml:id="isa-driver-busmem">
+ <title>总线内存映射</title>
+
+ <para>很多情况下设备驱动程序和设备之间的数据交换是通过内存
+ 进行的。有两种可能的变体:</para>
+
+ <para>(a) 内存位于设备卡上</para>
+ <para>(b) 内存为计算机的主内存</para>
+
+ <para>情况(a)中,驱动程序可能需要在卡上的内存与主存之间来回
+ 拷贝数据。为了将卡上的内存映射到内核的虚地址空间,卡上内存的
+ 物理地址和长度必须被定义为<literal>SYS_RES_MEMORY</literal>资源。
+ 然后资源就可以被分配并激活,它的虚地址通过使用
+ <function>rman_get_virtual()</function>获取。较老的驱动程序
+ 将函数<function>pmap_mapdev()</function>用于此目的,现在
+ 不应当再直接使用此函数。它已成为资源激活的一个内部步骤。</para>
+
+ <para>大多数ISA卡的内存配置为物理地位于640KB-1MB范围之间的
+ 某个位置。某些ISA卡需要更大的内存范围,位于16M以下的某个
+ 位置(由于ISA总线上24位地址限制)。这种情况下,如果机器有
+ 比设备内存的起始地址更多的内存(换句话说,它们重叠),则
+ 必须在被设备使用的内存起始地址处配置一个内存空洞。许多
+ BIOS允许在起始于14MB或15MB处配置1M的内存空洞。如果BIOS
+ 正确地报告内存空洞,FreeBSD就能够正确处理它们(此特性
+ 在老BIOS上可能会出问题)。</para>
+
+ <para>情况(b)中,只是数据的地址被发送到设备,设备使用DMA实际
+ 访问主存中的数据。存在两个限制:首先,ISA卡只能访问16MB以下
+ 的内存。其次,虚地址空间中连续的页面在物理地址空间中可能不
+ 连续,设备可能不得不进行分散/收集操作。总线子系统为这些问题
+ 提供现成现成的解决办法,剩下的必须由驱动程序自己完成。</para>
+
+ <para>DMA内存分配使用了两个结构, <varname>bus_dma_tag_t</varname>
+ 和 <varname>bus_dmamap_t</varname>。
+ 标签(tag)描述了DMA内存要求的特性。映射(map)表示按照这些
+ 特性分配的内存块。多个映射可以与同一标签关联。</para>
+
+ <para>标签按照对特性的继承而被组织成树型层次结构。子标签继承父
+ 标签的所有要求,可以令其更严格,但不允许放宽要求。</para>
+
+ <para>一般地,每个设备单元创建一个顶层标签(没有父标签)。如果
+ 每个设备需要不同要求的内存区,则为每个内存区都会创建一个标签,这些
+ 标签作为父标签的孩子。</para>
+
+ <para>使用标签创建映射的方法有两种。</para>
+
+ <para>其一,分配一大块符合标签要求的连续内存(以后可以被释放)。
+ 这一般用于分配为了与设备通信而存在相对较长时间的那些内存区。
+ 将这样的内存加载到映射中非常容易:它总是被看作位于适当物理
+ 内存范围的一整块。</para>
+
+ <para>其二,将虚拟内存中的任意区域加载到映射中。这片内存的
+ 每一页都被检查,看是否符合映射的要求。如何符合则留在原始位置。
+ 如果不符合则分配一个新的符合要求的
+ <quote>反弹页面(bounce page)</quote>,用作中间存储。
+ 当从不符合的原始页面写入数据时,数据首先被拷贝到反弹页面,
+ 然后从反弹页面传递到设备。当读取时,数据将会从设备到反弹页面,
+ 然后被拷贝到它们不符合的原始页面。原始和反弹页面之间的拷贝
+ 处理被称作同步。这一般用于单次传输的基础之上:每次传输时
+ 加载缓冲区,完成传输,卸载缓冲区。</para>
+
+ <para>工作在DMA内存上的函数有:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><function>int bus_dma_tag_create(bus_dma_tag_t parent,
+ bus_size_t alignment, bus_size_t boundary, bus_addr_t
+ lowaddr, bus_addr_t highaddr, bus_dma_filter_t *filter, void
+ *filterarg, bus_size_t maxsize, int nsegments, bus_size_t
+ maxsegsz, int flags, bus_dma_tag_t *dmat)</function></para>
+
+ <para>创建新标签。成功则返回0,否则返回错误码。</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><emphasis>parent</emphasis> - 父标签或者NULL,
+ NULL用于创建顶层标签。</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>alignment</emphasis> -
+ 对将要分配给标签的内存区的对齐要求。<quote>no specific
+ alignment</quote>时值为1。仅应用于以后的
+ <function>bus_dmamem_alloc()</function>而不是
+ <function>bus_dmamap_create()</function>调用。</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>boundary</emphasis> - 物理地址边界,
+ 分配内存时不能穿过。对于<quote>no boundary</quote>
+ 使用0。仅应用于以后的
+ <function>bus_dmamem_alloc()</function>而不是
+ <function>bus_dmamap_create()</function>调用。
+ 必须为2的乘方。如果计划以非层叠DMA方式使用内存(也就是说,
+ DMA地址由ISA DMA控制器提供而不是设备自身),则由于DMA硬件
+ 限制,边界必须不能大于64KB (64*1024)。</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>lowaddr, highaddr</emphasis> - 名字稍微
+ 有些误导。这些值用于限制可用于内存分配的物理地址的允许
+ 范围。其确切含义根据以后不同的使用而有所不同。</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>对于<function>bus_dmamem_alloc()</function>,
+ 从0到lowaddr-1的所有地址被视为允许,更高的地址不允许
+ 使用。</para>
+ </listitem>
+
+ <listitem>
+ <para>对于<function>bus_dmamap_create()</function>,
+ 闭区间[lowaddr; highaddr]之外的所有地址被视为可访问。
+ 范围之内的地址页面被传递给过滤函数,由它决定是否可访问。
+ 如果没有提供过滤函数,则整个区间被视为不可访问。</para>
+ </listitem>
+
+ <listitem>
+ <para>对于ISA设备,正常值(没有过滤函数)为:</para>
+ <para>lowaddr = BUS_SPACE_MAXADDR_24BIT</para>
+ <para>highaddr = BUS_SPACE_MAXADDR</para>
+ </listitem>
+ </itemizedlist>
+
+ </listitem>
+
+ <listitem>
+ <para><emphasis>filter, filterarg</emphasis> - 过滤函数及其
+ 参数。如果filter为NULL,则当调用
+ <function>bus_dmamap_create()</function>时,整个区间
+ [lowaddr, highaddr]被视为不可访问。
+ 否则,区间[lowaddr; highaddr]内的每个被试图访问的页面的
+ 物理地址被传递给过滤函数,由它决定是否可访问。过滤函数的
+ 原型为:<function>int filterfunc(void *arg,
+ bus_addr_t paddr)</function>。当页面可以被访问时它必须
+ 返回0,否则返回非零值。</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>maxsize</emphasis> - 通过此标签可以分配的
+ 最大内存值(以字节计)。有时这个值很难估算,或者可以任意大,
+ 这种情况下,对于ISA设备这个值可以设为
+ <literal>BUS_SPACE_MAXSIZE_24BIT</literal>。</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>nsegments</emphasis> - 设备支持的分散/收集段
+ 的最大数目。如果不加限制,则使用应当使用值
+ <literal>BUS_SPACE_UNRESTRICTED</literal>。
+ 建议对父标签使用这个值,而为子孙标签指定实际限制。
+ nsegments值等于 <literal>BUS_SPACE_UNRESTRICTED</literal>
+ 的标签不能用于实际加载映射,仅可以将它们作为父标签。 nsetments
+ 的实际限制大约为250-300,再高的值将导致内核堆栈溢出(硬件
+ 无法正常支持那么多的分散/收集缓冲区)。</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>maxsegsz</emphasis> - 设备支持的分散/收集段
+ 的最大尺寸。对于ISA设备的最大值为
+ <literal>BUS_SPACE_MAXSIZE_24BIT</literal>。</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>flags</emphasis> - 旗标的位图。感兴趣的旗标
+ 只有:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><emphasis>BUS_DMA_ALLOCNOW</emphasis> - 创建标签时
+ 请求分配所有可能用到的反射页面。</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>BUS_DMA_ISA</emphasis> - 比较神秘的一个标志,
+ 仅用于Alpha机器。i386机器没有定义它。Alpha机器的所有ISA设备
+ 都应当使用这个标志,但似乎还没有这样的驱动程序。</para>
+ </listitem>
+ </itemizedlist>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>dmat</emphasis> - 指向返回的新标签的存储的
+ 指针。</para>
+ </listitem>
+
+ </itemizedlist>
+
+ </listitem>
+
+ <listitem> <!-- Second entry in list alpha -->
+ <para><function>int bus_dma_tag_destroy(bus_dma_tag_t
+ dmat)</function></para>
+
+ <para>销毁标签。成功则返回0,否则返回错误码。</para>
+
+ <para>dmat - 被销毁的标签。</para>
+
+ </listitem>
+
+ <listitem> <!-- Third entry in list alpha -->
+ <para><function>int bus_dmamem_alloc(bus_dma_tag_t dmat,
+ void** vaddr, int flags, bus_dmamap_t
+ *mapp)</function></para>
+
+ <para>分配标签所描述的一块连续内存区。被分配的内存的大小为标签的
+ maxsize。成功则返回0,否则返回错误码。调用结果被用于获取内存的
+ 物理地址,但在此之前必须用<function>bus_dmamap_load()</function>
+ 将其加载。</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>dmat</emphasis> - 标签
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>vaddr</emphasis> - 指向存储的指针,该存储空间
+ 用于返回的分配区域的内核虚地址。
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ flags - 旗标的位图。唯一感兴趣的旗标为:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>BUS_DMA_NOWAIT</emphasis> - 如果内存不能
+ 立即可用则返回错误。如果此标志没有设置,则允许例程
+ 睡眠,直到内存可用为止。
+ </para>
+ </listitem>
+ </itemizedlist>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>mapp</emphasis> - 指向返回的新映射的存储的指针。
+ </para>
+ </listitem>
+ </itemizedlist>
+ </listitem>
+
+ <listitem> <!-- Fourth entry in list alpha -->
+ <para>
+ <function>void bus_dmamem_free(bus_dma_tag_t dmat, void
+ *vaddr, bus_dmamap_t map)</function>
+ </para>
+ <para>
+ 释放由<function>bus_dmamem_alloc()</function>分配的内存。
+ 目前,对分配的带有ISA限制的内存的释放没有实现。因此,建议的
+ 使用模型为尽可能长时间地保持和重用分配的区域。不要轻易地
+ 释放某些区域,然后再短时间地分配它。这并不意味着不应当使用
+ <function>bus_dmamem_free()</function>:希望很快它就会被
+ 完整地实现。
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para><emphasis>dmat</emphasis> - 标签
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>vaddr</emphasis> - 内存的内核虚地址
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>map</emphasis> - 内存的映射(跟
+ <function>bus_dmamem_alloc()</function>返回的一样)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </listitem>
+
+ <listitem> <!-- The fifth entry in list alpha -->
+ <para>
+ <function>int bus_dmamap_create(bus_dma_tag_t dmat, int
+ flags, bus_dmamap_t *mapp)</function>
+ </para>
+ <para>
+ 为标签创建映射,以后用于
+ <function>bus_dmamap_load()</function>。成功则返回0,否则
+ 返回错误码。
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>dmat</emphasis> - 标签
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>flags</emphasis> - 理论上是旗标的位图。但还
+ 从未定义过任何旗标,因此目前总是0。
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>mapp</emphasis> - 指向返回的新映射的存储的指针。
+ </para>
+ </listitem>
+ </itemizedlist>
+ </listitem>
+
+ <listitem> <!-- Sixth entry in the alpha list -->
+ <para>
+ <function>int bus_dmamap_destroy(bus_dma_tag_t dmat,
+ bus_dmamap_t map)</function>
+ </para>
+ <para>
+ 销毁映射。成功则返回0,否则返回错误码。
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ dmat - 与映射关联的标签
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ map - 将要被销毁的映射
+ </para>
+ </listitem>
+ </itemizedlist>
+ </listitem>
+
+ <listitem> <!-- Seventh entry in list alpha -->
+ <para>
+ <function>int bus_dmamap_load(bus_dma_tag_t dmat,
+ bus_dmamap_t map, void *buf, bus_size_t buflen,
+ bus_dmamap_callback_t *callback, void *callback_arg, int
+ flags)</function>
+ </para>
+ <para>
+ 加载缓冲区到映射中(映射必须事先由
+ <function>bus_dmamap_create()</function>或者
+ <function>bus_dmamem_alloc()</function>)创建。缓冲区的所有
+ 页面都会被检查,看是否符合标签的要求,并为那些不符合的分配
+ 反弹页面。会创建物理段描述符的数组,并将其传递给回调函数。
+ 回调函数以某种方式处理这个数组。系统中的反弹缓冲区是受限的,
+ 因此如果需要的反弹缓冲区不能立即获得,则将请求入队,当反弹
+ 缓冲区可用时再调用回调函数。如果回调函数立即执行则返回0,
+ 如果请求被排队,等待将来执行,则返回
+ <errorname>EINPROGRESS</errorname>。后一种情况下,
+ 与排队的回调函数之间的同步由驱动程序负责。
+ </para>
+ <!--<blockquote>-->
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>dmat</emphasis> - 标签
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>map</emphasis> - 映射
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>buf</emphasis> - 缓冲区的内核虚地址
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>buflen</emphasis> - 缓冲区的长度
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>callback</emphasis>,<function>
+ callback_arg</function> - 回调函数及其参数
+ </para>
+ </listitem>
+ </itemizedlist>
+ <!--</blockquote>-->
+ <para>
+ 回调函数的原型为:
+ </para>
+ <para>
+ <function>void callback(void *arg, bus_dma_segment_t
+ *seg, int nseg, int error)</function>
+ </para>
+ <!-- <blockquote> -->
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>arg</emphasis> - 与传递给
+ <function>bus_dmamap_load()</function>的callback_arg
+ 相同。
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>seg</emphasis> - 段描述符的数组
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>nseg</emphasis> - 数组中的描述符个数
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>error</emphasis> - 表示段数目溢出:如被设为
+ <errorname>EFBIG</errorname>,
+ 则标签允许的最大数目的段无法容纳缓冲区。
+ 这种情况下数组中的描述符的数目只有标签许可的那么多。
+ 对这种情况的处理由驱动程序决定:根据希望的语义,驱动
+ 程序可以视其为错误,或将缓冲区分为两个并单独处理第二个。
+ </para>
+ </listitem>
+ </itemizedlist>
+ <!-- </blockquote> -->
+ <para>
+ 段数组中的每一项包含如下字段:
+ </para>
+
+ <!-- <blockquote> -->
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>ds_addr</emphasis> - 段物理地址
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>ds_len</emphasis> - 段长度
+ </para>
+ </listitem>
+ </itemizedlist>
+ <!-- </blockquote>-->
+ </listitem>
+
+ <listitem> <!-- Eighth entry in alpha list -->
+ <para>
+ <function>void bus_dmamap_unload(bus_dma_tag_t dmat,
+ bus_dmamap_t map)</function>
+ </para>
+ <para>unload the map.
+ </para>
+ <!-- <blockquote> -->
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>dmat</emphasis> - 标签
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>map</emphasis> - 已加载的映射
+ </para>
+ </listitem>
+ </itemizedlist>
+ <!-- </blockquote> -->
+ </listitem>
+
+ <listitem> <!-- Ninth entry list alpha -->
+ <para>
+ <function>void bus_dmamap_sync (bus_dma_tag_t dmat,
+ bus_dmamap_t map, bus_dmasync_op_t op)</function>
+ </para>
+ <para>
+ 与设备进行物理传输前后,将加载的缓冲区与其反弹页面进行同步。
+ 此函数完成原始缓冲区与其映射版本之间所有必需的数据拷贝工作。
+ 进行传输之前和之后必须对缓冲区进行同步。
+ </para>
+ <!-- <blockquote> -->
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>dmat</emphasis> - 标签
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>map</emphasis> - 已加载的映射
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>op</emphasis> - 要执行的同步操作的类型:
+ </para>
+ </listitem>
+ </itemizedlist>
+ <!-- <blockquote> -->
+ <itemizedlist>
+ <listitem>
+ <para>
+ <function>BUS_DMASYNC_PREREAD</function> - 从设备到
+ 缓冲区的读操作之前
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <function>BUS_DMASYNC_POSTREAD</function> - 从设备到
+ 缓冲区的读操作之后
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <function>BUS_DMASYNC_PREWRITE</function> - 从缓冲区到
+ 设备的写操作之前
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <function>BUS_DMASYNC_POSTWRITE</function> - 从缓冲区到
+ 设备的写操作之后
+ </para>
+ </listitem>
+ </itemizedlist>
+ </listitem>
+ </itemizedlist> <!-- End of list alpha -->
+<!-- </blockquote>
+</blockquote> -->
+
+ <para>
+ 当前PREREAD和POSTWRITE为空操作,但将来可能会改变,因此驱动程序
+ 中不能忽略它们。由<function>bus_dmamem_alloc()</function>
+ 获得的内存不需要同步。
+ </para>
+ <para>
+ 从<function>bus_dmamap_load()</function>中调用回调函数之前,
+ 段数组是存储在栈中的。并且是按标签允许的最大数目的段预先分配
+ 好的。这样由于i386体系结构上对段数目的实际限制约为250-300
+ (内核栈为4KB减去用户结构的大小,段数组条目的大小为8字节,和
+ 其它必须留出来的空间)。由于数组基于最大数目而分配,因此这个值
+ 必须不能设置成超出实际需要。幸运的是,对于大多数硬件而言,
+ 所支持的段的最大数目低很多。但如果驱动程序想处理具有非常多
+ 分散/收集段的缓冲区,则应当一部分一部分地处理:加载缓冲区的
+ 一部分,传输到设备,然后加载缓冲区的下一部分,如此反复。
+ </para>
+ <para>
+ 另一个实践结论是段数目可能限制缓冲区的大小。如果缓冲区中的
+ 所有页面碰巧物理上不连续,则分片情况下支持的最大缓冲区尺寸
+ 为(nsegments * page_size)。例如,如果支持的段的最大数目为10,
+ 则在i386上可以确保支持的最大缓冲区大小为40K。如果希望更大的
+ 则需要在驱动程序中使用一些特殊技巧。
+ </para>
+ <para>
+ 如果硬件根本不支持分散/收集,或者驱动程序希望即使在严重分片的
+ 情况下仍然支持某种缓冲区大小,则解决办法是:如果无法容纳下原始
+ 缓冲区,就在驱动程序中分配一个连续的缓冲区作为中间存储。
+ </para>
+ <para>
+ 下面是当使用映射时的典型调用顺序,根据对映射的具体使用而不同。
+ 字符-&gt;用于显示时间流。
+ </para>
+ <para>
+ 对于从连接到分离设备,这期间位置一直不变的缓冲区:</para>
+ <para>
+ bus_dmamem_alloc -&gt; bus_dmamap_load -&gt; ...use buffer... -&gt;
+ -&gt; bus_dmamap_unload -&gt; bus_dmamem_free
+ </para>
+
+ <para>对于从驱动程序外部传递进去,并且经常变化的缓冲区:
+
+ <!-- XXX is this correct? -->
+ <programlisting> bus_dmamap_create -&gt;
+ -&gt; bus_dmamap_load -&gt; bus_dmamap_sync(PRE...) -&gt; do transfer -&gt;
+ -&gt; bus_dmamap_sync(POST...) -&gt; bus_dmamap_unload -&gt;
+ ...
+ -&gt; bus_dmamap_load -&gt; bus_dmamap_sync(PRE...) -&gt; do transfer -&gt;
+ -&gt; bus_dmamap_sync(POST...) -&gt; bus_dmamap_unload -&gt;
+ -&gt; bus_dmamap_destroy </programlisting>
+
+ </para>
+ <para>
+ 当加载由<function>bus_dmamem_alloc()</function>创建的映射时,传递
+ 进去的缓冲区的地址和大小必须和
+ <function>bus_dmamem_alloc()</function>中使用的一样。这种情况下就
+ 可以保证整个缓冲区被作为一个段而映射(因而回调可以基于此假设),
+ 并且请求被立即执行(永远不会返回EINPROGRESS)。这种情况下回调函数
+ 需要作的只是保存物理地址。
+ </para>
+ <para>
+ 典型示例如下:
+ </para>
+
+ <programlisting> static void
+ alloc_callback(void *arg, bus_dma_segment_t *seg, int nseg, int error)
+ {
+ *(bus_addr_t *)arg = seg[0].ds_addr;
+ }
+
+ ...
+ int error;
+ struct somedata {
+ ....
+ };
+ struct somedata *vsomedata; /* 虚地址 */
+ bus_addr_t psomedata; /* 物理总线相关的地址 */
+ bus_dma_tag_t tag_somedata;
+ bus_dmamap_t map_somedata;
+ ...
+
+ error=bus_dma_tag_create(parent_tag, alignment,
+ boundary, lowaddr, highaddr, /*filter*/ NULL, /*filterarg*/ NULL,
+ /*maxsize*/ sizeof(struct somedata), /*nsegments*/ 1,
+ /*maxsegsz*/ sizeof(struct somedata), /*flags*/ 0,
+ &#38;tag_somedata);
+ if(error)
+ return error;
+
+ error = bus_dmamem_alloc(tag_somedata, &#38;vsomedata, /* flags*/ 0,
+ &#38;map_somedata);
+ if(error)
+ return error;
+
+ bus_dmamap_load(tag_somedata, map_somedata, (void *)vsomedata,
+ sizeof (struct somedata), alloc_callback,
+ (void *) &#38;psomedata, /*flags*/0); </programlisting>
+
+ <para>
+ 代码看起来有点长,也比较复杂,但那是正确的使用方法。实际结果是:
+ 如果分配多个内存区域,则总将它们组合成一个结构并作为整体分配
+ (如果对齐和边界限制允许的话)是一个很好的主意。
+ </para>
+ <para>
+ 当加载任意缓冲区到由<function>bus_dmamap_create()</function>
+ 创建的映射时,由于回调可能被延迟,因此必须采取特殊措施与回调
+ 函数进行同步。代码看起来像下面的样子:
+ </para>
+
+ <programlisting> {
+ int s;
+ int error;
+
+ s = splsoftvm();
+ error = bus_dmamap_load(
+ dmat,
+ dmamap,
+ buffer_ptr,
+ buffer_len,
+ callback,
+ /*callback_arg*/ buffer_descriptor,
+ /*flags*/0);
+ if (error == EINPROGRESS) {
+ /*
+ * 执行必要的操作以确保与回调的同步。
+ * 回调被确保直到我们执行了splx()或tsleep()才会被调用。
+ */
+ }
+ splx(s);
+ } </programlisting>
+
+ <para>
+ 处理请求的两种方法分别是:
+ </para>
+ <para>
+ 1. 如果通过显式地标记请求已经结束来完成请求(例如CAM请求),则
+ 将所有进一步的处理放入回调驱动程序中会比较简单,回调结束后会
+ 标记请求。之后不需要太多额外的同步。由于流控制的原因,冻结请求
+ 队列直到请求完成才释放可能是个好主意。
+ </para>
+ <para>
+ 2. 如果请求是在函数返回时完成(例如字符设备上传统的读写请求),
+ 则需要在缓冲区描述符上设置同步标志,并调用
+ <function>tsleep()</function>。后面当回调函数被调用时,它将
+ 执行处理并检查同步标志。如果设置了同步标志,它应该发出一个
+ 唤醒操作。在这种方法中,回调函数或者进行所由必需的处理(就像
+ 前面的情况),或者简单在缓冲区描述符中存储段数组。回调完成
+ 后,回调函数就能使用这个存储的段数组并进行所有的处理。
+ </para>
+ </sect1>
+<!--_________________________________________________________________________-->
+<!--~~~~~~~~~~~~~~~~~~~~END OF SECTION~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+ <sect1 xml:id="isa-driver-dma">
+ <title>DMA</title>
+ <!-- Section Marked up by Wylie -->
+
+ <indexterm><primary>Direct Memory Access (DMA, 直接内存访问)</primary></indexterm>
+
+ <para>
+ ISA总线中Direct Memory Access (DMA)是通过DMA控制器(实际上是它们
+ 中的两个,但这只是无关细节)实现的。为了使以前的ISA设备简单便宜,
+ 总线控制和地址产生的逻辑都集中在DMA控制器中。幸运的是,FreeBSD
+ 提供了一套函数,这些函数大多把DMA控制器的繁琐细节对设备驱动程序
+ 隐藏了起来。
+ </para>
+
+ <para>
+ 最简单情况是那些比较智能的设备。就象PCI上的总线主设备一样,
+ 它们自己能产生总线周期和内存地址。它们真正从DMA控制器需要的
+ 唯一事情是总线仲裁。所以为了此目的,它们假装是级联从DMA控制器。
+ 当连接驱动程序时,系统DMA控制器需要做的唯一事情就是通过调用
+ 如下函数在一个DMA通道上激活级联模式。
+ </para>
+
+ <para>
+ <function>void isa_dmacascade(int channel_number)</function>
+ </para>
+
+ <para>
+ 所有进一步的活动通过对设备编程完成。当卸载驱动程序时,不需要
+ 调用DMA相关的函数。
+ </para>
+
+ <para>
+ 对于较简单的设备,事情反而变得复杂。使用的函数包括:
+ </para>
+
+ <itemizedlist>
+
+ <listitem>
+ <para>
+ <function>int isa_dma_acquire(int chanel_number)</function>
+ </para>
+ <para>
+ 保留一个DMA通道。成功则返回0,如果通道已经被保留或被其它
+ 驱动程序保留则返回EBUSY。大多数的ISA设备都不能共享DMA通道,
+ 因此这个函数通常在连接设备时调用。总线资源的现代接口使得
+ 这种保留成为多余,但目前仍必须使用。如果不使用,则后面其它
+ DMA例程将会panic。
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ <function>int isa_dma_release(int chanel_number)</function>
+ </para>
+ <para>
+ 释放先前保留的DMA通道。释放通道时必须不能有正在进行中的
+ 传输(另外,释放通道后设备必须不能再试图发起传输)。
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ <function>void isa_dmainit(int chan, u_int
+ bouncebufsize)</function>
+ </para>
+ <para>
+ 分配由特定通道使用的反弹缓冲区。请求的缓冲区大小不能超过
+ 64KB。以后,如果传输缓冲区碰巧不是物理连续的,或超出ISA
+ 总线可访问的内存范围,或跨越64KB的边界,则会自动使用反弹
+ 缓冲区。如果传输总是使用符合上述条件的缓冲区(例如,由
+ <function>bus_dmamem_alloc()</function>分配的那些),则
+ 不需要调用<function>isa_dmainit()</function>。但使用此函数
+ 会让通过DMA控制器传输任意数据变得非常方便。
+ </para>
+ <!-- <blockquote> -->
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>chan</emphasis> - 通道号
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>bouncebufsize</emphasis> - 以字节计数的反弹
+ 缓冲区的大小
+ </para>
+ </listitem>
+ </itemizedlist>
+<!-- </blockquote> -->
+<!--</para> -->
+ </listitem>
+
+ <listitem>
+ <para>
+ <function>void isa_dmastart(int flags, caddr_t addr, u_int
+ nbytes, int chan)</function>
+ </para>
+ <para>
+ 准备启动DMA传输。实际启动设备上的传输之前必需调用此函数
+ 来设置DMA控制器。它检查缓冲区是否连续的且在ISA内存范围
+ 之内,如果不是则自动使用反弹缓冲区。如果需要反弹缓冲区,
+ 但反弹缓冲区没有用<function>isa_dmainit()</function>
+ 设置,或对于请求的传输大小来说太小,则系统将panic。
+ 写请求且使用反弹缓冲区的情况下,数据将被自动拷贝到反弹
+ 缓冲区。
+ </para>
+ </listitem>
+ <listitem>
+ <para>flags - 位掩码,决定将要完成的操作的类型。方向位B_READ和
+ B_WRITE互斥。
+ </para>
+ <!-- <blockquote> -->
+ <itemizedlist>
+ <listitem>
+ <para>
+ B_READ - 从ISA总线读到内存
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ B_WRITE - 从内存写到ISA总线上
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ B_RAW - 如果设置则DMA控制器将会记住缓冲区,并在传输结束后
+ 自动重新初始化它自己,再次重复传输同一缓冲区(当然,驱动
+ 程序可能发起设备的另一个传输之前改变缓冲区中的数据)。
+ 如果没有设置,参数只对一次传输有效,在发起下一次传输之前
+ 必须再次调用<function>isa_dmastart()</function>。只有在不
+ 使用反弹缓冲区时使用B_RAW才有意义。
+ </para>
+ </listitem>
+ </itemizedlist>
+<!-- </blockquote> -->
+ </listitem>
+ <listitem>
+ <para>
+ addr - 缓冲区的虚地址
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ nbytes - 缓冲区长度。必须小于等于64KB。不允许长度为0:因为
+ DMA控制器将会理解为64KB,而内核代码把它理解为0,那样就会导致
+ 不可预测的效果。对于通道号等于和高于4的情况,长度必需为偶数,
+ 因为这些通道每次传输2字节。奇数长度情况下,最后一个字节不被
+ 传输。
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ chan - 通道号
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ <function>void isa_dmadone(int flags, caddr_t addr, int
+ nbytes, int chan)</function>
+ </para>
+ <para>
+ 设备报告传输完成后,同步内存。如果是使用反弹缓冲区的读操作,
+ 则将数据从反弹缓冲区拷贝到原始缓冲区。参量与
+ <function>isa_dmastart()</function>的相同。允许使用B_RAW标志,
+ 但它一点也不会影响<function>isa_dmadone()</function>。
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ <function>int isa_dmastatus(int channel_number)</function>
+ </para>
+ <para>
+ 返回当前传输中剩余的字节数。在
+ <function>isa_dmastart()</function>中设置了B_READ的情况下,
+ 返回的数字一定不会等于零。传输结束时它会被自动复位到缓冲区的
+ 长度。正式的用法是在设备发信号指示传输已完成时检查剩余的字节数。
+ 如果字节数不为0,则此次传输可能有问题。
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ <function>int isa_dmastop(int channel_number)</function>
+ </para>
+ <para>
+ 放弃当前的传输并返回剩余未传输的字节数。
+ </para>
+ </listitem>
+ </itemizedlist>
+ </sect1>
+
+ <sect1 xml:id="isa-driver-probe">
+ <title>xxx_isa_probe</title>
+ <!-- Section marked up by Wylie -->
+
+ <para>
+ 这个函数探测设备是否存在。如果驱动程序支持自动侦测设备配置的
+ 某些部分(如中断向量或内存地址),则自动侦测必须在此例程中完成。
+ </para>
+
+ <para>
+ 对于任意其他总线,如果不能侦测到设备,或者侦测到但自检失败,
+ 或者发生某些其他问题,则应当返回一个正值的错误。如果设备不
+ 存在则必须返回值 <errorname>ENXIO</errorname>。
+ 其他错误值可能表示其他条件。零或负值
+ 意味着成功。大多数驱动程序返回零表示成功。
+ </para>
+
+ <para>
+ 当PnP设备支持多个接口时使用负返回值。例如,不同驱动程序支持
+ 老的兼容接口和较新的高级接口。则两个驱动程序都将侦测设备。
+ 在探测例程中返回较高值的驱动程序获得优先(换句话说,返回0的
+ 驱动程序具有最高的优先级,返回-1的其次,返回-2的更后,等等)。
+ 这样,仅支持老接口的设备将被老驱动程序处理(其应当从探测例程中
+ 返回-1),而同时也支持新接口的设备将由新驱动程序处理(其应当从
+ 探测例程中返回0)。
+ </para>
+
+ <para>
+ 设备描述符结构xxx_softc由系统在调用探测例程之前分配。如果
+ 探测例程返回错误,描述符会被系统自动取消分配。因此如果出现
+ 探测错误,驱动程序必须保证取消分配探测期间它使用的所有资源,
+ 且确保没有什么能够阻止描述符被安全地取消分配。如果探测成功
+ 完成,描述符将由系统保存并在以后传递给例程
+ <function>xxx_isa_attach()</function>。如果驱动程序返回负值,
+ 就不能保证它将获得最高优先权且其连接例程会被调用。因此这种
+ 情况下它也必须在返回前释放所有的资源,并在需要的时候在连接
+ 例程中重新分配它们。当<function>xxx_isa_probe()</function>
+ 返回0时,在返回前释放资源也是一个好主意,而且中规中矩的驱动
+ 程序应当这样做。但在释放资源会存在某些问题的情况下,允许驱动
+ 程序在从探测例程返回0和连接例程的执行之间保持资源。
+ </para>
+
+ <para>
+ 典型的探测例程以取得设备描述符和单元号开始:
+ </para>
+
+ <programlisting> struct xxx_softc *sc = device_get_softc(dev);
+ int unit = device_get_unit(dev);
+ int pnperror;
+ int error = 0;
+
+ sc-&gt;dev = dev; /* 链接回来 */
+ sc-&gt;unit = unit; </programlisting>
+
+ <para>
+ 然后检查PnP设备。检查是通过一个包含PnP ID列表的表进行的。此表
+ 包含这个驱动程序支持的PnP ID和以人工可读形式给出的对应这些ID的
+ 设备型号的描述。
+ </para>
+
+ <programlisting>
+ pnperror=ISA_PNP_PROBE(device_get_parent(dev), dev,
+ xxx_pnp_ids); if(pnperror == ENXIO) return ENXIO;
+ </programlisting>
+
+ <para>
+ ISA_PNP_PROBE的逻辑如下:如果卡(设备单元)没有被作为PnP侦测到,
+ 则返回ENOENT。如果被作为PnP侦测到,但侦测到的ID不匹配表中的
+ 任一ID,则返回ENXIO。最后,如果设备能支持PnP且匹配表中的一个
+ ID,则返回0,并且由<function>device_set_desc()</function>从
+ 表中取得适当的描述进行设置。
+ </para>
+
+ <para>
+ 如果设备驱动程序仅支持PnP设备,则情况看起来如下:
+ </para>
+
+ <programlisting> if(pnperror != 0)
+ return pnperror; </programlisting>
+
+ <para>
+ 对于不支持PnP的驱动程序不需要特殊处理,因为驱动程序会传递空的
+ PnP ID表,且如果在PnP卡上调用会得到ENXIO。
+ </para>
+
+ <para>
+ 探测例程通常至少需要某些最少量的资源,如I/O端口号,来发现并探测卡。
+ 对于不同的硬件,驱动程序可能会自动发现其他必需的资源。PnP设备的
+ 所有资源由PnP子系统预先设置,因此驱动程序不需要自己发现它们。</para>
+
+ <para>
+ 通常访问设备所需要的最少信息就是端口号。然后某些设备允许从设备
+ 配置寄存器中取得其余信息(尽管不是所有的设备都这样)。因此首先
+ 我们尝试取得端口起始值:
+ </para>
+
+ <programlisting> sc-&gt;port0 = bus_get_resource_start(dev,
+ SYS_RES_IOPORT, 0 /*rid*/); if(sc-&gt;port0 == 0) return ENXIO;
+ </programlisting>
+
+ <para>
+ 基端口地址被保存在softc结构中,以便将来使用。如果需要经常使用
+ 端口,则每次都调用资源函数将会慢的无法忍受。如果我们没有得到
+ 端口,则返回错误即可。相反,一些设备驱动程序相当聪明,尝试探测
+ 所有可能的端口,如下:
+ </para>
+
+ <programlisting>
+ /* 此设备所有可能的基I/O端口地址表 */
+ static struct xxx_allports {
+ u_short port; /* 端口地址 */
+ short used; /* 旗标:此端口是否已被其他单元使用 */
+ } xxx_allports = {
+ { 0x300, 0 },
+ { 0x320, 0 },
+ { 0x340, 0 },
+ { 0, 0 } /* 表结束 */
+ };
+
+ ...
+ int port, i;
+ ...
+
+ port = bus_get_resource_start(dev, SYS_RES_IOPORT, 0 /*rid*/);
+ if(port !=0 ) {
+ for(i=0; xxx_allports[i].port!=0; i++) {
+ if(xxx_allports[i].used || xxx_allports[i].port != port)
+ continue;
+
+ /* 找到了 */
+ xxx_allports[i].used = 1;
+ /* 在已知端口上探测 */
+ return xxx_really_probe(dev, port);
+ }
+ return ENXIO; /* 端口无法识别或已经被使用 */
+ }
+
+ /* 仅在需要猜测端口的时候才会到达这儿 */
+ for(i=0; xxx_allports[i].port!=0; i++) {
+ if(xxx_allports[i].used)
+ continue;
+
+ /* 标记为已被使用 - 即使我们在此端口什么也没有发现
+ * 至少我们以后不会再次探测
+ */
+ xxx_allports[i].used = 1;
+
+ error = xxx_really_probe(dev, xxx_allports[i].port);
+ if(error == 0) /* 在那个端口找到一个设备 */
+ return 0;
+ }
+ /* 探测过所有可能的地址,但没有可用的 */
+ return ENXIO;</programlisting>
+
+ <para>
+ 当然,做这些事情通常应该使用驱动程序的
+ <function>identify()</function>例程。但可能有一个正当的理由来
+ 说明为什么在函数<function>probe()</function>中完成更好:如果
+ 这种探测会让一些其他敏感设备发疯。探测例程按旗标
+ <literal>sensitive</literal>排序:敏感设备首先被探测,然后是
+ 其他设备。但<function>identify()</function>例程在所有探测之前
+ 被调用,因此它们不会考虑敏感设备并可能扰乱这些设备。
+ </para>
+
+ <para>
+ 现在,我们得到起始端口以后就需要设置端口数(PnP设备除外),因为
+ 内核在配置文件中没有这个信息。
+ </para>
+
+ <programlisting>
+ if(pnperror /* 只对非PnP设备 */
+ &#38;&#38; bus_set_resource(dev, SYS_RES_IOPORT, 0, sc-&gt;port0,
+ XXX_PORT_COUNT)&lt;0)
+ return ENXIO;</programlisting>
+
+ <para>
+ 最后分配并激活一片端口地址空间(特殊值start和end意思是说
+ <quote>使用我们通过<function>bus_set_resource()</function>
+ 设置的那些值</quote>):
+ </para>
+
+ <programlisting>
+ sc-&gt;port0_rid = 0;
+ sc-&gt;port0_r = bus_alloc_resource(dev, SYS_RES_IOPORT,
+ &#38;sc-&gt;port0_rid,
+ /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE);
+
+ if(sc-&gt;port0_r == NULL)
+ return ENXIO;</programlisting>
+
+ <para>
+ 现在可以访问端口映射的寄存器后,我们就可以以某种方式向设备写入
+ 数据并检查设备是否如我们期望的那样作出反应。如果没有,则说明
+ 可能其他的设备在这个地址上,或者这个地址上根本没有设备。
+ </para>
+
+ <para>
+ 通常驱动程序直到连接例程才会设置中断处理函数。这之前我们替代以
+ 轮询模式进行探测,超时则以<function>DELAY()</function>实现。
+ 探测例程必须确保不能永久挂起,设备上的所有等待必须在超时内完成。
+ 如果设备不在这段时间内响应,则可能设备出故障或配置错误,驱动程序
+ 必须返回错误,当确定超时间隔时,给设备一些额外时间以确保可靠:
+ 尽管假定<function>DELAY()</function>在任何机器上都延时相同数量的
+ 时间,但随具体CPU的不同,此函数还是有一定的误差幅度。
+ </para>
+
+ <para>
+ 如果探测例程真的想检查中断是否真的工作,它可以也配置和探测中断。
+ 但不建议这样。
+ </para>
+
+ <programlisting>
+ /* 以严重依赖于具体设备的方式实现 */
+ if(error = xxx_probe_ports(sc))
+ goto bad; /* 返回前释放资源 */
+ </programlisting>
+
+ <para>
+ 依赖于所发现设备的确切型号,函数
+ <function>xxx_probe_ports()</function>也可能设置设备描述。但
+ 如果只支持一种设备型号,则也可以硬编码的形式完成。当然,对于
+ PnP设备,PnP支持从表中自动设置描述。
+ </para>
+
+
+ <programlisting> if(pnperror)
+ device_set_desc(dev, "Our device model 1234");
+ </programlisting>
+
+ <para>
+ 探测例程应当或者通过读取设备配置寄存器来发现所有资源的范围,
+ 或者确保由用户显式设置。我们将假定一个带板上内存的例子。
+ 探测例程应当尽可能是非插入式的,这样分配和检查其余资源功能性
+ 的工作就可以更好地留给连接例程来做。
+ </para>
+
+ <para>
+ 内存地址可以在内核配置文件中指定,或者对应某些设备可以在非易失性
+ 配置寄存器中预先配置。如果两种做法均可用却不同,那么应当用
+ 哪个呢?可能用户厌烦在内核配置文件中明确设置地址,但他们知道
+ 自己在干什么,则应当优先使用这个。一个实现的例子可能是这样的:
+ </para>
+ <programlisting>
+ /* 首先试图找出配置地址 */
+ sc-&gt;mem0_p = bus_get_resource_start(dev, SYS_RES_MEMORY, 0 /*rid*/);
+ if(sc-&gt;mem0_p == 0) { /* 没有,用户没指定 */
+ sc-&gt;mem0_p = xxx_read_mem0_from_device_config(sc);
+
+
+ if(sc-&gt;mem0_p == 0)
+ /* 从设备配置寄存器也到不了这儿 */
+ goto bad;
+ } else {
+ if(xxx_set_mem0_address_on_device(sc) &lt; 0)
+ goto bad; /* 设备不支持那地址 */
+ }
+
+ /* 就像端口,设置内存大小,
+ * 对于某些设备,内存大小不是常数,
+ * 而应当从设备配置寄存器中读取,以适应设备的不同型号
+ * 另一个选择是让用户把内存大小设置为“msize”配置资源,
+ * 由ISA总线自动处理
+ */
+ if(pnperror) { /*仅对非PnP设备 */
+ sc-&gt;mem0_size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0 /*rid*/);
+ if(sc-&gt;mem0_size == 0) /* 用户没有指定 */
+ sc-&gt;mem0_size = xxx_read_mem0_size_from_device_config(sc);
+
+ if(sc-&gt;mem0_size == 0) {
+ /* 假定这是设备非常老的一种型号,没有自动配置特性,
+ * 用户也没有偏好设置,因此假定最低要求的情况
+ * (当然,真实值将根据设备驱动程序而不同)
+ */
+ sc-&gt;mem0_size = 8*1024;
+ }
+
+ if(xxx_set_mem0_size_on_device(sc) &lt; 0)
+ goto bad; /*设备不支持那个大小 */
+
+ if(bus_set_resource(dev, SYS_RES_MEMORY, /*rid*/0,
+ sc-&gt;mem0_p, sc-&gt;mem0_size)&lt;0)
+ goto bad;
+ } else {
+ sc-&gt;mem0_size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0 /*rid*/);
+ } </programlisting>
+
+ <para>
+ 类似, 很容易检查IRQ和DRQ所用的资源。
+ </para>
+
+ <para>
+ 如果一切进行正常,然后就可以释放所有资源并返回成功。
+ </para>
+
+ <programlisting> xxx_free_resources(sc);
+ return 0;</programlisting>
+
+ <para>
+ 最后,处理棘手情况。所有资源应当在返回前被释放。我们利用这样一个
+ 事实:softc结构在传递给我们以前被零化,因此我们能够找出是否分配了
+ 某些资源:如果分配则这些资源的描述符非零。
+ </para>
+
+ <programlisting> bad:
+
+ xxx_free_resources(sc);
+ if(error)
+ return error;
+ else /* 确切错误未知 */
+ return ENXIO;</programlisting>
+
+ <para>
+ 这是完整的探测例程。资源的释放从多个地方完成,因此将它挪到一个
+ 函数中,看起来可能像下面的样子:
+ </para>
+
+<programlisting>static void
+ xxx_free_resources(sc)
+ struct xxx_softc *sc;
+ {
+ /* 检查每个资源,如果非0则释放 */
+
+ /* 中断处理函数 */
+ if(sc-&gt;intr_r) {
+ bus_teardown_intr(sc-&gt;dev, sc-&gt;intr_r, sc-&gt;intr_cookie);
+ bus_release_resource(sc-&gt;dev, SYS_RES_IRQ, sc-&gt;intr_rid,
+ sc-&gt;intr_r);
+ sc-&gt;intr_r = 0;
+ }
+
+ /* 我们分配过的所有种类的内存 */
+ if(sc-&gt;data_p) {
+ bus_dmamap_unload(sc-&gt;data_tag, sc-&gt;data_map);
+ sc-&gt;data_p = 0;
+ }
+ if(sc-&gt;data) { /* sc-&gt;data_map等于0有可能合法 */
+ /* the map will also be freed */
+ bus_dmamem_free(sc-&gt;data_tag, sc-&gt;data, sc-&gt;data_map);
+ sc-&gt;data = 0;
+ }
+ if(sc-&gt;data_tag) {
+ bus_dma_tag_destroy(sc-&gt;data_tag);
+ sc-&gt;data_tag = 0;
+ }
+
+ ... 如果有,释放其他的映射和标签 ...
+
+ if(sc-&gt;parent_tag) {
+ bus_dma_tag_destroy(sc-&gt;parent_tag);
+ sc-&gt;parent_tag = 0;
+ }
+
+ /* 释放所有总线资源 */
+ if(sc-&gt;mem0_r) {
+ bus_release_resource(sc-&gt;dev, SYS_RES_MEMORY, sc-&gt;mem0_rid,
+ sc-&gt;mem0_r);
+ sc-&gt;mem0_r = 0;
+ }
+ ...
+ if(sc-&gt;port0_r) {
+ bus_release_resource(sc-&gt;dev, SYS_RES_IOPORT, sc-&gt;port0_rid,
+ sc-&gt;port0_r);
+ sc-&gt;port0_r = 0;
+ }
+ }</programlisting>
+
+ </sect1>
+
+ <sect1 xml:id="isa-driver-attach">
+ <title>xxx_isa_attach</title>
+ <!-- Section Marked up by Wylie -->
+
+ <para>如果探测例程返回成功并且系统选择连接那个驱动程序,则连接例程
+ 负责将驱动程序实际连接到系统。如果探测例程返回0 ,则连接例程期望
+ 接收完整的设备结构softc,此结构由探测例程设置。同时,如果探测例程
+ 返回0,它可能期望这个设备的连接例程应当在将来的某点被调用。如果
+ 探测例程返回负值,则驱动程序可能不会作此假设。
+ </para>
+
+ <para>如果成功完成,连接例程返回0,否则返回错误码。
+ </para>
+
+ <para>连接例程的启动跟探测例程相似,将一些常用数据取到一些更容易
+ 访问的变量中。
+ </para>
+
+ <programlisting> struct xxx_softc *sc = device_get_softc(dev);
+ int unit = device_get_unit(dev);
+ int error = 0;</programlisting>
+
+ <para>然后分配并激活所需资源。由于端口范围通常在从探测返回前就
+ 被释放,因此需要重新分配。我们希望探测例程已经适当地设置了
+ 所有的资源范围,并将它们保存在结构softc中。如果探测例程留下了
+ 一些被分配的资源,就不需要再次分配(重新分配被视为错误)。
+ </para>
+
+ <programlisting> sc-&gt;port0_rid = 0;
+ sc-&gt;port0_r = bus_alloc_resource(dev, SYS_RES_IOPORT, &#38;sc-&gt;port0_rid,
+ /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE);
+
+ if(sc-&gt;port0_r == NULL)
+ return ENXIO;
+
+ /* 板上内存 */
+ sc-&gt;mem0_rid = 0;
+ sc-&gt;mem0_r = bus_alloc_resource(dev, SYS_RES_MEMORY, &#38;sc-&gt;mem0_rid,
+ /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE);
+
+ if(sc-&gt;mem0_r == NULL)
+ goto bad;
+
+ /* 取得虚地址 */
+ sc-&gt;mem0_v = rman_get_virtual(sc-&gt;mem0_r);</programlisting>
+
+ <para>DMA请求通道(DRQ)以相似方式被分配。使用
+ <function>isa_dma*()</function>函数族进行初始化。例如:
+ </para>
+
+ <para><function>isa_dmacascade(sc-&gt;drq0);</function></para>
+
+ <para>中断请求线(IRQ)有点特殊。除了分配以外,驱动程序的中断处理
+ 函数也应当与它关联。在古老的ISA驱动程序中,由系统传递给中断处理
+ 函数的参量是设备单元号。但在现代驱动程序中,按照约定,建议传递
+ 指向结构softc的指针。一个很重要的原因在于当结构softc被动态分配后,
+ 从softc取得单元号很容易,而从单元号取得softc很困难。同时,这个
+ 约定也使得用于不同总线的应用程序看起来统一,并允许它们共享代码:
+ 每个总线有其自己的探测,连接,分离和其他总线相关的例程,而它们
+ 之间可以共享大块的驱动程序代码。
+ </para>
+
+ <programlisting>
+ sc-&gt;intr_rid = 0;
+ sc-&gt;intr_r = bus_alloc_resource(dev, SYS_RES_MEMORY, &#38;sc-&gt;intr_rid,
+ /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE);
+
+ if(sc-&gt;intr_r == NULL)
+ goto bad;
+
+ /*
+ * 假定对XXX_INTR_TYPE的定义依赖于驱动程序的类型,
+ * 例如INTR_TYPE_CAM用于CAM的驱动程序
+ */
+ error = bus_setup_intr(dev, sc-&gt;intr_r, XXX_INTR_TYPE,
+ (driver_intr_t *) xxx_intr, (void *) sc, &#38;sc-&gt;intr_cookie);
+ if(error)
+ goto bad;
+
+ </programlisting>
+
+
+ <para>如果驱动程序需要与内存进行DMA,则这块内存应当按前述方式分配:
+ </para>
+
+ <programlisting> error=bus_dma_tag_create(NULL, /*alignment*/ 4,
+ /*boundary*/ 0, /*lowaddr*/ BUS_SPACE_MAXADDR_24BIT,
+ /*highaddr*/ BUS_SPACE_MAXADDR, /*filter*/ NULL, /*filterarg*/ NULL,
+ /*maxsize*/ BUS_SPACE_MAXSIZE_24BIT,
+ /*nsegments*/ BUS_SPACE_UNRESTRICTED,
+ /*maxsegsz*/ BUS_SPACE_MAXSIZE_24BIT, /*flags*/ 0,
+ &#38;sc-&gt;parent_tag);
+ if(error)
+ goto bad;
+
+ /* 很多东西是从父标签继承而来
+ * 假设sc-&gt;data指向存储共享数据的结构,例如一个环缓冲区可能是:
+ * struct {
+ * u_short rd_pos;
+ * u_short wr_pos;
+ * char bf[XXX_RING_BUFFER_SIZE]
+ * } *data;
+ */
+ error=bus_dma_tag_create(sc-&gt;parent_tag, 1,
+ 0, BUS_SPACE_MAXADDR, 0, /*filter*/ NULL, /*filterarg*/ NULL,
+ /*maxsize*/ sizeof(* sc-&gt;data), /*nsegments*/ 1,
+ /*maxsegsz*/ sizeof(* sc-&gt;data), /*flags*/ 0,
+ &#38;sc-&gt;data_tag);
+ if(error)
+ goto bad;
+
+ error = bus_dmamem_alloc(sc-&gt;data_tag, &#38;sc-&gt;data, /* flags*/ 0,
+ &#38;sc-&gt;data_map);
+ if(error)
+ goto bad;
+
+ /* 在&#38;sc-&gt;data_p的情况下,xxx_alloc_callback()只是将物理地址
+ * 保存到作为其参量传递进去的指针中。
+ * 参看关于总线内存映射一节中的详细内容。
+ * 其实现可以像这样:
+ *
+ * static void
+ * xxx_alloc_callback(void *arg, bus_dma_segment_t *seg,
+ * int nseg, int error)
+ * {
+ * *(bus_addr_t *)arg = seg[0].ds_addr;
+ * }
+ */
+ bus_dmamap_load(sc-&gt;data_tag, sc-&gt;data_map, (void *)sc-&gt;data,
+ sizeof (* sc-&gt;data), xxx_alloc_callback, (void *) &#38;sc-&gt;data_p,
+ /*flags*/0);</programlisting>
+
+
+ <para>分配了所有的资源后,设备应当被初始化。初始化可能包括测试
+ 所有特性,确保它们起作用。</para>
+
+ <programlisting> if(xxx_initialize(sc) &lt; 0)
+ goto bad; </programlisting>
+
+
+ <para>总线子系统将自动在控制台上打印由探测例程设置的设备描述。但
+ 如果驱动程序想打印一些关于设备的额外信息,也是可能的,例如:</para>
+
+ <programlisting>
+ device_printf(dev, "has on-card FIFO buffer of %d bytes\n", sc-&gt;fifosize);
+ </programlisting>
+
+ <para>如果初始化例程遇到任何问题,建议返回错误之前打印有关信息。</para>
+
+ <para>连接例程的最后一步是将设备连接到内核中的功能子系统。完成
+ 这个步骤的精确方式依赖于驱动程序的类型:字符设备、块设备、网络
+ 设备、CAM SCSI总线设备等等。</para>
+
+ <para>如果所有均工作正常则返回成功。</para>
+
+ <programlisting> error = xxx_attach_subsystem(sc);
+ if(error)
+ goto bad;
+
+ return 0; </programlisting>
+
+ <para>最后,处理棘手情况。返回错误前,所有资源应当被取消分配。
+ 我们利用这样一个事实:结构softc传递给我们之前被零化,因此我们
+ 能找出是否分配了某些资源:如果分配则它们的描述符非零。</para>
+
+ <programlisting> bad:
+
+ xxx_free_resources(sc);
+ if(error)
+ return error;
+ else /* exact error is unknown */
+ return ENXIO;</programlisting>
+
+ <para>这就是连接例程的全部。</para>
+
+ </sect1>
+
+
+ <sect1 xml:id="isa-driver-detach">
+ <title>xxx_isa_detach</title>
+
+ <para>
+ 如果驱动程序中存在这个函数,且驱动程序被编译为可加载模块,则
+ 驱动程序具有被卸载的能力。如果硬件支持热插拔,这是一个很重要的
+ 特性。但ISA总线不支持热插拔,因此这个特性对于ISA设备不是特别
+ 重要。卸载驱动程序的能力可能在调试时有用,但很多情况下只有在
+ 老版本的驱动程序莫名其妙地卡住系统的情况下才需要安装新版本的
+ 驱动程序,并且无论如何都需要重启,这样使得花费精力写分离例程
+ 有些不值得。另一个宣称卸载允许在用于生产的机器上升级驱动程序的
+ 论点看起来似乎更多的只是理论而已。升级驱动程序是一项危险的操作,
+ 决不不应当在用于生产的机器上实行(并且当系统运行于安全模式时这
+ 也是不被允许的)。然而,出于完整性考虑,还是会提供分离例程。
+ </para>
+
+ <para>
+ 如果驱动程序成功分离,分离例程返回0,否则返回错误码。
+ </para>
+
+ <para>
+ 分离逻辑是连接的镜像。要做的第一件事情就是将驱动程序从内核
+ 子系统分离。如果设备当前正打开着,驱动程序有两个选择:拒绝分离
+ 或者强制关闭并继续进行分离。选用哪种方式取决于特定内核子系统
+ 执行强制关闭的能力和驱动程序作者的偏好。通常强制关闭似乎是
+ 更好的选择。
+ <programlisting> struct xxx_softc *sc = device_get_softc(dev);
+ int error;
+
+ error = xxx_detach_subsystem(sc);
+ if(error)
+ return error;</programlisting>
+ </para>
+ <para>
+ 下一步,驱动程序可能希望复位硬件到某种一致的状态。包括停止任何
+ 将要进行的传输,禁用DMA通道和中断以避免设备破坏内存。对于大多数
+ 驱动程序而言,这正是关闭例程所做的,因此如果驱动程序中包括关闭
+ 例程,我们只要调用它就可以了。
+ </para>
+ <para><function>xxx_isa_shutdown(dev);</function></para>
+
+ <para>
+ 最后释放所有资源并返回成功。
+ <programlisting> xxx_free_resources(sc);
+ return 0;</programlisting>
+
+ </para>
+ </sect1>
+
+ <sect1 xml:id="isa-driver-shutdown">
+ <title>xxx_isa_shutdown</title>
+
+ <para>
+ 当系统要关闭的时候调用此例程。通过它使硬件进入某种一致的状态。
+ 对于大多数ISA设备而言不需要特殊动作,因此这个函数并非真正必需,
+ 因为不管怎样重启动时设备会被重新初始化。但有些设备必须按特定
+ 步骤关闭,以确保在软重启后能被正确地检测到(对于很多使用私有
+ 识别协议的设备特别有用)。很多情况下,在设备寄存器中禁用DMA和
+ 中断,并停止将要进行的传输是个好主意。确切动作取决于硬件,因此
+ 我们无法在此详细讨论。
+ </para>
+ </sect1>
+
+ <sect1 xml:id="isa-driver-intr">
+ <title>xxx_intr</title>
+
+ <indexterm><primary>interrupt handler(中断处理程序)</primary></indexterm>
+
+ <para>
+ 当收到来自特定设备的中断时就会调用中断处理函数。ISA总线不支持
+ 中断共享(某些特殊情况例外),因此实际上如果中断处理函数被调用,
+ 几乎可以确信中断是来自其设备。然而,中断处理函数必须轮询设备
+ 寄存器并确保中断是由它的设备产生的。如果不是,中断处理函数应当
+ 返回。
+ </para>
+
+ <para>
+ ISA驱动程序的旧约定是取设备单元号作为参量。现在已经废弃,当
+ 调用<function>bus_setup_intr()</function>时新驱动程序接收任何
+ 在连接例程中为他们指定的参量。根据新约定,它应当是指向结构
+ softc的指针。因此中断处理函数通常像下面那样开始:
+ </para>
+
+ <programlisting>
+ static void
+ xxx_intr(struct xxx_softc *sc)
+ {
+
+ </programlisting>
+
+ <para>
+ 它运行在由<function>bus_setup_intr()</function>的中断类型参数指定
+ 的中断优先级上。这意味着禁用所有其他同类型的中断和所有软件中断。
+ </para>
+
+ <para>
+ 为了避免竞争,中断处理例程通写成循环形式:
+ </para>
+
+ <programlisting>
+ while(xxx_interrupt_pending(sc)) {
+ xxx_process_interrupt(sc);
+ xxx_acknowledge_interrupt(sc);
+ } </programlisting>
+
+ <para>
+ 中断处理函数必须只向设备应答中断,但不能向中断控制器应答,后者由
+ 系统负责处理。
+ </para>
+
+ </sect1>
+</chapter>
diff --git a/zh_CN.UTF-8/books/arch-handbook/jail/chapter.xml b/zh_CN.UTF-8/books/arch-handbook/jail/chapter.xml
new file mode 100644
index 0000000000..541d9cdb67
--- /dev/null
+++ b/zh_CN.UTF-8/books/arch-handbook/jail/chapter.xml
@@ -0,0 +1,636 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ The FreeBSD Documentation Project
+ The FreeBSD Simplified Chinese Project
+
+ Original Revision: 1.22
+ $FreeBSD$
+-->
+<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" xml:id="jail">
+ <info><title>Jail子系统</title>
+ <author><personname><firstname>Evan</firstname><surname>Sarmiento</surname></personname><affiliation>
+ <address><email>evms@cs.bu.edu</email></address>
+ </affiliation></author>
+ <copyright>
+ <year>2001</year>
+ <holder role="mailto:evms@cs.bu.edu">Evan Sarmiento</holder>
+ </copyright>
+ <authorgroup>
+ <author><personname/><contrib>&cnproj.translated.by;</contrib></author>
+ </authorgroup>
+ </info>
+
+
+ <indexterm><primary>security(安全)</primary></indexterm>
+ <indexterm><primary>Jail(囚禁)</primary></indexterm>
+ <indexterm><primary>root(根用户,管理员用户)</primary></indexterm>
+ <para>在大多数&unix;系统中,用户<literal>root</literal>是万能的。这也就增加了许多危险。
+ 如果一个攻击者获得了一个系统中的<literal>root</literal>,就可以在他的指尖掌握系统中所有的功能。
+ 在FreeBSD里,有一些sysctl项削弱了<literal>root</literal>的权限,
+ 这样就可以将攻击者造成的损害减小到最低限度。这些安全功能中,有一种叫安全级别。
+ 另一种在FreeBSD 4.0及以后版本中提供的安全功能,就是&man.jail.8;。
+ <application>Jail</application>将一个运行环境的文件树根切换到某一特定位置,
+ 并且对这样环境中叉分生成的进程做出限制。例如,
+ 一个被监禁的进程不能影响这个<application>jail</application>之外的进程、不能使用一些特定的系统调用,
+ 也就不能对主计算机造成破坏。<tip><title>译者注</title>
+ <para>英文单词“jail”的中文意思是“囚禁、监禁”。</para></tip></para>
+
+ <para><application>Jail</application>已经成为一种新型的安全模型。
+ 人们可以在jail中运行各种可能很脆弱的服务器程序,如<application>Apache</application>、
+ <application>BIND</application>和<application>sendmail</application>。
+ 这样一来,即使有攻击者取得了<application>jail</application>中的<literal>root</literal>,
+ 这最多让人们皱皱眉头,而不会使人们惊慌失措。
+ 本文主要关注<application>jail</application>的内部原理(源代码)。
+ 如果你正在寻找设置<application>Jail</application>的指南性文档,
+ 我建议你阅读我的另一篇文章,发表在Sys Admin Magazine, May 2001,
+ 《Securing FreeBSD using <application>Jail</application>》。</para>
+
+ <sect1 xml:id="jail-arch">
+ <title>Jail的系统结构</title>
+
+ <para><application>Jail</application>由两部分组成:用户级程序,
+ 也就是&man.jail.8;;还有在内核中Jail的实现代码:&man.jail.2;
+ 系统调用和相关的约束。我将讨论用户级程序和<application>jail</application>在内核中的实现原理。</para>
+
+ <sect2>
+ <title>用户级代码</title>
+
+ <indexterm><primary>Jail(囚禁)</primary>
+ <secondary>userland program(用户级程序)</secondary></indexterm>
+
+ <para><application>Jail</application>的用户级源代码在<filename>/usr/src/usr.sbin/jail</filename>,
+ 由一个文件<filename>jail.c</filename>组成。这个程序有这些参数:<application>jail</application>的路径,
+ 主机名,IP地址,还有需要执行的命令。</para>
+
+ <sect3>
+ <title>数据结构</title>
+
+ <para>在<filename>jail.c</filename>中,我将最先注解的是一个重要结构体
+ <literal>struct jail j;</literal>的声明,这个结构类型的声明包含在
+ <filename>/usr/include/sys/jail.h</filename>之中。</para>
+
+ <para><literal>jail</literal>结构的定义是:</para>
+
+<programlisting><filename>/usr/include/sys/jail.h</filename>:
+
+struct jail {
+ u_int32_t version;
+ char *path;
+ char *hostname;
+ u_int32_t ip_number;
+};</programlisting>
+
+ <para>正如你所见,传送给命令&man.jail.8;的每个参数都在这里有对应的一项。
+ 事实上,当命令&man.jail.8;被执行时,这些参数才由命令行真正传入:</para>
+
+ <programlisting><filename>/usr/src/usr.sbin/jail.c</filename>
+char path[PATH_MAX];
+...
+if(realpath(argv[0], path) == NULL)
+ err(1, "realpath: %s", argv[0]);
+if (chdir(path) != 0)
+ err(1, "chdir: %s", path);
+memset(&amp;j, 0, sizeof(j));
+j.version = 0;
+j.path = path;
+j.hostname = argv[1];</programlisting>
+
+ </sect3>
+
+ <sect3>
+ <title>网络</title>
+
+ <para>传给&man.jail.8;的参数中有一个是IP地址。这是在网络上访问<application>jail</application>时的地址。
+ &man.jail.8;将IP地址翻译成网络字节顺序,并存入<literal>j</literal>(<literal>jail</literal>类型的结构体)。</para>
+
+ <programlisting><filename>/usr/src/usr.sbin/jail/jail.c</filename>:
+struct in_addr in;
+...
+if (inet_aton(argv[2], &amp;in) == 0)
+ errx(1, "Could not make sense of ip-number: %s", argv[2]);
+j.ip_number = ntohl(in.s_addr);</programlisting>
+
+ <para>函数&man.inet.aton.3;“将指定的字符串解释为一个Internet地址,
+ 并将其转存到指定的结构体中”。&man.inet.aton.3;设定了结构体in,
+ 之后in中的内容再用&man.ntohl.3;转换成主机字节顺序,
+ 并置入<literal>jail</literal>结构体的<literal>ip_number</literal>成员。</para>
+
+ </sect3>
+
+ <sect3>
+ <title>囚禁进程</title>
+
+ <para>最后,用户级程序囚禁进程。现在Jail自身变成了一个被囚禁的进程,
+ 并使用&man.execv.3;执行用户指定的命令。</para>
+
+ <programlisting><filename>/usr/src/usr.sbin/jail/jail.c</filename>
+i = jail(&amp;j);
+...
+if (execv(argv[3], argv + 3) != 0)
+ err(1, "execv: %s", argv[3]);</programlisting>
+
+ <para>正如你所见,函数<literal>jail()</literal>被调用,参数是结构体<literal>jail</literal>中被填入数据项,
+ 而如前所述,这些数据项又来自&man.jail.8;的命令行参数。
+ 最后,执行了用户指定的命令。下面我将开始讨论<literal>jail</literal>在内核中的实现。</para>
+ </sect3>
+ </sect2>
+
+ <sect2>
+ <title>相关的内核源代码</title>
+
+ <indexterm><primary>Jail(囚禁)</primary>
+ <secondary>kernel architecture(内核架构)</secondary></indexterm>
+
+ <para>现在我们来看文件<filename>/usr/src/sys/kern/kern_jail.c</filename>。
+ 在这里定义了&man.jail.2;的系统调用、相关的sysctl项,还有网络函数。</para>
+
+ <sect3>
+ <title>sysctl项</title>
+
+ <indexterm><primary>sysctl(系统控制项)</primary></indexterm>
+
+ <para>在<filename>kern_jail.c</filename>里定义了如下sysctl项:</para>
+
+ <programlisting><filename>/usr/src/sys/kern/kern_jail.c:</filename>
+
+int jail_set_hostname_allowed = 1;
+SYSCTL_INT(_security_jail, OID_AUTO, set_hostname_allowed, CTLFLAG_RW,
+ &amp;jail_set_hostname_allowed, 0,
+ "Processes in jail can set their hostnames");
+ /* Jail中的进程可设定自身的主机名 */
+
+int jail_socket_unixiproute_only = 1;
+SYSCTL_INT(_security_jail, OID_AUTO, socket_unixiproute_only, CTLFLAG_RW,
+ &amp;jail_socket_unixiproute_only, 0,
+ "Processes in jail are limited to creating UNIX/IPv4/route sockets only");
+ /* Jail中的进程被限制只能建立UNIX套接字、IPv4套接字、路由套接字 */
+
+int jail_sysvipc_allowed = 0;
+SYSCTL_INT(_security_jail, OID_AUTO, sysvipc_allowed, CTLFLAG_RW,
+ &amp;jail_sysvipc_allowed, 0,
+ "Processes in jail can use System V IPC primitives");
+ /* Jail中的进程可以使用System V进程间通讯原语 */
+
+static int jail_enforce_statfs = 2;
+SYSCTL_INT(_security_jail, OID_AUTO, enforce_statfs, CTLFLAG_RW,
+ &amp;jail_enforce_statfs, 0,
+ "Processes in jail cannot see all mounted file systems");
+ /* jail 中的进程查看系统中挂接的文件系统时受到何种限制 */
+
+int jail_allow_raw_sockets = 0;
+SYSCTL_INT(_security_jail, OID_AUTO, allow_raw_sockets, CTLFLAG_RW,
+ &amp;jail_allow_raw_sockets, 0,
+ "Prison root can create raw sockets");
+ /* jail 中的 root 用户是否可以创建 raw socket */
+
+int jail_chflags_allowed = 0;
+SYSCTL_INT(_security_jail, OID_AUTO, chflags_allowed, CTLFLAG_RW,
+ &amp;jail_chflags_allowed, 0,
+ "Processes in jail can alter system file flags");
+ /* jail 中的进程是否可以修改系统级文件标记 */
+
+int jail_mount_allowed = 0;
+SYSCTL_INT(_security_jail, OID_AUTO, mount_allowed, CTLFLAG_RW,
+ &amp;jail_mount_allowed, 0,
+ "Processes in jail can mount/unmount jail-friendly file systems");
+ /* jail 中的进程是否可以挂载或卸载对jail友好的文件系统 */</programlisting>
+
+ <para>这些sysctl项中的每一个都可以用命令&man.sysctl.8;访问。在整个内核中,
+ 这些sysctl项按名称标识。例如,上述第一个sysctl项的名字是
+ <literal>security.jail.set_hostname_allowed</literal>。</para>
+ </sect3>
+
+ <sect3>
+ <title>&man.jail.2;系统调用</title>
+
+ <para>像所有的系统调用一样,系统调用&man.jail.2;带有两个参数,
+ <literal>struct thread *td</literal>和<literal>struct jail_args *uap</literal>。
+ <literal>td</literal>是一个指向<literal>thread</literal>结构体的指针,该指针用于描述调用&man.jail.2;的线程。
+ 在这个上下文中,<literal>uap</literal>指向一个结构体,这个结构体中包含了一个指向从用户级
+ <filename>jail.c</filename>传送过来的<literal>jail</literal>结构体的指针。
+ 在前面我讲述用户级程序时,你已经看到过一个<literal>jail</literal>结构体被作为参数传送给系统调用
+ &man.jail.2;。</para>
+
+ <programlisting><filename>/usr/src/sys/kern/kern_jail.c:</filename>
+/*
+ * struct jail_args {
+ * struct jail *jail;
+ * };
+ */
+int
+jail(struct thread *td, struct jail_args *uap)</programlisting>
+
+ <para>于是<literal>uap-&gt;jail</literal>可以用于访问被传递给&man.jail.2;的<literal>jail</literal>结构体。
+ 然后,&man.jail.2;使用&man.copyin.9;将<literal>jail</literal>结构体复制到内核内存空间中。
+ &man.copyin.9;需要三个参数:要复制进内核内存空间的数据的地址
+ <literal>uap-&gt;jail</literal>,在内核内存空间存放数据的<literal>j</literal>,
+ 以及数据的大小。<literal>uap-&gt;jail</literal>指向的Jail结构体被复制进内核内存空间,
+ 并被存放在另一个<literal>jail</literal>结构体<literal>j</literal>里。</para>
+
+ <programlisting><filename>/usr/src/sys/kern/kern_jail.c: </filename>
+error = copyin(uap-&gt;jail, &amp;j, sizeof(j));</programlisting>
+
+ <para>在jail.h中定义了另一个重要的结构体型prison。
+ 结构体<literal>prison</literal>只被用在内核空间中。
+ 下面是<literal>prison</literal>结构体的定义。</para>
+
+ <programlisting><filename>/usr/include/sys/jail.h</filename>:
+struct prison {
+ LIST_ENTRY(prison) pr_list; /* (a) all prisons */
+ int pr_id; /* (c) prison id */
+ int pr_ref; /* (p) refcount */
+ char pr_path[MAXPATHLEN]; /* (c) chroot path */
+ struct vnode *pr_root; /* (c) vnode to rdir */
+ char pr_host[MAXHOSTNAMELEN]; /* (p) jail hostname */
+ u_int32_t pr_ip; /* (c) ip addr host */
+ void *pr_linux; /* (p) linux abi */
+ int pr_securelevel; /* (p) securelevel */
+ struct task pr_task; /* (d) destroy task */
+ struct mtx pr_mtx;
+ void **pr_slots; /* (p) additional data */
+};</programlisting>
+
+ <para>然后,系统调用&man.jail.2;为一个<literal>prison</literal>结构体分配一块内存,
+ 并在<literal>jail</literal>和<literal>prison</literal>结构体之间复制数据。</para>
+
+ <programlisting><filename>/usr/src/sys/kern/kern_jail.c</filename>:
+MALLOC(pr, struct prison *, sizeof(*pr), M_PRISON, M_WAITOK | M_ZERO);
+...
+error = copyinstr(j.path, &amp;pr-&gt;pr_path, sizeof(pr-&gt;pr_path), 0);
+if (error)
+ goto e_killmtx;
+...
+error = copyinstr(j.hostname, &amp;pr-&gt;pr_host, sizeof(pr-&gt;pr_host), 0);
+if (error)
+ goto e_dropvnref;
+pr-&gt;pr_ip = j.ip_number;</programlisting>
+
+ <para>下面,我们将讨论另外一个重要的系统调用&man.jail.attach.2;,它实现了将进程监禁的功能。</para>
+ <programlisting><filename>/usr/src/sys/kern/kern_jail.c</filename>
+/*
+ * struct jail_attach_args {
+ * int jid;
+ * };
+ */
+int
+jail_attach(struct thread *td, struct jail_attach_args *uap)</programlisting>
+ <para>这个系统调用做出一些可以用于区分被监禁和未被监禁的进程的改变。
+ 要理解&man.jail.attach.2;为我们做了什么,我们首先要理解一些背景信息。</para>
+ <para>在FreeBSD中,每个对内核可见的线程是通过其<literal>thread</literal>结构体来识别的,
+ 同时,进程都由它们自己的<literal>proc</literal>结构体描述。
+ 你可以在<filename>/usr/include/sys/proc.h</filename>中找到<literal>thread</literal>和<literal>proc</literal>结构体的定义。
+ 例如,在任何系统调用中,参数<literal>td</literal>实际上是个指向调用线程的<literal>thread</literal>结构体的指针,
+ 正如前面所说的那样。<literal>td</literal>所指向的<literal>thread</literal>结构体中的<literal>td_proc</literal>成员是一个指针,
+ 这个指针指向<literal>td</literal>所表示的线程所属进程的<literal>proc</literal>结构体。
+ 结构体<literal>proc</literal>包含的成员可以描述所有者的身份
+ (<literal>p_ucred</literal>),进程资源限制(<literal>p_limit</literal>),
+ 等等。在由<literal>proc</literal>结构体的<literal>p_ucred</literal>成员所指向的ucred结构体的定义中,
+ 还有一个指向<literal>prison</literal>结构体的指针(<literal>cr_prison</literal>)。</para>
+ <programlisting><filename>/usr/include/sys/proc.h: </filename>
+struct thread {
+ ...
+ struct proc *td_proc;
+ ...
+};
+struct proc {
+ ...
+ struct ucred *p_ucred;
+ ...
+};
+<filename>/usr/include/sys/ucred.h</filename>
+struct ucred {
+ ...
+ struct prison *cr_prison;
+ ...
+};</programlisting>
+
+ <para>在<filename>kern_jail.c</filename>中,函数<literal>jail()</literal>以给定的<literal>jid</literal>
+ 调用函数<literal>jail_attach()</literal>。随后<literal>jail_attach()</literal>调用函数<literal>change_root()</literal>以改变
+ 调用进程的根目录。接下来,<literal>jail_attach()</literal>创建一个新的<literal>ucred</literal>结构体,并在
+ 成功地将<literal>prison</literal>结构体连接到这个<literal>ucred</literal>结构体后,将这个<literal>ucred</literal>结构体连接
+ 到调用进程上。从此时起,这个调用进程就会被识别为被监禁的。
+ 当我们以新创建的这个<literal>ucred</literal>结构体为参数调用内核路径<literal>jailed()</literal>时,
+ 它将返回1来说明这个用户身份是和一个<application>jail</application>相连的。
+ 在<application>jail</application>中叉分出来的所有进程的的公共祖先进程就是这个执行了&man.jail.2;的进程,
+ 因为正是它调用了&man.jail.2;系统调用。当一个程序通过&man.execve.2;而被执行时,
+ 它将从其父进程的<literal>ucred</literal>结构体继承被监禁的属性,
+ 因而它也会拥有一个被监禁的<literal>ucred</literal>结构体。</para>
+
+ <programlisting><filename>/usr/src/sys/kern/kern_jail.c</filename>
+int
+jail(struct thread *td, struct jail_args *uap)
+{
+...
+ struct jail_attach_args jaa;
+...
+ error = jail_attach(td, &amp;jaa);
+ if (error)
+ goto e_dropprref;
+...
+}
+
+int
+jail_attach(struct thread *td, struct jail_attach_args *uap)
+{
+ struct proc *p;
+ struct ucred *newcred, *oldcred;
+ struct prison *pr;
+...
+ p = td-&gt;td_proc;
+...
+ pr = prison_find(uap-&gt;jid);
+...
+ change_root(pr-&gt;pr_root, td);
+...
+ newcred-&gt;cr_prison = pr;
+ p-&gt;p_ucred = newcred;
+...
+}</programlisting>
+ <para>当一个进程被从其父进程叉分来的时候,
+ 系统调用&man.fork.2;将用<literal>crhold()</literal>来维护其身份凭证。
+ 这样,很自然的就保持了子进程的身份凭证于其父进程一致,所以子进程也是被监禁的。</para>
+
+ <programlisting><filename>/usr/src/sys/kern/kern_fork.c</filename>:
+p2-&gt;p_ucred = crhold(td-&gt;td_ucred);
+...
+td2-&gt;td_ucred = crhold(p2-&gt;p_ucred);</programlisting>
+
+ </sect3>
+ </sect2>
+ </sect1>
+
+ <sect1 xml:id="jail-restrictions">
+ <title>系统对被囚禁程序的限制</title>
+
+ <para>在整个内核中,有一系列对被囚禁程序的约束措施。
+ 通常,这些约束只对被囚禁的程序有效。如果这些程序试图突破这些约束,
+ 相关的函数将出错返回。例如:</para>
+
+ <programlisting>
+if (jailed(td-&gt;td_ucred))
+ return EPERM;</programlisting>
+
+ <sect2>
+ <title>SysV进程间通信(IPC)</title>
+
+ <indexterm><primary>System V IPC(系统V进程间通信)</primary></indexterm>
+
+ <para>System V 进程间通信 (IPC) 是通过消息实现的。
+ 每个进程都可以向其它进程发送消息, 告诉对方该做什么。
+ 处理消息的函数是: &man.msgctl.3;、&man.msgget.3;、&man.msgsnd.3; 和
+ &man.msgrcv.3;。前面已经提到,一些 sysctl 开关可以影响 <application>jail</application> 的行为,
+ 其中有一个是 <literal>security.jail.sysvipc_allowed</literal>。 在大多数系统上,
+ 这个 sysctl 项会设成0。 如果将它设为1, 则会完全失去 <application>jail</application> 的意义:
+ 因为那样在 <application>jail</application> 中特权进程就可以影响被监禁的环境外的进程了。
+ 消息与信号的区别是:消息仅由一个信号编号组成。</para>
+
+ <para><filename>/usr/src/sys/kern/sysv_msg.c</filename>:</para>
+
+ <itemizedlist>
+ <listitem><para><literal>msgget(key, msgflg)</literal>:
+ <literal>msgget</literal>返回(也可能创建)一个消息描述符,
+ 以指派一个在其它函数中使用的消息队列。</para></listitem>
+
+ <listitem><para><literal>msgctl(msgid, cmd, buf)</literal>: 通过这个函数,
+ 一个进程可以查询一个消息描述符的状态。</para></listitem>
+
+ <listitem><para><literal>msgsnd(msgid, msgp, msgsz, msgflg)</literal>:
+ <literal>msgsnd</literal>向一个进程发送一条消息。</para></listitem>
+
+ <listitem><para><literal>msgrcv(msgid, msgp, msgsz, msgtyp, msgflg)</literal>:
+ 进程用这个函数接收消息。</para></listitem>
+
+ </itemizedlist>
+
+ <para>在这些函数对应的系统调用的代码中,都有这样一个条件判断:</para>
+
+ <programlisting><filename>/usr/src/sys/kern/sysv_msg.c</filename>:
+if (!jail_sysvipc_allowed &amp;&amp; jailed(td-&gt;td_ucred))
+ return (ENOSYS);</programlisting>
+
+ <indexterm><primary>semaphores(信号量)</primary></indexterm>
+ <para>信号量系统调用使得进程可以通过一系列原子操作实现同步。
+ 信号量为进程锁定资源提供了又一种途径。
+ 然而,进程将为正在被使用的信号量进入等待状态,一直休眠到资源被释放。
+ 在<application>jail</application>中如下的信号量系统调用将会失效: &man.semget.2;, &man.semctl.2;
+ 和&man.semop.2;。</para>
+
+ <para><filename>/usr/src/sys/kern/sysv_sem.c</filename>:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><literal>semctl(semid, num, cmd, ...)</literal>:
+ <literal>semctl</literal>对在信号量队列中用<literal>semid</literal>标识的信号量执行<literal>cmd</literal>指定的命令。</para></listitem>
+
+ <listitem>
+ <para><literal>semget(key, nsems, flag)</literal>:
+ <literal>semget</literal>建立一个对应于<literal>key</literal>的信号量数组。</para>
+
+ <para><literal>参数key和flag与他们在msgget()的意义相同。</literal></para></listitem>
+
+ <listitem><para><literal>setop(semid, array, nops)</literal>:
+ <literal>semop</literal>对semid标识的信号量完成一组由array所指定的操作。</para></listitem>
+ </itemizedlist>
+
+ <indexterm><primary>shared memory(共享内存)</primary></indexterm>
+ <para>System V IPC使进程间可以共享内存。进程之间可以通过它们虚拟地址空间
+ 的共享部分以及相关数据读写操作直接通讯。这些系统调用在被监禁的环境中将会失效:
+ &man.shmdt.2;、&man.shmat.2;、&man.shmctl.2;和&man.shmget.2;</para>
+
+ <para><filename>/usr/src/sys/kern/sysv_shm.c</filename>:</para>
+
+ <itemizedlist>
+ <listitem><para><literal>shmctl(shmid, cmd, buf)</literal>:
+ <literal>shmctl</literal>对<literal>id</literal>标识的共享内存区域做各种各样的控制。</para></listitem>
+
+ <listitem><para><literal>shmget(key, size, flag)</literal>:
+ <literal>shmget</literal>建立/打开<literal>size</literal>字节的共享内存区域。</para></listitem>
+
+ <listitem><para><literal>shmat(shmid, addr, flag)</literal>:
+ <literal>shmat</literal>将<literal>shmid</literal>标识的共享内存区域指派到进程的地址空间里。</para></listitem>
+
+ <listitem><para><literal>shmdt(addr)</literal>:
+ <literal>shmdt</literal>取消共享内存区域的地址指派。</para></listitem>
+
+ </itemizedlist>
+ </sect2>
+
+ <sect2>
+ <title>套接字</title>
+
+ <indexterm><primary>sockets(套接字)</primary></indexterm>
+ <para><application>Jail</application>以一种特殊的方式处理&man.socket.2;系统调用和相关的低级套接字函数。
+ 为了决定一个套接字是否允许被创建,它先检查sysctl项
+ <literal>security.jail.socket_unixiproute_only</literal>是否被设置为1。
+ 如果被设为1,套接字建立时将只能指定这些协议族:
+ <literal>PF_LOCAL</literal>, <literal>PF_INET</literal>,
+ <literal>PF_ROUTE</literal>。否则,&man.socket.2;将会返回出错。</para>
+
+ <programlisting><filename>/usr/src/sys/kern/uipc_socket.c</filename>:
+int
+socreate(int dom, struct socket **aso, int type, int proto,
+ struct ucred *cred, struct thread *td)
+{
+ struct protosw *prp;
+...
+ if (jailed(cred) &amp;&amp; jail_socket_unixiproute_only &amp;&amp;
+ prp-&gt;pr_domain-&gt;dom_family != PF_LOCAL &amp;&amp;
+ prp-&gt;pr_domain-&gt;dom_family != PF_INET &amp;&amp;
+ prp-&gt;pr_domain-&gt;dom_family != PF_ROUTE) {
+ return (EPROTONOSUPPORT);
+ }
+...
+}</programlisting>
+
+ </sect2>
+
+ <sect2>
+ <title>Berkeley包过滤器</title>
+
+ <indexterm><primary>Berkeley Packet Filter(伯克利包过滤器)</primary></indexterm>
+ <indexterm><primary>data link layer(数据链路层)</primary></indexterm>
+
+ <para><application>Berkeley包过滤器</application>提供了一个与协议无关的,直接通向数据链路层的低级接口。
+ 现在<application>BPF</application>是否可以在监禁的环境中被使用是通过&man.devfs.8;来控制的。</para>
+ </sect2>
+
+ <sect2>
+ <title>网络协议</title>
+
+ <indexterm><primary>protocols(协议)</primary></indexterm>
+
+ <para>网络协议TCP, UDP, IP和ICMP很常见。IP和ICMP处于同一协议层次:第二层,
+ 网络层。当参数<literal>nam</literal>被设置时,
+ 有一些限制措施会防止被囚禁的程序绑定到一些网络接口上。
+ <literal>nam</literal>是一个指向<literal>sockaddr</literal>结构体的指针,
+ 描述可以绑定服务的地址。一个更确切的定义:<literal>sockaddr</literal>“是一个模板,包含了地址的标识符和地址的长度”。
+ 在函数<literal>in_pcbbind_setup()</literal>中<literal>sin</literal>是一个指向<literal>sockaddr_in</literal>结构体的指针,
+ 这个结构体包含了套接字可以绑定的端口、地址、长度、协议族。
+ 这就禁止了在<application>jail</application>中的进程指定不属于这个进程所存在于的<application>jail</application>的IP地址。</para>
+ <programlisting><filename>/usr/src/sys/kern/netinet/in_pcb.c</filename>:
+int
+in_pcbbind_setup(struct inpcb *inp, struct sockaddr *nam, in_addr_t *laddrp,
+ u_short *lportp, struct ucred *cred)
+{
+ ...
+ struct sockaddr_in *sin;
+ ...
+ if (nam) {
+ sin = (struct sockaddr_in *)nam;
+ ...
+ if (sin-&gt;sin_addr.s_addr != INADDR_ANY)
+ if (prison_ip(cred, 0, &amp;sin-&gt;sin_addr.s_addr))
+ return(EINVAL);
+ ...
+ if (lport) {
+ ...
+ if (prison &amp;&amp; prison_ip(cred, 0, &amp;sin-&gt;sin_addr.s_addr))
+ return (EADDRNOTAVAIL);
+ ...
+ }
+ }
+ if (lport == 0) {
+ ...
+ if (laddr.s_addr != INADDR_ANY)
+ if (prison_ip(cred, 0, &amp;laddr.s_addr))
+ return (EINVAL);
+ ...
+ }
+...
+ if (prison_ip(cred, 0, &amp;laddr.s_addr))
+ return (EINVAL);
+...
+}</programlisting>
+
+ <para>你也许想知道函数<literal>prison_ip()</literal>做什么。
+ <literal>prison_ip()</literal>有三个参数,一个指向身份凭证的指针(用<literal>cred</literal>表示),
+ 一些标志和一个IP地址。当这个IP地址不属于这个<application>jail</application>时,返回1;
+ 否则返回0。正如你从代码中看见的,如果,那个IP地址确实不属于这个<application>jail</application>,
+ 就不再允许向这个网络地址绑定协议。</para>
+
+ <programlisting><filename>/usr/src/sys/kern/kern_jail.c:</filename>
+int
+prison_ip(struct ucred *cred, int flag, u_int32_t *ip)
+{
+ u_int32_t tmp;
+
+ if (!jailed(cred))
+ return (0);
+ if (flag)
+ tmp = *ip;
+ else
+ tmp = ntohl(*ip);
+ if (tmp == INADDR_ANY) {
+ if (flag)
+ *ip = cred-&gt;cr_prison-&gt;pr_ip;
+ else
+ *ip = htonl(cred-&gt;cr_prison-&gt;pr_ip);
+ return (0);
+ }
+ if (tmp == INADDR_LOOPBACK) {
+ if (flag)
+ *ip = cred-&gt;cr_prison-&gt;pr_ip;
+ else
+ *ip = htonl(cred-&gt;cr_prison-&gt;pr_ip);
+ return (0);
+ }
+ if (cred-&gt;cr_prison-&gt;pr_ip != tmp)
+ return (1);
+ return (0);
+}</programlisting>
+ </sect2>
+
+ <sect2>
+ <title>文件系统</title>
+
+ <indexterm><primary>filesystem(文件系统)</primary></indexterm>
+ <para>如果完全级别大于0,即便是<application>jail</application>里面的<literal>root</literal>,
+ 也不允许在Jail中取消或更改文件标志,如“不可修改”、“只可添加”、“不可删除”标志。</para>
+ <programlisting><filename>/usr/src/sys/ufs/ufs/ufs_vnops.c:</filename>
+static int
+ufs_setattr(ap)
+ ...
+{
+ ...
+ if (!priv_check_cred(cred, PRIV_VFS_SYSFLAGS, 0)) {
+ if (ip-&gt;i_flags
+ &amp; (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) {
+ error = securelevel_gt(cred, 0);
+ if (error)
+ return (error);
+ }
+ ...
+ }
+}
+<filename>/usr/src/sys/kern/kern_priv.c</filename>
+int
+priv_check_cred(struct ucred *cred, int priv, int flags)
+{
+ ...
+ error = prison_priv_check(cred, priv);
+ if (error)
+ return (error);
+ ...
+}
+<filename>/usr/src/sys/kern/kern_jail.c</filename>
+int
+prison_priv_check(struct ucred *cred, int priv)
+{
+ ...
+ switch (priv) {
+ ...
+ case PRIV_VFS_SYSFLAGS:
+ if (jail_chflags_allowed)
+ return (0);
+ else
+ return (EPERM);
+ ...
+ }
+ ...
+}</programlisting>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
diff --git a/zh_CN.UTF-8/books/arch-handbook/kobj/chapter.xml b/zh_CN.UTF-8/books/arch-handbook/kobj/chapter.xml
new file mode 100644
index 0000000000..28e1c74805
--- /dev/null
+++ b/zh_CN.UTF-8/books/arch-handbook/kobj/chapter.xml
@@ -0,0 +1,284 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ The FreeBSD Documentation Project
+ The FreeBSD Simplified Chinese Project
+
+ Original Revision: 1.7
+ $FreeBSD$
+-->
+<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" xml:id="kernel-objects">
+ <info><title>内核对象</title>
+ <authorgroup>
+ <author><personname/><contrib>&cnproj.translated.by;</contrib></author>
+ </authorgroup>
+ </info>
+
+
+ <indexterm><primary>Kernel Objects(内核对象)</primary></indexterm>
+ <indexterm><primary>Object-Oriented(面向对象)</primary></indexterm>
+ <indexterm><primary>binary compatibility(二进制兼容性)</primary></indexterm>
+ <para>内核对象,也就是<firstterm>Kobj</firstterm>,为内核提供了一种面向对象
+ 的C语言编程方式。被操作的数据也承载操作它的方法。
+ 这使得在不破坏二进制兼容性的前提下,某一个接口能够增/减相应的操作。</para>
+
+ <sect1 xml:id="kernel-objects-term">
+ <title>术语</title>
+
+ <indexterm><primary>object(对象)</primary></indexterm>
+ <indexterm><primary>method(方法)</primary></indexterm>
+ <indexterm><primary>class(类)</primary></indexterm>
+ <indexterm><primary>interface(接口)</primary></indexterm>
+
+ <variablelist>
+ <varlistentry>
+ <term>对象</term>
+ <listitem><para>数据集合-数据结构-数据分配的集合</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>方法</term>
+ <listitem>
+ <para>某一种操作&mdash;函数</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>类</term>
+ <listitem>
+ <para>一种或多种方法</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>接口</term>
+ <listitem>
+ <para>一种或多种方法的一个标准集合</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect1>
+
+ <sect1 xml:id="kernel-objects-operation">
+ <title>Kobj的工作流程</title>
+
+ <tip><title>译者注</title><para>这一小节两段落中原作者的用词有些含混,
+ 请参考我在括号中的注释阅读。</para></tip>
+
+ <para>Kobj工作时,产生方法的描述。每个描述有一个唯一的标识和一个缺省函数。
+ 某个描述的地址被用来在一个类的方法表里唯一的标识方法。</para>
+
+ <para>构建一个类,就是要建立一张方法表,并将这张表关联到一个或多个函数(方法);
+ 这些函数(方法)都带有方法描述。使用前,类要被编译。编译时要为这个类分配一些缓存。
+ 在方法表中的每个方法描述都会被指派一个唯一的标识,
+ 除非已经被其它引用它的类在编译时指派了标识。对于每个将要被使用的方法,
+ 都会由脚本生成一个函数(方法查找函数),以解析外来参数,
+ 并在被查询时给出方法描述的地址。被生成的函数(方法查找函数)
+ 凭着那个方法描述的唯一标识按Hash的方法查找对象的类的缓存。
+ 如果这个方法不在缓存中,函数会查找使用类的方法表。如果这个方法被找到了,
+ 类里的相关函数(也就是某个方法的实现代码)就会被使用。
+ 否则,这个方法描述的缺省函数将被使用。</para>
+
+ <para>这些过程可被表示如下:</para>
+
+ <programlisting>对象-&gt;缓存&lt;-&gt;类</programlisting>
+
+ </sect1>
+
+ <sect1 xml:id="kernel-objects-using">
+ <title>使用Kobj</title>
+
+ <sect2>
+ <title>结构</title>
+
+ <programlisting>struct kobj_method</programlisting>
+ </sect2>
+
+ <sect2>
+ <title>函数</title>
+
+ <programlisting>void kobj_class_compile(kobj_class_t cls);
+void kobj_class_compile_static(kobj_class_t cls, kobj_ops_t ops);
+void kobj_class_free(kobj_class_t cls);
+kobj_t kobj_create(kobj_class_t cls, struct malloc_type *mtype, int mflags);
+void kobj_init(kobj_t obj, kobj_class_t cls);
+void kobj_delete(kobj_t obj, struct malloc_type *mtype);</programlisting>
+ </sect2>
+
+ <sect2>
+ <title>宏</title>
+
+ <programlisting>KOBJ_CLASS_FIELDS
+KOBJ_FIELDS
+DEFINE_CLASS(name, methods, size)
+KOBJMETHOD(NAME, FUNC)</programlisting>
+ </sect2>
+
+ <sect2>
+ <title>头文件</title>
+
+ <programlisting>&lt;sys/param.h&gt;
+&lt;sys/kobj.h&gt;</programlisting>
+ </sect2>
+
+ <sect2>
+ <title>建立一个接口的模板</title>
+
+ <indexterm><primary>Kernel Objects(内核对象)</primary>
+ <secondary>interface(接口)</secondary></indexterm>
+
+ <para>使用Kobj的第一步是建立一个接口。建立接口包括建立模板的工作。
+ 建立模板可用脚本<filename>src/sys/kern/makeobjops.pl</filename>完成,
+ 它会产生申明方法的头文件和代码,脚本还会生成方法查找函数。</para>
+
+ <para>在这个模板中如下关键词会被使用:
+ <literal>#include</literal>, <literal>INTERFACE</literal>,
+ <literal>CODE</literal>, <literal>METHOD</literal>,
+ <literal>STATICMETHOD</literal>, 和
+ <literal>DEFAULT</literal>.</para>
+
+ <para><literal>#include</literal>语句的整行内容将被一字不差的
+ 复制到被生成的代码文件的头部。</para>
+
+ <para>例如:</para>
+
+ <programlisting>#include &lt;sys/foo.h&gt;</programlisting>
+
+ <para>关键词<literal>INTERFACE</literal>用来定义接口名。
+ 这个名字将与每个方法名接合在一起,形成 [interface name]_[method name]。
+ 语法是:INTERFACE [接口名];</para>
+
+ <para>例如:</para>
+
+ <programlisting>INTERFACE foo;</programlisting>
+
+ <para>关键词<literal>CODE</literal>会将它的参数一字不差的复制到代码文件中。
+ 语法是<literal>CODE { [任何代码] };</literal></para>
+
+ <para>例如:</para>
+
+ <programlisting>CODE {
+ struct foo * foo_alloc_null(struct bar *)
+ {
+ return NULL;
+}
+};</programlisting>
+
+ <para>关键词<literal>METHOD</literal>用来描述一个方法。语法是:
+ <literal>METHOD [返回值类型] [方法名] { [对象 [,
+ 参数若干]] };</literal></para>
+
+ <para>例如:</para>
+
+ <programlisting>METHOD int bar {
+ struct object *;
+ struct foo *;
+ struct bar;
+};</programlisting>
+
+ <para>关键词<literal>DEFAULT</literal>跟在关键词<literal>METHOD</literal>之后,
+ 是对关键词<literal>METHOD</literal>的补充。它给这个方法补充上缺省函数。语法是:
+ <literal>METHOD [返回值类型] [方法名] {
+ [对象; [其它参数]] }DEFAULT [缺省函数];
+ </literal></para>
+
+ <para>例如:</para>
+
+ <programlisting>METHOD int bar {
+ struct object *;
+ struct foo *;
+ int bar;
+} DEFAULT foo_hack;</programlisting>
+
+ <para>关键词<literal>STATICMETHOD</literal>类似关键词<literal>METHOD</literal>。
+ 对于每个Kobj对象,一般其头部都有一些Kobj专有的数据。
+ <literal>METHOD</literal>定义的方法就假设这些专有数据位于对象头部;
+ 假如对象头部没有这些专有数据,这些方法对这个对象的访问就可能出错。
+ 而<literal>STATICMETHOD</literal>定义的对象可以不受这个限制:
+ 这样描述出的方法,其操作的数据不由这个类的某个对象实例给出,
+ 而是全都由调用这个方法时的操作数(译者注:即参数)给出。
+ 这也对于在某个类的方法表之外调用这个方法有用。
+ <tip><title>译者注</title><para>这一段的语言与原文相比调整很大。
+ 静态方法是不依赖于对象实例的方法。
+ 参看C++类中的“静态函数”的概念。</para></tip></para>
+
+ <para>其它完整的例子:</para>
+
+ <programlisting>src/sys/kern/bus_if.m
+src/sys/kern/device_if.m</programlisting>
+
+ </sect2>
+
+ <sect2>
+ <title>建立一个类</title>
+
+ <indexterm><primary>Kernel Objects(内核对象)</primary>
+ <secondary>class(类)</secondary></indexterm>
+
+ <para>使用Kobj的第二步是建立一个类。一个类的组有名字、方法表;
+ 假如使用了Kobj的“对象管理工具”(Object Handling Facilities),
+ 类中还包含对象的大小。建立类时使用宏<function>DEFINE_CLASS()</function>。
+ 建立方法表时,须建立一个kobj_method_t数组,用NULL项结尾。
+ 每个非NULL项可用宏<function>KOBJMETHOD()</function>建立。</para>
+
+ <para>例如:</para>
+
+ <programlisting>DEFINE_CLASS(fooclass, foomethods, sizeof(struct foodata));
+
+kobj_method_t foomethods[] = {
+ KOBJMETHOD(bar_doo, foo_doo),
+ KOBJMETHOD(bar_foo, foo_foo),
+ { NULL, NULL}
+};</programlisting>
+
+ <para>类须被<quote>编译</quote>。根据该类被初始化时系统的状态,
+ 将要用到一个静态分配的缓存和<quote>操作数表</quote>(ops table,
+ 译者注:即<quote>参数表</quote>)。这些操作可通过声明一个结构体
+ <varname remap="structname">struct kobj_ops</varname>并使用
+ <function>kobj_class_compile_static()</function>,
+ 或是只使用<function>kobj_class_compile()</function>来完成。</para>
+ </sect2>
+
+ <sect2>
+ <title>建立一个对象</title>
+
+ <indexterm><primary>Kernel Objects(内核对象)</primary>
+ <secondary>object(对象)</secondary></indexterm>
+
+ <para>使用Kobj的第三步是定义对象。Kobj对象建立程序假定Kobj
+ 专有数据在一个对象的头部。如果不是如此,应当先自行分配对象,
+ 再使用<function>kobj_init()</function>初始化对象中的Kobj专有数据;
+ 其实可以使用<function>kobj_create()</function>分配对象,
+ 并自动初始化对象中的Kobj专有内容。<function>kobj_init()</function>
+ 也可以用来改变一个对象所使用的类。</para>
+
+ <para>将Kobj的数据集成到对象中要使用宏KOBJ_FIELDS。</para>
+
+ <para>例如</para>
+
+ <programlisting>struct foo_data {
+ KOBJ_FIELDS;
+ foo_foo;
+ foo_bar;
+};</programlisting>
+ </sect2>
+
+ <sect2>
+ <title>调用方法</title>
+
+ <para>使用Kobj的最后一部就是通过生成的函数调用对象类中的方法。
+ 调用时,接口名与方法名用'_'接合,而且全部使用大写字母。</para>
+
+ <para>例如,接口名为foo,方法为bar,调用就是:</para>
+
+ <programlisting>[返回值 = ] FOO_BAR(对象 [, 其它参数]);</programlisting>
+
+ </sect2>
+
+ <sect2>
+ <title>善后处理</title>
+
+ <para>当一个用<function>kobj_create()</function>不再需要被使用时,
+ 可对这个对象调用<function>kobj_delete()</function>。
+ 当一个类不再需要被使用时,
+ 可对这个类调用<function>kobj_class_free()</function>。</para>
+ </sect2>
+ </sect1>
+</chapter>
diff --git a/zh_CN.UTF-8/books/arch-handbook/locking/chapter.xml b/zh_CN.UTF-8/books/arch-handbook/locking/chapter.xml
new file mode 100644
index 0000000000..afe6e5a7b2
--- /dev/null
+++ b/zh_CN.UTF-8/books/arch-handbook/locking/chapter.xml
@@ -0,0 +1,334 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ The FreeBSD Documentation Project
+ The FreeBSD SMP Next Generation Project
+ The FreeBSD Simplified Chinese Project
+
+ Original Revision: 1.11
+ $FreeBSD$
+-->
+<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" xml:id="locking">
+ <info><title>内核中的锁</title>
+ <authorgroup>
+ <author><personname/><contrib>&cnproj.translated.by;</contrib></author>
+ </authorgroup>
+ </info>
+
+
+ <indexterm><primary>SMP Next Generation Project(下一代对称多处理工程)</primary></indexterm>
+ <para><emphasis>这一章由 FreeBSD SMP Next Generation Project 维护。
+ 请将评论和建议发送给&a.smp;.</emphasis></para>
+
+
+ <indexterm><primary>locking(锁)</primary></indexterm>
+ <indexterm><primary>multi-processing(多处理)</primary></indexterm>
+ <indexterm><primary>mutexes(同时/独占, mutual exclusion)</primary></indexterm>
+ <indexterm><primary>lockmgr(锁管理器)</primary></indexterm>
+ <indexterm><primary>atomic operations(原子操作)</primary></indexterm>
+ <para>这篇文档提纲挈领的讲述了在FreeBSD内核中的锁,这些锁使得有效的多处理成为可能。
+ 锁可以用几种方式获得。数据结构可以用mutex或&man.lockmgr.9;保护。
+ 对于为数不多的若干个变量,假如总是使用原子操作访问它们,这些变量就可以得到保护。
+ <tip><title>译者注</title><para>仅读本章内容,还不足以找出<quote>mutex</quote>
+ 和<quote>共享互斥锁</quote>的区别。似乎它们的功能有重叠之处,
+ 前者比后者的功能选项更多。它们似乎都是&man.lockmgr.9;的子集。</para></tip></para>
+
+ <sect1 xml:id="locking-mutexes">
+ <title>Mutex</title>
+
+ <para>Mutex就是一种用来解决共享/排它矛盾的锁。
+ 一个mutex在一个时刻只可以被一个实体拥有。如果另一个实体要获得已经被拥有的mutex,
+ 就会进入等待,直到这个mutex被释放。在FreeBSD内核中,mutex被进程所拥有。</para>
+
+ <para>Mutex可以被递归的索要,但是mutex一般只被一个实体拥有较短的一段时间,
+ 因此一个实体不能在持有mutex时睡眠。如果你需要在持有mutex时睡眠,
+ 可使用一个 &man.lockmgr.9; 的锁。</para>
+
+ <para>每个mutex有几个令人感兴趣的属性:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term>变量名</term>
+ <listitem>
+ <para>在内核源代码中<type>struct mtx</type>变量的名字</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>逻辑名</term>
+ <listitem>
+ <para>由函数<function>mtx_init</function>指派的mutex的名字。
+ 这个名字显示在KTR跟踪消息和witness出错与警告信息里。
+ 这个名字还用于区分标识在witness代码中的各个mutex</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>类型</term>
+ <listitem>
+ <para>Mutex的类型,用标志<constant>MTX_*</constant>表示。
+ 每个标志的意义在&man.mutex.9;有所描述。</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>MTX_DEF</constant></term>
+ <listitem>
+ <para>一个睡眠mutex</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>MTX_SPIN</constant></term>
+ <listitem>
+ <para>一个循环mutex</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>MTX_RECURSE</constant></term>
+ <listitem>
+ <para>这个mutex允许递归</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>保护对象</term>
+ <listitem>
+ <para>这个入口所要保护的数据结构列表或数据结构成员列表。
+ 对于数据结构成员,将按照
+ <varname remap="structname">结构名</varname>.<varname remap="structfield">成员名</varname>的形式命名。</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>依赖函数</term>
+ <listitem>
+ <para>仅当mutex被持有时才可以被调用的函数</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <table frame="all" colsep="1" rowsep="1" pgwide="1">
+ <title>Mutex列表</title>
+
+ <indexterm><primary>locks(锁)</primary>
+ <secondary>sched_lock(调度器锁)</secondary></indexterm>
+
+ <indexterm><primary>locks(锁)</primary>
+ <secondary>vm86pcb_lock(虚拟8086模式进程控制块锁)</secondary></indexterm>
+
+ <indexterm><primary>locks(锁)</primary>
+ <secondary>Giant(巨锁)</secondary></indexterm>
+
+ <indexterm><primary>locks(锁)</primary>
+ <secondary>callout_lock(延时调用锁)</secondary></indexterm>
+
+ <tgroup cols="5">
+ <thead>
+ <row>
+ <entry>变量名</entry>
+ <entry>逻辑名</entry>
+ <entry>类型</entry>
+ <entry>保护对象</entry>
+ <entry>依赖函数</entry>
+ </row>
+ </thead>
+
+ <!-- The scheduler lock -->
+ <tbody>
+ <row>
+ <entry>sched_lock</entry>
+ <entry><quote>sched lock</quote>(调度器锁)</entry>
+ <entry>
+ <constant>MTX_SPIN</constant> |
+ <constant>MTX_RECURSE</constant>
+ </entry>
+ <entry>
+ <varname>_gmonparam</varname>,
+ <varname>cnt.v_swtch</varname>,
+ <varname>cp_time</varname>,
+ <varname>curpriority</varname>,
+ <varname remap="structname">mtx</varname>.<varname remap="structfield">mtx_blocked</varname>,
+ <varname remap="structname">mtx</varname>.<varname remap="structfield">mtx_contested</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_procq</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_slpq</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_sflag</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_stat</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_estcpu</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_cpticks</varname>
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_pctcpu</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_wchan</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_wmesg</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_swtime</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_slptime</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_runtime</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_uu</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_su</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_iu</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_uticks</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_sticks</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_iticks</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_oncpu</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_lastcpu</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_rqindex</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_heldmtx</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_blocked</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_mtxname</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_contested</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_priority</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_usrpri</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_nativepri</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_nice</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_rtprio</varname>,
+ <varname>pscnt</varname>,
+ <varname>slpque</varname>,
+ <varname>itqueuebits</varname>,
+ <varname>itqueues</varname>,
+ <varname>rtqueuebits</varname>,
+ <varname>rtqueues</varname>,
+ <varname>queuebits</varname>,
+ <varname>queues</varname>,
+ <varname>idqueuebits</varname>,
+ <varname>idqueues</varname>,
+ <varname>switchtime</varname>,
+ <varname>switchticks</varname>
+ </entry>
+ <entry>
+ <function>setrunqueue</function>,
+ <function>remrunqueue</function>,
+ <function>mi_switch</function>,
+ <function>chooseproc</function>,
+ <function>schedclock</function>,
+ <function>resetpriority</function>,
+ <function>updatepri</function>,
+ <function>maybe_resched</function>,
+ <function>cpu_switch</function>,
+ <function>cpu_throw</function>,
+ <function>need_resched</function>,
+ <function>resched_wanted</function>,
+ <function>clear_resched</function>,
+ <function>aston</function>,
+ <function>astoff</function>,
+ <function>astpending</function>,
+ <function>calcru</function>,
+ <function>proc_compare</function>
+ </entry>
+ </row>
+
+ <!-- The vm86 pcb lock -->
+ <row>
+ <entry>vm86pcb_lock</entry>
+ <entry><quote>vm86pcb lock</quote>(虚拟8086模式进程控制块锁)</entry>
+ <entry>
+ <constant>MTX_DEF</constant>
+ </entry>
+ <entry>
+ <varname>vm86pcb</varname>
+ </entry>
+ <entry>
+ <function>vm86_bioscall</function>
+ </entry>
+ </row>
+
+ <!-- Giant -->
+ <row>
+ <entry>Giant</entry>
+ <entry><quote>Giant</quote>(巨锁)</entry>
+ <entry>
+ <constant>MTX_DEF</constant> |
+ <constant>MTX_RECURSE</constant>
+ </entry>
+ <entry>几乎可以是任何东西</entry>
+ <entry>许多</entry>
+ </row>
+
+ <!-- The callout lock -->
+ <row>
+ <entry>callout_lock</entry>
+ <entry><quote>callout lock</quote>(延时调用锁)</entry>
+ <entry>
+ <constant>MTX_SPIN</constant> |
+ <constant>MTX_RECURSE</constant>
+ </entry>
+ <entry>
+ <varname>callfree</varname>,
+ <varname>callwheel</varname>,
+ <varname>nextsoftcheck</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_itcallout</varname>,
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_slpcallout</varname>,
+ <varname>softticks</varname>,
+ <varname>ticks</varname>
+ </entry>
+ <entry>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
+ <sect1 xml:id="locking-sx">
+ <title>共享互斥锁</title>
+
+ <para>这些锁提供基本的读/写类型的功能,可以被一个正在睡眠的进程持有。
+ 现在它们被统一到&man.lockmgr.9;之中。</para>
+ <indexterm><primary>locks(锁)</primary>
+ <secondary>shared exclusive(共享互斥)</secondary></indexterm>
+
+ <table>
+ <title>共享互斥锁列表</title>
+ <indexterm><primary>locks(锁)</primary>
+ <secondary>allproc_lock(全进程锁)</secondary></indexterm>
+ <indexterm><primary>locks(锁)</primary>
+ <secondary>proctree_lock(进程树锁)</secondary></indexterm>
+
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>变量名</entry>
+ <entry>保护对象</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><varname>allproc_lock</varname></entry>
+ <entry>
+ <varname>allproc</varname>
+ <varname>zombproc</varname>
+ <varname>pidhashtbl</varname>
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_list</varname>
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_hash</varname>
+ <varname>nextpid</varname>
+ </entry>
+ </row>
+ <row>
+ <entry><varname>proctree_lock</varname></entry>
+ <entry>
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_children</varname>
+ <varname remap="structname">proc</varname>.<varname remap="structfield">p_sibling</varname>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
+ <sect1 xml:id="locking-atomic">
+ <title>原子保护变量</title>
+
+ <indexterm><primary>atomically protected variables(原子保护变量)</primary></indexterm>
+
+ <para>原子保护变量并非由一个显在的锁保护的特殊变量,而是:
+ 对这些变量的所有数据访问都要使用特殊的原子操作(&man.atomic.9;)。
+ 尽管其它的基本同步机制(例如mutex)就是用原子保护变量实现的,
+ 但是很少有变量直接使用这种处理方式。</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><varname remap="structname">mtx</varname>.<varname remap="structfield">mtx_lock</varname></para>
+ </listitem>
+ </itemizedlist>
+ </sect1>
+</chapter>
diff --git a/zh_CN.UTF-8/books/arch-handbook/mac.ent b/zh_CN.UTF-8/books/arch-handbook/mac.ent
new file mode 100644
index 0000000000..8e211b409e
--- /dev/null
+++ b/zh_CN.UTF-8/books/arch-handbook/mac.ent
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Original Revision: 1.4
+$FreeBSD$ -->
+
+<!ENTITY mac.mpo "mpo">
+<!ENTITY mac.thead '
+ <colspec xmlns="http://docbook.org/ns/docbook" colname="first" colwidth="0"/>
+ <colspec xmlns="http://docbook.org/ns/docbook" colwidth="0"/>
+ <colspec xmlns="http://docbook.org/ns/docbook" colname="last" colwidth="0"/>
+
+ <thead xmlns="http://docbook.org/ns/docbook">
+ <row>
+ <entry>参数</entry>
+ <entry>说明</entry>
+ <entry>锁定</entry>
+ </row>
+ </thead>
+'>
+
+<!ENTITY mac.externalize.paramdefs '
+ <paramdef xmlns="http://docbook.org/ns/docbook">struct label *<parameter>label</parameter></paramdef>
+ <paramdef xmlns="http://docbook.org/ns/docbook">char *<parameter>element_name</parameter></paramdef>
+ <paramdef xmlns="http://docbook.org/ns/docbook">struct sbuf *<parameter>sb</parameter></paramdef>
+ <paramdef xmlns="http://docbook.org/ns/docbook">int <parameter>*claimed</parameter></paramdef>
+'>
+
+<!ENTITY mac.externalize.tbody '
+ <tbody xmlns="http://docbook.org/ns/docbook">
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>将用外部形式表示的标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>element_name</parameter></entry>
+ <entry>需要外部表示标记的策略的名字</entry>
+ </row>
+
+ <row>
+ <entry><parameter>sb</parameter></entry>
+ <entry>用来存放标记的文本表示形式的字符buffer</entry>
+ </row>
+
+ <row>
+ <entry><parameter>claimed</parameter></entry>
+ <entry>如果可以填充element_data 域,则其数值递增</entry>
+ </row>
+ </tbody>
+'>
+
+<!ENTITY mac.externalize.para '
+ <para xmlns="http://docbook.org/ns/docbook">根据传入的标记结构,产生一个以外部形式表示的标记。
+ 一个外部形式标记,是标记内容的文本表示,它由用户级的应用程序使用,是用户可读的。
+ 目前的MAC实现方案将依次调用策略的相应入口函数,因此,
+ 具体策略的实现代码,需要在填写sb之前,先检查element_name中指定的名字。
+ 如果element_name中的内容与你的策略名字不相符,则直接返回0。
+ 仅当转换标记数据的过程中出现错误时,才返回非0值。
+ 一旦策略决定填写element_data,递增*claim的数值。</para>
+'>
+
+<!ENTITY mac.internalize.paramdefs '
+ <paramdef xmlns="http://docbook.org/ns/docbook">struct label *<parameter>label</parameter></paramdef>
+ <paramdef xmlns="http://docbook.org/ns/docbook">char *<parameter>element_name</parameter></paramdef>
+ <paramdef xmlns="http://docbook.org/ns/docbook">char *<parameter>element_data</parameter></paramdef>
+ <paramdef xmlns="http://docbook.org/ns/docbook">int *<parameter>claimed</parameter></paramdef>
+'>
+
+<!ENTITY mac.internalize.tbody '
+ <tbody xmlns="http://docbook.org/ns/docbook">
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>将被填充的标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>element_name</parameter></entry>
+ <entry>需要内部表示标记的策略的名字</entry>
+ </row>
+
+ <row>
+ <entry><parameter>element_data</parameter></entry>
+ <entry>需要被转换的文本数据</entry>
+ </row>
+
+ <row>
+ <entry><parameter>claimed</parameter></entry>
+ <entry>如果数据被正确转换,则其数值递增</entry>
+ </row>
+ </tbody>
+'>
+
+<!ENTITY mac.internalize.para '
+ <para xmlns="http://docbook.org/ns/docbook">根据一个文本形式的外部表示标记数据,创建一个内部形式的标记结构。
+ 目前的MAC方案将依次调用所有策略的相关入口函数,来响应标记的内部转换请求,
+ 因此,实现代码必须首先通过比较element_name中的内容和自己的策略名字,
+ 来确定是否需要转换element_data中存放的数据。
+ 类似的,如果名字不匹配或者数据转换操作成功,该函数返回0,并递增*claimed的值。</para>
+'>
diff --git a/zh_CN.UTF-8/books/arch-handbook/mac/chapter.xml b/zh_CN.UTF-8/books/arch-handbook/mac/chapter.xml
new file mode 100644
index 0000000000..50db789d53
--- /dev/null
+++ b/zh_CN.UTF-8/books/arch-handbook/mac/chapter.xml
@@ -0,0 +1,7233 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (c) 2002-2005 Networks Associates Technology, Inc.
+ All rights reserved.
+
+ This software was developed for the FreeBSD Project by
+ Chris Costello at Safeport Network Services and Network Associates Labs,
+ the Security Research Division of Network Associates, Inc. under
+ DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
+ DARPA CHATS research program.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+ Original Revision: 1.46
+ $FreeBSD$
+-->
+<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" xml:id="mac">
+ <info><title>TrustedBSD MAC 框架</title>
+ <authorgroup>
+ <author><personname><firstname>Chris</firstname><surname>Costello</surname></personname><affiliation>
+ <orgname>TrustedBSD 项目</orgname>
+ <address><email>chris@FreeBSD.org</email></address>
+ </affiliation></author>
+
+ <author><personname><firstname>Robert</firstname><surname>Watson</surname></personname><affiliation>
+ <orgname>TrustedBSD 项目</orgname>
+ <address><email>rwatson@FreeBSD.org</email></address>
+ </affiliation></author>
+ </authorgroup>
+
+ <authorgroup>
+ <author><personname/><contrib>&cnproj.translated.by;</contrib></author>
+ </authorgroup>
+
+ </info>
+
+
+
+ <sect1 xml:id="mac-copyright">
+ <title>MAC 文档版权声明</title>
+
+ <para>本文档是作为 DARPA CHATS 研究计划的一部分,由供职于 Security Research Division of Network Associates
+ 公司Safeport Network Services and Network Associates Laboratories 的Chris Costello依据 DARPA/SPAWAR 合同
+ N66001-01-C-8035 (<quote>CBOSS</quote>),为 FreeBSD 项目编写的。</para>
+
+ <para>Redistribution and use in source (SGML DocBook) and
+ 'compiled' forms (SGML, HTML, PDF, PostScript, RTF and so forth)
+ with or without modification, are permitted provided that the
+ following conditions are met:</para>
+
+ <orderedlist>
+ <listitem>
+ <para>Redistributions of source code (SGML DocBook) must
+ retain the above copyright notice, this list of conditions
+ and the following disclaimer as the first lines of this file
+ unmodified.</para>
+ </listitem>
+
+ <listitem>
+ <para>Redistributions in compiled form (transformed to other
+ DTDs, converted to PDF, PostScript, RTF and other formats)
+ must reproduce the above copyright notice, this list of
+ conditions and the following disclaimer in the documentation
+ and/or other materials provided with the
+ distribution.</para>
+ </listitem>
+ </orderedlist>
+
+ <important>
+ <para>THIS DOCUMENTATION IS PROVIDED BY THE NETWORKS ASSOCIATES
+ TECHNOLOGY, INC "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL NETWORKS ASSOCIATES TECHNOLOGY,
+ INC BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS DOCUMENTATION, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</para>
+ </important>
+
+ <important><para>本文中许可证的非官方中文翻译仅供参考,
+ 不作为判定任何责任的依据。如与英文原文有出入,则以英文原文为准。</para></important>
+
+ <para>在满足下列许可条件的前提下,允许再分发或以源代码 (SGML DocBook) 或 “编译” (SGML, HTML, PDF, PostScript, RTF 等)
+ 的经过修改或未修改的形式:</para>
+
+ <orderedlist>
+ <listitem>
+ <para>再分发源代码 (SGML DocBook) 必须不加修改的保留上述版权告示、本条件清单和下述弃权书作为该文件的最先若干行。</para>
+ </listitem>
+
+ <listitem>
+ <para>再分发编译的形式 (转换为其它DTD、 PDF、 PostScript、 RTF 或其它形式),必须将上述版权告示、
+ 本条件清单和下述弃权书复制到与分发品一同提供的文件,以及其它材料中。</para>
+ </listitem>
+ </orderedlist>
+
+ <important>
+ <para>本文档由 NETWORKS ASSOCIATES TECHNOLOGY, INC “按现状条件”提供,并在此明示不提供任何明示或暗示的保障,
+ 包括但不限于对商业适销性、对特定目的的适用性的暗示保障。任何情况下,
+ NETWORKS ASSOCIATES TECHNOLOGY, INC 均不对任何直接、 间接、 偶然、 特殊、 惩罚性的,
+ 或必然的损失 (包括但不限于替代商品或服务的采购、 使用、 数据或利益的损失或营业中断) 负责,
+ 无论是如何导致的并以任何有责任逻辑的, 无论是否是在本文档使用以外以任何方式产生的契约、严格责任或是民事侵权行为(包括疏忽或其它)中的,
+ 即使已被告知发生该损失的可能性。</para>
+ </important>
+ </sect1>
+
+ <sect1 xml:id="mac-synopsis">
+ <title>术语解析</title>
+
+ <para>FreeBSD 以一个内核安全扩展性框架(TrustedBSD MAC 框架)的方式,为若干强制访问控制策略(也称“集权式访问控制策略”)
+ 提供试验性支持。MAC 框架是一个插入式的访问控制框架,允许新的安全策略更方便地融入内核:安全策略可以静态链入内核,也可以
+ 在引导时加载,甚至在运行时动态加载。该框架所提供的标准化接口,使得运行在其上的安全策略模块能对系统对象的安全属性进行诸如标记等一系列操作。
+ MAC 框架的存在,简化了这些操作在策略模块中的实现,从而显著降低了新安全策略模块的开发难度。</para>
+
+ <para>本章将介绍 MAC 策略框架,为读者提供一个示例性的 MAC 策略模块文档。</para>
+ </sect1>
+
+
+ <sect1 xml:id="mac-introduction">
+ <title>概述</title>
+
+ <para>TrustedBSD MAC 框架提供的机制,允许在其上运行的内核模块在内核编译或者运行时,对内核的访问控制模型进行扩展。
+ 新的系统安全策略作为一个内核模块实现,并被链接到内核中;如果系统中同时存在多个安全策略模块,则它们的决策结果将以某种确定的方式组合。
+ 为了给简化新安全策略的开发,MAC 向上提供了大量用于访问控制的基础设施,特别是,对临时的或者持久的、策略无关的对象安全标记的支持。
+ 该支持目前仍是试验性质的。</para>
+
+ <para>本章所提供的信息不仅将使在 MAC 使能环境下工作的潜在用户受益,
+ 也可以为需要了解 MAC 框架是如何支持对内核访问控制进行扩展的策略模块开发人员所用。</para>
+ </sect1>
+
+ <sect1 xml:id="mac-background">
+ <title>安全策略背景知识</title>
+
+ <para>强制访问控制(简称 MAC),是指由操作系统强制实施的一组针对用户的访问控制策略。
+ 在某些情况下,强制访问控制的策略可能会与自主访问控制(简称 DAC)所提供的保护措施发生冲突,
+ 后者是用来向非管理员用户对数据采取保护措施提供支持的。在传统的 UNIX 系统中,
+ DAC 保护措施包括文件访问模式和访问控制列表;而 MAC 则提供进程控制和防火墙等。
+ 操作系统设计者和安全机制研究人员对许多经典的 MAC 安全策略作了形式化的表述,比如,
+ 多级安全(MLS)机密性策略,Biba 完整性策略,基于角色的访问控制策略(RBAC),域和型裁决策略(DTE),以及型裁决策略(TE)。
+ 安全策略的形式化表述被称为安全模型。每个模型根据一系列条件做出安全相关的决策,这些条件包括,
+ 用户的身份、角色和安全信任状,以及对象的安全标记(用来代表该对象数据的机密性/完整性级别)。</para>
+
+ <para>TrustedBSD MAC 框架所提供的对策略模块的支持,不仅可以用来实现上述所有策略,
+ 还能用于实现其他利用已有安全属性(如,用户和组ID、文件扩展属性等)决策的系统安全强化策略。
+ 此外,因为具体策略模块在访问授权方面所拥有的高度灵活性和自主性,所以MAC 框架同样可以用来实现完全自主式的安全策略.</para>
+ </sect1>
+
+ <sect1 xml:id="mac-framework-kernel-arch">
+ <title>MAC 框架的内核体系结构</title>
+
+ <para>TrustedBSD MAC 框架为大多数的访问控制模块提供基本设施,允许它们以内核模块的形式灵活地扩展系统中实施的安全策略。
+ 如果系统中同时加载了多个策略,MAC 框架将负责将各个策略的授权结果以一种(某种程度上)有意义的方式组合,形成最后的决策。</para>
+
+ <sect2 xml:id="mac-framework-kernel-arch-elements">
+ <title>内核元素</title>
+
+ <para>MAC 框架由下列内核元素组成:</para>
+
+ <itemizedlist>
+ <listitem><para>框架管理接口</para></listitem>
+ <listitem><para>并发与同步原语</para></listitem>
+ <listitem><para>策略注册</para></listitem>
+ <listitem><para>内核对象的扩展性安全标记</para></listitem>
+ <listitem><para>策略入口函数的组合操作</para></listitem>
+ <listitem><para>标记管理原语</para></listitem>
+ <listitem><para>由内核服务调用的入口函数 API</para></listitem>
+ <listitem><para>策略模块的入口函数 API</para></listitem>
+ <listitem><para>入口函数的实现(包括策略生命周期管理、标记管理和访问控制检查三部分)</para></listitem>
+ <listitem><para>管理策略无关标记的系统调用</para></listitem>
+ <listitem><para>复用的<function>mac_syscall()</function> 系统调用</para></listitem>
+ <listitem><para>以 MAC 的策略加载模块形式实现的各种安全策略</para></listitem>
+ </itemizedlist>
+ </sect2>
+
+ <sect2 xml:id="mac-framework-kernel-arch-management">
+ <title>框架管理接口</title>
+
+ <para>对 TrustedBSD MAC 框架进行直接管理的方式有三种:通过 sysctl 子系统、通过 loader 配置, 或者使用系统调用。</para>
+
+ <para>多数情况下,与同一个内核内部变量相关联的 sysctl 变量和 loader 参数的名字是相同的,
+ 通过设置它们,可以控制保护措施的实施细节,比如,某个策略在各个内核子系统中的实施与否等等。
+ 另外,如果在内核编译时选择支持 MAC 调试选项,内核将维护若干计数器以跟踪标记的分配使用情况。
+ 通常不建议在实用环境下通过在不同子系统上设置不同的变量或参数来实施控制,因为这种方法将会作用于系统中所有的活跃策略。
+ 如果希望对具体策略实施管理而不相影响其他活跃策略,则应当使用策略级别的控制,因为这种方法的控制粒度更细,
+ 并能更好地保证策略模块的功能一致性。</para>
+
+ <para>与其他内核模块一样,系统管理员可以通过系统的模块管理系统调用和其他系统接口,包括 boot loader 变量,对策略模块执行加载与卸载操作;
+ 策略模块可以在加载时,设置加载标志,来指示系统对其加载、卸载操作进行相应控制,比如阻止非期望的卸载操作。</para>
+ </sect2>
+
+ <sect2 xml:id="mac-framework-kernel-arch-synchronization">
+ <title>策略链表的并发与同步</title>
+
+ <para>在运行时,系统中活跃的策略集合可能发生变化,然而对策略入口函数的使用操作并不是原子性的,因此,当某一个入口函数正被使用时,
+ 系统需要提供额外的同步机制来阻止对该策略模块的加载与卸载,以确保当前活跃的策略集合不会在此过程中发生改变。
+ 通过使用"框架忙”计数器,就可以做到这一点:一旦某个入口函数被调用,计数器的值被增加1;而每当一个入口函数调用结束时,计数器的值被减少1。
+ 检查计数器的值,如果其值为正,框架将阻止对策略链表的修改操作,请求操作的线程将被迫进入睡眠,直到计数器的值重新减少到0为止。
+ 计数器本身由一个互斥锁保护,同时结合一个条件变量(用于唤醒等待对策略链表进行修改操作的睡眠线程)。
+ 采用这种同步模型的一个副作用是,在同一个策略模块内部,允许嵌套地调用框架,不过这种情况其实很少出现。</para>
+
+ <para>为了减少由于采用计数器引入的额外开销,设计者采用了各种优化措施。其中包括,当策略链表为空或者其中仅含有静态表项
+ (那些只能在系统运行之前加载而且不能动态卸载的策略)时,框架不对计数器进行操作,其值总是为0,从而将此时的同步开销减到0。
+ 另一个极端的办法是,使用一个编译选项来禁止在运行时对加载的策略链表进行修改,此时不再需要对策略链表的使用进行同步保护。</para>
+
+ <para>因为 MAC 框架不允许在某些入口函数之内阻塞,所以不能使用普通的睡眠锁。
+ 故而,加载或卸载操作可能会为等待框架空闲而被阻塞相当长的一段时间。</para>
+ </sect2>
+
+ <sect2 xml:id="mac-framework-kernel-arch-label-synchronization">
+ <title>标记同步</title>
+
+ <para>MAC 框架必须对其负责维护的安全属性标记的存储访问提供同步保护。下列两种情形,可能导致对安全属性标记的不一致访问:
+ 第一,作为安全属性标记的持有者,内核对象本身可能同时被多个线程访问;第二,MAC 框架代码是可重入的,
+ 即允许多个线程同时在框架内执行。通常,MAC 框架使用内核对象数据上已有的内核同步机制来保护该其上附加的 MAC 安全标记。
+ 例如,套接字上的 MAC 标记由已有的套接字互斥锁保护。类似的,对于安全标记的并发访问的过程与对其所在对象进行的并发访问在语义上是一样的,
+ 例如,信任状安全标记,将保持与该数据结构中其他内容一致的"写时复制"的更新过程。
+ MAC 框架在引用一个内核对象时,将首先对访问该对象上的标记需要用到的锁进行断言。
+ 策略模块的编写者必须了解这些同步语义, 因为它们可能会限制对安全标记所能进行的访问类型。
+ 举个例子,如果通过入口函数传给策略模块的是对某个信任状的只读引用,那么在策略内部,只能读该结构对应的标记状态。</para>
+ </sect2>
+
+ <sect2 xml:id="mac-framework-kernel-arch-policy-synchronization">
+ <title>策略间的同步与并发</title>
+
+ <para>FreeBSD 内核是一个可抢占式的内核,因此,作为内核一部分的策略模块也必须是可重入的,也就是说,
+ 在开发策略模块时必须假设多个内核线程可以同时通过不同的入口函数进入该模块。
+ 如果策略模块使用可被修改的内核状态,那么还需要在策略内部使用恰当的同步原语,确保在策略内部的多个线程不会因此观察到不一致的内核状态,
+ 从而避免由此产生的策略误操作。为此,策略可以使用 FreeBSD 现有的同步原语,包括互斥锁、睡眠锁、条件变量和计数信号量。
+ 对这些同步原语的使用必须慎重,需要特别注意两点:第一,保持现有的内核上锁次序;
+ 第二,在非睡眠的入口函数之内不要使用互斥锁和唤醒操作。</para>
+
+ <para>为避免违反内核上锁次序或造成递归上锁,策略模块在调用其他内核子系统之前,通常要释放所有在策略内部申请的锁。
+ 这样做的结果是,在全局上锁次序形成的拓朴结构中,策略内部的锁总是作为叶子节点,
+ 从而保证了这些锁的使用不会导致由于上锁次序混乱造成的死锁。</para>
+ </sect2>
+
+ <sect2 xml:id="mac-framework-kernel-arch-registration">
+ <title>策略注册</title>
+
+ <para>为了记录当前使用的策略模块集合,MAC 框架维护两个链表:一个静态链表和一个动态链表。
+ 两个链表的数据结构和操作基本相同,只是动态链表还额外使用了一个"引用计数"以同步对其的访问操作。
+ 当包含 MAC 框架策略的内核模块被加载时,该策略模块会通过 <literal>SYSINIT</literal> 调用一个注册函数;
+ 相对应的,每当一个策略模块被卸载,<literal>SYSINIT</literal> 也会调用一个注销函数。
+ 只有当遇到下列情况之一时,注册过程才会失败: 一个策略模块被加载多次,或者系统资源不足不能满足注册过程的需要(
+ 例如,策略模块需要对内核对象添加标记而可用资源不足),或者其他的策略加载前提条件不满足(有些策略要求只能在系统引导之前加载)。
+ 类似的,如果一个策略被标记为不可卸载的,对其调用注销过程将会失败。</para>
+ </sect2>
+
+ <sect2 xml:id="mac-framework-kernel-arch-entrypoints">
+ <title>入口函数</title>
+
+ <para>内核服务与 MAC 框架之间进行交互有两种途径:
+ 一是,内核服务调用一系列 API 通知 MAC 框架安全事件的发生;
+ 二是,内核服务向 MAC 框架提供一个指向安全对象的策略无关安全标记数据结构的指针。
+ 标记指针由 MAC 框架经由标记管理入口函数进行维护,
+ 并且,只要对管理相关对象的内核子系统稍作修改,就可以允许 MAC 框架向策略模块提供标记服务。
+ 例如,在进程、进程信任状、套接字、管道、Mbuf、网络接口、IP 重组队列和其他各种安全相关的数据结构中均增加了指向安全标记的指针。
+ 另外,当需要做出重要的安全决策时,内核服务也会调用 MAC 框架,以便各个策略模块根据其自己的标准(可以使用存储在安全标记中的数据)完善这些决策。
+ 绝大多数安全相关的关键决策是显式的访问控制检查;
+ 也有少数涉及更加一般的决策函数,比如,套接字的数据包匹配和程序执行时刻的标记转换。</para>
+ </sect2>
+
+ <sect2 xml:id="mac-framework-kernel-arch-composition">
+ <title>策略组合</title>
+
+ <para>如果内核中同时加载了多个策略模块,这些策略的决策结果将由框架使用一个合成运算子来进行组合汇总,得出最终的结果。
+ 目前,该算子是硬编码的,并且只有当所有的活跃策略均对请求表示同意时才会返回成功。
+ 由于各个策略返回的出错条件可能并不相同(成功、访问被拒绝、请求对象不存在等等),
+ 需要使用一个选择子先从各个策略返回的错误条件集合中选择出一个作为最终返回结果。
+ 一般情况下,与“访问被拒绝”相比,将更倾向于选择“请求对象不存在”。
+ 尽管不能从理论上保证合成结果的有效性与安全性,但试验结果表明,对于许多实用的策略集合来说,事实的确如此。
+ 例如,传统的可信系统常常采用类似的方法对多个安全策略进行组合。</para>
+ </sect2>
+
+ <sect2 xml:id="mac-framework-kernel-arch-labels">
+ <title>标记支持</title>
+
+ <para>与许多需要给对象添加安全标记的访问控制扩展一样,MAC 框架为各种用户可见的对象提供了一组用于管理策略无关标记的系统调用。
+ 常用的标记类型有,partition标识符、机密性标记、完整性标记、区间(非等级类别)、域、角色和型。
+ “策略无关”的意思是指,标记的语法与使用它的具体策略模块无关,而同时策略模块能够完全独立地定义和使用与对象相关联的元数据的语义。
+ 用户应用程序提供统一格式的基于字符串的标记,由使用它的策略模块负责解析其内在含义并决定其外在表示。
+ 如果需要,应用程序可以使用多重标记元素。</para>
+
+ <para>内存中的标记实例被存放在由 slab 分配的<varname remap="structname">struct label</varname>数据结构中。
+ 该结构是一个固定长度的数组,每个元素是由一个 <literal>void *</literal>
+ 指针和一个 <literal>long</literal>组成的联合结构。
+ 申请标记存储的策略模块在向 MAC 注册时,将被分配一个“slot”值,作为框架分配给其使用的策略标记元素在整个标记存储结构中的位置索引。
+ 而所分配的存储空间的语义则完全由该策略模块来决定:MAC 框架向策略模块提供了一系列入口函数用于对内核对象生命周期的各种事件进行控制,包括,
+ 对象的初始化、标记的关联/创建和对象的注销。使用这些接口,可以实现诸如访问计数等存储模型。
+ MAC 框架总是给入口函数传入一个指向相关对象的指针和一个指向该对象标记的指针,因此,策略模块能够直接访问标记而无需知悉该对象的内部结构。
+ 唯一的例外是进程信任状结构,指向其标记的指针必须由策略模块手动解析计算。今后的 MAC 框架实现可能会对此进行改进。</para>
+
+ <para>初始化入口函数通常有一个睡眠标志位,用来表明一个初始化操作是否允许中途睡眠等待;
+ 如果不允许,则可能会失败返回,并要求撤销此次标记分配操作(乃至对象分配操作)。
+ 例如,如果在网络栈上处理中断时因为不允许睡眠或者调用者持有一个互斥锁,就可能出现这种情况。
+ 考虑到在处理中的网络数据包(Mbufs)上维护标记的性能损失太大,策略必须就自己对 Mbuf 进行标记的要求向 MAC 框架做出特别声明。
+ 动态加载到系统中而又使用标记的策略必须为处理未被其初始化函数处理过的对象作好准备,
+ 这些对象在策略加载之前就已经存在,故而无法在初始化时调用策略的相关函数进行处理。
+ MAC 框架向策略保证,没有被初始化的标记 slot 的值必为0或者 NULL,策略可以借此检测到未初始化的标记。
+ 需要注意的是,因为对 Mbuf 标记的存储分配是有条件的,因此需要使用其标记的动态加载策略还可能需要处理 Mbuf 中值为 NULL 的标记指针。</para>
+
+ <para>对于文件系统对象的标记,MAC 框架在文件的扩展属性中为其分配永久存储。
+ 只要可能,扩展属性的原子化的事务操作就被用于保证对 vnode 上安全标记的复合更新操作的一致性--目前,该特性只被 UFS2 文件系统支持。
+ 为了实现细粒度的文件系统对象标记(即每个文件系统对象一个标记),策略编写者可能选择使用一个(或者若干)扩展属性块。
+ 为了提高性能, vnode 数据结构中有一个标记 (<literal>v_label</literal>)字段,用作磁盘标记的缓冲;
+ vnode 结构实例化时,策略可以将标记值装入该缓冲,并在需要时对其进行更新。
+ 如此,不必在每次进行访问控制检查时,均无条件地访问磁盘上的扩展属性。</para>
+
+ <note><para>目前,如果一个使用标记的策略允许被动态卸载,则卸载该模块之后,其状态 slot 尚无法被系统回收重用,
+ 由此导致了 MAC 框架对标记策略卸载-重载操作数目上的严格限制。</para></note>
+ </sect2>
+
+ <sect2 xml:id="mac-framework-kernel-arch-syscalls">
+ <title>相关系统调用</title>
+
+ <para>MAC 框架向应用程序提供了一组系统调用:其中大多数用于向进行查询和修改策略无关标记操作的应用 API提供支持。</para>
+
+ <para>这些标记管理系统调用,接受一个标记描述结构, <varname remap="structname">struct mac</varname>,作为输入参数。
+ 这个结构的主体是一个数组,其中每个元素包含了一个应用级的 MAC 标记形式。每个元素又由两部分组成:一个字符串名字,和其对应的值。
+ 每个策略可以向系统声明一个特定的元素名字,这样一来,如果需要,就可以将若干个相互独立的元素作为一个整体进行处理。
+ 策略模块经由入口函数,在内核标记和用户提供的标记之间作翻译转换的工作,这种实现提供了标记元素语义上的高度灵活性。
+ 标记管理系统调用通常有对应的库函数包装,这些包装函数可以提供内存分配和错误处理功能,从而简化了用户应用程序的标记管理工作。</para>
+
+ <para>目前的FreeBSD 内核提供了下列 MAC 相关的系统调用:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><function>mac_get_proc()</function> 用于查询当前进程的安全标记。</para>
+ </listitem>
+
+ <listitem>
+ <para><function>mac_set_proc()</function> 用于请求改变当前进程的安全标记。</para>
+ </listitem>
+
+ <listitem>
+ <para><function>mac_get_fd()</function> 用于查询由文件描述符所引用的对象( 文件、
+ 套接字、 管道文件等等) 的安全标记。</para>
+ </listitem>
+
+ <listitem>
+ <para><function>mac_get_file()</function> 用于查询由文件系统路径所描述的对象的安全标记。</para>
+ </listitem>
+
+ <listitem>
+ <para><function>mac_set_fd()</function> 用于请求改变由文件描述符所引用的对象(
+ 文件、套接字、 管道文件等等) 的安全标记。</para>
+ </listitem>
+
+ <listitem>
+ <para><function>mac_set_file()</function> 用于请求改变由文件系统路径所描述的对象的安全标记。</para>
+ </listitem>
+
+ <listitem>
+ <para><function>mac_syscall()</function> 通过复用该系统调用,策略模块能够在不修改系统调用表的前提下创建新的系统调用;
+ 其调用参数包括:目标策略名字、 操作编号和将被该策略内部使用的参数。</para>
+ </listitem>
+
+ <listitem>
+ <para><function>mac_get_pid()</function> 用于查询由进程号指定的另一个进程的安全标记。</para>
+ </listitem>
+
+ <listitem>
+ <para><function>mac_get_link()</function> 与
+ <function>mac_get_file()</function> 功能相同,
+ 只是当路径参数的最后一项为符号链接时,
+ 前者将返回该符号链接的安全标记,
+ 而后者将返回其所指文件的安全标记。</para>
+ </listitem>
+
+ <listitem>
+ <para><function>mac_set_link()</function> 与
+ <function>mac_set_file()</function> 功能相同,
+ 只是当路径参数的最后一项为符号链接时,
+ 前者将设置该符号链接的安全标记,
+ 而后者将设置其所指文件的安全标记。</para>
+ </listitem>
+
+ <listitem>
+ <para><function>mac_execve()</function> 与
+ <function>execve()</function> 功能类似,
+ 只是前者还可以在开始执行一个新程序时,根据传入的请求参数,设置执行进程的安全标记。
+ 由于执行一个新程序而导致的进程安全标记的改变,被称为“转换”。</para>
+ </listitem>
+
+ <listitem>
+ <para><function>mac_get_peer()</function>,
+ 通过一个套接字选项自动实现,
+ 用于查询一个远程套接字对等实体的安全标记。</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>除了上述系统调用之外,
+ 也可以通过 <literal>SIOCSIGMAC</literal> 和 <literal>SIOCSIFMAC</literal>
+ 网络接口的 ioctl 类系统调用来查询和设置网络接口的安全标记。</para>
+ </sect2>
+ </sect1>
+
+ <sect1 xml:id="mac-policy-architecture">
+ <title>MAC策略模块体系结构</title>
+
+ <para>安全策略可以直接编入内核,也可以编译成独立的内核模块,在系统引导时或者运行时使用模块加载命令加载。
+ 策略模块通过一组预先定义好的入口函数与系统交互。通过它们,策略模块能够掌握某些系统事件的发生,并且在必要的时候影响系统的访问控制决策。
+ 每个策略模块包含下列组成部分:</para>
+
+ <itemizedlist>
+ <listitem><para>可选:策略配置参数</para></listitem>
+ <listitem><para>策略逻辑和参数的集中实现</para></listitem>
+ <listitem><para>可选:策略生命周期事件的实现,比如,策略的初始化和销毁</para></listitem>
+ <listitem><para>可选:对所选内核对象的安全标记进行初始化、维护和销毁的支持</para></listitem>
+ <listitem><para>可选:对所选对象的使用进程进行监控以及修改对象安全标记的支持</para></listitem>
+ <listitem><para>策略相关的访问控制入口函数的实现</para></listitem>
+ <listitem><para>对策略标志、模块入口函数和策略特性的声明</para></listitem>
+ </itemizedlist>
+
+ <sect2 xml:id="mac-policy-declaration">
+ <title>策略注销</title>
+
+ <para>策略模块可以使用 <function>MAC_POLICY_SET()</function> 宏来声明。
+ 该宏完成以下工作:为该策略命名(向系统声明该策略提供的名字);提交策略定义的 MAC 入口函数向量的地址;
+ 按照策略的要求设置该策略的加载标志位,保证 MAC 框架将以策略所期望的方式对其进行操作;
+ 另外,还可能请求框架为策略分配标记状态 slot 值。</para>
+
+ <programlisting>static struct mac_policy_ops mac_<replaceable>policy</replaceable>_ops =
+{
+ .mpo_destroy = mac_<replaceable>policy</replaceable>_destroy,
+ .mpo_init = mac_<replaceable>policy</replaceable>_init,
+ .mpo_init_bpfdesc_label = mac_<replaceable>policy</replaceable>_init_bpfdesc_label,
+ .mpo_init_cred_label = mac_<replaceable>policy</replaceable>_init_label,
+/* ... */
+ .mpo_check_vnode_setutimes = mac_<replaceable>policy</replaceable>_check_vnode_setutimes,
+ .mpo_check_vnode_stat = mac_<replaceable>policy</replaceable>_check_vnode_stat,
+ .mpo_check_vnode_write = mac_<replaceable>policy</replaceable>_check_vnode_write,
+};</programlisting>
+
+ <para>如上所示,MAC 策略入口函数向量,<varname>mac_<replaceable>policy</replaceable>_ops</varname>,
+ 将策略模块中定义的功能函数挂接到特定的入口函数地址上。
+ 在稍后的“入口函数参考”小节中,将提供可用入口函数功能描述和原型的完整列表。
+ 与模块注册相关的入口函数有两个:<symbol>.mpo_destroy</symbol>和<symbol>.mpo_init</symbol>。
+ 当某个策略向模块框架注册操作成功时,<symbol>.mpo_init</symbol>将被调用,此后其他的入口函数才能被使用。
+ 这种特殊的设计使得策略有机会根据自己的需要,进行特定的分配和初始化操作,比如对特殊数据或锁的初始化。
+ 卸载一个策略模块时,将调用 <symbol>.mpo_destroy</symbol> 用来释放策略分配的内存空间或注销其申请的锁。
+ 目前,为了防止其他入口函数被同时调用,调用上述两个入口函数的进程必须持有 MAC 策略链表的互斥锁:这种限制将被放开,
+ 但与此同时,将要求策略必须谨慎使用内核原语,以避免由于上锁次序或睡眠造成死锁。</para>
+
+ <para>之所以向策略声明提供模块名字域,是为了能够唯一标识该模块,以便解析模块依赖关系。选择使用恰当的字符串作为名字。
+ 在策略加载和卸载时,策略的完整字符串名字将经由内核日志显示给用户。另外,当向用户进程报告状态信息时也会包含该字符串。</para>
+ </sect2>
+
+ <sect2 xml:id="mac-policy-flags">
+ <title>策略标志</title>
+
+ <para>在声明时提供标志参数域的机制,允许策略模块在作为模块被加载时,就自身特性向 MAC 框架提供说明。
+ 目前,已经定义的标志有三个:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term>MPC_LOADTIME_FLAG_UNLOADOK</term>
+
+ <listitem>
+ <para>表示该策略模块可以被卸载。
+ 如果未提供该标志,则表示该策略模块拒绝被卸载。
+ 那些使用安全标记的状态,而又不能在运行时释放该状态的模块可能会设置该标志。
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>MPC_LOADTIME_FLAG_NOTLATE</term>
+
+ <listitem>
+ <para>表示该策略模块必须在系统引导过程时进行加载和初始化。
+ 如果该标志被设置,那么在系统引导之后注册该模块的请求将被 MAC 框架所拒绝。
+ 那些需要为大范围的系统对象进行安全标记初始化工作,而又不能处理含有未被正确初始化安全标记的对象的策略模块可能会设置该标志。</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>MPC_LOADTIME_FLAG_LABELMBUFS</term>
+
+ <listitem>
+ <para>表示该策略模块要求为 Mbuf 指定安全标记,并且为存储其标记所需的内存空间总是提前分配好的。
+ 缺省情况下,MAC 框架并不会为 Mbuf 分配标记存储,除非系统中注册的策略模块中至少有一个设置了该标志。
+ 这种做法在没有策略需要对 Mbuf 做标记时,显著地提升了系统网络性能。另外,在某些特殊环境下,可以通过设置内核选项,
+ <literal>MAC_ALWAYS_LABEL_MBUF</literal>,强制 MAC 框架为 Mbuf 的安全标记分配存储,而不论上述标志如何设置。</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <note><para>那些使用了
+ <literal>MPC_LOADTIME_FLAG_LABELMBUFS</literal> 标志但没有设置
+ <literal>MPC_LOADTIME_FLAG_NOTLATE</literal> 标志的
+ 策略模块必须能够正确地处理通过入口函数传入的值为 <literal>NULL</literal>
+ 的 Mbuf 安全标记指针。
+ 这是因为那些没有分配标记存储的处理中的 Mbuf 在一个需要 Mbuf 安全标记的策略模块加载之后,
+ 其安全标记的指针将仍然为空。
+ 如果策略在网络子系统活跃之前被加载(即,该策略不是被推迟加载的),那么所有的 Mbuf 的标记存储的分配就可以得到保证。</para></note>
+ </sect2>
+
+ <sect2 xml:id="mac-policy-entry-points">
+ <title>策略入口函数</title>
+
+ <para>MAC 框架为注册的策略提供四种类型的入口函数:
+ 策略注册和管理入口函数;
+ 用于处理内核对象声明周期事件,如初始化、
+ 创建和销毁,的入口函数;
+ 处理该策略模块感兴趣的访问控制决策事件的入口函数;
+ 以及用于管理对象安全标记的调用入口函数。
+ 此外,
+ 还有一个 <function>mac_syscall()</function> 入口函数,
+ 被策略模块用于在不注册新的系统调用的前提下,
+ 扩展内核接口。</para>
+
+ <para>策略模块的编写人员除了必须清楚在进入特定入口函数之后,
+ 哪些对象锁是可用的之外,
+ 还应该熟知内核所采用的加锁策略。
+ 编程人员在入口函数之内应该避免使用非叶节点锁,
+ 并且遵循访问和修改对象时的加锁规程,
+ 以降低导致死锁的可能性。
+ 特别地,
+ 程序员应该清楚,
+ 虽然在通常情况下,
+ 进入入口函数之后,
+ 已经上了一些锁,
+ 可以安全地访问对象及其安全标记,
+ 但是这并不能保证对它们进行修改(
+ 包括对象本身和其安全标记)
+ 也是安全的。
+ 相关的上锁信息,可以参考 MAC 框架入口函数的相关文档。</para>
+
+ <para>策略入口函数把两个分别指向对象本身和其安全标记的指针传递给策略模块。
+ 这样一来,即使策略并不熟悉对象内部结构,也能基于标记作出正确决策。
+ 只有进程信任状这个对象例外:MAC 框架总是假设所有的策略模块是理解其内部结构的。</para>
+ </sect2>
+ </sect1>
+
+ <sect1 xml:id="mac-entry-point-reference">
+ <title>MAC策略入口函数参考</title>
+
+ <sect2 xml:id="mac-mpo-general">
+ <title>通用的模块入口函数</title>
+
+ <sect3 xml:id="mac-mpo-init">
+ <title><function>&mac.mpo;_init</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_init</function></funcdef>
+
+ <paramdef>struct mac_policy_conf
+ *<parameter>conf</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>conf</parameter></entry>
+ <entry>MAC 策略定义</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>策略加载事件。当前进程正持有策略链表上的互斥锁,因此是非睡眠的,对其他内核子系统的调用也须慎重。
+ 如果需要在策略初始化阶段进行可能造成睡眠阻塞的存储分配操作,可以将它们放在一个单独的模块 SYSINIT()
+ 过程中集中进行。</para>
+ </sect3>
+
+ <sect3 xml:id="mpo-destroy">
+ <title><function>&mac.mpo;_destroy</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_destroy</function></funcdef>
+
+ <paramdef>struct mac_policy_conf
+ *<parameter>conf</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>conf</parameter></entry>
+ <entry>MAC 策略定义</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>策略加载事件。必须持有策略链表互斥锁,因此需要慎重行事。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-syscall">
+ <title><function>&mac.mpo;_syscall</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_syscall</function></funcdef>
+
+ <paramdef>struct thread
+ *<parameter>td</parameter></paramdef>
+ <paramdef>int <parameter>call</parameter></paramdef>
+ <paramdef>void *<parameter>arg</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>td</parameter></entry>
+ <entry>调用线程</entry>
+ </row>
+
+ <row>
+ <entry><parameter>call</parameter></entry>
+ <entry>策略特有的系统调用编号</entry>
+ </row>
+
+ <row>
+ <entry><parameter>arg</parameter></entry>
+ <entry>系统调用参数的指针</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>该入口函数提供策略复用的系统调用,这样策略模块不需要为其向用户进程提供的每一个额外服务而注册专用的系统调用。
+ 由应用程序提供的策略注册名字来确定提供其所申请服务的特定策略,所有参数将通过该入口函数传递给被调用的策略。
+ 当实现新服务时,安全模块必须在必要时通过 MAC 框架调用相应的访问控制检查机制。
+ 比方说,假如一个策略实现了某种额外的信号功能,那么它应该调用相关的信号访问控制检查,以接受 MAC 框架中注册的其他策略的检查。</para>
+
+ <note><para>不同的模块需要并发地手动进行<function>copyin()</function>拷贝系统调用数据。</para></note>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-thread-userret">
+ <title><function>&mac.mpo;_thread_userret</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_thread_userret</function></funcdef>
+
+ <paramdef>struct thread
+ *<parameter>td</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>td</parameter></entry>
+ <entry>返回线程</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <!-- XXX: Maybe rewrite this section. -->
+ <para>使用该入口函数,策略模块能够在线程返回用户空间时(系统调用返回、异常返回等等)进行 MAC 相关的处理工作。
+ 使用动态进程标记的策略需要使用该入口函数,因为在处理系统调用的过程中,并不是在任意时刻都能申请到进程锁的;
+ 进程的标记可能表示传统的认证信息、进程历史记录或者其他数据。为使用该入口函数,对进程信任状所作的修改
+ 可能被存放在 <literal>p_label</literal> ,该域受一个进程级自旋锁的保护;接下来,设置线程级的<literal>TDF_ASTPENDING</literal>
+ 标志位和进程级的<literal>PS_MACPENDM</literal>标志位,表明将调度一个对 userret 入口函数的调用。通过该入口函数,
+ 策略可以在相对简单的同步上下文中创建信任状的替代品。策略编程人员必须清楚,需要保证与调度一个 AST 相关的事件执行次序,
+ 同时所执行的 AST 可能很复杂,而且在处理多线程应用程序时可能被重入。</para>
+ </sect3>
+ </sect2>
+
+ <sect2 xml:id="mac-label-ops">
+ <title>操作标记</title>
+
+ <sect3 xml:id="mac-mpo-init-bpfdesc">
+ <title><function>&mac.mpo;_init_bpfdesc_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_init_bpfdesc_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>将被应用的新标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为一个新近实例化的 bpfdesc(BPF 描述子)初始化标记。可以睡眠。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-init-cred-label">
+ <title><function>&mac.mpo;_init_cred_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_init_cred_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>将被初始化的新标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为一个新近实例化的用户信任状初始化标记。可以睡眠。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-init-devfsdirent">
+ <title><function>&mac.mpo;_init_devfsdirent_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_init_devfsdirent_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>将被应用的新标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为一个新近实例化的 devfs 表项初始化标记。可以睡眠。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-init-ifnet">
+ <title><function>&mac.mpo;_init_ifnet_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_init_ifnet_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>将被应用的新标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为一个新近实例化的网络接口初始化标记。可以睡眠。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-init-ipq">
+ <title><function>&mac.mpo;_init_ipq_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_init_ipq_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ <paramdef>int <parameter>flag</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>将被应用的新标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>flag</parameter></entry>
+ <entry>睡眠/不睡眠 &man.malloc.9;; 参见下文</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为一个新近实例化的 IP 分片重组队列初始化标记。其中的<parameter>flag</parameter>域可能取<symbol>M_WAITOK</symbol>
+ 或<symbol>M_NOWAIT</symbol>之一,用来避免在该初始化调用中因为 &man.malloc.9; 而进入睡眠。IP 分片重组队列的分配操作通常是在
+ 对性能有严格要求的环境下进行的,因此实现代码必须小心地避免睡眠和长时间的操作。IP 分片重组队列分配操作失败时上述入口函数将失败返回。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-init-mbuf">
+ <title><function>&mac.mpo;_init_mbuf_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_init_mbuf_label</function></funcdef>
+
+ <paramdef>int <parameter>flag</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>flag</parameter></entry>
+ <entry>睡眠/不睡眠 &man.malloc.9;; 参见下文</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>将被初始化的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为一个新近实例化的 mbuf 数据包头部(<parameter>mbuf</parameter>)初始化标记。
+ 其中的<parameter>flag</parameter>的值可能取<symbol>M_WAITOK</symbol>和<symbol>M_NOWAIT</symbol>之一,
+ 用来避免在该初始化调用中因为 &man.malloc.9; 而进入睡眠。Mbuf 头部的分配操作常常在对性能有严格要求的环境下被频繁执行,
+ 因此实现代码必须小心地避免睡眠和长时间的操作。上述入口函数在 Mbuf 头部分配操作失败时将失败返回。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-init-mount">
+ <title><function>&mac.mpo;_init_mount_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_init_mount_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>mntlabel</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>fslabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <!-- XXX: Wording on label descriptions. -->
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>mntlabel</parameter></entry>
+ <entry>将被初始化的mount 结构策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>fslabel</parameter></entry>
+ <entry>将被初始化的文件系统策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为一个新近实例化的 mount 点初始化标记。可以睡眠。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-init-mount-fs-label">
+ <title><function>&mac.mpo;_init_mount_fs_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_init_mount_fs_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>将被初始化的标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为一个新近加载的文件系统初始化标记。可以睡眠。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-init-pipe-label">
+ <title><function>&mac.mpo;_init_pipe_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_init_pipe_label</function></funcdef>
+
+ <paramdef>struct
+ label*<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>将被填写的标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为一个刚刚实例化的管道初始化安全标记。可以睡眠。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-init-socket">
+ <title><function>&mac.mpo;_init_socket_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_init_socket_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ <paramdef>int <parameter>flag</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>将被初始化的新标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>flag</parameter></entry>
+ <entry>&man.malloc.9; flags</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为一个刚刚实例化的套接字初始化安全标记。其中的 <parameter>flag</parameter> 域的值必须被指定为
+ <symbol>M_WAITOK</symbol>和<symbol>M_NOWAIT</symbol>之一,以避免在该初始化程中使用可能睡眠的&man.malloc.9; 。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-init-socket-peer-label">
+ <title><function>&mac.mpo;_init_socket_peer_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_init_socket_peer_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ <paramdef>int <parameter>flag</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>将被初始化的新标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>flag</parameter></entry>
+ <entry>&man.malloc.9; flags</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为刚刚实例化的套接字对等体进行标记的初始化。其中的 <parameter>flag</parameter> 域的值必须被指定为
+ <symbol>M_WAITOK</symbol> 和 <symbol>M_NOWAIT</symbol> 之一,以避免在该初始化程中使用可能睡眠的
+ &man.malloc.9;。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-init-proc-label">
+ <title><function>&mac.mpo;_init_proc_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_init_proc_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>将被初始化的新标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为一个刚刚实例化的进程初始化安全标记。可以睡眠。</para>
+ </sect3>
+
+
+ <sect3 xml:id="mac-mpo-init-vnode">
+ <title><function>&mac.mpo;_init_vnode_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_init_vnode_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>将被初始化的新标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为一个刚刚实例化的 vnode 初始化安全标记。可以睡眠。</para>
+ </sect3>
+ <sect3 xml:id="mac-mpo-destroy-bpfdesc">
+ <title><function>&mac.mpo;_destroy_bpfdesc_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_destroy_bpfdesc_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>bpfdesc 标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>销毁一个 BPF 描述子上的标记。在该入口函数中,策略应当释放所有在内部分配与 <parameter>label</parameter>
+ 相关联的存储空间,以便销毁该标记。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-destroy-cred">
+ <title><function>&mac.mpo;_destroy_cred_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_destroy_cred_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>将被销毁的标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>销毁一个信任状上的标记。在该入口函数中,策略应当释放所有在内部分配的与 <parameter>label</parameter>
+ 相关联的存储空间,以便销毁该标记。</para>
+ </sect3>
+
+
+ <sect3 xml:id="mac-mpo-destroy-devfsdirent">
+ <title><function>&mac.mpo;_destroy_devfsdirent_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_destroy_devfsdirent_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>将被销毁的标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>销毁一个 devfs 表项上的标记。在该入口函数中,策略应当释放所有在内部分配的与 <parameter>label</parameter>
+ 相关联的存储空间,以便销毁该标记。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-destroy-ifnet-label">
+ <title><function>&mac.mpo;_destroy_ifnet_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_destroy_ifnet_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>将被销毁的标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>销毁与一个已删除接口相关联的标记。在该入口函数中,策略应当释放所有在内部分配的与 <parameter>label</parameter>
+ 相关联的存储空间,以便销毁该标记。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-destroy-ipq-label">
+ <title><function>&mac.mpo;_destroy_ipq_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_destroy_ipq_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>将被销毁的标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>销毁与一个 IP 分片队列相关联的标记。在该入口函数中,策略应当释放所有在内部分配的与 <parameter>label</parameter>
+ 相关联的存储空间,以便销毁该标记。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-destroy-mbuf-label">
+ <title><function>&mac.mpo;_destroy_mbuf_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_destroy_mbuf_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>将被销毁的标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>销毁与一个 Mbuf 相关联的标记。在该入口函数中,策略应当释放所有在内部分配的与 <parameter>label</parameter>
+ 相关联的存储空间,以便销毁该标记。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-destroy-mount-label">
+ <title><function>&mac.mpo;_destroy_mount_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_destroy_mount_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>将被销毁的 Mount 点标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>销毁与一个 mount 点相关联的标记。在该入口函数中,策略应当释放所有在内部分配的与 <parameter>mntlabel</parameter>
+ 相关联的存储空间,以便销毁该标记。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-destroy-mount">
+ <title><function>&mac.mpo;_destroy_mount_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_destroy_mount_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>mntlabel</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>fslabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>mntlabel</parameter></entry>
+ <entry>将被销毁的 Mount 点标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>fslabel</parameter></entry>
+ <entry>File system label being destroyed&gt;</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>销毁与一个 mount 点相关联的标记。在该入口函数中,策略应当释放所有在内部分配的,与 <parameter>mntlabel</parameter>
+ 和<parameter>fslabel</parameter> 相关联的存储空间,以便销毁该标记。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-destroy-socket">
+ <title><function>&mac.mpo;_destroy_socket_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_destroy_socket_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>将被销毁的套接字标记</entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>销毁与一个套接字相关联的标记。在该入口函数中,策略应当释放所有在内部分配的,与 <parameter>label</parameter>
+ 相关联的存储空间,以便销毁该标记。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-destroy-socket-peer-label">
+ <title><function>&mac.mpo;_destroy_socket_peer_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_destroy_socket_peer_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>peerlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>peerlabel</parameter></entry>
+ <entry>将被销毁的套接字对等实体标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>销毁与一个套接字相关联的对等实体标记。在该入口函数中,策略应当释放所有在内部分配的,与 <parameter>label</parameter>
+ 相关联的存储空间,以便销毁该标记。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-destroy-pipe-label">
+ <title><function>&mac.mpo;_destroy_pipe_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_destroy_pipe_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>管道标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>销毁一个管道的标记。在该入口函数中,策略应当释放所有在内部分配的,与 <parameter>label</parameter>
+ 相关联的存储空间,以便销毁该标记。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-destroy-proc-label">
+ <title><function>&mac.mpo;_destroy_proc_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_destroy_proc_label</function></funcdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>进程标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>销毁一个进程的标记。在该入口函数中,策略应当释放所有在内部分配的,与 <parameter>label</parameter>
+ 相关联的存储空间,以便销毁该标记。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-destroy-vnode-label">
+ <title><function>&mac.mpo;_destroy_vnode_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_destroy_vnode_label</function></funcdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>进程标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>销毁一个 vnode 的标记。在该入口函数中,策略应当释放所有在内部分配的,与 <parameter>label</parameter>
+ 相关联的存储空间,以便销毁该标记。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-copy-mbuf-label">
+ <title><function>&mac.mpo;_copy_mbuf_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_copy_mbuf_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>src</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>dest</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>src</parameter></entry>
+ <entry>源标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dest</parameter></entry>
+ <entry>目标标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>将 <parameter>src</parameter> 中的标记信息拷贝到 <parameter>dest</parameter>中。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-copy-pipe-label">
+ <title><function>&mac.mpo;_copy_pipe_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_copy_pipe_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>src</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>dest</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>src</parameter></entry>
+ <entry>源标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dest</parameter></entry>
+ <entry>目标标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>将 <parameter>src</parameter> 中的标记信息拷贝至 <parameter>dest</parameter>。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-copy-vnode-label">
+ <title><function>&mac.mpo;_copy_vnode_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_copy_vnode_label</function></funcdef>
+
+ <paramdef>struct label
+ *<parameter>src</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>dest</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>src</parameter></entry>
+ <entry>源标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dest</parameter></entry>
+ <entry>目标标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>将 <parameter>src</parameter> 中的标记信息拷贝至 <parameter>dest</parameter>。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-externalize-cred-label">
+ <title><function>&mac.mpo;_externalize_cred_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_externalize_cred_label</function></funcdef>
+
+ &mac.externalize.paramdefs;
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ &mac.externalize.tbody;
+ </tgroup>
+ </informaltable>
+
+ &mac.externalize.para;
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-externalize-ifnet-label">
+ <title><function>&mac.mpo;_externalize_ifnet_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_externalize_ifnet_label</function></funcdef>
+
+ &mac.externalize.paramdefs;
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ &mac.externalize.tbody;
+ </tgroup>
+ </informaltable>
+
+ &mac.externalize.para;
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-externalize-pipe-label">
+ <title><function>&mac.mpo;_externalize_pipe_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_externalize_pipe_label</function></funcdef>
+
+ &mac.externalize.paramdefs;
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ &mac.externalize.tbody;
+ </tgroup>
+ </informaltable>
+
+ &mac.externalize.para;
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-externalize-socket-label">
+ <title><function>&mac.mpo;_externalize_socket_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_externalize_socket_label</function></funcdef>
+
+ &mac.externalize.paramdefs;
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ &mac.externalize.tbody;
+ </tgroup>
+ </informaltable>
+
+ &mac.externalize.para;
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-externalize-socket-peer-label">
+ <title><function>&mac.mpo;_externalize_socket_peer_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_externalize_socket_peer_label</function></funcdef>
+
+ &mac.externalize.paramdefs;
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ &mac.externalize.tbody;
+ </tgroup>
+ </informaltable>
+
+ &mac.externalize.para;
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-externalize-vnode-label">
+ <title><function>&mac.mpo;_externalize_vnode_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_externalize_vnode_label</function></funcdef>
+
+ &mac.externalize.paramdefs;
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ &mac.externalize.tbody;
+ </tgroup>
+ </informaltable>
+
+ &mac.externalize.para;
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-internalize-cred-label">
+ <title><function>&mac.mpo;_internalize_cred_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_internalize_cred_label</function></funcdef>
+
+ &mac.internalize.paramdefs;
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ &mac.internalize.tbody;
+ </tgroup>
+ </informaltable>
+
+ &mac.internalize.para;
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-internalize-ifnet-label">
+ <title><function>&mac.mpo;_internalize_ifnet_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_internalize_ifnet_label</function></funcdef>
+
+ &mac.internalize.paramdefs;
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ &mac.internalize.tbody;
+ </tgroup>
+ </informaltable>
+
+ &mac.internalize.para;
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-internalize-pipe-label">
+ <title><function>&mac.mpo;_internalize_pipe_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_internalize_pipe_label</function></funcdef>
+
+ &mac.internalize.paramdefs;
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ &mac.internalize.tbody;
+ </tgroup>
+ </informaltable>
+
+ &mac.internalize.para;
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-internalize-socket-label">
+ <title><function>&mac.mpo;_internalize_socket_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_internalize_socket_label</function></funcdef>
+
+ &mac.internalize.paramdefs;
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ &mac.internalize.tbody;
+ </tgroup>
+ </informaltable>
+
+ &mac.internalize.para;
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-internalize-vnode-label">
+ <title><function>&mac.mpo;_internalize_vnode_label</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_internalize_vnode_label</function></funcdef>
+
+ &mac.internalize.paramdefs;
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ &mac.internalize.tbody;
+ </tgroup>
+ </informaltable>
+
+ &mac.internalize.para;
+ </sect3>
+ </sect2>
+
+ <sect2 xml:id="mac-label-events">
+ <title>标记事件</title>
+
+ <para>策略模块使用MAC 框架提供的“标记事件”类入口函数,对内核对象的标记进行操作。策略模块将感兴趣的被标记内核对象的相关生命周期事件
+ 注册在恰当的入口点上。对象的初始化、创建和销毁事件均提供了钩子点。在某些对象上还可以实现重新标记,即,允许用户进程改变对象上的标记值。
+ 对某些对象可以实现其特定的对象事件,比如与 IP 重组相关的标记事件。一个典型的被标记对象在其生命周期中将拥有下列入口函数:</para>
+
+ <programlisting>标记初始化 o
+(对象相关的等待) \
+标记创建 o
+ \
+重新标记事件, o--&lt;--.
+各种对象相关的, | |
+访问控制事件 ~--&gt;--o
+ \
+标记销毁 o</programlisting>
+
+ <para>使用标记初始化入口函数,策略可以以一种统一的、与对象使用环境无关的方式设置标记的初始值。
+ 分配给一个策略的缺省 slot 值为0,这样不使用标记的策略可能并不需要执行专门的初始化操作。</para>
+
+ <para>标记的创建事件发生在将一个内核数据结构同一个真实的内核对象相关联(内核对象实例化)的时刻。
+ 例如,在真正被使用之前,在一个缓冲池内已分配的 mbuf 数据结构,将保持为“未使用”状态。
+ 因此,mbuf 的分配操作将导致针对该 mbuf 的标记初始化操作,而 mbuf 的创建操作则被推迟到该 mbuf 真正与一个数据报相关联的时刻。
+ 通常,调用者将会提供创建事件的上下文,包括创建环境、创建过程中涉及的其他对象的标记等。例如,当一个套接字创建一个 mbuf 时,
+ 除了新创建的 mbuf 及其标记之外,作为创建者的套接字与其标记也被提交给策略检查。
+ 不提倡在创建对象时就为其分配内存的原因有两个:创建操作可能发生在对性能有严格要求的内核接口上;
+ 而且,因为创建调用不允许失败,所以无法报告内存分配失败。</para>
+
+ <para>对象特有的事件一般不会引发其他的标记事件,但是在对象上下文发生改变时,策略使用它们可以对相关标记进行修改或更新操作。
+ 例如,在<symbol>MAC_UPDATE_IPQ</symbol> 入口函数之内,某个 IP 分片重组队列的标记可能会因为队列中接收了新的 mbuf 而被更新。</para>
+
+ <para>访问控制事件将在后续章节中详细讨论。</para>
+
+ <para>策略通过执行标记销毁操作,释放为其分配的存储空间或维护的状态,之后内核才可以重用或者释放对象的内核数据结构。</para>
+
+ <para>除了与特定内核对象绑定的普通标记之外,还有一种额外的标记类型:临时标记。这些标记用于存放由用户进程提交的更新信息。
+ 它们的初始化和销毁操作与其他标记一样,只是创建事件,<symbol>MAC_INTERNALIZE</symbol>,略有不同:
+ 该函数接受用户提交的标记,负责将其转化为内核表示形式。 </para>
+
+ <sect3 xml:id="mac-fs-label-event-ops">
+ <title>文件系统对象标记事件操作</title>
+
+ <sect4 xml:id="mac-mpo-associate-vnode-devfs">
+ <title><function>&mac.mpo;_associate_vnode_devfs</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_associate_vnode_devfs</function></funcdef>
+
+ <paramdef>struct mount
+ *<parameter>mp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>fslabel</parameter></paramdef>
+ <paramdef>struct devfs_dirent
+ *<parameter>de</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>delabel</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>vlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>mp</parameter></entry>
+ <entry>Devfs 挂载点</entry>
+ </row>
+
+ <row>
+ <entry><parameter>fslabel</parameter></entry>
+ <entry>Devfs 文件系统标记
+ (<varname>mp-&gt;mnt_fslabel</varname>)</entry>
+ </row>
+
+ <row>
+ <entry><parameter>de</parameter></entry>
+ <entry>Devfs 目录项</entry>
+ </row>
+
+ <row>
+ <entry><parameter>delabel</parameter></entry>
+ <entry>与 <parameter>de</parameter> 相关联的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>与 <parameter>de</parameter> 相关联的 vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vlabel</parameter></entry>
+ <entry>与 <parameter>vp</parameter> 相关联的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>根据参数 <parameter>de</parameter> 传入的 devfs 目录项及其标记信息,为一个新近创建的
+ devfs vnode 填充标记(<parameter>vlabel</parameter>)。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-associate-vnode-extattr">
+ <title><function>&mac.mpo;_associate_vnode_extattr</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_associate_vnode_extattr</function></funcdef>
+
+ <paramdef>struct mount
+ *<parameter>mp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>fslabel</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>vlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>mp</parameter></entry>
+ <entry>文件系统挂载点</entry>
+ </row>
+
+ <row>
+ <entry><parameter>fslabel</parameter></entry>
+ <entry>文件系统标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>将被标记的 vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vlabel</parameter></entry>
+ <entry>与 <parameter>vp</parameter> 相关联的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>从文件系统扩展属性中读取 <parameter>vp</parameter> 的标记。成功,返回 <literal>0</literal>。
+ 不成功,则在 <varname>errno</varname> 指定的相应的错误编码。
+ 如果文件系统不支持扩展属性的读取操作,则可以考虑将 <parameter>fslabel</parameter> 拷贝至 <parameter>vlabel</parameter>。
+ </para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-associate-vnode-singlelabel">
+ <title><function>&mac.mpo;_associate_vnode_singlelabel</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_associate_vnode_singlelabel</function></funcdef>
+
+ <paramdef>struct mount
+ *<parameter>mp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>fslabel</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>vlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>mp</parameter></entry>
+ <entry>文件系统挂载点</entry>
+ </row>
+
+ <row>
+ <entry><parameter>fslabel</parameter></entry>
+ <entry>文件系统标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>将被标记的 vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vlabel</parameter></entry>
+ <entry>与 <parameter>vp</parameter> 相关联的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>在非多重标记文件系统上,使用该入口函数,根据文件系统标记,<parameter>fslabel</parameter>,
+ 为 <parameter>vp</parameter> 设置策略标记。</para>
+ </sect4>
+
+
+ <sect4 xml:id="mac-mpo-create-devfs-device">
+ <title><function>&mac.mpo;_create_devfs_device</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_devfs_device</function></funcdef>
+
+ <paramdef>dev_t <parameter>dev</parameter></paramdef>
+ <paramdef>struct devfs_dirent
+ *<parameter>devfs_dirent</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>dev</parameter></entry>
+ <entry><parameter>devfs_dirent</parameter> 对应的设备</entry>
+ </row>
+
+ <row>
+ <entry><parameter>devfs_dirent</parameter></entry>
+ <entry>将被标记的 Devfs 目录项</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>将被填写的 <parameter>devfs_dirent</parameter> 标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为传入设备新建的 devfs_dirent 填写标记。该函数将在设备文件系统加载、重构或添加新设备时被调用。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-create-devfs-directory">
+ <title><function>&mac.mpo;_create_devfs_directory</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_devfs_directory</function></funcdef>
+
+ <paramdef>char *<parameter>dirname</parameter></paramdef>
+ <paramdef>int <parameter>dirnamelen</parameter></paramdef>
+ <paramdef>struct devfs_dirent
+ *<parameter>devfs_dirent</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>dirname</parameter></entry>
+ <entry>新建目录的名字</entry>
+ </row>
+
+ <row>
+ <entry><parameter>namelen</parameter></entry>
+ <entry>字符串 <parameter>dirname</parameter> 的长度</entry>
+ </row>
+
+ <row>
+ <entry><parameter>devfs_dirent</parameter></entry>
+ <entry>新建目录在 Devfs 中对应的目录项</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为传入目录参数的新建 devfs_dirent 填写标记。该函数将在加载、重构设备文件系统,或者添加一个需要指定目录结构的新设备时被调用。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-create-devfs-symlink">
+ <title><function>&mac.mpo;_create_devfs_symlink</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_devfs_symlink</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct mount
+ *<parameter>mp</parameter></paramdef>
+ <paramdef>struct devfs_dirent
+ *<parameter>dd</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>ddlabel</parameter></paramdef>
+ <paramdef>struct devfs_dirent
+ *<parameter>de</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>delabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>mp</parameter></entry>
+ <entry>devfs 挂载点</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dd</parameter></entry>
+ <entry>链接目标</entry>
+ </row>
+
+ <row>
+ <entry><parameter>ddlabel</parameter></entry>
+ <entry>与 <parameter>dd</parameter> 相关联的标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>de</parameter></entry>
+ <entry>符号链接项</entry>
+ </row>
+
+ <row>
+ <entry><parameter>delabel</parameter></entry>
+ <entry>与 <parameter>de</parameter> 相关联的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为新近创建的 &man.devfs.5; 符号链接项填写标记(<parameter>delabel</parameter>)。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-create-vnode-extattr">
+ <title><function>&mac.mpo;_create_vnode_extattr</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_create_vnode_extattr</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct mount
+ *<parameter>mp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>fslabel</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>dvp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>dlabel</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>vlabel</parameter></paramdef>
+ <paramdef>struct componentname
+ *<parameter>cnp</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>mount</parameter></entry>
+ <entry>文件系统挂载点</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>文件系统标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dvp</parameter></entry>
+ <entry>父目录 vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dlabel</parameter></entry>
+ <entry>与 <parameter>dvp</parameter> 相关联的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>新创建的 vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vlabel</parameter></entry>
+ <entry>与 <parameter>vp</parameter> 相关联的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>cnp</parameter></entry>
+ <entry><parameter>vp</parameter>中的子域名字</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>将 <parameter>vp</parameter> 的标记写入文件扩展属性。成功,将标记填入 <parameter>vlabel</parameter>,
+ 并返回 <returnvalue>0</returnvalue>。否则,返回对应的错误编码。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-create-mount">
+ <title><function>&mac.mpo;_create_mount</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_mount</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct mount
+ *<parameter>mp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>mnt</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>fslabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>mp</parameter></entry>
+ <entry>客体;将被挂载的文件系统</entry>
+ </row>
+
+ <row>
+ <entry><parameter>mntlabel</parameter></entry>
+ <entry>将被填写的 <parameter>mp</parameter> 的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>fslabel</parameter></entry>
+ <entry>将被挂载到 <parameter>mp</parameter> 的文件系统的策略标记。</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为传入的主体信任状所创建的挂载点填写标记。该函数将在文件系统挂载时被调用。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-create-root-mount">
+ <title><function>&mac.mpo;_create_root_mount</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_root_mount</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct mount
+ *<parameter>mp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>mntlabel</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>fslabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry namest="first" nameend="last">见 <xref linkend="mac-mpo-create-mount"/>.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为传入的主体信任状所创建的挂载点填写标记。该函数将在挂载根文件系统时,&mac.mpo;_create_mount; 之后被调用。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-relabel-vnode">
+ <title><function>&mac.mpo;_relabel_vnode</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_relabel_vnode</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>vnodelabel</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>newlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>将被重新标记的 vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vnodelabel</parameter></entry>
+ <entry><parameter>vp</parameter> 现有的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>newlabel</parameter></entry>
+ <entry>将取代<parameter>vnodelabel</parameter>的新(可能只是部分)标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>根据传入的新标记和主体信任状,更新参数 vnode 的标记。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-setlabel-vnode-extattr">
+ <title><function>&mac.mpo;_setlabel_vnode_extattr</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_setlabel_vnode_extattr</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>vlabel</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>intlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>写出标记所对应的 vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vlabel</parameter></entry>
+ <entry><parameter>vp</parameter>的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>intlabel</parameter></entry>
+ <entry>将被写入磁盘的标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>将参数 <parameter>intlabel</parameter> 给出的标记信息写入指定 vnode 的扩展属性。
+ 该函数被 <function>vop_stdcreatevnode_ea</function> 所调用。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-update-devfsdirent">
+ <title><function>&mac.mpo;_update_devfsdirent</function></title>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_update_devfsdirent</function></funcdef>
+
+ <paramdef>struct devfs_dirent
+ *<parameter>devfs_dirent</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>direntlabel</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>vnodelabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>devfs_dirent</parameter></entry>
+ <entry>客体;devfs 目录项</entry>
+ </row>
+
+ <row>
+ <entry><parameter>direntlabel</parameter></entry>
+ <entry>将被更新的<parameter>devfs_dirent</parameter>的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>父 vnode</entry>
+ <entry>已锁定</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vnodelabel</parameter></entry>
+ <entry><parameter>vp</parameter>的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>根据所传入的 devfs vnode 标记,对 <parameter>devfs_dirent</parameter> 的标记进行更新。
+ 重新标记一个 devfs vnode 的操作成功之后,将调用该函数来确认标记的改变,如此,即使相应的 vnode 数据结构被内核回收重用,
+ 也不会丢失标记的新状态。另外,在 devfs 中新建一个符号链接时,紧接着<function>mac_vnode_create_from_vnode</function>,
+ 也将调用该函数,对 vnode 标记进行初始化操作。</para>
+ </sect4>
+ </sect3>
+
+ <sect3 xml:id="mac-ipc-label-ops">
+ <title>IPC 对象标记事件操作</title>
+
+
+ <sect4 xml:id="mac-mpo-create-mbuf-from-socket">
+ <title><function>&mac.mpo;_create_mbuf_from_socket</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_mbuf_from_socket</function></funcdef>
+
+ <paramdef>struct socket
+ *<parameter>so</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>socketlabel</parameter></paramdef>
+ <paramdef>struct mbuf *<parameter>m</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>mbuflabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>socket</parameter></entry>
+ <entry>套接字</entry>
+ <entry>套接字锁定 WIP</entry>
+ </row>
+
+ <row>
+ <entry><parameter>socketlabel</parameter></entry>
+ <entry><parameter>socket</parameter> 的策略标记 </entry>
+ </row>
+
+ <row>
+ <entry><parameter>m</parameter></entry>
+ <entry>客体;mbuf</entry>
+ </row>
+
+ <row>
+ <entry><parameter>mbuflabel</parameter></entry>
+ <entry>将被填写的 <parameter>m</parameter> 的策略标记 </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>根据传入的套接字标记为新创建的mbuf头部设置标记。
+ 每当套接字产生一个新的数据报或者消息,并将其存储在参数 mbuf 中时,将调用该函数。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-create-pipe">
+ <title><function>&mac.mpo;_create_pipe</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_pipe</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct pipe
+ *<parameter>pipe</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>pipelabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>pipe</parameter></entry>
+ <entry>管道</entry>
+ </row>
+
+ <row>
+ <entry><parameter>pipelabel</parameter></entry>
+ <entry><parameter>pipe</parameter> 的策略标记 </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>根据传入的主体信任状参数,设置新建管道的标记。每当一个新管道被创建,该函数将被调用。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-create-socket">
+ <title><function>&mac.mpo;_create_socket</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_socket</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct socket
+ *<parameter>so</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>socketlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ <entry>不可改变</entry>
+ </row>
+
+ <row>
+ <entry><parameter>so</parameter></entry>
+ <entry>客体;将被标记的套接字</entry>
+ </row>
+
+ <row>
+ <entry><parameter>socketlabel</parameter></entry>
+ <entry>将被填写的 <parameter>so</parameter> 的标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>根据传入的主体信任状参数,设置新建套接字的标记。每当新建一个套接字,该函数将被调用。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-create-socket-from-socket">
+ <title><function>&mac.mpo;_create_socket_from_socket</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_socket_from_socket</function></funcdef>
+
+ <paramdef>struct socket
+ *<parameter>oldsocket</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>oldsocketlabel</parameter></paramdef>
+ <paramdef>struct socket
+ *<parameter>newsocket</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>newsocketlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>oldsocket</parameter></entry>
+ <entry>监听套接字</entry>
+ </row>
+
+ <row>
+ <entry><parameter>oldsocketlabel</parameter></entry>
+ <entry><parameter>oldsocket</parameter> 的策略标记 </entry>
+ </row>
+
+ <row>
+ <entry><parameter>newsocket</parameter></entry>
+ <entry>新建套接字</entry>
+ </row>
+
+ <row>
+ <entry><parameter>newsocketlabel</parameter></entry>
+ <entry><parameter>newsocket</parameter> 的策略标记 </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>根据 &man.listen.2; 套接字 <parameter>oldsocket</parameter>,
+ 为新建 &man.accept.2; 的套接字 <parameter>newsocket</parameter>,设置标记。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-relabel-pipe">
+ <title><function>&mac.mpo;_relabel_pipe</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_relabel_pipe</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct pipe
+ *<parameter>pipe</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>oldlabel</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>newlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>pipe</parameter></entry>
+ <entry>管道</entry>
+ </row>
+
+ <row>
+ <entry><parameter>oldlabel</parameter></entry>
+ <entry><parameter>pipe</parameter> 的当前策略标记 </entry>
+ </row>
+
+ <row>
+ <entry><parameter>newlabel</parameter></entry>
+ <entry>将为<parameter>pipe</parameter> 设置的新的策略标记 </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为<parameter>pipe</parameter>设置新标记<parameter>newlabel</parameter>。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-relabel-socket">
+ <title><function>&mac.mpo;_relabel_socket</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_relabel_socket</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct socket
+ *<parameter>so</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>oldlabel</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>newlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ <entry>不可改变</entry>
+ </row>
+
+ <row>
+ <entry><parameter>so</parameter></entry>
+ <entry>客体;套接字</entry>
+ </row>
+
+ <row>
+ <entry><parameter>oldlabel</parameter></entry>
+ <entry><parameter>so</parameter> 的当前标记 </entry>
+ </row>
+
+ <row>
+ <entry><parameter>newlabel</parameter></entry>
+ <entry>将为<parameter>socket</parameter> 设置的新标记 </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>根据传入的标记参数,对套接字的当前标记进行更新。</para>
+ </sect4>
+
+ <sect4 xml:id="mpo-set-socket-peer-from-mbuf">
+ <title><function>&mac.mpo;_set_socket_peer_from_mbuf</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_set_socket_peer_from_mbuf</function></funcdef>
+
+ <paramdef>struct mbuf
+ *<parameter>mbuf</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>mbuflabel</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>oldlabel</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>newlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>mbuf</parameter></entry>
+ <entry>从套接字接收到的第一个数据报</entry>
+ </row>
+
+ <row>
+ <entry><parameter>mbuflabel</parameter></entry>
+ <entry><parameter>mbuf</parameter> 的标记 </entry>
+ </row>
+
+ <row>
+ <entry><parameter>oldlabel</parameter></entry>
+ <entry>套接字的当前标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>newlabel</parameter></entry>
+ <entry>将为套接字填写的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>根据传入的 mbuf 标记,设置某个 stream 套接字的对等标志。
+ 除Unix域的套接字之外,每当一个 stream 套接字接收到第一个数据报时,该函数将被调用。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-set-socket-peer-from-socket">
+ <title><function>&mac.mpo;_set_socket_peer_from_socket</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_set_socket_peer_from_socket</function></funcdef>
+
+ <paramdef>struct socket
+ *<parameter>oldsocket</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>oldsocketlabel</parameter></paramdef>
+ <paramdef>struct socket
+ *<parameter>newsocket</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>newsocketpeerlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>oldsocket</parameter></entry>
+ <entry>本地套接字</entry>
+ </row>
+
+ <row>
+ <entry><parameter>oldsocketlabel</parameter></entry>
+ <entry><parameter>oldsocket</parameter> 的策略标记 </entry>
+ </row>
+
+ <row>
+ <entry><parameter>newsocket</parameter></entry>
+ <entry>对等套接字</entry>
+ </row>
+
+ <row>
+ <entry><parameter>newsocketpeerlabel</parameter></entry>
+ <entry>将为<parameter>newsocket</parameter>填写的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <!-- XXX Passed _remote_ socket endpoint ? -->
+ <para>根据传入的远程套接字端点,为一个 stream UNIX 与套接字设置对等标记。
+ 每当相应的套接字对之间进行连接时,该函数将在两端分别被调用。</para>
+ </sect4>
+ </sect3>
+
+ <sect3 xml:id="mac-net-labeling-event-ops">
+ <title>Network Object Labeling Event Operations</title>
+
+ <sect4 xml:id="mac-mpo-create-bpfdesc">
+ <title><function>&mac.mpo;_create_bpfdesc</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_bpfdesc</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct bpf_d
+ *<parameter>bpf_d</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>bpflabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ <entry>不可改变</entry>
+ </row>
+
+ <row>
+ <entry><parameter>bpf_d</parameter></entry>
+ <entry>客体;bpf 描述子</entry>
+ </row>
+
+ <row>
+ <entry><parameter>bpf</parameter></entry>
+ <entry>将为<parameter>bpf_d</parameter>填写的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>根据传入的主体信任状参数,为新建的 BPF 描述子设置标记。
+ 当进程打开 BPF 设备节点时,该函数将被调用。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-create-ifnet">
+ <title><function>&mac.mpo;_create_ifnet</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_ifnet</function></funcdef>
+
+ <paramdef>struct ifnet
+ *<parameter>ifnet</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>ifnetlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>ifnet</parameter></entry>
+ <entry>网络接口</entry>
+ </row>
+
+ <row>
+ <entry><parameter>ifnetlabel</parameter></entry>
+ <entry>将为<parameter>ifnet</parameter>填写的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为新建的网络接口设置标记。该函数在以下情况下被调用:
+ 当一个新的物理接口变为可用时,或者当一个伪接口在引导时或由于某个用户操作而实例化时。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-create-ipq">
+ <title><function>&mac.mpo;_create_ipq</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_ipq</function></funcdef>
+
+ <paramdef>struct mbuf
+ *<parameter>fragment</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>fragmentlabel</parameter></paramdef>
+ <paramdef>struct ipq
+ *<parameter>ipq</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>ipqlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>fragment</parameter></entry>
+ <entry>第一个被接收的 IP 分片</entry>
+ </row>
+
+ <row>
+ <entry><parameter>fragmentlabel</parameter></entry>
+ <entry><parameter>fragment</parameter> 的策略标记 </entry>
+ </row>
+
+ <row>
+ <entry><parameter>ipq</parameter></entry>
+ <entry>将被标记的 IP 重组队列</entry>
+ </row>
+
+ <row>
+ <entry><parameter>ipqlabel</parameter></entry>
+ <entry>将为<parameter>ipq</parameter>填写的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>根据第一个接收到的分片的 mbuf 头部信息,为新建的 IP 分片重组队列设置标记。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-create-datagram-from-ipq">
+ <title><function>&mac.mpo;_create_datagram_from_ipq</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_create_datagram_from_ipq</function></funcdef>
+
+ <paramdef>struct ipq
+ *<parameter>ipq</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>ipqlabel</parameter></paramdef>
+ <paramdef>struct mbuf
+ *<parameter>datagram</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>datagramlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>ipq</parameter></entry>
+ <entry>IP 重组队列</entry>
+ </row>
+
+ <row>
+ <entry><parameter>ipqlabel</parameter></entry>
+ <entry><parameter>ipq</parameter> 的策略标记 </entry>
+ </row>
+
+ <row>
+ <entry><parameter>datagram</parameter></entry>
+ <entry>将被标记的数据报</entry>
+ </row>
+
+ <row>
+ <entry><parameter>datagramlabel</parameter></entry>
+ <entry>将为<parameter>datagramlabel</parameter>填写的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>根据 IP 分片重组队列,为刚刚重组完毕的 IP 数据报设置标记。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-create-fragment">
+ <title><function>&mac.mpo;_create_fragment</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_fragment</function></funcdef>
+
+ <paramdef>struct mbuf
+ *<parameter>datagram</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>datagramlabel</parameter></paramdef>
+ <paramdef>struct mbuf
+ *<parameter>fragment</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>fragmentlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>datagram</parameter></entry>
+ <entry>数据报</entry>
+ </row>
+
+ <row>
+ <entry><parameter>datagramlabel</parameter></entry>
+ <entry><parameter>datagram</parameter> 的策略标记 </entry>
+ </row>
+
+ <row>
+ <entry><parameter>fragment</parameter></entry>
+ <entry>将被标记的分片</entry>
+ </row>
+
+ <row>
+ <entry><parameter>fragmentlabel</parameter></entry>
+ <entry>将为<parameter>datagram</parameter>填写的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>根据数据报所对应的 mbuf 头部信息,为其新建的分片的 mbuf 头部设置标记。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-create-mbuf-from-mbuf">
+ <title><function>&mac.mpo;_create_mbuf_from_mbuf</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_mbuf_from_mbuf</function></funcdef>
+
+ <paramdef>struct mbuf
+ *<parameter>oldmbuf</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>oldmbuflabel</parameter></paramdef>
+ <paramdef>struct mbuf
+ *<parameter>newmbuf</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>newmbuflabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>oldmbuf</parameter></entry>
+ <entry>已有的(源)mbuf</entry>
+ </row>
+
+ <row>
+ <entry><parameter>oldmbuflabel</parameter></entry>
+ <entry><parameter>oldmbuf</parameter> 的策略标记 </entry>
+ </row>
+
+ <row>
+ <entry><parameter>newmbuf</parameter></entry>
+ <entry>将被标记的新建 mbuf</entry>
+ </row>
+
+ <row>
+ <entry><parameter>newmbuflabel</parameter></entry>
+ <entry>将为<parameter>newmbuf</parameter>填写的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>根据某个现有数据报的 mbuf 头部信息,为新建数据报的 mbuf 头部设置标记。在许多条件下将会调用该函数,
+ 比如,由于对齐要求而重新分配某个 mbuf 时。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-create-mbuf-linklayer">
+ <title><function>&mac.mpo;_create_mbuf_linklayer</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_mbuf_linklayer</function></funcdef>
+
+ <paramdef>struct ifnet
+ *<parameter>ifnet</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>ifnetlabel</parameter></paramdef>
+ <paramdef>struct mbuf
+ *<parameter>mbuf</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>mbuflabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>ifnet</parameter></entry>
+ <entry>网络接口</entry>
+ </row>
+
+ <row>
+ <entry><parameter>ifnetlabel</parameter></entry>
+ <entry><parameter>ifnet</parameter> 的策略标记 </entry>
+ </row>
+
+ <row>
+ <entry><parameter>mbuf</parameter></entry>
+ <entry>新建数据报的 mbuf 头部</entry>
+ </row>
+
+ <row>
+ <entry><parameter>mbuflabel</parameter></entry>
+ <entry>将为<parameter>mbuf</parameter>填写的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为在给定接口上由于某个链路层响应而新建的数据报的mbuf头部设置标记。
+ 该函数将在若干条件下被调用,比如当IPv4和IPv6协议栈在响应ARP或者ND6时。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-create-mbuf-from-bpfdesc">
+ <title><function>&mac.mpo;_create_mbuf_from_bpfdesc</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_mbuf_from_bpfdesc</function></funcdef>
+
+ <paramdef>struct bpf_d
+ *<parameter>bpf_d</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>bpflabel</parameter></paramdef>
+ <paramdef>struct mbuf
+ *<parameter>mbuf</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>mbuflabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>bpf_d</parameter></entry>
+ <entry>BPF 描述子</entry>
+ </row>
+
+ <row>
+ <entry><parameter>bpflabel</parameter></entry>
+ <entry><parameter>bpflabel</parameter> 的策略标记 </entry>
+ </row>
+
+ <row>
+ <entry><parameter>mbuf</parameter></entry>
+ <entry>将被标记的新建 mbuf</entry>
+ </row>
+
+ <row>
+ <entry><parameter>mbuflabel</parameter></entry>
+ <entry>将为<parameter>mbuf</parameter>填写的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为使用参数 BPF 描述子创建的新数据报的 mbuf 头部设置标记。
+ 当对参数 BPF 描述子所关联的 BPF 设备进行写操作时,该函数将被调用。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-create-mbuf-from-ifnet">
+ <title><function>&mac.mpo;_create_mbuf_from_ifnet</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_mbuf_from_ifnet</function></funcdef>
+
+ <paramdef>struct ifnet
+ *<parameter>ifnet</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>ifnetlabel</parameter></paramdef>
+ <paramdef>struct mbuf
+ *<parameter>mbuf</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>mbuflabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>ifnet</parameter></entry>
+ <entry>网络接口</entry>
+ </row>
+
+ <row>
+ <entry><parameter>ifnetlabel</parameter></entry>
+ <entry><parameter>ifnetlabel</parameter> 的策略标记 </entry>
+ </row>
+
+ <row>
+ <entry><parameter>mbuf</parameter></entry>
+ <entry>新建数据报的 mbuf 头部</entry>
+ </row>
+
+ <row>
+ <entry><parameter>mbuflabel</parameter></entry>
+ <entry>将为<parameter>mbuf</parameter>填写的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为从网络接口参数创建的数据报的 mbuf 头部设置标记。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-create-mbuf-multicast-encap">
+ <title><function>&mac.mpo;_create_mbuf_multicast_encap</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_mbuf_multicast_encap</function></funcdef>
+
+ <paramdef>struct mbuf
+ *<parameter>oldmbuf</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>oldmbuflabel</parameter></paramdef>
+ <paramdef>struct ifnet
+ *<parameter>ifnet</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>ifnetlabel</parameter></paramdef>
+ <paramdef>struct mbuf
+ *<parameter>newmbuf</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>newmbuflabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>oldmbuf</parameter></entry>
+ <entry>现有数据报的 mbuf 头部</entry>
+ </row>
+
+ <row>
+ <entry><parameter>oldmbuflabel</parameter></entry>
+ <entry><parameter>oldmbuf</parameter> 的策略标记 </entry>
+ </row>
+
+ <row>
+ <entry><parameter>ifnet</parameter></entry>
+ <entry>网络接口</entry>
+ </row>
+
+ <row>
+ <entry><parameter>ifnetlabel</parameter></entry>
+ <entry><parameter>ifnet</parameter> 的策略标记 </entry>
+ </row>
+
+ <row>
+ <entry><parameter>newmbuf</parameter></entry>
+ <entry>将被标记的新建数据报 mbuf 头部</entry>
+ </row>
+
+ <row>
+ <entry><parameter>newmbuflabel</parameter></entry>
+ <entry>将为<parameter>newmbuf</parameter>填写的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>当传入的已有数据报被给定多播封装接口(multicast encapsulation interface)处理时被调用,
+ 为新创建的数据报所在 mbuf 头部设置标记。
+ 每当使用该虚拟接口传递一个mbuf时,将调用该函数。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-create-mbuf-netlayer">
+ <title><function>&mac.mpo;_create_mbuf_netlayer</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_mbuf_netlayer</function></funcdef>
+
+ <paramdef>struct mbuf
+ *<parameter>oldmbuf</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>oldmbuflabel</parameter></paramdef>
+ <paramdef>struct mbuf
+ *<parameter>newmbuf</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>newmbuflabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>oldmbuf</parameter></entry>
+ <entry>接收的数据报</entry>
+ </row>
+
+ <row>
+ <entry><parameter>oldmbuflabel</parameter></entry>
+ <entry><parameter>oldmbuf</parameter> 的策略标记 </entry>
+ </row>
+
+ <row>
+ <entry><parameter>newmbuf</parameter></entry>
+ <entry>新建数据报</entry>
+ </row>
+
+ <row>
+ <entry><parameter>newmbuflabel</parameter></entry>
+ <entry><parameter>newmbuf</parameter> 的策略标记 </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为由 IP 堆栈因为响应接收数据报(<parameter>oldmbuf</parameter>)而新建的数据报设置其 mbuf 头部的标记。
+ 许多情况下需要调用该函数,比如,响应 ICMP 请求数据报时。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-fragment-match">
+ <title><function>&mac.mpo;_fragment_match</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_fragment_match</function></funcdef>
+
+ <paramdef>struct mbuf
+ *<parameter>fragment</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>fragmentlabel</parameter></paramdef>
+ <paramdef>struct ipq
+ *<parameter>ipq</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>ipqlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>fragment</parameter></entry>
+ <entry>IP 数据报分片</entry>
+ </row>
+
+ <row>
+ <entry><parameter>fragmentlabel</parameter></entry>
+ <entry><parameter>fragment</parameter> 的策略标记 </entry>
+ </row>
+
+ <row>
+ <entry><parameter>ipq</parameter></entry>
+ <entry>IP 分片重组队列</entry>
+ </row>
+
+ <row>
+ <entry><parameter>ipqlabel</parameter></entry>
+ <entry><parameter>ipq</parameter> 的策略标记 </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>根据所传入的 IP 分片重组队列(<parameter>ipq</parameter>)的标记,
+ 检查包含一个 IP 数据报(<parameter>fragment</parameter>)的 mbuf 的头部是否符合其要求。
+ 符合,则返回<returnvalue>1</returnvalue>。否则,返回<returnvalue>0</returnvalue>。
+ 每当 IP 堆栈尝试将一个刚刚接收到的分片放入某个已有的分片重组队列中时,将调用该函数进行安全检查;
+ 如果失败,将为分片重新实例化一个新的分片重组队列。
+ 策略可以利用该入口函数,根据标记或者其他信息阻止不期望的 IP 分片重组。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-ifnet-relabel">
+ <title><function>&mac.mpo;_relabel_ifnet</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_relabel_ifnet</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct ifnet
+ *<parameter>ifnet</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>ifnetlabel</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>newlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>ifnet</parameter></entry>
+ <entry>客体;网络接口</entry>
+ </row>
+
+ <row>
+ <entry><parameter>ifnetlabel</parameter></entry>
+ <entry><parameter>ifnet</parameter> 的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>newlabel</parameter></entry>
+ <entry>将为<parameter>ifnet</parameter>设置的新标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>根据所传入的新标记,<parameter>newlabel</parameter>,以及主体信任状,
+ <parameter>cred</parameter>,对网络接口的标记进行更新。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-update-ipq">
+ <title><function>&mac.mpo;_update_ipq</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_update_ipq</function></funcdef>
+
+ <paramdef>struct mbuf
+ *<parameter>fragment</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>fragmentlabel</parameter></paramdef>
+ <paramdef>struct ipq
+ *<parameter>ipq</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>ipqlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>mbuf</parameter></entry>
+ <entry>IP 分片</entry>
+ </row>
+
+ <row>
+ <entry><parameter>mbuflabel</parameter></entry>
+ <entry><parameter>mbuf</parameter> 的策略标记 </entry>
+ </row>
+
+ <row>
+ <entry><parameter>ipq</parameter></entry>
+ <entry>IP 分片重组队列</entry>
+ </row>
+
+ <row>
+ <entry><parameter>ipqlabel</parameter></entry>
+ <entry>将被更新的<parameter>ipq</parameter>的当前策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>根据所传入的 IP 分片 mbuf 头部(<parameter>mbuf</parameter>)为接收
+ 它的 IP 分片重组队列(<parameter>ipq</parameter>)的标记进行更新。</para>
+ </sect4>
+ </sect3>
+
+ <sect3 xml:id="mac-proc-labeling-event-ops">
+ <title>进程标记事件操作</title>
+
+ <sect4 xml:id="mac-mpo-create-cred">
+ <title><function>&mac.mpo;_create_cred</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_cred</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>parent_cred</parameter></paramdef>
+ <paramdef>struct ucred
+ *<parameter>child_cred</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>parent_cred</parameter></entry>
+ <entry>父主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>child_cred</parameter></entry>
+ <entry>子主体信任状</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>根据所传入的主体信任状,为新建的主体信任状设置标记。
+ 每当为一个新建的 <type>struct ucred</type>调用 &man.crcopy.9; 时,将调用此函数。
+ 该函数不应与进程复制(forking)或者创建事件混为一谈。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-execve-transition">
+ <title><function>&mac.mpo;_execve_transition</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_execve_transition</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>old</parameter></paramdef>
+ <paramdef>struct ucred
+ *<parameter>new</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>vnodelabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>old</parameter></entry>
+ <entry>已有的主体信任状</entry>
+ <entry>不可改变</entry>
+ </row>
+
+ <row>
+ <entry><parameter>new</parameter></entry>
+ <entry>将被标记的新主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>将被执行的文件</entry>
+ <entry>已被锁定</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vnodelabel</parameter></entry>
+ <entry><parameter>vp</parameter> 的策略标记 </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>一个拥有信任状<parameter>old</parameter>的主体由于执行(<parameter>vp</parameter>文件而导致标记转换时,
+ 该函数根据vnode标记为该主体重新标记为<parameter>new</parameter>。 每当一个进程请求执行vnode文件,而通过
+ 入口函数<function>mpo_execve_will_transition</function> 有成功返回的策略时,将调用该函数。
+ 策略模块可以通过传入两个主体信任状和简单地调用 <function>mpo_create_cred</function> 来实现该入口函数,
+ so as not to implement a
+ transitioning event. 一旦策略实现了<function>mpo_create_cred</function>函数,即使没有实现
+ <function>mpo_execve_will_transition</function>,也应该实现该函数。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-execve-will-transition">
+ <title><function>&mac.mpo;_execve_will_transition</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_execve_will_transition</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>old</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>vnodelabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>old</parameter></entry>
+ <entry>在执行&man.execve.2;之前的主体信任状</entry>
+ <entry>不可改变</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>将被执行的文件</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vnodelabel</parameter></entry>
+ <entry><parameter>vp</parameter> 的策略标记 </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>由策略决定,当参数主体信任状执行参数 vnode 时,是否需要进行一个标记转换操作。如果需要,返回<returnvalue>1</returnvalue>;
+ 否则,返回<returnvalue>0</returnvalue>。即使一个策略返回<returnvalue>0</returnvalue>,它也必须为自己不期望的对
+ <function>mpo_execve_transition</function>的调用作好准备,因为只要有其他任何一个策略要求转换,就将执行此函数。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-create-proc0">
+ <title><function>&mac.mpo;_create_proc0</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_proc0</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>将被填写的主体信任状</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为进程0,所有内核进程的祖先,创建主体信任状。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-create-proc1">
+ <title><function>&mac.mpo;_create_proc1</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_create_proc1</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>将被填写的主体信任状</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>为进程1,所有用户进程的祖先,创建主体信任状。</para>
+ </sect4>
+
+ <sect4 xml:id="mac-mpo-relabel-cred">
+ <title><function>&mac.mpo;_relabel_cred</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_relabel_cred</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>newlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>newlabel</parameter></entry>
+ <entry>将被应用到 <parameter>cred</parameter> 上的新标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>根据传入的新标记,对主体信任状上的标记进行更新。</para>
+ </sect4>
+
+ </sect3>
+ </sect2>
+
+ <sect2 xml:id="mac-access-control-checks">
+ <title>访问控制检查</title>
+
+ <para>通过访问控制入口函数,策略模块能影响内核的访问控制决策。
+ 通常情况下,不是绝对,一个访问控制入口函数的参数有,一个或者若干个授权信任状,和相关操作涉及的其他任何对象的信息(其中可能包含标记)。
+ 访问控制入口函数返回0,表示允许该操作;否则,返回一个 &man.errno.2; 错误编码。调用该入口函数,将遍历所有系统注册的策略模块,逐一进行
+ 策略相关的检查和决策,之后按照下述方法组合不同策略的返回结果:只有当所有的模块均允许该操作时,才成功返回。
+ 否则,如果有一个或者若干模块失败返回,则整个检查不通过。如果有多个模块的检查出错返回,将由定义在<filename>kern_mac.c</filename> 中的
+ <function>error_select()</function> 函数从它们返回的错误编码中,选择一个合适的,返回给用户。</para>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="2">
+ <tbody>
+ <row>
+ <entry>最高优先级</entry>
+ <entry><errorcode>EDEADLK</errorcode></entry></row>
+
+ <row>
+ <entry/>
+ <entry><errorcode>EINVAL</errorcode></entry>
+ </row>
+ <row>
+ <entry/>
+ <entry><errorcode>ESRCH</errorcode></entry>
+ </row>
+ <row>
+ <entry/>
+ <entry>EACCES</entry>
+ </row>
+ <row>
+ <entry>最低优先级</entry>
+ <entry>EPERM</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>如果所有策略模块返回的错误编码均没有出现在上述优先级序列表中,则任意选择一个返回。
+ 选择错误编码的一般次序为:内核错误,无效的参数,对象不存在,访问被拒绝,和其他错误。</para>
+
+ <sect3 xml:id="mac-mpo-bpfdesc-check-receive-from-ifnet">
+ <title><function>&mac.mpo;_check_bpfdesc_receive</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_bpfdesc_receive</function></funcdef>
+
+ <paramdef>struct bpf_d
+ *<parameter>bpf_d</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>bpflabel</parameter></paramdef>
+ <paramdef>struct ifnet
+ *<parameter>ifnet</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>ifnetlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>bpf_d</parameter></entry>
+ <entry>主体;BPF 描述子</entry>
+ </row>
+
+ <row>
+ <entry><parameter>bpflabel</parameter></entry>
+ <entry><parameter>bpf_d</parameter> 的策略标记 </entry>
+ </row>
+
+ <row>
+ <entry><parameter>ifnet</parameter></entry>
+ <entry>客体;网络接口</entry>
+ </row>
+
+ <row>
+ <entry><parameter>ifnetlabel</parameter></entry>
+ <entry><parameter>ifnet</parameter> 的策略标记 </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>决定 MAC 框架是否应该允许将由参数接口接收到的数据报传递给由 BPF 描述子所对应的缓冲区。成功,则返回<returnvalue>0</returnvalue>;
+ 否则,返回错误编码信息<varname>errno</varname>。建议使用的错误编码有:<errorcode>EACCES</errorcode>,用于标记不符的情况;
+ <errorcode>EPERM</errorcode>,用于缺少特权的情况。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-kenv-dump">
+ <title><function>&mac.mpo;_check_kenv_dump</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_kenv_dump</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>决定相关主体是否应该被允许查询内核环境状态(参考 &man.kenv.2;)。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-kenv-get">
+ <title><function>&mac.mpo;_check_kenv_get</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_kenv_get</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>char *<parameter>name</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>name</parameter></entry>
+ <entry>内核的环境变量名字</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>决定相关主体是否可以查询内核中给定环境变量的状态。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-kenv-set">
+ <title><function>&mac.mpo;_check_kenv_set</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_kenv_set</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>char *<parameter>name</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>name</parameter></entry>
+ <entry>内核的环境变量名字</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>决定相关主体是否有权设置给定内核环境变量的值。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-kenv-unset">
+ <title><function>&mac.mpo;_check_kenv_unset</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_kenv_unset</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>char *<parameter>name</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>name</parameter></entry>
+ <entry>内核的环境变量名字Kernel environment variable name</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>决定相关主体是否有权清除给定的内核环境变量的设置。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-kld-load">
+ <title><function>&mac.mpo;_check_kld_load</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_kld_load</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>vlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>内核模块的 vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vlabel</parameter></entry>
+ <entry><parameter>vp</parameter>的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>决定相关主体是否有权加载给定的模块文件。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-kld-stat">
+ <title><function>&mac.mpo;_check_kld_stat</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_kld_stat</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>决定相关主体是否有权访问内核的加载模块文件链表以及相关的统计数据。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-kld-unload">
+ <title><function>&mac.mpo;_check_kld_unload</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_kld_unload</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>决定相关主体是否有权卸载一个内核模块。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-pipe-ioctl">
+ <title><function>&mac.mpo;_check_pipe_ioctl</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_pipe_ioctl</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct pipe
+ *<parameter>pipe</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>pipelabel</parameter></paramdef>
+ <paramdef>unsigned long
+ <parameter>cmd</parameter></paramdef>
+ <paramdef>void *<parameter>data</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>pipe</parameter></entry>
+ <entry>管道</entry>
+ </row>
+
+ <row>
+ <entry><parameter>pipelabel</parameter></entry>
+ <entry><parameter>pipe</parameter>的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>cmd</parameter></entry>
+ <entry>&man.ioctl.2; 命令</entry>
+ </row>
+
+ <row>
+ <entry><parameter>data</parameter></entry>
+ <entry>&man.ioctl.2; 数据</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>决定相关主体是否有权调用指定的 &man.ioctl.2; 系统调用。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-pipe-poll">
+ <title><function>&mac.mpo;_check_pipe_poll</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_pipe_poll</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct pipe
+ *<parameter>pipe</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>pipelabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>pipe</parameter></entry>
+ <entry>管道</entry>
+ </row>
+
+ <row>
+ <entry><parameter>pipelabel</parameter></entry>
+ <entry><parameter>pipe</parameter>的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>决定相关主体是否有权对管道<parameter>pipe</parameter>执行poll操作。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-pipe-read">
+ <title><function>&mac.mpo;_check_pipe_read</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_pipe_read</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct pipe
+ *<parameter>pipe</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>pipelabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>pipe</parameter></entry>
+ <entry>管道</entry>
+ </row>
+
+ <row>
+ <entry><parameter>pipelabel</parameter></entry>
+ <entry><parameter>pipe</parameter>的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>决定该主体是否有权读取<parameter>pipe</parameter>。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-pipe-relabel">
+ <title><function>&mac.mpo;_check_pipe_relabel</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_pipe_relabel</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct pipe
+ *<parameter>pipe</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>pipelabel</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>newlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>pipe</parameter></entry>
+ <entry>管道</entry>
+ </row>
+
+ <row>
+ <entry><parameter>pipelabel</parameter></entry>
+ <entry><parameter>pipe</parameter>的当前策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>newlabel</parameter></entry>
+ <entry>将为<parameter>pipelabel</parameter>设置的新标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>决定该主体是否有权为<parameter>pipe</parameter>重新设置标记。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-pipe-stat">
+ <title><function>&mac.mpo;_check_pipe_stat</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_pipe_stat</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct pipe
+ *<parameter>pipe</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>pipelabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>pipe</parameter></entry>
+ <entry>管道</entry>
+ </row>
+
+ <row>
+ <entry><parameter>pipelabel</parameter></entry>
+ <entry><parameter>pipe</parameter>的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>决定该主体是否有权查询与<parameter>pipe</parameter>相关的统计信息。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-pipe-write">
+ <title><function>&mac.mpo;_check_pipe_write</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_pipe_write</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct pipe
+ *<parameter>pipe</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>pipelabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>pipe</parameter></entry>
+ <entry>管道</entry>
+ </row>
+
+ <row>
+ <entry><parameter>pipelabel</parameter></entry>
+ <entry><parameter>pipe</parameter>的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>决定该主体是否有权写<parameter>pipe</parameter>。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-socket-bind">
+ <title><function>&mac.mpo;_check_socket_bind</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_socket_bind</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct socket
+ *<parameter>socket</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>socketlabel</parameter></paramdef>
+ <paramdef>struct sockaddr
+ *<parameter>sockaddr</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>socket</parameter></entry>
+ <entry>将被绑定的套接字</entry>
+ </row>
+
+ <row>
+ <entry><parameter>socketlabel</parameter></entry>
+ <entry><parameter>socket</parameter>的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>sockaddr</parameter></entry>
+ <entry><parameter>socket</parameter>的地址</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ </sect3>
+
+
+ <sect3 xml:id="mac-mpo-cred-check-socket-connect">
+ <title><function>&mac.mpo;_check_socket_connect</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_socket_connect</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct socket
+ *<parameter>socket</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>socketlabel</parameter></paramdef>
+ <paramdef>struct sockaddr
+ *<parameter>sockaddr</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>socket</parameter></entry>
+ <entry>将被连接的套接字</entry>
+ </row>
+
+ <row>
+ <entry><parameter>socketlabel</parameter></entry>
+ <entry><parameter>socket</parameter>的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>sockaddr</parameter></entry>
+ <entry><parameter>socket</parameter>的地址</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>决定该主体(<parameter>cred</parameter>)是否有权将套接字(<parameter>socket</parameter>)绑定到地址
+ <parameter>sockaddr</parameter>。成功,返回<returnvalue>0</returnvalue>,否则返回一个错误编码<varname>errno</varname>。
+ 建议采用的错误编码有:<errorcode>EACCES</errorcode>,用于标记不符的情况;<errorcode>EPERM</errorcode>,用于特权不足的情况。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-socket-receive">
+ <title><function>&mac.mpo;_check_socket_receive</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_socket_receive</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct socket
+ *<parameter>so</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>socketlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>so</parameter></entry>
+ <entry>套接字</entry>
+ </row>
+
+ <row>
+ <entry><parameter>socketlabel</parameter></entry>
+ <entry><parameter>so</parameter>的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>决定该主体是否有权查询套接字<parameter>so</parameter>的相关信息。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-socket-send">
+ <title><function>&mac.mpo;_check_socket_send</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_socket_send</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct socket
+ *<parameter>so</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>socketlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>so</parameter></entry>
+ <entry>套接字</entry>
+ </row>
+
+ <row>
+ <entry><parameter>socketlabel</parameter></entry>
+ <entry><parameter>so</parameter>的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>决定该主体是否有权通过套接字<parameter>so</parameter>发送信息。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-cred-visible">
+ <title><function>&mac.mpo;_check_cred_visible</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_cred_visible</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>u1</parameter></paramdef>
+ <paramdef>struct ucred
+ *<parameter>u2</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>u1</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>u2</parameter></entry>
+ <entry>对象信任状</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定该主体信任状<parameter>u1</parameter>是否有权 <quote>see</quote> 具有信任状<parameter>u2</parameter> 的其他主体。
+ 成功,返回<returnvalue>0</returnvalue>;否则,返回错误编码<varname>errno</varname>。建议采用的错误编码有:
+ <errorcode>EACCES</errorcode>,用于标记不符的情况;<errorcode>EPERM</errorcode>,用于特权不足的情况;<errorcode>ESRCH</errorcode>,
+ 用来提供不可见性。该函数可在许多环境下使用,包括命令<command>ps</command>所使用的进程间的状态 sysctl,以及通过procfs 的状态查询操作。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-socket-visible">
+ <title><function>&mac.mpo;_check_socket_visible</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_socket_visible</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct socket
+ *<parameter>socket</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>socketlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>socket</parameter></entry>
+ <entry>客体;套接字</entry>
+ </row>
+
+ <row>
+ <entry><parameter>socketlabel</parameter></entry>
+ <entry><parameter>socket</parameter>的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-ifnet-relabel">
+ <title><function>&mac.mpo;_check_ifnet_relabel</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_ifnet_relabel</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct ifnet
+ *<parameter>ifnet</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>ifnetlabel</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>newlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>ifnet</parameter></entry>
+ <entry>客体;网络接口</entry>
+ </row>
+
+ <row>
+ <entry><parameter>ifnetlabel</parameter></entry>
+ <entry><parameter>ifnet</parameter>现有的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>newlabel</parameter></entry>
+ <entry>将被应用到<parameter>ifnet</parameter>上的新的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>决定该主体信任状是否有权使用传入的标记更新参数对给定的网络接口的标记进行重新设置。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-socket-relabel">
+ <title><function>&mac.mpo;_check_socket_relabel</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_socket_relabel</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct socket
+ *<parameter>socket</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>socketlabel</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>newlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>socket</parameter></entry>
+ <entry>客体;套接字</entry>
+ </row>
+
+ <row>
+ <entry><parameter>socketlabel</parameter></entry>
+ <entry><parameter>socket</parameter>现有的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>newlabel</parameter></entry>
+ <entry>将被应用到<parameter>socketlabel</parameter>上的更新标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>决定该主体信任状是否有权采用传入的标记对套接字参数的标记进行重新设置。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-cred-relabel">
+ <title><function>&mac.mpo;_check_cred_relabel</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_cred_relabel</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>newlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>newlabel</parameter></entry>
+ <entry>将被应用到<parameter>cred</parameter>上的更新标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>决定该主体信任状是否有权将自己的标记重新设置为给定的更新标记。</para>
+ </sect3>
+
+
+ <sect3 xml:id="mac-mpo-cred-check-vnode-relabel">
+ <title><function>&mac.mpo;_check_vnode_relabel</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_relabel</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>vnodelabel</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>newlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ <entry>不可改变</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>客体;vnode</entry>
+ <entry>已被锁定</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vnodelabel</parameter></entry>
+ <entry><parameter>vp</parameter>现有的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>newlabel</parameter></entry>
+ <entry>将被应用到<parameter>vp</parameter>上的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>决定该主体信任状是否有权将参数 vnode 的标记重新设置为指定标记。</para>
+ </sect3>
+
+ <sect3 xml:id="mpo-cred-check-mount-stat">
+ <title><function>&mac.mpo;_check_mount_stat</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>&mac.mpo;_check_mount_stat</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct mount
+ *<parameter>mp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>mountlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>mp</parameter></entry>
+ <entry>客体;文件系统挂载</entry>
+ </row>
+
+ <row>
+ <entry><parameter>mountlabel</parameter></entry>
+ <entry><parameter>mp</parameter>的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <!-- XXX Update ? -->
+ <para>确定相关主体信任状是否有权查看在给定文件系统上执行 statfs 的结果。
+ 成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;
+ <errorcode>EPERM</errorcode>,用于权限不够。
+ 该函数可能在下列情况下被调用:
+ 在 &man.statfs.2; 和其他相关调用期间,或者当需要从文件系统列表中选择排除哪个文件系统时,比如,
+ 调用 &man.getfsstat.2;时。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-proc-debug">
+ <title><function>&mac.mpo;_check_proc_debug</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_proc_debug</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct proc
+ *<parameter>proc</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ <entry>不可改变</entry>
+ </row>
+
+ <row>
+ <entry><parameter>proc</parameter></entry>
+ <entry>客体;进程</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状是否有权 debug 给定进程。
+ 成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;<errorcode>EPERM</errorcode>,用于权限不够;
+ <errorcode>ESRCH</errorcode>,用于隐瞒目标的存在。
+ &man.ptrace.2; 和 &man.ktrace.2; API,以及某些 procfs 操作将调用该函数。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-vnode-access">
+ <title><function>&mac.mpo;_check_vnode_access</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_access</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ <paramdef>int <parameter>flags</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>客体;vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry><parameter>vp</parameter>的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>flags</parameter></entry>
+ <entry>&man.access.2; 标志</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>根据相关主体信任状决定其对给定 vnode 以给定访问标志执行的 &man.access.2;
+ 和其他相关调用的返回值。一般,应采用与<function>&mac.mpo;_check_vnode_open</function>
+ 相同的语义来实现该函数。
+ 成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;
+ <errorcode>EPERM</errorcode>,用于权限不够。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-vnode-chdir">
+ <title><function>&mac.mpo;_check_vnode_chdir</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_chdir</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>dvp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>dlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dvp</parameter></entry>
+ <entry>客体;&man.chdir.2; 的目的 vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dlabel</parameter></entry>
+ <entry><parameter>dvp</parameter>的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状是否有权将进程工作目录切换到给定 vnode。成功,则返回 <returnvalue>0</returnvalue>;
+ 否则,返回一个 <varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;
+ <errorcode>EPERM</errorcode>,用于权限不够。</para>
+
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-vnode-chroot">
+ <title><function>&mac.mpo;_check_vnode_chroot</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_chroot</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>dvp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>dlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dvp</parameter></entry>
+ <entry>目录 vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dlabel</parameter></entry>
+ <entry>与<parameter>dvp</parameter>相关联的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体是否有权 &man.chroot.2; 到由
+ (<parameter>dvp</parameter>)给定的目录。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-vnode-create">
+ <title><function>&mac.mpo;_check_vnode_create</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_create</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>dvp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>dlabel</parameter></paramdef>
+ <paramdef>struct componentname
+ *<parameter>cnp</parameter></paramdef>
+ <paramdef>struct vattr
+ *<parameter>vap</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dvp</parameter></entry>
+ <entry>客体;vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dlabel</parameter></entry>
+ <entry><parameter>dvp</parameter>的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>cnp</parameter></entry>
+ <entry><parameter>dvp</parameter>中的成员名</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vap</parameter></entry>
+ <entry><parameter>vap</parameter>的 vnode 属性</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状是否有权在给定父目录,以给定的名字和属性,
+ 常见一个 vnode。成功,则返回 <returnvalue>0</returnvalue>;否则,
+ 返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode> 来表示用于标记不匹配, 而用
+ <errorcode>EPERM</errorcode>,用于权限不足。
+ 以<symbol>O_CREAT</symbol>为参数调用 &man.open.2;,或对 &man.mknod.2;,&man.mkfifo.2;
+ 等的调用将导致该函数被调用。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-vnode-delete">
+ <title><function>&mac.mpo;_check_vnode_delete</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_delete</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>dvp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>dlabel</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>void *<parameter>label</parameter></paramdef>
+ <paramdef>struct componentname
+ *<parameter>cnp</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dvp</parameter></entry>
+ <entry>父目录 vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dlabel</parameter></entry>
+ <entry><parameter>dvp</parameter>的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>客体;将被删除的 vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry><parameter>vp</parameter>的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>cnp</parameter></entry>
+ <entry><parameter>vp</parameter>中的成员名</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状是否有权从给定的父目录中,删除给定名字的 vnode。
+ 成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;
+ <errorcode>EPERM</errorcode>,用于权限不够。
+ 使用 &man.unlink.2; 和 &man.rmdir.2;,将导致该函数被调用。
+ 提供该入口函数的策略还必须实现一个
+ <function>mpo_check_rename_to</function>,
+ 用来授权由于重命名操作导致的目标文件的删除。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-vnode-deleteacl">
+ <title><function>&mac.mpo;_check_vnode_deleteacl</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_deleteacl</function></funcdef>
+
+ <paramdef>struct ucred *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label *<parameter>label</parameter></paramdef>
+ <paramdef>acl_type_t <parameter>type</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ <entry>不可改变</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>客体;vnode</entry>
+ <entry>被锁定</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry><parameter>vp</parameter>的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>type</parameter></entry>
+ <entry>ACL 类型</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状是否有权删除给定 vnode 的给定类型的 ACL。
+ 成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;
+ <errorcode>EPERM</errorcode>,用于权限不够。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-vnode-exec">
+ <title><function>&mac.mpo;_check_vnode_exec</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_exec</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>客体;将被执行的 vnode </entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry><parameter>vp</parameter>的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状是否有权执行给定 vnode。
+ 对于执行特权的决策与任何瞬时事件的决策是严格分开的。
+ 成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;
+ <errorcode>EPERM</errorcode>,用于权限不够。</para>
+ </sect3>
+
+ <sect3 xml:id="mpo-cred-check-vnode-getacl">
+ <title><function>&mac.mpo;_check_vnode_getacl</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_getacl</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ <paramdef>acl_type_t
+ <parameter>type</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>客体;vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry><parameter>vp</parameter>的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>type</parameter></entry>
+ <entry>ACL 类型</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状是否有权查询给定 vnode 上的给定类型的 ACL。
+ 成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;
+ <errorcode>EPERM</errorcode>,用于权限不够。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-vnode-getextattr">
+ <title><function>&mac.mpo;_check_vnode_getextattr</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_getextattr</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ <paramdef>int
+ <parameter>attrnamespace</parameter></paramdef>
+ <paramdef>const char
+ *<parameter>name</parameter></paramdef>
+ <paramdef>struct uio
+ *<parameter>uio</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>客体;vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry><parameter>vp</parameter>的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>attrnamespace</parameter></entry>
+ <entry>扩展属性名字空间</entry>
+ </row>
+
+ <row>
+ <entry><parameter>name</parameter></entry>
+ <entry>扩展属性名</entry>
+ </row>
+
+ <row>
+ <entry><parameter>uio</parameter></entry>
+ <entry>I/O 结构指针;参见 &man.uio.9;</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状是否有权查询给定 vnode 上给定名字空间和名字的扩展属性。
+ 使用扩展属性实现标记存储的策略模块可能会需要对这些扩展属性的操作进行特殊处理。
+ 成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;
+ <errorcode>EPERM</errorcode>,用于权限不够。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-vnode-link">
+ <title><function>&mac.mpo;_check_vnode_link</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_link</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>dvp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>dlabel</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ <paramdef>struct componentname
+ *<parameter>cnp</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dvp</parameter></entry>
+ <entry>目录 vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dlabel</parameter></entry>
+ <entry>与<parameter>dvp</parameter>相关联的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>链接目的 vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>与<parameter>vp</parameter>相关联的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>cnp</parameter></entry>
+ <entry>将被创建的链接对应的成员名</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体是否有权为参数<parameter>vp</parameter>给定的 vnode
+ 创建一个由参数<parameter>cnp</parameter>给定名字的链接。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-vnode-mmap">
+ <title><function>&mac.mpo;_check_vnode_mmap</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_mmap</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ <paramdef>int <parameter>prot</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>将被映射的 vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>与<parameter>vp</parameter>相关联的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>prot</parameter></entry>
+ <entry>mmap 保护 (参见 &man.mmap.2;)</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体是否有权将给定 vnode <parameter>vp</parameter> 以
+ <parameter>prot</parameter>指定的保护方式进行映射.</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-vnode-mmap-downgrade">
+ <title><function>&mac.mpo;_check_vnode_mmap_downgrade</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>void
+ <function>&mac.mpo;_check_vnode_mmap_downgrade</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ <paramdef>int *<parameter>prot</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry morerows="2">See
+ <xref linkend="mac-mpo-check-vnode-mmap"/>.</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ </row>
+
+ <row>
+ <entry><parameter>prot</parameter></entry>
+ <entry>将被降级的 mmap protections</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>根据主体和客体标记,降低 mmap protections。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-vnode-mprotect">
+ <title><function>&mac.mpo;_check_vnode_mprotect</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_mprotect</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ <paramdef>int <parameter>prot</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>映射的 vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>prot</parameter></entry>
+ <entry>存储保护</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体是否有权将给定 vnode<parameter>vp</parameter> 映射内存空间的存储保护参数设置为指定值。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-vnode-poll">
+ <title><function>&mac.mpo;_check_vnode_poll</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_poll</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>active_cred</parameter></paramdef>
+ <paramdef>struct ucred
+ *<parameter>file_cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>active_cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>file_cred</parameter></entry>
+ <entry>与<type>struct file</type>相关联的信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>将被执行 poll 操作的 vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>与<parameter>vp</parameter>相关联的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体是否有权对给定 vnode <parameter>vp</parameter>执行 poll 操作。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-vnode-rename-from">
+ <title><function>&mac.mpo;_check_vnode_rename_from</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_vnode_rename_from</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>dvp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>dlabel</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ <paramdef>struct componentname
+ *<parameter>cnp</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dvp</parameter></entry>
+ <entry>目录 vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dlabel</parameter></entry>
+ <entry>与<parameter>dvp</parameter>相关联的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>将被重命名的 vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>与<parameter>vp</parameter>相关联的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>cnp</parameter></entry>
+ <entry><parameter>vp</parameter>中的成员名</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体是否有权重命名给定vnode,<parameter>vp</parameter>。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-vnode-rename-to">
+ <title><function>&mac.mpo;_check_vnode_rename_to</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_rename_to</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>dvp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>dlabel</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ <paramdef>int <parameter>samedir</parameter></paramdef>
+ <paramdef>struct componentname
+ *<parameter>cnp</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dvp</parameter></entry>
+ <entry>目录 vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dlabel</parameter></entry>
+ <entry>与<parameter>dvp</parameter>相关联的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>被覆盖的 vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry>与<parameter>vp</parameter>相关联的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>samedir</parameter></entry>
+ <entry>布尔型变量;如果源和目的目录是相同的,则被置为<literal>1</literal></entry>
+ </row>
+
+ <row>
+ <entry><parameter>cnp</parameter></entry>
+ <entry>目标component名</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体是否有权重命名给定 vnode <parameter>vp</parameter>,至指定目录
+ <parameter>dvp</parameter>,或更名为<parameter>cnp</parameter>。如果无需覆盖已有文件,
+ 则<parameter>vp</parameter> 和
+ <parameter>label</parameter> 的值将为 NULL.</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-socket-listen">
+ <title><function>&mac.mpo;_check_socket_listen</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_socket_listen</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct socket
+ *<parameter>socket</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>socketlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>socket</parameter></entry>
+ <entry>客体;套接字</entry>
+ </row>
+
+ <row>
+ <entry><parameter>socketlabel</parameter></entry>
+ <entry><parameter>socket</parameter>的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体是否有权监听给定套接字。
+ 成功,则返回<returnvalue>0</returnvalue>;否则,返回错误编码值<varname>errno</varname>。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;
+ <errorcode>EPERM</errorcode>,用于权限不够。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-vnode-lookup">
+ <title><function>&mac.mpo;_check_vnode_lookup</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_lookup</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter/>cred</paramdef>
+ <paramdef>struct vnode
+ *<parameter/>dvp</paramdef>
+ <paramdef>struct label
+ *<parameter/>dlabel</paramdef>
+ <paramdef>struct componentname
+ *<parameter>cnp</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dvp</parameter></entry>
+ <entry>客体;vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dlabel</parameter></entry>
+ <entry><parameter>dvp</parameter>的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>cnp</parameter></entry>
+ <entry>被检查的成员名</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状是否有权在给定的目录 vnode 中为查找给定名字执行lookup操作。
+ 成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个 <varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;
+ <errorcode>EPERM</errorcode>,用于权限不够。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-vnode-open">
+ <title><function>&mac.mpo;_check_vnode_open</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_open</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ <paramdef>int
+ <parameter>acc_mode</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>客体;vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry><parameter>vp</parameter>的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>acc_mode</parameter></entry>
+ <entry>&man.open.2; 访问模式</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状是否有权在给定 vnode 上以给定的访问模式执行 open 操作。
+ 如果成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个错误编码。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;
+ <errorcode>EPERM</errorcode>,用于权限不够。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-vnode-readdir">
+ <title><function>&mac.mpo;_check_vnode_readdir</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_readdir</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter/>cred</paramdef>
+ <paramdef>struct vnode
+ *<parameter/>dvp</paramdef>
+ <paramdef>struct label
+ *<parameter/>dlabel</paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dvp</parameter></entry>
+ <entry>客体;目录 vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>dlabel</parameter></entry>
+ <entry><parameter>dvp</parameter>的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状是否有权在给定的目录 vnode 上执行
+ <function>readdir</function> 操作。
+ 成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个错误编码 <varname>errno</varname>。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;<errorcode>EPERM</errorcode>,用于权限不够。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-vnode-readlink">
+ <title><function>&mac.mpo;_check_vnode_readlink</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_readlink</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>客体;vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry><parameter>vp</parameter>的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状是否有权在给定符号链接 vnode 上执行
+ <function>readlink</function> 操作。成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;<errorcode>EPERM</errorcode>,用于权限不够。
+ 该函数可能在若干环境下被调用,包括由用户进程显式执行的 <function>readlink</function> 调用,
+ 或者是在进程执行名字查询时隐式执行的 <function>readlink</function> 。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-vnode-revoke">
+ <title><function>&mac.mpo;_check_vnode_revoke</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_revoke</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>客体;vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry><parameter>vp</parameter>的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状是否有权撤销对给定 vnode 的访问。
+ 成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;<errorcode>EPERM</errorcode>,用于权限不够。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-vnode-setacl">
+ <title><function>&mac.mpo;_check_vnode_setacl</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_setacl</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ <paramdef>acl_type_t
+ <parameter>type</parameter></paramdef>
+ <paramdef>struct acl
+ *<parameter>acl</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>客体;vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry><parameter>vp</parameter>的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>type</parameter></entry>
+ <entry>ACL 类型</entry>
+ </row>
+
+ <row>
+ <entry><parameter>acl</parameter></entry>
+ <entry>ACL</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状是否有权设置给定 vnode 的给定类型的 ACL。
+ 成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;<errorcode>EPERM</errorcode>,用于权限不够。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-vnode-setextattr">
+ <title><function>&mac.mpo;_check_vnode_setextattr</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_setextattr</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ <paramdef>int
+ <parameter>attrnamespace</parameter></paramdef>
+ <paramdef>const char
+ *<parameter>name</parameter></paramdef>
+ <paramdef>struct uio
+ *<parameter>uio</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>客体;vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry><parameter>vp</parameter>的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>attrnamespace</parameter></entry>
+ <entry>扩展属性名字空间</entry>
+ </row>
+
+ <row>
+ <entry><parameter>name</parameter></entry>
+ <entry>扩展属性名</entry>
+ </row>
+
+ <row>
+ <entry><parameter>uio</parameter></entry>
+ <entry>I/O 结构指针;参见 &man.uio.9;</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状是否有权设置给定 vnode 上给定名字空间中给定名字的扩展属性的值。
+ 使用扩展属性备份安全标记的策略模块可能需要对其使用的属性实施额外的保护。另外,
+ 由于在检查和实际操作时间可能存在的竞争,
+ 策略模块应该避免根据来自<parameter>uio</parameter>中的数据做出决策。
+ 如果正在执行一个删除操作,则参数 <parameter>uio</parameter> 的值也可能为 <literal>NULL</literal>。
+ 成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;<errorcode>EPERM</errorcode>,用于权限不够。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-vnode-setflags">
+ <title><function>&mac.mpo;_check_vnode_setflags</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_setflags</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ <paramdef>u_long <parameter>flags</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>客体;vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry><parameter>vp</parameter>的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>flags</parameter></entry>
+ <entry>文件标志;参见 &man.chflags.2;</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状是否有权为给定的 vnode 设置给定的标志。
+ 成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;
+ <errorcode>EPERM</errorcode>,用于权限不够。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-vnode-setmode">
+ <title><function>&mac.mpo;_check_vnode_setmode</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_setmode</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ <paramdef>mode_t <parameter>mode</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>客体;vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry><parameter>vp</parameter>的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>mode</parameter></entry>
+ <entry>文件模式;参见 &man.chmod.2;</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状是否有权将给定 vnode 的模式设置为给定值。
+ 成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;
+ <errorcode>EPERM</errorcode>,用于权限不够。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-vnode-setowner">
+ <title><function>&mac.mpo;_check_vnode_setowner</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_setowner</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ <paramdef>uid_t <parameter>uid</parameter></paramdef>
+ <paramdef>gid_t <parameter>gid</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>客体;vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry><parameter>vp</parameter>的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>uid</parameter></entry>
+ <entry>用户ID</entry>
+ </row>
+
+ <row>
+ <entry><parameter>gid</parameter></entry>
+ <entry>组ID</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状是否有权将给定 vnode 的文件 uid 和文件 gid 设置为给定值。如果无需更新,
+ 相关参数值可能被设置为(<literal>-1</literal>)。
+ 成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;
+ <errorcode>EPERM</errorcode>,用于权限不够。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-vnode-setutimes">
+ <title><function>&mac.mpo;_check_vnode_setutimes</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_setutimes</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter/>cred</paramdef>
+ <paramdef>struct vnode
+ *<parameter/>vp</paramdef>
+ <paramdef>struct label
+ *<parameter/>label</paramdef>
+ <paramdef>struct timespec
+ <parameter/>atime</paramdef>
+ <paramdef>struct timespec
+ <parameter/>mtime</paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>客体;vp</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry><parameter>vp</parameter>的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>atime</parameter></entry>
+ <entry>访问时间;参见 &man.utimes.2;</entry>
+ </row>
+
+ <row>
+ <entry><parameter>mtime</parameter></entry>
+ <entry>修改时间;参见 &man.utimes.2;</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状是否有权将给定 vnode 的访问时间标签设置为给定值。
+ 成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;
+ <errorcode>EPERM</errorcode>,用于权限不够。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-proc-sched">
+ <title><function>&mac.mpo;_check_proc_sched</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_proc_sched</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>ucred</parameter></paramdef>
+ <paramdef>struct proc
+ *<parameter>proc</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>proc</parameter></entry>
+ <entry>客体;进程</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状是否有权改变给定进程的调度参数。
+ 成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;
+ <errorcode>EPERM</errorcode>,用于权限不够;
+ <errorcode>ESRCH</errorcode>,用于提供不可见性质。</para>
+
+ <para>See &man.setpriority.2; for more information.</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-proc-signal">
+ <title><function>&mac.mpo;_check_proc_signal</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_proc_signal</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct proc
+ *<parameter>proc</parameter></paramdef>
+ <paramdef>int <parameter>signal</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>proc</parameter></entry>
+ <entry>客体;进程</entry>
+ </row>
+
+ <row>
+ <entry><parameter>signal</parameter></entry>
+ <entry>信号;参见 &man.kill.2;</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状是否有权向给定进程发送给定信号。
+ 成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,
+ 用于标记不匹配;<errorcode>EPERM</errorcode>,用于权限不够;
+ <errorcode>ESRCH</errorcode>,用于提供不可见性质。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-vnode-stat">
+ <title><function>&mac.mpo;_check_vnode_stat</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_vnode_stat</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>label</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>客体;vnode</entry>
+ </row>
+
+ <row>
+ <entry><parameter>label</parameter></entry>
+ <entry><parameter>vp</parameter>的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状是否有权在给定 vnode 上执行 <function>stat</function> 操作。
+ 成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;
+ <errorcode>EPERM</errorcode>,用于权限不够。</para>
+
+ <para>See &man.stat.2; for more information.</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-ifnet-transmit">
+ <title><function>&mac.mpo;_check_ifnet_transmit</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_ifnet_transmit</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct ifnet
+ *<parameter>ifnet</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>ifnetlabel</parameter></paramdef>
+ <paramdef>struct mbuf
+ *<parameter>mbuf</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>mbuflabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>ifnet</parameter></entry>
+ <entry>网络接口</entry>
+ </row>
+
+ <row>
+ <entry><parameter>ifnetlabel</parameter></entry>
+ <entry><parameter>ifnet</parameter>的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>mbuf</parameter></entry>
+ <entry>客体;将被发送的 mbuf </entry>
+ </row>
+
+ <row>
+ <entry><parameter>mbuflabel</parameter></entry>
+ <entry><parameter>mbuf</parameter>的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关网络接口是否有权传送给定的 mbuf。成功,则返回 <returnvalue>0</returnvalue>;
+ 否则,返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;
+ <errorcode>EPERM</errorcode>,用于权限不够。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-cred-check-socket-deliver">
+ <title><function>&mac.mpo;_check_socket_deliver</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_socket_deliver</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct ifnet
+ *<parameter>ifnet</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>ifnetlabel</parameter></paramdef>
+ <paramdef>struct mbuf
+ *<parameter>mbuf</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>mbuflabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>ifnet</parameter></entry>
+ <entry>网络接口</entry>
+ </row>
+
+ <row>
+ <entry><parameter>ifnetlabel</parameter></entry>
+ <entry><parameter>ifnet</parameter>的策略标记</entry>
+ </row>
+
+ <row>
+ <entry><parameter>mbuf</parameter></entry>
+ <entry>客体;将被传送的 mbuf </entry>
+ </row>
+
+ <row>
+ <entry><parameter>mbuflabel</parameter></entry>
+ <entry><parameter>mbuf</parameter>的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关套接字是否有权从给定的 mbuf 中接收数据报。
+ 成功,则返回 <returnvalue>0</returnvalue>;否则,返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;
+ <errorcode>EPERM</errorcode>,用于权限不够。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-socket-visible">
+ <title><function>&mac.mpo;_check_socket_visible</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_socket_visible</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct socket
+ *<parameter>so</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>socketlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ <entry>不可改变</entry>
+ </row>
+
+ <row>
+ <entry><parameter>so</parameter></entry>
+ <entry>客体;套接字</entry>
+ </row>
+
+ <row>
+ <entry><parameter>socketlabel</parameter></entry>
+ <entry><parameter>so</parameter>的策略标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体信任状cred 是否有权使用系统监控函数,比如,
+ 由&man.netstat.8; 和 &man.sockstat.1;使用的程序来观察
+ 给定的套接字(<parameter>socket</parameter>)。成功,
+ 则返回 <returnvalue>0</returnvalue>;否则,返回一个<varname>errno</varname>值。
+ 建议使用的错误编码:<errorcode>EACCES</errorcode>,用于标记不匹配;
+ <errorcode>EPERM</errorcode>,用于权限不够;
+ <errorcode>ESRCH</errorcode>,用于提供不可见性质。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-system-acct">
+ <title><function>&mac.mpo;_check_system_acct</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_system_acct</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>ucred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>vlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>ucred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>审计文件;&man.acct.5;</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vlabel</parameter></entry>
+ <entry>与<parameter>vp</parameter>相关联的标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>根据主体标记和审计日志文件的标记,确定该主体是否有权启动审计。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-system-nfsd">
+ <title><function>&mac.mpo;_check_system_nfsd</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_system_nfsd</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体是否有权调用
+ &man.nfssvc.2;。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-system-reboot">
+ <title><function>&mac.mpo;_check_system_reboot</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_system_reboot</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>int <parameter>howto</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>howto</parameter></entry>
+ <entry>来自 &man.reboot.2;的<parameter>howto</parameter> 参数</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体是否有权以指定方式重启系统。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-system-settime">
+ <title><function>&mac.mpo;_check_system_settime</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_system_settime</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关用户是否有权设置系统时钟。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-system-swapon">
+ <title><function>&mac.mpo;_check_system_swapon</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_system_swapon</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>struct vnode
+ *<parameter>vp</parameter></paramdef>
+ <paramdef>struct label
+ *<parameter>vlabel</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vp</parameter></entry>
+ <entry>swap设备</entry>
+ </row>
+
+ <row>
+ <entry><parameter>vlabel</parameter></entry>
+ <entry>与<parameter>vp</parameter>相关联的标记</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体是否有权增加一个作为swap设备的 <parameter>vp</parameter> 。</para>
+ </sect3>
+
+ <sect3 xml:id="mac-mpo-check-system-sysctl">
+ <title><function>&mac.mpo;_check_system_sysctl</function></title>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int
+ <function>&mac.mpo;_check_system_sysctl</function></funcdef>
+
+ <paramdef>struct ucred
+ *<parameter>cred</parameter></paramdef>
+ <paramdef>int *<parameter>name</parameter></paramdef>
+ <paramdef>u_int *<parameter>namelen</parameter></paramdef>
+ <paramdef>void *<parameter>old</parameter></paramdef>
+ <paramdef>size_t
+ *<parameter>oldlenp</parameter></paramdef>
+ <paramdef>int <parameter>inkernel</parameter></paramdef>
+ <paramdef>void *<parameter>new</parameter></paramdef>
+ <paramdef>size_t <parameter>newlen</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <informaltable frame="none" pgwide="1">
+ <tgroup cols="3">
+ &mac.thead;
+
+ <tbody>
+ <row>
+ <entry><parameter>cred</parameter></entry>
+ <entry>主体信任状</entry>
+ </row>
+
+ <row>
+ <entry><parameter>name</parameter></entry>
+ <entry morerows="3">参见 &man.sysctl.3;</entry>
+ </row>
+
+ <row>
+ <entry><parameter>namelen</parameter></entry>
+ </row>
+
+ <row>
+ <entry><parameter>old</parameter></entry>
+ </row>
+
+ <row>
+ <entry><parameter>oldlenp</parameter></entry>
+ </row>
+
+ <row>
+ <entry><parameter>inkernel</parameter></entry>
+ <entry>布尔型变量;如果从内核被调用,其值被置为<literal>1</literal></entry>
+ </row>
+
+ <row>
+ <entry><parameter>new</parameter></entry>
+ <entry morerows="1">参见 &man.sysctl.3;</entry>
+ </row>
+
+ <row>
+ <entry><parameter>newlen</parameter></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ <para>确定相关主体是否应该被允许执行指定的 &man.sysctl.3; 事务。</para>
+ </sect3>
+ </sect2>
+
+ <sect2 xml:id="mac-label-management">
+ <title>标记管理调用</title>
+
+ <para>当用户进程请求对某个对象的标记进行修改时,将引发重新标记事件。对应的更新操作分两步进行:
+ 首先,进行访问控制检查,确认此次更新操作是有效且被允许的;然后,调用另一个独立的入口函数对标记进行修改。
+ 重新标记入口函数通常接收由请求进程提交的对象、对象标记指针和请求新标记,作为输入参数。
+ 对象重新标记操作的失败将由先期的标记检查报告,所以,不允许在接下来的标记修改过程中报告失败,故而不提倡在此过程中新分配内存。</para>
+ </sect2>
+ </sect1>
+
+ <sect1 xml:id="mac-userland-arch">
+ <title>应用层体系结构</title>
+
+ <para>TrustedBSD MAC 框架包含了一组策略无关的组成元素,包括管理抽象标记的 MAC 接口库,
+ 对系统信任状管理体系的修改, 为用户分配 MAC 标记提供支持的 login 库函数,
+ 以及若干负责维护和更新内核对象(进程、文件和网络接口等)安全标记的工具。
+ 不久,将有更多关于应用层体系结构的详细信息被包含进来。</para>
+
+ <sect2 xml:id="mac-userland-labels">
+ <title>策略无关的标记管理 API</title>
+
+ <para>TrustedBSD MAC 提供的大量库函数和系统调用,允许应用程序使用一种统一的、策略无关的接口来处理对象的 MAC 标记。
+ 如此,应用程序可以轻松管理各种策略的标记,无需为增加对某个特定策略的支持而重新编码。许多通用工具,比如
+ &man.ifconfig.8;,&man.ls.1; 和 &man.ps.1;,使用这些策略无关的接口查询网络结构、文件和进程的标记信息。
+ 这些 API 也被用于支持 MAC 管理工具,比如,&man.getfmac.8;,&man.getpmac.8;, &man.setfmac.8;, &man.setfsmac.8;,和 &man.setpmac.8;。
+ MAC API的设计细节可参考 &man.mac.3;.</para>
+
+ <para>应用程序处理的 MAC 标记有两种存在形式:内部形式,用来返回和设置进程和对象的标记(<literal>mac_t</literal>);
+ 基于 C 字符串的外部形式,作为标记在配置文件中的存放形式,用于向用户显示或者由用户输入。
+ 每一个 MAC 标记由一组标记元素组成,其中每个元素是一个形如(名字,值)的二元组。
+ 内核中的每个策略模块分别被指定一个特定的名字,由它们对标记中与该名字对应的值采用其策略特有的方式进行解析。
+ 采用外部形式表示的标记,其标记元素表示为名字 <literal>/</literal> 值,元素之间以逗号分隔。
+ 应用程序可以使用 MAC 框架提供的 API 将一个安全标记在内部形式和文本形式之间进行转换。
+ 每当向内核查询某个对象的安全标记时,内部形式的标记必须针对所需的元素集合作好内部标记存储准备。
+ 为此,通常采用下面两种方式之一:使用 &man.mac.prepare.3; 和一个包含所需标记元素的任意列表;或者,
+ 使用从&man.mac.conf.5; 配置文件中加载缺省元素集合的某个系统调用。在对象级别设置缺省标记,将允许应用程序在不确定
+ 系统是否采用相关策略的情况下,也能向用户返回与对象相关联的有意义的安全标记。</para>
+
+ <note><para>目前的 MAC 库不支持直接修改内部形式的标记元素,所有的修改必须按照下列的步骤进行:
+ 将内部形式的标记转换成文本字符串,对字符串进行编辑,最后将其转换成内部形式标记。如果应用程序的作者证明确实有需要,
+ 可以在将来的版本中加入对内部形式标记进行直接修改的接口。</para></note>
+ </sect2>
+
+ <sect2 xml:id="mac-userland-credentials">
+ <title>为用户指定标记</title>
+
+ <para>用户上下文管理的标记接口,
+ &man.setusercontext.3;
+ ,的行为已经被修改为,从
+ &man.login.conf.5;
+ 中查询与某个用户登录类别相关联的 MAC 安全标记。
+ 当 <literal>LOGIN_SETALL</literal>
+ 被设置,或者当 <literal>LOGIN_SETMAC</literal>
+ 被明确指定时,这些安全标记将和其他用户上下文参数一起被设置。</para>
+
+ <note><para>可以预期,在今后的某个版本中,FreeBSD 将把 MAC 标记从
+ <filename>login.conf</filename> 的用户类别数据库中抽出,为其维护一个独立的数据库。
+ 不过在此前后,&man.setusercontext.3; API应该保持不变。</para></note>
+ </sect2>
+ </sect1>
+
+ <sect1 xml:id="mac-conclusion">
+ <title>小结</title>
+
+ <para>TrustedBSD MAC 框架使得内核模块能以一种集中的方式,完善系统的安全策略。
+ 它们既可利用现有的内核对象属性,又能使用由 MAC 框架协助维护的安全标记数据,来实施访问控制。
+ 框架提供的灵活性使得开发人员可以在其上实现各种策略,如利用 BSD 现有的信任状(credential)
+ 与文件保护机制的策略,以及信息流安全策略(如 MLS 和 Biba)。
+ 实现新安全服务的策略编程人员,可以参考本文档,以了解现有安全模块的信息。</para>
+ </sect1>
+</chapter>
diff --git a/zh_CN.UTF-8/books/arch-handbook/newbus/chapter.xml b/zh_CN.UTF-8/books/arch-handbook/newbus/chapter.xml
new file mode 100644
index 0000000000..0b80f6e707
--- /dev/null
+++ b/zh_CN.UTF-8/books/arch-handbook/newbus/chapter.xml
@@ -0,0 +1,328 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ The FreeBSD Documentation Project
+ The FreeBSD Simplified Chinese Project
+
+ Original Revision: 1.10
+ $FreeBSD$
+
+ Originally by: Jeroen Ruigrok van der Warven
+ Date: newbus-draft.txt,v 1.8 2001/01/25 08:01:08
+ Copyright (c) 2000 Jeroen Ruigrok van der Warven (asmodai@wxs.nl)
+ Copyright (c) 2002 Hiten Mahesh Pandya (hiten@uk.FreeBSD.org)
+
+ Future Additions:
+
+ o Expand the information about device_t
+ o Add information about the bus_* functions.
+ o Add information about bus specific (e.g. PCI) functions.
+ o Add a reference section for additional information.
+ o Add more newbus related structures and typedefs.
+ o Add a 'Terminology' section.
+ o Add information on resource manager functions, busspace
+ manager functions, newbus events related functions.
+ o More cleanup ... !
+
+ Provided under the FreeBSD Documentation License.
+-->
+<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" xml:id="newbus">
+ <info><title>Newbus</title>
+ <authorgroup>
+ <author><personname><firstname>Jeroen</firstname><surname>Ruigrok van der Werven (asmodai)</surname></personname><affiliation><address><email>asmodai@FreeBSD.org</email></address>
+ </affiliation><contrib>&cnproj.written.by;</contrib></author>
+ <author><personname><firstname>Hiten</firstname><surname>Pandya</surname></personname><affiliation><address><email>hiten@uk.FreeBSD.org</email></address>
+ </affiliation></author>
+ </authorgroup>
+ <authorgroup>
+ <author><personname/><contrib>&cnproj.translated.by;</contrib></author>
+ </authorgroup>
+ </info>
+
+
+ <para><emphasis>特别感谢Matthew N. Dodd, Warner Losh, Bill Paul,
+ Doug Rabson, Mike Smith, Peter Wemm and Scott Long</emphasis>.</para>
+
+ <para>本章详细解释了Newbus设备框架。</para>
+ <sect1 xml:id="newbus-devdrivers">
+ <title>设备驱动程序</title>
+ <sect2>
+ <title>设备驱动程序的目的</title>
+
+ <indexterm><primary>device driver(设备驱动程序)</primary></indexterm>
+ <indexterm><primary>device driver(设备驱动程序)</primary><secondary>introduction(介绍)</secondary></indexterm>
+ <para>设备驱动程序是软件组件,它在内核关于外围设备(例如,磁盘、网络
+ 适配卡)的通用视图和外围设备的实际实现之间提供了接口。
+ <emphasis>设备驱动程序接口(DDI)</emphasis>是内核与设备驱动程序组件
+ 之间定义的接口。
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>设备驱动程序的类型</title>
+ <para>在&unix;那个时代,FreeBSD也从中延续而来,定义了四种类型的
+ 设备:</para>
+
+ <itemizedlist>
+ <listitem><para>块设备驱动程序</para></listitem>
+ <listitem><para>字符设备驱动程序</para></listitem>
+ <listitem><para>网络设备驱动程序</para></listitem>
+ <listitem><para>伪设备驱动程序</para></listitem>
+ </itemizedlist>
+
+ <indexterm><primary>block devices(块设备)</primary></indexterm>
+
+ <para><emphasis>块设备</emphasis>以使用固定大小的[数据]块的方式运行。
+ 这种类型的驱动程序依赖所谓的
+ <emphasis>缓冲区缓存(buffer cache)</emphasis>,其目的
+ 是在内存中的专用区域缓存访问过的数据块。这种缓冲区缓存常常基于后台写
+ (write-behind),这意味着数据在内存中被修改后,当系统进行其周期性
+ 磁盘刷新时才会被同步到磁盘,从而优化写操作。</para>
+ </sect2>
+
+ <sect2>
+ <title>字符设备</title>
+
+ <indexterm><primary>character devices(字符设备)</primary></indexterm>
+
+ <para>然而,在FreeBSD 4.0版本以及后续版本中,
+ 块设备和字符设备的区别变得不存在了。</para>
+ </sect2>
+ </sect1>
+
+ <sect1 xml:id="newbus-overview">
+ <!--
+ Real title:
+ Newbus, Busspace and the Resource Manager, an Explanation of the Possibilities
+ -->
+ <title>Newbus概览</title>
+
+ <indexterm><primary>Newbus</primary></indexterm>
+
+ <para><emphasis>Newbus</emphasis>实现了一种基于抽象层的新型总线结构,
+ 可以在FreeBSD 3.0中看到这种总线结构的介绍,当时Alpha的移植被导入到
+ 代码树中。直到4.0它才成为设备驱动程序使用的默认系统。其目的是为主机
+ 系统提供给<emphasis>操作系统</emphasis>的各种总线和设备的互连提供更加
+ 面向对象的方法。</para>
+
+ <para>其主要特性包括:</para>
+
+ <itemizedlist>
+ <listitem><para>动态连接</para></listitem>
+ <listitem><para>驱动程序容易模块化</para></listitem>
+ <listitem><para>伪总线</para></listitem>
+ </itemizedlist>
+
+ <para>最显著的改变之一是从平面和特殊系统演变为设备树布局。</para>
+
+ <para>顶层驻留的是<emphasis><quote>根</quote></emphasis>设备,它作为
+ 父设备,所有其他设备挂接在它上面。对于每个结构,通常<quote>根</quote>
+ 只有单个孩子,其上连接着诸如<emphasis>host-to-PCI桥</emphasis>
+ 等东西。对于x86,这种<quote>根</quote>设备为
+ <emphasis><quote>nexus</quote></emphasis>设备,对于Alpha,Alpha的各种
+ 不同型号有不同的顶层设备,对应不同的硬件芯片组,包括
+ <emphasis>lca</emphasis>,<emphasis>apecs</emphasis>,
+ <emphasis>cia</emphasis>和<emphasis>tsunami</emphasis>。</para>
+
+ <para>Newbus上下文中的设备表示系统中的单个硬件实体。例如,每个PCI设备被
+ 表示为一个Newbus设备。系统中的任何设备可以有孩子;有孩子的设备通常被
+ 称为<emphasis><quote>bus</quote></emphasis>。系统中常用总线的例子就是
+ ISA和PCI,他们各自管理连接到ISA和PCI总线上的设备列表。</para>
+
+ <para>通常,不同类型的总线之间的连接被表示为
+ <emphasis><quote>桥</quote></emphasis>设备,它的孩子就是它所连接的
+ 总线。一个例子就是<emphasis>PCI-to-PCI桥</emphasis>,它在父PCI总线上被
+ 表示为<emphasis><filename>pcibN</filename></emphasis>,而用它的孩子
+ <emphasis><filename>pciN</filename></emphasis>表示连接在它上面的
+ 总线。这种布局简化了PCI总线树的实现,允许公共代码同时用于顶层和桥接的
+ 总线。</para>
+
+ <para>Newbus结构中的每个设备请求它的父设备来为其映射资源。父设备接着请求
+ 它的父设备,直到到达nexus。因此,基本上nexus是Newbus系统中唯一知道所有
+ 资源的部分。</para>
+
+ <tip><para>ISA设备可能想在<literal>0x230</literal>映射其IO端口,因此它向其
+ 父设备请求,这种情况下是ISA总线。ISA总线将它交给PCI-to-ISA桥,PCI-to-ISA
+ 桥接着请求PCI总线,PCI总线到达host-to-PCI桥,最后到达nexus。这种向上
+ 过渡的优美之处在于可以有空间来变换请求。对<literal>0x230</literal>IO端口
+ 的请求在<acronym>MIPS</acronym>机器上可以被PCI桥变成
+ <literal>0xb0000230</literal>处的内存映射。</para></tip>
+
+ <para>资源分配可以在设备树的任何地方加以控制。例如,在很多Alpha平台上,
+ ISA中断与PCI中断是单独管理的,对ISA中断的资源分配是由Alpha的ISA总线设备
+ 管理的。在IA-32上,ISA和PCI中断都由顶层的nexus设备管理。对于两种移植,
+ 内存和端口地址空间由单个实体管理 - 在IA-32上是nexus,在Alpha(例如,CIA
+ 或tsunami)上是相关的芯片组驱动程序。</para>
+
+ <para>为了规范化对内存和端口映射资源的访问,Newbus整合了NetBSD的
+ <literal>bus_space</literal> API。他们提供了单一的API来代替inb/outb
+ 和直接内存读写。这样做的优势在于单个驱动程序就可以使用内存映射寄存器
+ 或端口映射寄存器(有些硬件支持两者)。</para>
+
+ <para>这种支持被合并到了资源分配机制中。分配资源时,驱动程序可以从资源
+ 中检取关联的<varname remap="structfield">bus_space_tag_t</varname>和
+ <varname remap="structfield">bus_space_handle_t</varname>。</para>
+
+ <para>Newbus也允许在专用于此目的的文件中定义接口方法。这些是
+ <filename>.m</filename>文件,可以在<filename>src/sys</filename>
+ 目录树中找到。</para>
+
+ <para>Newbus系统的核心是可扩展的<quote>基于对象编程(object-based
+ programming)</quote>的模型。系统中的每个设备具有它所支持的一个方法表。
+ 系统和其他设备使用这些方法来控制设备并请求服务。设备所支持的不同方法
+ 被定义为多个<quote>接口</quote>。<quote>接口</quote>只是
+ 设备实现的一组相关的方法。</para>
+
+ <para>在Newbus系统中,设备方法是通过系统中的各种设备驱动程序提供的。当
+ <emphasis>自动配置(auto-configuration)</emphasis>期间设备被连接(attach)
+ 到驱动程序,它使用驱动程序声明的方法表。以后设备可以从其驱动程序
+ <emphasis>分离(detach)</emphasis>,并
+ <emphasis>重新连接(re-attach)</emphasis>到具有新方法表的新驱动程序。这就
+ 允许驱动程序的动态替换,而动态替换对于驱动程序的开发非常有用。</para>
+
+ <para>接口通过与文件系统中用于定义vnode操作的语言相似的接口定义语言来
+ 描述。接口被保存在方法文件中(通常命名为<filename>foo_if.m</filename>)。
+ </para>
+
+ <example>
+ <title>Newbus的方法</title>
+ <programlisting>
+ # Foo 子系统/驱动程序(注释...)
+
+ INTERFACE foo
+
+ METHOD int doit {
+ device_t dev;
+ };
+
+ # 如果没有通过DEVMETHOD()提供一个方法,则DEFAULT为将会被使用的方法
+
+ METHOD void doit_to_child {
+ device_t dev;
+ driver_t child;
+ } DEFAULT doit_generic_to_child;
+ </programlisting>
+ </example>
+
+ <para>当接口被编译后,它产生一个头文件
+ <quote><filename>foo_if.h</filename></quote>,其中包含函数声明:</para>
+
+ <programlisting>
+ int FOO_DOIT(device_t dev);
+ int FOO_DOIT_TO_CHILD(device_t dev, device_t child);
+ </programlisting>
+
+ <para>伴随自动产生的头文件,也会创建一个源文件
+ <quote><filename>foo_if.c</filename></quote>;其中包含一些函数的实现,
+ 这些函数用于在对象方法表中查找相关函数的位置并调用那个函数。</para>
+
+ <para>系统定义了两个主要接口。第一个基本接口被称为
+ <emphasis><quote>设备(device)</quote></emphasis>,并包括与所有设备相关
+ 的方法。<emphasis><quote>设备(device)</quote></emphasis>接口中的方法
+ 包括<emphasis><quote>探测(probe)</quote></emphasis>,
+ <emphasis><quote>连接(attach)</quote></emphasis>和
+ <emphasis><quote>分离(detach)</quote></emphasis>,他们用来控制硬件的侦测,
+ 以及<emphasis><quote>关闭(shutdown)</quote></emphasis>,
+ <emphasis><quote>挂起(suspend)</quote></emphasis>和
+ <emphasis><quote>恢复(resume)</quote></emphasis>,他们用于关键事件通知。
+ </para>
+
+ <para>另一个,更加复杂接口是<emphasis><quote>bus</quote></emphasis>。
+ 此接口包含的方法适用于带有孩子的设备,包括访问总线特定的每设备信息
+ <footnote><para>&man.bus.generic.read.ivar.9; and
+ &man.bus.generic.write.ivar.9;</para></footnote>,事件通知
+ (<emphasis><literal>child_detached</literal></emphasis>,
+ <emphasis><literal>driver_added</literal></emphasis>)和响应管理
+ (<emphasis><literal>alloc_resource</literal></emphasis>,
+ <emphasis><literal>activate_resource</literal></emphasis>,
+ <emphasis><literal>deactivate_resource</literal></emphasis>,
+ <emphasis><literal>release_resource</literal></emphasis>)。</para>
+
+ <para><quote>bus</quote>接口中的很多方法为总线设备的某些孩子执行服务。
+ 这些方法通常使用前两个参量指定提供服务的总线和请求服务的子设备。为了
+ 简化设备驱动程序代码,这些方法中的很多都有访问者(accessor)函数,访问者
+ 函数用来查找父设备并调用父设备上的方法。例如,方法
+ <literal>BUS_TEARDOWN_INTR(device_t dev, device_t child, ...)</literal>
+ 可以使用函数
+ <literal>bus_teardown_intr(device_t child, ...)</literal>来调用。</para>
+
+ <para>系统中的某些总线类型提供了额外接口以提供对总线特定功能的访问。
+ 例如,PCI总线驱动程序定义了<quote>pci</quote>接口,此接口有两个方法
+ <emphasis><literal>read_config</literal></emphasis>和
+ <emphasis><literal>write_config</literal></emphasis>,用于访问PCI设备
+ 的配置寄存器。</para>
+ </sect1>
+
+ <sect1 xml:id="newbus-api">
+ <title>Newbus API</title>
+ <para>由于Newbus API非常庞大,本节努力将它文档化。本文档的下一版本会
+ 带来更多信息。</para>
+
+ <sect2>
+ <title>源代码目录树中的重要位置</title>
+
+ <para><filename>src/sys/[arch]/[arch]</filename> - 特定机器结构的
+ 内核代码位于这个目录。例如<literal>i386</literal>结构或
+ <literal>SPARC64</literal>结构。</para>
+
+ <para><filename>src/sys/dev/[bus]</filename> - 支持特定
+ <literal>[bus]</literal>的设备位于这个目录。</para>
+
+ <para><filename>src/sys/dev/pci</filename> - PCI总线支持代码位于
+ 这个目录。</para>
+
+ <para><filename>src/sys/[isa|pci]</filename> - PCI/ISA设备驱动程序
+ 位于这个目录。FreeBSD<literal>4.0</literal>版本中,PCI/ISA支持代码
+ 过去存在于这个目录中。</para>
+ </sect2>
+
+ <sect2>
+ <title>重要结构和类型定义</title>
+ <para><literal>devclass_t</literal> - 这是指向
+ <literal>struct devclass</literal>的指针的类型定义。</para>
+
+ <para><literal>device_method_t</literal> - 与
+ <literal>kobj_method_t</literal>相同(参看
+ <filename>src/sys/kobj.h</filename>)。</para>
+
+ <para><literal>device_t</literal> - 这是指向
+ <literal>struct device</literal>的指针的类型定义。
+ <literal>device_t</literal> 表示系统中的设备。它是内核对象。
+ 实现细节参看<filename>src/sys/sys/bus_private.h</filename>。</para>
+
+ <para><literal>driver_t</literal> - 这是一个类型定义,它引用
+ <literal>struct driver</literal>。
+ <literal>driver</literal>结构是一类
+ <literal>device(设备)</literal>内核对象;它也保存着驱动程序的私有数据。
+ </para>
+
+ <figure>
+ <title><emphasis>driver_t</emphasis>实现</title>
+ <programlisting>
+ struct driver {
+ KOBJ_CLASS_FIELDS;
+ void *priv; /* 驱动程序私有数据 */
+ };
+ </programlisting>
+ </figure>
+
+ <para><literal>device_state_t</literal>是一个枚举类型,即
+ <literal>device_state</literal>。它包含Newbus设备在自动配置前后
+ 可能的状态。</para>
+
+ <figure>
+ <title>设备状态<emphasis>device_state_t</emphasis></title>
+ <programlisting>
+ /*
+ * src/sys/sys/bus.h
+ */
+ typedef enum device_state {
+ DS_NOTPRESENT, /* 未探测或探测失败 */
+ DS_ALIVE, /* 探测成功 */
+ DS_ATTACHED, /* 调用了连接方法 */
+ DS_BUSY /* 设备已打开 */
+ } device_state_t;
+ </programlisting>
+ </figure>
+ </sect2>
+ </sect1>
+</chapter>
diff --git a/zh_CN.UTF-8/books/arch-handbook/pccard/chapter.xml b/zh_CN.UTF-8/books/arch-handbook/pccard/chapter.xml
new file mode 100644
index 0000000000..4b77dcc6b3
--- /dev/null
+++ b/zh_CN.UTF-8/books/arch-handbook/pccard/chapter.xml
@@ -0,0 +1,302 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ The FreeBSD Documentation Project
+ The FreeBSD Simplified Chinese Project
+
+ Original Revision: 1.12
+ $FreeBSD$
+-->
+<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" xml:id="pccard">
+ <info><title>PC Card</title>
+ <authorgroup>
+ <author><personname/><contrib>&cnproj.translated.by;</contrib></author>
+ </authorgroup>
+ </info>
+
+
+ <indexterm><primary>PC卡(PCMCIA卡, Personal Computer Memory Card International Association)</primary></indexterm>
+ <indexterm><primary>CardBus</primary></indexterm>
+
+ <para>本章将讨论FreeBSD为编写PC Card或CardBus设备的驱动程序而提供的机制。
+ 但目前本文只记录了如何向现有的pccard驱动程序中添加驱动程序。</para>
+
+ <sect1 xml:id="pccard-adddev">
+ <title>添加设备</title>
+
+ <para>向所支持的pccard设备列表中添加新设备的步骤已经与系统在FreeBSD 4
+ 中使用的方法不同了。在以前的版本中,需要编辑
+ <filename>/etc</filename>中的一个文件来列出设备。从FreeBSD 5.0开始,
+ 设备驱动程序知道它们支持什么设备。现在内核中有一个受支持设备的表,
+ 驱动程序用它来连接设备。</para>
+
+ <sect2 xml:id="pccard-overview">
+ <title>概览</title>
+
+ <indexterm><primary>CIS</primary></indexterm>
+ <para>可以有两种方法来识别PC Card,他们都基于卡上的
+ <acronym>CIS</acronym>信息。第一种方法是使用制造商和产品的数字编号。
+ 第二种方法是使用人可读的字符串,字符串也是包含在CIS中。PC Card总线
+ 使用集中式数据库和一些宏来提供一个易用的设计模式,让驱动程序的编写
+ 者很容易地确定匹配其驱动程序的设备。</para>
+
+ <para>一个很普遍的实际情况是,某个公司为一款PC Card产品开发出参考
+ 设计,然后把这个设计卖给另外的公司,以便在市场上出售。那些公司改进
+ 原设计,把向他们的目标客户群或地理区域出售产品,并将他们自己的名字
+ 放到卡中。然而所谓的对现有卡的改进,即使做过任何修改,这些修改通常
+ 也微乎其微。然而,为了强化他们自己版本的品牌,这些供货商常常会把他们
+ 公司的名字放入CIS空间的可读字符串中,却不会改动制造商和产品的ID。
+ </para>
+
+ <indexterm><primary>NetGear</primary></indexterm>
+ <indexterm><primary>Linksys</primary></indexterm>
+ <indexterm><primary>D-Link</primary></indexterm>
+
+ <para>鉴于以上情况,对于FreeBSD来说使用数字ID可以减小工作量。同时也
+ 会减小将ID加入到系统的过程中所带来的复杂性。必须仔细检查谁是卡的
+ 真正制造者,尤其当提供原卡的供货商在中心数据库中已经有一个不同的ID
+ 时。Linksys,D-Link和NetGear是经常出售相同设计的几个美国制造商。
+ 相同的设计可能在日本以诸如Buffalo和Corega的名字出售。然而,这些
+ 设备常常具有相同的制造商和产品ID。</para>
+
+ <para>PC Card总线在其中心数据库
+ <filename>/sys/dev/pccard/pccarddevs</filename>中保存了卡的信息,
+ 但不包含哪个驱动程序与它们关联的信息。它也提供了一套宏,以允许在
+ 驱动程序用来声明设备的表中容易地创建简单条目。</para>
+
+ <para>最后,某些非常低端的设备根本不包含制造商标识。这些设备需要使用
+ 可读CIS字符串来匹配它们。如果我们不需要这种应急办法该有多好,但对于
+ 某些非常低端却非常流行的CD-ROM播放器来说却是必需的。通常应当避免
+ 使用这种方法,但本节中还是列出了很多设备,因为它们是在认识到PC
+ Card商业的<acronym>OEM</acronym>本质之前加入的,应当优先使用
+ 数字方法。</para>
+
+ </sect2>
+
+ <sect2 xml:id="pccard-pccarddevs">
+ <title><filename>pccarddevs</filename>的格式</title>
+
+ <para><filename>pccarddevs</filename>文件有四节。第一节为使用
+ 它们的那些供货商列出了制造商号码。本节按数字排序。下一节包含了
+ 这些供货商使用的所有产品,包括他们的产品ID号码和描述字符串。
+ 描述字符串通常不会被使用(相反,即使我们可以匹配数字版本号,我们
+ 仍然基于人可读的CIS设置设备的描述)。然后为使用字符串匹配方法的
+ 那些设备重复这两节的东西。最后,文件任何地方可以使用C风格的注释。
+ </para>
+
+ <para>文件的第一节包含供货商ID。请保持列表按数字排序。此外,为了
+ 能有一个通用清晰的保存地来方便地保存这些信息,我们与NetBSD共享此
+ 文件,因此请协调对此文件的任何更改。例如:</para>
+
+<programlisting>vendor FUJITSU 0x0004 Fujitsu Corporation
+vendor NETGEAR_2 0x000b Netgear
+vendor PANASONIC 0x0032 Matsushita Electric Industrial Co.
+vendor SANDISK 0x0045 Sandisk Corporation</programlisting>
+
+ <para>显示了几个供货商ID。很凑巧的是<literal>NETGEAR_2</literal>
+ 实际上是NETGEAR从其购买卡的OEM,对那些卡提供支持的作者那时并不知道
+ NETgear使用的是别人的ID。这些条目相当直接易懂。每行上都有供货商
+ 关键字来指示本行的类别。也有供货商的名字。名字将会在pccarddevs文件
+ 的后面重复出现,名字也会用在驱动程序的匹配表中,因此保持它的短小
+ 并且是有效的C标识符。还有一个给供货商的十六进制数字ID。不要添加
+ <literal>0xffffffff</literal>或<literal>0xffff</literal>形式的ID,
+ 因为它们是保留ID(前者是'空ID集合',而后者有时会在质量极其差的卡中
+ 看到,用来指示none)。最后还有关于制卡公司的描述字符串。这个字符串
+ 在FreeBSD中除了用于注释目的外并没有被使用过。</para>
+
+ <para>文件的第二节包含产品. 如你在下面例子中看到的: </para>
+
+<programlisting>/* Allied Telesis K.K. */
+product ALLIEDTELESIS LA_PCM 0x0002 Allied Telesis LA-PCM
+
+/* Archos */
+product ARCHOS ARC_ATAPI 0x0043 MiniCD</programlisting>
+
+ <para>格式与供货商的那些行相似。其中有产品关键字。然后是供货商名字,
+ 由上面重复而来。后面跟着产品名字,此名字在驱动程序中使用,且应当
+ 是一个有效C标识符,但可以以数字开头。然后是卡的十六进制产品ID。
+ 供货商通常对<literal>0xffffffff</literal>和
+ <literal>0xffff</literal>有相同的约定。最后是关于设备自身的字符串
+ 描述。由于FreeBSD的pccard总线驱动程序会从人可读的CIS条目创建一个
+ 字符串,因此这个字符串在FreeBSD中通常不被使用,但某些CIS条目不能
+ 满足要求的情况下还可能使用。产品按制造商的字母顺序排序,然后再按
+ 产品ID的数字排序。每个制造商条目前有一条C注释,条目之间有一个空行。
+ </para>
+
+ <para>第三节很象前面的供货商一节,但所由的制造商ID为
+ <literal>-1</literal>。<literal>-1</literal>在FreeBSD pccard总线
+ 代码中意味着<quote>匹配发现的任何东西</quote>。由于它们是C标识符,
+ 它们的名字必须唯一。除此之外格式等同于文件的第一节。</para>
+
+ <para>最后一节包含那些必须用字符串匹配的卡。这一节的格式与通用
+ 节的格式有点不同:</para>
+
+<programlisting>product ADDTRON AWP100 { "Addtron", "AWP-100&amp;spWireless&amp;spPCMCIA", "Version&amp;sp01.02", NULL }
+product ALLIEDTELESIS WR211PCM { "Allied&amp;spTelesis&amp;spK.K.", "WR211PCM", NULL, NULL } Allied Telesis WR211PCM</programlisting>
+
+ <para>我们已经熟悉了产品关键字,后跟供货商名字,然后再跟卡的名字,
+ 就象在文件第二节中那样。然而,这之后就与那格式不同了。有一个
+ {}分组,后跟几个字符串。这些字符串对应CIS_INFO三元组中定义的
+ 供货商,产品和额外信息。这些字符串被产生
+ <filename>pccarddevs.h</filename>的程序过滤,将 &amp;sp替换为
+ 实际的空格。空条目意味着条目的这部分应当被忽略。在我选择的例子中
+ 有一个错误的条目。除非对卡的操作来说至关重要,否则不应当在其中
+ 包含版本号。有时供货商在这个字段中会有卡的很多不同版本,这些版本
+ 都能工作,这种情况下那些信息只会让那些拥有相似卡的人在FreeBSD中
+ 更难以使用。有时当供货商出于市场考虑(可用性,价格等等),希望出售
+ 同一品牌下的很多不同部分时,这也是有必要的。如果这样,则在那些
+ 供货商仍然保持相同的制造商/产品对的少见情况下,能否区分开卡至关
+ 重要. 此时不能使用正则表达式匹配。</para>
+
+ </sect2>
+
+ <sect2 xml:id="pccard-probe">
+ <title>探测例程样例</title>
+
+ <indexterm><primary>PC卡(PCMCIA卡, Personal Computer Memory Card International Association)</primary><secondary>probe(探测)</secondary></indexterm>
+
+ <para>要懂得如何向所支持的设备列表中添加设备,就必须懂得很多驱动程序
+ 都有的探测和/或匹配例程。由于也为老卡提供了一个兼容层,这在
+ FreeBSD 5.x中有一点复杂。由于只是window-dressing不同,这儿给出了
+ 一个理想化的版本。</para>
+
+<programlisting>static const struct pccard_product wi_pccard_products[] = {
+ PCMCIA_CARD(3COM, 3CRWE737A, 0),
+ PCMCIA_CARD(BUFFALO, WLI_PCM_S11, 0),
+ PCMCIA_CARD(BUFFALO, WLI_CF_S11G, 0),
+ PCMCIA_CARD(TDK, LAK_CD011WL, 0),
+ { NULL }
+};
+
+static int
+wi_pccard_probe(dev)
+ device_t dev;
+{
+ const struct pccard_product *pp;
+
+ if ((pp = pccard_product_lookup(dev, wi_pccard_products,
+ sizeof(wi_pccard_products[0]), NULL)) != NULL) {
+ if (pp-&gt;pp_name != NULL)
+ device_set_desc(dev, pp-&gt;pp_name);
+ return (0);
+ }
+ return (ENXIO);
+}</programlisting>
+
+ <para>这儿我们有一个可以匹配少数几个设备的简单pccard探测例程。如上面
+ 所提到,名字可能不同(如果不是
+ <function>foo_pccard_probe()</function>则就是
+ <function>foo_pccard_match()</function>)。函数
+ <function>pccard_product_lookup()</function>是一个通用函数,它遍历
+ 表并返回指向它所匹配的第一项的指针。一些驱动程序可能使用这个机制来
+ 将某些卡的附加信息传递到驱动程序的其它部分,因此表中可能有些变体。
+ 唯一的要求就是如果你有一个不同的表,则让表的结构的第一个元素为
+ 结构pccard_product。</para>
+
+ <para>观察一下表<varname remap="structname">wi_pccard_products</varname>就会发现,
+ 所有条目都是
+ <function>PCMCIA_CARD(<replaceable>foo</replaceable>,
+ <replaceable>bar</replaceable>,
+ <replaceable>baz</replaceable>)</function>的形式。
+ <replaceable>foo</replaceable>部分为来自
+ <filename>pccarddevs</filename>的制造商ID。
+ <replaceable>bar</replaceable>部分为产品。
+ <replaceable>baz</replaceable>为此卡所期望的功能号。许多pccards
+ 可以有多个功能,需要有办法区分开功能1和功能0。你可以看一下
+ <literal>PCMCIA_CARD_D</literal>,它包括了来自
+ <filename>pccarddevs</filename>文件的设备描述。你也可以看看
+ <literal>PCMCIA_CARD2</literal>和
+ <literal>PCMCIA_CARD2_D</literal>,当你需要按
+ <quote>使用默认描述</quote>和<quote>从pccarddevs中取得</quote>
+ 做法,同时匹配CIS字符串和制造商号码时就会用到它们。</para>
+
+ </sect2>
+
+ <sect2 xml:id="pccard-add">
+ <title>将它合在一起</title>
+
+ <para>因此,为了一个增加新设备,必须进行下面步骤。首先,必须从设备
+ 获得标识信息。完成这个最容易的方法就是将设备插入到PC Card或CF槽中,
+ 并发出<command>devinfo -v</command>。你可能会看到一些类似下面的
+ 东西:</para>
+
+<programlisting> cbb1 pnpinfo vendor=0x104c device=0xac51 subvendor=0x1265 subdevice=0x0300 class=0x060700 at slot=10 function=1
+ cardbus1
+ pccard1
+ unknown pnpinfo manufacturer=0x026f product=0x030c cisvendor="BUFFALO" cisproduct="WLI2-CF-S11" function_type=6 at function=0</programlisting>
+
+ <para>作为输出的一部分。制造商和产品为产品的数字ID。而cisvender和
+ cisproduct为CIS中提供的描述本产品的字符串。</para>
+
+ <para>由于我们首先想优先使用数字选项,因此首先尝试创建基于此的条目。
+ 为了示例,上面的卡已经被稍稍虚构化了。我们看到的供货商为BUFFALO,
+ 它已经有一个条目了:</para>
+
+<programlisting>vendor BUFFALO 0x026f BUFFALO (Melco Corporation)</programlisting>
+
+ <para>这样我们就可以了。为这个卡查找一个条目,但我们没有发现。但我们
+ 发现:</para>
+
+<programlisting>/* BUFFALO */
+product BUFFALO WLI_PCM_S11 0x0305 BUFFALO AirStation 11Mbps WLAN
+product BUFFALO LPC_CF_CLT 0x0307 BUFFALO LPC-CF-CLT
+product BUFFALO LPC3_CLT 0x030a BUFFALO LPC3-CLT Ethernet Adapter
+product BUFFALO WLI_CF_S11G 0x030b BUFFALO AirStation 11Mbps CF WLAN</programlisting>
+
+ <para>我们就可以向<filename>pccarddevs</filename>中添加:</para>
+
+<programlisting>product BUFFALO WLI2_CF_S11G 0x030c BUFFALO AirStation ultra 802.11b CF</programlisting>
+
+ <para>目前,需要一个手动步骤来
+ 重新产生<filename>pccarddevs.h</filename>,用来将这些标识符转换
+ 到客户驱动程序。你在驱动程序中使用它们之前必须完成下面步骤:
+ </para>
+
+<screen>&prompt.root; <userinput>cd src/sys/dev/pccard</userinput>
+&prompt.root; <userinput>make -f Makefile.pccarddevs</userinput>
+</screen>
+
+ <para>一旦完成了这些步骤,你就可以向驱动程序中添加卡了。这只是一个
+ 添加一行的简单操作:</para>
+
+<programlisting>static const struct pccard_product wi_pccard_products[] = {
+ PCMCIA_CARD(3COM, 3CRWE737A, 0),
+ PCMCIA_CARD(BUFFALO, WLI_PCM_S11, 0),
+ PCMCIA_CARD(BUFFALO, WLI_CF_S11G, 0),
++ PCMCIA_CARD(BUFFALO, WLI_CF2_S11G, 0),
+ PCMCIA_CARD(TDK, LAK_CD011WL, 0),
+ { NULL }
+};</programlisting>
+
+ <para>注意,我在我添加的行前面包含了'<literal>+</literal>',但这只是
+ 用来强调这一行。不要把它添加到实际驱动程序中。一旦你添加了这行,就
+ 可以重新编译内核或模块,并试着看它是否能识别设备。如果它识别出设备
+ 并能工作,请提交补丁。如果它不工作,请找出让它工作所需要的东西并
+ 提交一个补丁。如果它根本不识别设备,那么你可能做错了什么,应当重新
+ 检查每一步。</para>
+
+ <para>如果你是一个FreeBSD源代码的committer,并且所有东西看起来都
+ 正常工作,则你应当把这些改变提交到树中。然而有些小技巧的东西你
+ 需要考虑。首先,你必须提交<filename>pccarddevs</filename>文件到
+ 树中。完成后,你必须重新产生<filename>pccarddevs.h</filename>
+ 并将它作为另一次提交来提交(这是为了确保正确的
+ &dollar;FreeBSD&dollar;标签会留在后面的文件中)。最后,你需要把
+ 其它东西提交到驱动程序。</para>
+
+ </sect2>
+
+ <sect2 xml:id="pccard-pr">
+ <title>提交新设备</title>
+
+ <para>很多人直接把新设备的条目发送给作者。请不要那样做。请将它们作为
+ PR来提交,并将PR号码发送给作者用于记录。这样确保条目不会丢失。提交
+ PR时,补丁中没有必要包含<filename>pccardevs.h</filename>的diff,
+ 因为那些东西可以重新产生。包含设备的描述和客户驱动程序的补丁是必要
+ 的。如果你不知道名字,使用OEM99作为名字,作者将会调查后相应地调整
+ OEM99。提交者不应当提交OEM99,而应该找到最高的OEM条目并提交高于那个
+ 的一个。</para>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
diff --git a/zh_CN.UTF-8/books/arch-handbook/pci/chapter.xml b/zh_CN.UTF-8/books/arch-handbook/pci/chapter.xml
new file mode 100644
index 0000000000..5b48abc4d1
--- /dev/null
+++ b/zh_CN.UTF-8/books/arch-handbook/pci/chapter.xml
@@ -0,0 +1,438 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ The FreeBSD Documentation Project
+ The FreeBSD Simplified Chinese Project
+
+ Original Revision: 1.24
+ $FreeBSD$
+-->
+<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" xml:id="pci">
+ <info><title>PCI设备</title>
+ <authorgroup>
+ <author><personname/><contrib>&cnproj.translated.by;</contrib></author>
+ </authorgroup>
+ </info>
+
+
+ <indexterm><primary>PCI总线</primary></indexterm>
+
+ <para>本章将讨论FreeBSD为了给PCI总线上的设备编写驱动程序而提供的机制。</para>
+
+ <sect1 xml:id="pci-probe">
+ <title>探测与连接</title>
+
+ <para>这儿的信息是关于PCI总线代码如何迭代通过未连接的设备,并查看新
+ 加载的kld是否会连接其中一个。</para>
+
+ <sect2>
+ <title>示例驱动程序源代码(<filename>mypci.c</filename>)</title>
+
+<programlisting>/*
+ * 与PCI函数进行交互的简单KLD
+ *
+ * Murray Stokely
+ */
+
+#include &lt;sys/param.h&gt; /* kernel.h中使用的定义 */
+#include &lt;sys/module.h&gt;
+#include &lt;sys/systm.h&gt;
+#include &lt;sys/errno.h&gt;
+#include &lt;sys/kernel.h&gt; /* 模块初始化中使用的类型 */
+#include &lt;sys/conf.h&gt; /* cdevsw结构 */
+#include &lt;sys/uio.h&gt; /* uio结构 */
+#include &lt;sys/malloc.h&gt;
+#include &lt;sys/bus.h&gt; /* pci总线用到的结构、原型 */
+
+#include &lt;machine/bus.h&gt;
+#include &lt;sys/rman.h&gt;
+#include &lt;machine/resource.h&gt;
+
+#include &lt;dev/pci/pcivar.h&gt; /* 为了使用get_pci宏! */
+#include &lt;dev/pci/pcireg.h&gt;
+
+/* softc保存我们每个实例的数据。 */
+struct mypci_softc {
+ device_t my_dev;
+ struct cdev *my_cdev;
+};
+
+/* 函数原型 */
+static d_open_t mypci_open;
+static d_close_t mypci_close;
+static d_read_t mypci_read;
+static d_write_t mypci_write;
+
+/* 字符设备入口点 */
+
+static struct cdevsw mypci_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = mypci_open,
+ .d_close = mypci_close,
+ .d_read = mypci_read,
+ .d_write = mypci_write,
+ .d_name = "mypci",
+};
+
+/*
+ * 在cdevsw例程中,我们通过结构体cdev中的成员si_drv1找出我们的softc。
+ * 当我们建立/dev项时,在我们的已附着的例程中,
+ * 我们设置这个变量指向我们的softc。
+ */
+
+int
+mypci_open(struct cdev *dev, int oflags, int devtype, d_thread_t *td)
+{
+ struct mypci_softc *sc;
+
+ /* Look up our softc. */
+ sc = dev-&gt;si_drv1;
+ device_printf(sc-&gt;my_dev, "Opened successfully.\n");
+ return (0);
+}
+
+int
+mypci_close(struct cdev *dev, int fflag, int devtype, d_thread_t *td)
+{
+ struct mypci_softc *sc;
+
+ /* Look up our softc. */
+ sc = dev-&gt;si_drv1;
+ device_printf(sc-&gt;my_dev, "Closed.\n");
+ return (0);
+}
+
+int
+mypci_read(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ struct mypci_softc *sc;
+
+ /* Look up our softc. */
+ sc = dev-&gt;si_drv1;
+ device_printf(sc-&gt;my_dev, "Asked to read %d bytes.\n", uio-&gt;uio_resid);
+ return (0);
+}
+
+int
+mypci_write(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ struct mypci_softc *sc;
+
+ /* Look up our softc. */
+ sc = dev-&gt;si_drv1;
+ device_printf(sc-&gt;my_dev, "Asked to write %d bytes.\n", uio-&gt;uio_resid);
+ return (0);
+}
+
+/* PCI支持函数 */
+
+/*
+ * 将某个设置的标识与这个驱动程序支持的标识相比较。
+ * 如果相符,设置描述字符并返回成功。
+ */
+static int
+mypci_probe(device_t dev)
+{
+
+ device_printf(dev, "MyPCI Probe\nVendor ID : 0x%x\nDevice ID : 0x%x\n",
+ pci_get_vendor(dev), pci_get_device(dev));
+
+ if (pci_get_vendor(dev) == 0x11c1) {
+ printf("We've got the Winmodem, probe successful!\n");
+ device_set_desc(dev, "WinModem");
+ return (BUS_PROBE_DEFAULT);
+ }
+ return (ENXIO);
+}
+
+/* 只有当探测成功时才调用连接函数 */
+
+static int
+mypci_attach(device_t dev)
+{
+ struct mypci_softc *sc;
+
+ printf("MyPCI Attach for : deviceID : 0x%x\n", pci_get_devid(dev));
+
+ /* Look up our softc and initialize its fields. */
+ sc = device_get_softc(dev);
+ sc-&gt;my_dev = dev;
+
+ /*
+ * Create a /dev entry for this device. The kernel will assign us
+ * a major number automatically. We use the unit number of this
+ * device as the minor number and name the character device
+ * "mypci&lt;unit&gt;".
+ */
+ sc-&gt;my_cdev = make_dev(<literal>&amp;</literal>mypci_cdevsw, device_get_unit(dev),
+ UID_ROOT, GID_WHEEL, 0600, "mypci%u", device_get_unit(dev));
+ sc-&gt;my_cdev-&gt;si_drv1 = sc;
+ printf("Mypci device loaded.\n");
+ return (0);
+}
+
+/* 分离设备。 */
+
+static int
+mypci_detach(device_t dev)
+{
+ struct mypci_softc *sc;
+
+ /* Teardown the state in our softc created in our attach routine. */
+ sc = device_get_softc(dev);
+ destroy_dev(sc-&gt;my_cdev);
+ printf("Mypci detach!\n");
+ return (0);
+}
+
+/* 系统关闭期间在sync之后调用。 */
+
+static int
+mypci_shutdown(device_t dev)
+{
+
+ printf("Mypci shutdown!\n");
+ return (0);
+}
+
+/*
+ * 设备挂起例程。
+ */
+static int
+mypci_suspend(device_t dev)
+{
+
+ printf("Mypci suspend!\n");
+ return (0);
+}
+
+/*
+ * 设备恢复(重新开始)例程。
+ */
+static int
+mypci_resume(device_t dev)
+{
+
+ printf("Mypci resume!\n");
+ return (0);
+}
+
+static device_method_t mypci_methods[] = {
+ /* 设备接口 */
+ DEVMETHOD(device_probe, mypci_probe),
+ DEVMETHOD(device_attach, mypci_attach),
+ DEVMETHOD(device_detach, mypci_detach),
+ DEVMETHOD(device_shutdown, mypci_shutdown),
+ DEVMETHOD(device_suspend, mypci_suspend),
+ DEVMETHOD(device_resume, mypci_resume),
+
+ { 0, 0 }
+};
+
+static devclass_t mypci_devclass;
+
+DEFINE_CLASS_0(mypci, mypci_driver, mypci_methods, sizeof(struct mypci_softc));
+DRIVER_MODULE(mypci, pci, mypci_driver, mypci_devclass, 0, 0);</programlisting>
+ </sect2>
+
+ <sect2>
+ <title>示例驱动程序的<filename>Makefile</filename></title>
+
+<programlisting># 驱动程序mypci的Makefile
+
+KMOD= mypci
+SRCS= mypci.c
+SRCS+= device_if.h bus_if.h pci_if.h
+
+.include &lt;bsd.kmod.mk&gt;</programlisting>
+
+ <para>如果你将上面的源文件和
+ <filename>Makefile</filename>放入一个目录,你可以运行
+ <command>make</command>编译示例驱动程序。
+ 还有,你可以运行<command>make load</command>
+ 将驱动程序装载到当前正在运行的内核中,而<command>make
+ unload</command>可在装载后卸载驱动程序。
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>更多资源</title>
+ <itemizedlist>
+ <listitem><simpara><link xlink:href="http://www.pcisig.org/">PCI
+ Special Interest Group</link></simpara></listitem>
+
+ <listitem><simpara>PCI System Architecture, Fourth Edition by
+ Tom Shanley, et al.</simpara></listitem>
+
+ </itemizedlist>
+ </sect2>
+ </sect1>
+
+ <sect1 xml:id="pci-bus">
+ <title>总线资源</title>
+
+ <indexterm><primary>PCI总线</primary><secondary>resources(资源)</secondary></indexterm>
+ <para>FreeBSD为从父总线请求资源提供了一种面向对象的机制。几乎所有设备
+ 都是某种类型的总线(PCI, ISA, USB, SCSI等等)的孩子成员,并且这些设备
+ 需要从他们的父总线获取资源(例如内存段, 中断线, 或者DMA通道)。</para>
+
+ <sect2>
+ <title>基地址寄存器</title>
+
+ <indexterm><primary>PCI总线</primary><secondary>Base Address Registers(基地址寄存器)</secondary></indexterm>
+
+ <para>为了对PCI设备做些有用的事情,你需要从PCI配置空间获取
+ <emphasis>Base Address Registers</emphasis> (BARs)。获取BAR时的
+ PCI特定的细节被抽象在函数<function>bus_alloc_resource()</function>中。
+ </para>
+
+ <para>例如,一个典型的驱动程序可能在<function>attach()</function>
+ 函数中有些类似下面的东西:</para>
+
+<programlisting> sc-&gt;bar0id = PCIR_BAR(0);
+ sc-&gt;bar0res = bus_alloc_resource(dev, SYS_RES_MEMORY, &amp;sc-&gt;bar0id,
+ 0, ~0, 1, RF_ACTIVE);
+ if (sc-&gt;bar0res == NULL) {
+ printf("Memory allocation of PCI base register 0 failed!\n");
+ error = ENXIO;
+ goto fail1;
+ }
+
+ sc-&gt;bar1id = PCIR_BAR(1);
+ sc-&gt;bar1res = bus_alloc_resource(dev, SYS_RES_MEMORY, &amp;sc-&gt;bar1id,
+ 0, ~0, 1, RF_ACTIVE);
+ if (sc-&gt;bar1res == NULL) {
+ printf("Memory allocation of PCI base register 1 failed!\n");
+ error = ENXIO;
+ goto fail2;
+ }
+ sc-&gt;bar0_bt = rman_get_bustag(sc-&gt;bar0res);
+ sc-&gt;bar0_bh = rman_get_bushandle(sc-&gt;bar0res);
+ sc-&gt;bar1_bt = rman_get_bustag(sc-&gt;bar1res);
+ sc-&gt;bar1_bh = rman_get_bushandle(sc-&gt;bar1res);</programlisting>
+
+ <para>每个基址寄存器的句柄被保存在<varname remap="structname">softc</varname>
+ 结构中,以便以后可以使用它们向设备写入。</para>
+
+ <para>然后就能使用这些句柄与<function>bus_space_*</function>函数一起
+ 读写设备寄存器。例如,驱动程序可能包含如下的快捷函数,用来读取板子
+ 特定的寄存器:</para>
+
+<programlisting>uint16_t
+board_read(struct ni_softc *sc, uint16_t address)
+{
+ return bus_space_read_2(sc-&gt;bar1_bt, sc-&gt;bar1_bh, address);
+}
+</programlisting>
+
+ <para>类似的,可以用下面的函数写寄存器:</para>
+
+<programlisting>void
+board_write(struct ni_softc *sc, uint16_t address, uint16_t value)
+{
+ bus_space_write_2(sc-&gt;bar1_bt, sc-&gt;bar1_bh, address, value);
+}
+</programlisting>
+
+ <para>这些函数以8位,16位和32位的版本存在,你应当相应地使用
+ <function>bus_space_{read|write}_{1|2|4}</function>。</para>
+
+ <note>
+ <para>在 FreeBSD 7.0 和更高版本中, 可以用
+ <function>bus_*</function> 函数来代替
+ <function>bus_space_*</function>。
+ <function>bus_*</function> 函数使用的参数是 <type>struct
+ resource *</type> 指针, 而不是 bus tag 和句柄。
+ 这样, 您就可以将 <varname remap="structname">softc</varname>
+ 中的 bus tag 和 bus 句柄这两个成员变量去掉, 并将
+ <function>board_read()</function> 函数改写为:</para>
+
+<programlisting>uint16_t
+board_read(struct ni_softc *sc, uint16_t address)
+{
+ return (bus_read(sc-&gt;bar1res, address));
+}
+</programlisting>
+ </note>
+ </sect2>
+ <sect2>
+ <title>中断</title>
+
+ <indexterm><primary>PCI总线</primary><secondary>interrupts(中断)</secondary></indexterm>
+ <para>中断按照和分配内存资源相似的方式从面向对象的总线代码分配。首先,
+ 必须从父总线分配IRQ资源,然后必须设置中断处理函数来处理这个IRQ。
+ </para>
+
+ <para>再一次,来自设备<function>attach()</function>函数的例子比文字
+ 更具说明性。</para>
+
+<programlisting>/* 取得IRQ资源 */
+
+ sc-&gt;irqid = 0x0;
+ sc-&gt;irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &amp;(sc-&gt;irqid),
+ 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
+ if (sc-&gt;irqres == NULL) {
+ printf("IRQ allocation failed!\n");
+ error = ENXIO;
+ goto fail3;
+ }
+
+ /* 现在我们应当设置中断处理函数 */
+
+ error = bus_setup_intr(dev, sc-&gt;irqres, INTR_TYPE_MISC,
+ my_handler, sc, &amp;(sc-&gt;handler));
+ if (error) {
+ printf("Couldn't set up irq\n");
+ goto fail4;
+ }
+</programlisting>
+
+ <para>在设备的分离例程中必须注意一些问题。你必须停顿设备的中断流,
+ 并移除中断处理函数。一旦<function>bus_teardown_intr()</function>
+ 返回,你知道你的中断处理函数不会再被调用,并且所有可能已经执行了
+ 这个中断处理函数的线程都已经返回。由于此函数可以睡眠,调用此函数时
+ 你必须不能拥有任何互斥体。</para>
+
+ </sect2>
+
+ <sect2>
+ <title>DMA</title>
+
+ <indexterm><primary>PCI总线</primary><secondary>DMA(直接内存访问)</secondary></indexterm>
+ <para>本节已废弃,只是由于历史原因而给出。处理这些问题的适当方法是
+ 使用<function>bus_space_dma*()</function>函数。当更新这一节以反映
+ 那样用法时,这段就可能被去掉。然而,目前API还不断有些变动,因此一旦
+ 它们固定下来后,更新这一节来反映那些改动就很好了。</para>
+
+ <para>在PC上,想进行总线主控DMA的外围设备必须处理物理地址,由于
+ FreeBSD使用虚拟内存并且只处理虚地址,这仍是个问题。幸运的是,有个
+ 函数,<function>vtophys()</function>可以帮助我们。</para>
+
+<programlisting>#include &lt;vm/vm.h&gt;
+#include &lt;vm/pmap.h&gt;
+
+#define vtophys(virtual_address) (...)
+</programlisting>
+
+ <para>然而这个解决办法在alpha上有点不一样,并且我们真正想要的是一个
+ 称为<function>vtobus()</function>的函数。</para>
+
+<programlisting>#if defined(__alpha__)
+#define vtobus(va) alpha_XXX_dmamap((vm_offset_t)va)
+#else
+#define vtobus(va) vtophys(va)
+#endif
+</programlisting>
+
+ </sect2>
+
+ <sect2>
+ <title>取消分配资源</title>
+
+ <para>取消<function>attach()</function>期间分配的所有资源非常重要。
+ 必须小心谨慎,即使在失败的条件下也要保证取消分配那些正确的东西,
+ 这样当你的驱动程序去掉后系统仍然可以使用。</para>
+
+ </sect2>
+ </sect1>
+
+</chapter>
diff --git a/zh_CN.UTF-8/books/arch-handbook/scsi/chapter.xml b/zh_CN.UTF-8/books/arch-handbook/scsi/chapter.xml
new file mode 100644
index 0000000000..a19c10ef27
--- /dev/null
+++ b/zh_CN.UTF-8/books/arch-handbook/scsi/chapter.xml
@@ -0,0 +1,1815 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ The FreeBSD Documentation Project
+ The FreeBSD Simplified Chinese Project
+
+ Original Revision: 1.18
+ $FreeBSD$
+-->
+<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" xml:id="scsi">
+ <info><title>通用访问方法SCSI控制器</title>
+ <authorgroup>
+ <author><personname><firstname>Sergey</firstname><surname>Babkin</surname></personname><contrib>&cnproj.written.by;</contrib></author>
+ </authorgroup>
+ <authorgroup>
+ <author><personname><firstname>Murray</firstname><surname>Stokely</surname></personname><contrib>&cnproj.modified.for.handbook.by;</contrib></author>
+ </authorgroup>
+ <authorgroup>
+ <author><personname/><contrib>&cnproj.translated.by;</contrib></author>
+ </authorgroup>
+ </info>
+
+
+
+ <sect1 xml:id="scsi-synopsis">
+ <title>提纲</title>
+
+ <indexterm><primary>SCSI(小型计算机系统接口)</primary></indexterm>
+ <para>本文档假定读者对FreeBSD的设备驱动程序和SCSI协议有大致了解,
+ 本文档中很多信息是从以下驱动程序中:</para>
+
+ <itemizedlist>
+
+ <listitem><para>ncr (<filename>/sys/pci/ncr.c</filename>)
+ 由Wolfgang Stanglmeier and Stefan Esser编写</para></listitem>
+
+ <listitem><para>sym (<filename>/sys/dev/sym/sym_hipd.c</filename>)
+ 由Gerard Roudier编写</para></listitem>
+
+ <listitem><para>aic7xxx
+ (<filename>/sys/dev/aic7xxx/aic7xxx.c</filename>)
+ 由Justin T. Gibbs编写</para></listitem>
+
+ </itemizedlist>
+
+ <para>和从CAM的代码本身(作者 Justin T. Gibbs,
+ 见<filename>/sys/cam/*</filename>)中摘录。当一些解决方法看起来
+ 极具逻辑性,并且基本上是从 Justin T. Gibbs 的代码中一字不差地摘录时,
+ 我将其标记为<quote>recommended</quote>。</para>
+
+ <para>本文档以伪代码例子进行说明。尽管有时例子中包含很多细节,并且
+ 看起来很像真正代码,但它仍然只是伪代码。这样写是为了以一种可理解
+ 的方式来展示概念。对于真正的驱动程序,其它方法可能更模块化,并且
+ 更加高效。文档也对硬件细节进行抽象,对于那些会模糊我们所要展示的
+ 概念的问题,或被认为在开发者手册的其他章节中已有描述的问题也做同样
+ 处理。这些细节通常以调用具有描述性名字的函数、注释或伪语句的形式展现。
+ 幸运的是,具有实际价值的完整例子,包括所有细节,可以在真正的驱动
+ 程序中找到。</para>
+
+ </sect1>
+
+ <sect1 xml:id="scsi-general">
+ <title>通用基础结构</title>
+
+ <indexterm><primary>Common Access Method (CAM, 通用访问方法)</primary></indexterm>
+
+ <para>CAM代表通用访问方法(Common Access Method)。它以类SCSI方式寻址
+ I/O总线。这就允许将通用设备驱动程序和控制I/O总线的驱动程序分离开来:
+ 例如磁盘驱动程序能同时控制SCSI、IDE、且/或任何其他总线上的磁盘,
+ 这样磁盘驱动程序部分不必为每种新的I/O总线而重写(或拷贝修改)。
+ 这样,两个最重要的活动实体是:</para>
+
+ <indexterm><primary>CD-ROM(只读光盘驱动器)</primary></indexterm>
+ <indexterm><primary>tape(磁带)</primary></indexterm>
+ <indexterm><primary>IDE(Intelligent Drive Electronics,智能驱动器电路;或Integrated Drive Electronics,集成驱动器电路)</primary></indexterm>
+ <itemizedlist>
+ <listitem><para><emphasis>外围设备模块</emphasis> - 外围设备(磁盘,
+ 磁带, CD-ROM等)的驱动程序</para></listitem>
+ <listitem><para><emphasis>SCSI接口模块</emphasis>(SIM)
+ - 连接到I/O总线,如SCSI或IDE,的主机总线适配器驱动程序。
+ </para></listitem>
+ </itemizedlist>
+
+ <para>外围设备驱动程序从OS接收请求,将它们转换为SCSI命令序列并将
+ 这些SCSI命令传递到SCSI接口模块。SCSI接口模块负责将这些命令传递给
+ 实际硬件(或者如果实际硬件不是SCSI,而是例如IDE,则也要将这些SCSI
+ 命令转换为硬件的native命令)。</para>
+
+ <para>由于这儿我们感兴趣的是编写SCSI适配器驱动程序,从此处开始我们
+ 将从SIM的角度考虑所有的事情。</para>
+
+ <para>典型的SIM驱动程序需要包括如下的CAM相关的头文件:</para>
+
+<programlisting>#include &lt;cam/cam.h&gt;
+#include &lt;cam/cam_ccb.h&gt;
+#include &lt;cam/cam_sim.h&gt;
+#include &lt;cam/cam_xpt_sim.h&gt;
+#include &lt;cam/cam_debug.h&gt;
+#include &lt;cam/scsi/scsi_all.h&gt;</programlisting>
+
+ <para>每个SIM驱动程序必须做的第一件事情是向CAM子系统注册它自己。
+ 这在驱动程序的<function>xxx_attach()</function>函数(此处和以后的
+ xxx_用于指带唯一的驱动程序名字前缀)期间完成。
+ <function>xxx_attach()</function>函数自身由系统总线自动配置代码
+ 调用,我们在此不描述这部分代码。</para>
+
+ <para>这需要好几步来完成:首先需要分配与SIM关联的请求队列:</para>
+
+<programlisting> struct cam_devq *devq;
+
+ if(( devq = cam_simq_alloc(SIZE) )==NULL) {
+ error; /* 一些处理错误的代码 */
+ }</programlisting>
+
+ <para>此处 <literal>SIZE</literal> 为要分配的队列的大小,
+ 它能包含的最大请求数目。 它是 SIM
+ 驱动程序在 SCSI 卡上能够并行处理的请求的数目。一般可以如下估算:</para>
+
+<programlisting>SIZE = NUMBER_OF_SUPPORTED_TARGETS * MAX_SIMULTANEOUS_COMMANDS_PER_TARGET</programlisting>
+
+ <para>下一步为我们的SIM创建描述符:</para>
+
+<programlisting> struct cam_sim *sim;
+
+ if(( sim = cam_sim_alloc(action_func, poll_func, driver_name,
+ softc, unit, max_dev_transactions,
+ max_tagged_dev_transactions, devq) )==NULL) {
+ cam_simq_free(devq);
+ error; /* 一些错误处理代码 */
+ }</programlisting>
+
+ <para>注意如果我们不能创建SIM描述符,我们也释放
+ <varname remap="structname">devq</varname>,因为我们对其无法做任何其他事情,
+ 而且我们想节约内存。</para>
+
+ <indexterm><primary>SCSI(小型计算机系统接口)</primary><secondary>bus(总线)</secondary></indexterm>
+ <para>如果SCSI卡上有多条SCSI总线,则每条总线需要它自己的
+ <varname remap="structname">cam_sim</varname> 结构。</para>
+
+ <para>一个有趣的问题是,如果SCSI卡有不只一条SCSI总线我们该怎么做,
+ 每个卡需要一个<varname remap="structname">devq</varname>结构还是每条SCSI总线?
+ 在CAM代码的注释中给出的答案是:任一方式均可,由驱动程序的作者
+ 选择。</para>
+
+ <para>参量为:
+ <itemizedlist>
+
+ <listitem><para><function>action_func</function> - 指向驱动程序
+ <function>xxx_action</function> 函数的指针。
+ <funcsynopsis><funcprototype>
+ <funcdef>static void
+ <function>xxx_action</function>
+ </funcdef>
+ <paramdef>
+ <parameter>struct cam_sim *sim</parameter>,
+ <parameter>union ccb *ccb</parameter>
+ </paramdef>
+ </funcprototype></funcsynopsis>
+ </para></listitem>
+
+ <listitem><para><function>poll_func</function> - 指向驱动程序
+ <function>xxx_poll()</function>函数的指针。
+ <funcsynopsis><funcprototype>
+ <funcdef>static void
+ <function>xxx_poll</function>
+ </funcdef>
+ <paramdef>
+ <parameter>struct cam_sim *sim</parameter>
+ </paramdef>
+ </funcprototype></funcsynopsis>
+ </para></listitem>
+
+ <listitem><para>driver_name - 实际驱动程序的名字,例如
+ <quote>ncr</quote>或<quote>wds</quote>。</para></listitem>
+
+ <listitem><para><varname remap="structname">softc</varname> - 指向这个SCSI卡
+ 驱动程序的内部描述符的指针。这个指针以后被驱动程序用来获取
+ 私有数据。</para></listitem>
+
+ <listitem><para>unit - 控制器单元号,例如对于控制器
+ <quote>wds0</quote>的此数字将为0。</para></listitem>
+
+ <listitem><para>max_dev_transactions - 无标签模式下每个SCSI目标的
+ 最大并发(simultaneous)事务数。这个值一般几乎总是等于1,只有非
+ SCSI卡才可能例外。此外,如果驱动程序希望执行一个事务的同时准备另
+ 一个事务,可以将其设置为2,但似乎不值得增加这种复杂性。
+ </para></listitem>
+
+ <listitem><para>max_tagged_dev_transactions - 同样的东西,但是
+ 在标签模式下。标签是SCSI在设备上发起多个事务的方式:每个事务
+ 被赋予一个唯一的标签,并被发送到设备。当设备完成某些事务,它
+ 将结果连同标签一起发送回来,这样SCSI适配器(和驱动程序)就能知道
+ 哪个事务完成了。此参量也被认为是最大标签深度。它取决于SCSI
+ 适配器的能力。</para></listitem>
+ </itemizedlist>
+ </para>
+
+ <indexterm><primary>SCSI(小型计算机系统接口)</primary><secondary>adapter(适配器)</secondary></indexterm>
+ <para>最后我们注册与我们的SCSI适配器关联的SCSI总线。</para>
+
+<programlisting> if(xpt_bus_register(sim, bus_number) != CAM_SUCCESS) {
+ cam_sim_free(sim, /*free_devq*/ TRUE);
+ error; /* 一些错误处理代码 */
+ }</programlisting>
+
+ <para>如果每条SCSI总线有一个<varname remap="structname">devq</varname>结构(即,
+ 我们将带有多条总线的卡看作多个卡,每个卡带有一条总线),则总线号
+ 总是为0,否则SCSI卡上的每条总线应当有不同的号。每条总线需要
+ 它自己单独的cam_sim结构。</para>
+
+ <para>这之后我们的控制器完全挂接到CAM系统。现在
+ <varname remap="structname">devq</varname>的值可以被丢弃:在所有以后从CAM发出的
+ 调用中将以sim为参量,devq可以由它导出。</para>
+
+ <para>CAM为这些异步事件提供了框架。有些事件来自底层(SIM驱动程序),
+ 有些来自外围设备驱动程序,还有一些来自CAM子系统本身。任何驱动
+ 程序都可以为某些类型的异步事件注册回调,这样那些事件发生时它就
+ 会被通知。</para>
+
+ <para>这种事件的一个典型例子就是设备复位。每个事务和事件以
+ <quote>path</quote>的方式区分它们所作用的设备。目标特定的事件
+ 通常在与设备进行事务处理期间发生。因此那个事务的路径可以被重用
+ 来报告此事件(这是安全的,因为事件路径的拷贝是在事件报告例程中进行的,
+ 而且既不会被deallocate也不作进一步传递)。在任何时刻,包括中断例程中,
+ 动态分配路径也是安全的,尽管那样会导致某些额外开销,并且这种方法
+ 可能存在的一个问题是碰巧那时可能没有空闲内存。对于总线复位事件,
+ 我们需要定义包括总线上所有设备在内的通配符路径。这样我们就能提前为
+ 以后的总线复位事件创建路径,避免以后内存不足的问题:</para>
+
+<programlisting> struct cam_path *path;
+
+ if(xpt_create_path(&amp;path, /*periph*/NULL,
+ cam_sim_path(sim), CAM_TARGET_WILDCARD,
+ CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+ xpt_bus_deregister(cam_sim_path(sim));
+ cam_sim_free(sim, /*free_devq*/TRUE);
+ error; /* 一些错误处理代码 */
+ }
+
+ softc-&gt;wpath = path;
+ softc-&gt;sim = sim;</programlisting>
+
+ <para>正如你所看到的,路径包括:</para>
+
+ <itemizedlist>
+ <listitem><para>外围设备驱动程序的ID(由于我们一个也没有,故此处为空)
+ </para></listitem>
+
+ <listitem><para>SIM驱动程序的ID
+ (<function>cam_sim_path(sim)</function>)</para></listitem>
+
+ <listitem><para>设备的SCSI目标号(CAM_TARGET_WILDCARD的意思指
+ <quote>所有devices</quote>)</para></listitem>
+
+ <listitem><para>子设备的SCSI LUN号(CAM_LUN_WILDCARD的意思指
+ <quote>所有LUNs</quote>)</para></listitem>
+ </itemizedlist>
+
+ <para>如果驱动程序不能分配这个路径,它将不能正常工作,因此那样情况下
+ 我们卸除(dismantle)那个SCSI总线。</para>
+
+ <para>我们在<varname remap="structname">softc</varname>结构中保存路径指针以便以后
+ 使用。这之后我们保存sim的值(或者如果我们愿意,也可以在从
+ <function>xxx_probe()</function>退出时丢弃它)。</para>
+
+ <para>这就是最低要求的初始化所需要做的一切。为了把事情做正确无误,
+ 还剩下一个问题。</para>
+
+ <para>对于SIM驱动程序,有一个特殊感兴趣的事件:何时目标设备被认为
+ 找不到了。这种情况下复位与这个设备的SCSI协商可能是个好主意。因此我们
+ 为这个事件向CAM注册一个回调。通过为这种类型的请求来请求CAM控制块上
+ 的CAM动作,请求就被传递到CAM:(译注:参看下面示例代码和原文)</para>
+
+<programlisting> struct ccb_setasync csa;
+
+ xpt_setup_ccb(&amp;csa.ccb_h, path, /*优先级*/5);
+ csa.ccb_h.func_code = XPT_SASYNC_CB;
+ csa.event_enable = AC_LOST_DEVICE;
+ csa.callback = xxx_async;
+ csa.callback_arg = sim;
+ xpt_action((union ccb *)&amp;csa);</programlisting>
+
+ <para>现在我们看一下<function>xxx_action()</function>
+ 和<function>xxx_poll()</function>的驱动程序入口点。</para>
+
+ <para>
+ <funcsynopsis><funcprototype>
+ <funcdef>static void
+ <function>xxx_action</function>
+ </funcdef>
+ <paramdef>
+ <parameter>struct cam_sim *sim</parameter>,
+ <parameter>union ccb *ccb</parameter>
+ </paramdef>
+ </funcprototype></funcsynopsis>
+ </para>
+
+ <para>响应CAM子系统的请求采取某些动作。Sim描述了请求的SIM,CCB为
+ 请求本身。CCB代表<quote>CAM Control Block</quote>。它是很多特定
+ 实例的联合,每个实例为某些类型的事务描述参量。所有这些实例共享
+ 存储着参量公共部分的CCB头部。(译注:这一段不很准确,请自行参考原文)
+ </para>
+
+ <para>CAM既支持SCSI控制器工作于发起者(initiator)(<quote>normal</quote>)
+ 模式,也支持SCSI控制器工作于目标(target)(模拟SCSI设备)模式。这儿
+ 我们只考虑与发起者模式有关的部分。</para>
+
+ <para>定义了几个函数和宏(换句话说,方法)来访问结构sim中公共数据:
+ </para>
+
+ <itemizedlist>
+ <listitem><para><function>cam_sim_path(sim)</function> - 路径ID
+ (参见上面)</para></listitem>
+
+ <listitem><para><function>cam_sim_name(sim)</function> -
+ sim的名字</para></listitem>
+
+ <listitem><para><function>cam_sim_softc(sim)</function> -
+ 指向softc(驱动程序私有数据)结构的指针</para></listitem>
+
+ <listitem><para><function> cam_sim_unit(sim)</function> -
+ 单元号</para></listitem>
+
+ <listitem><para><function> cam_sim_bus(sim)</function> -
+ 总线ID</para></listitem>
+ </itemizedlist>
+
+ <para>为了识别设备,<function>xxx_action()</function>可以使用这些
+ 函数得到单元号和指向它的softc结构的指针。</para>
+
+ <para>请求的类型被存储在
+ <varname remap="structfield">ccb-&gt;ccb_h.func_code</varname>。因此,通常
+ <function>xxx_action()</function>由一个大的switch组成:</para>
+
+<programlisting> struct xxx_softc *softc = (struct xxx_softc *) cam_sim_softc(sim);
+ struct ccb_hdr *ccb_h = &amp;ccb-&gt;ccb_h;
+ int unit = cam_sim_unit(sim);
+ int bus = cam_sim_bus(sim);
+
+ switch(ccb_h-&gt;func_code) {
+ case ...:
+ ...
+ default:
+ ccb_h-&gt;status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ break;
+ }</programlisting>
+
+ <para>从default case语句部分可以看出(如果收到未知命令),命令的返回码
+ 被设置到 <varname remap="structfield">ccb-&gt;ccb_h.status</varname> 中,并且通过
+ 调用<function>xpt_done(ccb)</function>将整个CCB返回到CAM中。</para>
+
+ <para><function>xpt_done()</function>不必从
+ <function>xxx_action()</function>中调用:例如I/O请求可以在SIM驱动程序
+ 和/或它的SCSI控制器中排队。(译注:它指I/O请求?)
+ 然后,当设备传递(post)一个中断信号,指示对此请求的处理已结束时,
+ <function>xpt_done()</function>可以从中断处理例程中被调用。</para>
+
+ <para>实际上,CCB状态不是仅仅被赋值为一个返回码,而是始终有某种状态。
+ CCB被传递给<function>xxx_action()</function>例程前,其取得状态
+ CCB_REQ_INPROG,表示其正在进行中。<filename>/sys/cam/cam.h</filename>
+ 中定义了数量惊人的状态值,它们应该能非常详尽地表示请求的状态。
+ 更有趣的是,状态实际上是一个枚举状态值(低6位)和一些可能出现的附加
+ 类(似)旗标位(高位)的<quote>位或(bitwise or)</quote>。枚举值会在以后
+ 更详细地讨论。对它们的汇总可以在错误概览节(Errors Summary section)
+ 找到。可能的状态旗标为:</para>
+
+ <itemizedlist>
+
+ <listitem><para><emphasis>CAM_DEV_QFRZN</emphasis> - 当处理CCB时,
+ 如果SIM驱动程序得到一个严重错误(例如,驱动程序不能响应选择或违反
+ 了SCSI协议),它应当调用<function>xpt_freeze_simq()</function>冻结
+ 请求队列,把此设备的其他已入队但尚未被处理的CCB返回到CAM队列,
+ 然后为有问题的CCB设置这个旗标并调用
+ <function>xpt_done()</function>。这个旗标会使得CAM子系统处理错误后
+ 解冻队列。</para></listitem>
+
+ <listitem><para><emphasis>CAM_AUTOSNS_VALID</emphasis> - 如果设备
+ 返回错误条件,且CCB中未设置旗标CAM_DIS_AUTOSENSE,SIM驱动程序
+ 必须自动执行REQUEST SENSE命令来从设备抽取sense(扩展错误信息)
+ 数据。如果这个尝试成功,sense数据应当被保存在CCB中且设置此旗标。
+ </para></listitem>
+
+ <listitem><para><emphasis>CAM_RELEASE_SIMQ</emphasis> - 类似于
+ CAM_DEV_QFRZN,但用于SCSI控制器自身出问题(或资源短缺)的情况。
+ 此后对控制器的所有请求会被<function>xpt_freeze_simq()</function>
+ 停止。SIM驱动程序克服这种短缺情况,并通过返回设置了此旗标的CCB
+ 通知CAM后,控制器队列将会被重新启动。</para></listitem>
+
+ <listitem><para><emphasis>CAM_SIM_QUEUED</emphasis> - 当SIM将一个
+ CCB放入其请求队列时应当设置此旗标(或当CCB出队但尚未返回给CAM时
+ 去掉)。现在此旗标还没有在CAM代码的任何地方使用过,因此其目的
+ 纯粹用于诊断)。</para></listitem>
+
+ </itemizedlist>
+
+ <para>函数<function>xxx_action()</function>不允许睡眠,因此对资源
+ 访问的所有同步必须通过冻结SIM或设备队列来完成。除了前述的旗标外,
+ CAM子系统提供了函数<function>xpt_release_simq()</function>和
+ <function>xpt_release_devq()</function>来直接解冻队列,而不必将
+ CCB传递到CAM。</para>
+
+ <para>CCB头部包含如下字段:</para>
+
+ <itemizedlist>
+
+ <listitem><para><emphasis>path</emphasis> - 请求的路径ID
+ </para></listitem>
+
+ <listitem><para><emphasis>target_id</emphasis> - 请求的目标设备ID
+ </para></listitem>
+
+ <listitem><para><emphasis>target_lun</emphasis> - 目标设备的LUN ID
+ </para></listitem>
+
+ <listitem><para><emphasis>timeout</emphasis> -
+ 这个命令的超时间隔,以毫秒计</para></listitem>
+
+ <listitem><para><emphasis>timeout_ch</emphasis> - 一个为SIM驱动
+ 程序存储超时处理函数的方便之所(CAM子系统自身并不对此作任何假设)
+ </para></listitem>
+
+ <listitem><para><emphasis>flags</emphasis> - 有关请求的各个
+ 信息位</para></listitem>
+
+ <listitem><para><emphasis>spriv_ptr0,spriv_ptr1</emphasis> -
+ SIM驱动程序保留私用的字段
+ (例如链接到SIM队列或SIM私有控制块);实际上,它们作为联合存在:
+ spriv_ptr0和spriv_ptr1具有类型(void *),spriv_field0和
+ spriv_field1具有类型unsigned long,sim_priv.entries[0].bytes和
+ sim_priv.entries[1].bytes为与联合的其他形式大小一致的字节数组,
+ sim_priv.bytes为一个两倍大小的数组</para></listitem>
+
+ </itemizedlist>
+
+ <para>使用CCB的SIM私有字段的建议方法是为它们定义一些有意义的名字,
+ 并且在驱动程序中使用这些有意义的名字,就像下面这样:</para>
+
+<programlisting>#define ccb_some_meaningful_name sim_priv.entries[0].bytes
+#define ccb_hcb spriv_ptr1 /* 用于硬件控制块 */</programlisting>
+
+ <para>最常见的发起者模式的请求是:</para>
+ <itemizedlist>
+ <listitem><para><emphasis>XPT_SCSI_IO</emphasis> - 执行I/O事务
+ </para>
+
+ <para>联合ccb的<quote>struct ccb_scsiio csio</quote>实例用于传递参量。
+ 它们是:</para>
+
+ <itemizedlist>
+ <listitem><para><emphasis>cdb_io</emphasis> -
+ 指向SCSI命令缓冲区的指针或缓冲区本身</para></listitem>
+
+ <listitem><para><emphasis>cdb_len</emphasis> -
+ SCSI命令长度</para></listitem>
+
+ <listitem><para><emphasis>data_ptr</emphasis> -
+ 指向数据缓冲区的指针(如果使用分散/集中会复杂一点)
+ </para></listitem>
+
+ <listitem>