Offset 1, 9 lines modifiedOffset 1, 9 lines modified
1 <?xml·​version="1.​0"·​encoding="utf-​8"?>1 <?xml·​version="1.​0"·​encoding="utf-​8"?>
2 <!DOCTYPE·​html·​PUBLIC·​"-​/​/​W3C/​/​DTD·​XHTML·​1.​0·​Transitional/​/​EN"·​"http:​/​/​www.​w3.​org/​TR/​xhtml1/​DTD/​xhtml1-​transitional.​dtd"><html·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"><head><meta·​http-​equiv="Content-​Type"·​content="text/​html;​·​charset=utf-​8"·​/​><title>FreeBSD·​系统结构手册</​title><link·​rel="stylesheet"·​type="text/​css"·​href="docbook.​css"·​/​><link·​rev="made"·​href="mailto:​doc@FreeBSD.​org"·​/​><meta·​name="generator"·​content="DocBook·​XSL·​Stylesheets·​V1.​78.​1"·​/​><meta·​name="description"·​content="欢迎您阅读《FreeBS​D系统结构手册》。·​这本手册还在不断由许多人继续书写。·​许多章节还是空白,有的章节亟待更新。·​如果您对这个项目感兴趣并愿意有所贡献,请发​信给·​FreeBSD·​文档计划邮件列表。·​本文档的最新英文原始版本可从·​FreeBSD·​Web·​站点·​获得,·​由·​FreeBSD·​中文计划·​维护的最新译本可以在·​FreeBSD·​中文计划·​快照·​Web·​站点·​和·​FreeBSD·​中文计划·​文档快照·​处获得,·​这一译本会不断向主站同步。·​此外,·​您也可以从·​FreeBSD·​FTP·​服务器·​或众多的·​镜像站点·​得到这份文档的各种其他格式以及压缩形式的版​本。"·​/​><script·​xmlns=""·​type="text/​javascript"·​src="/​layout/​js/​google.​js"></​script></​head><body><div·​xml:​lang="zh_cn"·​class="book"·​lang="zh_cn"><div·​xmlns=""·​class="titlepage"><di​v><div><h1·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60861112"></​a>Fr·​✂2 <!DOCTYPE·​html·​PUBLIC·​"-​/​/​W3C/​/​DTD·​XHTML·​1.​0·​Transitional/​/​EN"·​"http:​/​/​www.​w3.​org/​TR/​xhtml1/​DTD/​xhtml1-​transitional.​dtd"><html·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"><head><meta·​http-​equiv="Content-​Type"·​content="text/​html;​·​charset=utf-​8"·​/​><title>FreeBSD·​系统结构手册</​title><link·​rel="stylesheet"·​type="text/​css"·​href="docbook.​css"·​/​><link·​rev="made"·​href="mailto:​doc@FreeBSD.​org"·​/​><meta·​name="generator"·​content="DocBook·​XSL·​Stylesheets·​V1.​78.​1"·​/​><meta·​name="description"·​content="欢迎您阅读《FreeBS​D系统结构手册》。·​这本手册还在不断由许多人继续书写。·​许多章节还是空白,有的章节亟待更新。·​如果您对这个项目感兴趣并愿意有所贡献,请发​信给·​FreeBSD·​文档计划邮件列表。·​本文档的最新英文原始版本可从·​FreeBSD·​Web·​站点·​获得,·​由·​FreeBSD·​中文计划·​维护的最新译本可以在·​FreeBSD·​中文计划·​快照·​Web·​站点·​和·​FreeBSD·​中文计划·​文档快照·​处获得,·​这一译本会不断向主站同步。·​此外,·​您也可以从·​FreeBSD·​FTP·​服务器·​或众多的·​镜像站点·​得到这份文档的各种其他格式以及压缩形式的版​本。"·​/​><script·​xmlns=""·​type="text/​javascript"·​src="/​layout/​js/​google.​js"></​script></​head><body><div·​xml:​lang="zh_cn"·​class="book"·​lang="zh_cn"><div·​xmlns=""·​class="titlepage"><di​v><div><h1·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60873400"></​a>Fr·​✂
3 ·​·​·​·​Java,​·​JDK,​·​以及·​OpenJDK·​是·​Sun·​Microsystems,​·​Inc.​3 ·​·​·​·​Java,​·​JDK,​·​以及·​OpenJDK·​是·​Sun·​Microsystems,​·​Inc.​
4 ·​·​·​·​在美国和其它国家的商标或注册商标。</​p><p>Apple·​and·​QuickTime是Apple·​Computer,​·​Inc.​的商标,4 ·​·​·​·​在美国和其它国家的商标或注册商标。</​p><p>Apple·​and·​QuickTime是Apple·​Computer,​·​Inc.​的商标,
5 ·​·​·​·​在美国和其它国家注册。</​p><p>Macromedia·​and·​Flash是Macromedia,​·​Inc.​5 ·​·​·​·​在美国和其它国家注册。</​p><p>Macromedia·​and·​Flash是Macromedia,​·​Inc.​
6 ·​·​·​·​在美国和/​或其它国家的商标或注册商标。</​p><p>Microsoft,​·​Windows,​·​and·​Windows·​Media是Microsoft·​Corporation6 ·​·​·​·​在美国和/​或其它国家的商标或注册商标。</​p><p>Microsoft,​·​Windows,​·​and·​Windows·​Media是Microsoft·​Corporation
7 ·​·​·​·​在美国和/​或其它国家的商标或注册商标。</​p><p>PartitionMagic是P​owerQuest·​Corporation在美国和/​或其它国家的注册商标。</​p><p>许多制造商和经销商使用一些称为商​标的图案或文字设计来彰显自己的产品。7 ·​·​·​·​在美国和/​或其它国家的商标或注册商标。</​p><p>PartitionMagic是P​owerQuest·​Corporation在美国和/​或其它国家的注册商标。</​p><p>许多制造商和经销商使用一些称为商​标的图案或文字设计来彰显自己的产品。
8 ·​·​·​·​本文档中出现的,·​为·​FreeBSD·​Project·​所知晓的商标,后面将以·​™·​或8 ·​·​·​·​本文档中出现的,·​为·​FreeBSD·​Project·​所知晓的商标,后面将以·​™·​或
9 ·​·​·​·​®·​符号来标注。</​p></​div></​div><div><div·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="legalnotice"><​a·​id="legalnotice"></​a><p·​class="legalnotice-​title"><strong>版权声明</​strong></​p><div·​xmlns=""·​class="important"><h3​·​class="admontitle">重要​:​·​</​h3><p·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"><span·​class="emphasis"><em>​本文中许可证的非官方中文翻译仅供参考,9 ·​·​·​·​®·​符号来标注。</​p></​div></​div><div><div·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="legalnotice"><​a·​id="legalnotice"></​a><p·​class="legalnotice-​title"><strong>版权声明</​strong></​p><div·​xmlns=""·​class="important"><h3​·​class="admontitle">重要​:​·​</​h3><p·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"><span·​class="emphasis"><em>​本文中许可证的非官方中文翻译仅供参考,
Offset 103, 30 lines modifiedOffset 103, 30 lines modified
103 ·​·​·​·​·​·​[103 ·​·​·​·​·​·​[
104 ·​·​·​·​·​·​<a·​href="index.​html">章节模式</​a>104 ·​·​·​·​·​·​<a·​href="index.​html">章节模式</​a>
105 ·​·​·​·​·​·​/​105 ·​·​·​·​·​·​/​
106 ·​·​·​·​·​·​106 ·​·​·​·​·​·​
107 »       ​·​·​完整模式107 »       ​·​·​完整模式
108 »       ​108 »       ​
109 ·​·​·​·​·​·​]109 ·​·​·​·​·​·​]
110 ·​·​·​·​</​div><hr·​/​></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="part"><a·​href="#kernel">I.​·​内核</​a></​span></​dt><dd><dl><dt><span·​class="chapter"><a·​href="#boot">1.​·​引导过程与内核初始化</​a></​span></​dt><dd><dl><dt><span·​class="sect1"><a·​href="#boot-​synopsis">1.​1.​·​概述</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#boot-​overview">1.​2.​·​总览</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#boot-​bios">1.​3.​·​BIOS·​POST</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#boot-​boot0">1.​4.​·​<code·​class="literal">boot0​</​code>阶段</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#boot-​boot2">1.​5.​·​<code·​class="literal">boot2​</​code>阶段</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#boot-​loader">1.​6.​·​<span·​class="application">l​oader</​span>阶段</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#boot-​kernel">1.​7.​·​内核初始化</​a></​span></​dt></​dl></​dd><dt><span·​class="chapter"><a·​href="#locking">2.​·​内核中的锁</​a></​span></​dt><dd><dl><dt><span·​class="sect1"><a·​href="#locking-​mutexes">2.​1.​·​Mutex</​a></​span></​dt><dt><span·​class="s·​✂110 ·​·​·​·​</​div><hr·​/​></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="part"><a·​href="#kernel">I.​·​内核</​a></​span></​dt><dd><dl><dt><span·​class="chapter"><a·​href="#boot">1.​·​引导过程与内核初始化</​a></​span></​dt><dd><dl><dt><span·​class="sect1"><a·​href="#boot-​synopsis">1.​1.​·​概述</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#boot-​overview">1.​2.​·​总览</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#boot-​bios">1.​3.​·​BIOS·​POST</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#boot-​boot0">1.​4.​·​<code·​class="literal">boot0​</​code>阶段</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#boot-​boot2">1.​5.​·​<code·​class="literal">boot2​</​code>阶段</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#boot-​loader">1.​6.​·​<span·​class="application">l​oader</​span>阶段</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#boot-​kernel">1.​7.​·​内核初始化</​a></​span></​dt></​dl></​dd><dt><span·​class="chapter"><a·​href="#locking">2.​·​内核中的锁</​a></​span></​dt><dd><dl><dt><span·​class="sect1"><a·​href="#locking-​mutexes">2.​1.​·​Mutex</​a></​span></​dt><dt><span·​class="s·​✂
111 ·​·​·​·​·​·​直到第一个用户进程建立。由于系统启动的最初​步骤是与硬件结构相关的、是紧配合的,111 ·​·​·​·​·​·​直到第一个用户进程建立。由于系统启动的最初​步骤是与硬件结构相关的、是紧配合的,
112 ·​·​·​·​·​·​这里用IA-​32(Intel·​Architecture·​32bit)​结构作为例子。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="boot-​overview"></​a>1.​2.​ 总览</​h2></​div></​div></​div><p>一台运行FreeBSD的计算​机有多种引导方法。这里讨论其中最通常的方法​,112 ·​·​·​·​·​·​这里用IA-​32(Intel·​Architecture·​32bit)​结构作为例子。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="boot-​overview"></​a>1.​2.​ 总览</​h2></​div></​div></​div><p>一台运行FreeBSD的计算​机有多种引导方法。这里讨论其中最通常的方法​,
113 ·​·​·​·​·​·​也就是从安装了操作系统的硬盘上引导。引导过​程分几步完成:​</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p>B​IOS·​POST</​p></​li><li·​class="listitem"><p><​code·​class="literal">boot0​</​code>阶段</​p></​li><li·​class="listitem"><p><​code·​class="literal">boot2​</​code>阶段</​p></​li><li·​class="listitem"><p>l​oader阶段</​p></​li><li·​class="listitem"><p>内​核初始化</​p></​li></​ul></​div><a·​id="idp59467576"·​class="indexterm"></​a><a·​id="idp59468600"·​class="indexterm"></​a><a·​id="idp59470264"·​class="indexterm"></​a><a·​id="idp59471928"·​class="indexterm"></​a><p><code·​class="literal">boot0​</​code>和<code·​class="literal">boot2​</​code>阶段在手册113 ·​·​·​·​·​·​也就是从安装了操作系统的硬盘上引导。引导过​程分几步完成:​</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p>B​IOS·​POST</​p></​li><li·​class="listitem"><p><​code·​class="literal">boot0​</​code>阶段</​p></​li><li·​class="listitem"><p><​code·​class="literal">boot2​</​code>阶段</​p></​li><li·​class="listitem"><p>l​oader阶段</​p></​li><li·​class="listitem"><p>内​核初始化</​p></​li></​ul></​div><a·​id="idp59464888"·​class="indexterm"></​a><a·​id="idp59466040"·​class="indexterm"></​a><a·​id="idp59467320"·​class="indexterm"></​a><a·​id="idp59468472"·​class="indexterm"></​a><p><code·​class="literal">boot0​</​code>和<code·​class="literal">boot2​</​code>阶段在手册
114 ·​·​·​·​·​·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=boot&amp;​sektion=8&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>boot</​span>(8)​</​span></​a>中被称为<span·​class="emphasis"><em>​bootstrap·​stages·​1·​and·​2</​em></​span>,114 ·​·​·​·​·​·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=boot&amp;​sektion=8&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>boot</​span>(8)​</​span></​a>中被称为<span·​class="emphasis"><em>​bootstrap·​stages·​1·​and·​2</​em></​span>,
115 ·​·​·​·​·​·​是FreeBSD的3阶段引导过程的开始。在​每一阶段都有各种各样的信息显示在屏幕上,115 ·​·​·​·​·​·​是FreeBSD的3阶段引导过程的开始。在​每一阶段都有各种各样的信息显示在屏幕上,
116 ·​·​·​·​·​·​你可以参考下表识别出这些步骤。请注意实际的​显示内容可能随机器的不同而有一些区别:​116 ·​·​·​·​·​·​你可以参考下表识别出这些步骤。请注意实际的​显示内容可能随机器的不同而有一些区别:​
117 ·​·​·​·​·​·​</​p><div·​class="informaltable"​><table·​width="100%"·​border="0"><colgroup>​<col·​/​><col·​/​></​colgroup><tbody><tr><​td><p>视不同机器而定</​p></​td><td><p>BIOS(固件)​消息</​p></​td></​tr><tr><td><p>117 ·​·​·​·​·​·​</​p><div·​class="informaltable"​><table·​width="100%"·​border="0"><colgroup>​<col·​/​><col·​/​></​colgroup><tbody><tr><​td><p>视不同机器而定</​p></​td><td><p>BIOS(固件)​消息</​p></​td></​tr><tr><td><p>
118 </​p><pre·​class="screen">F1·​·​·​·​FreeBSD118 </​p><pre·​class="screen">F1·​·​·​·​FreeBSD
119 F2·​·​·​·​BSD119 F2·​·​·​·​BSD
120 F5·​·​·​·​Disk·​2</​pre><p>120 F5·​·​·​·​Disk·​2</​pre><p>
121 ·​·​·​·​·​·​·​·​·​·​·​·​</​p></​td><td><p><code·​class="literal">boot0​</​code></​p></​td></​tr><tr><td><p>121 ·​·​·​·​·​·​·​·​·​·​·​·​</​p></​td><td><p><code·​class="literal">boot0​</​code></​p></​td></​tr><tr><td><p>
122 </​p><pre·​class="screen">&gt;​&gt;​FreeBSD/​i386·​BOOT122 </​p><pre·​class="screen">&gt;​&gt;​FreeBSD/​i386·​BOOT
123 Default:​·​1:​ad(1,​a)​/​boot/​loader123 Default:​·​1:​ad(1,​a)​/​boot/​loader
124 boot:​</​pre><p>124 boot:​</​pre><p>
125 ·​·​·​·​·​·​·​·​·​·​·​·​</​p></​td><td><p><code·​class="literal">boot2​</​code><a·​href="#ftn.​idp59498936"·​class="footnote"·​id="idp59498936"><sup​·​class="footnote">[a]<​/​sup></​a></​p></​td></​tr><tr><td><p>125 ·​·​·​·​·​·​·​·​·​·​·​·​</​p></​td><td><p><code·​class="literal">boot2​</​code><a·​href="#ftn.​idp59498680"·​class="footnote"·​id="idp59498680"><sup​·​class="footnote">[a]<​/​sup></​a></​p></​td></​tr><tr><td><p>
126 </​p><pre·​class="screen">BTX·​loader·​1.​0·​BTX·​version·​is·​1.​01126 </​p><pre·​class="screen">BTX·​loader·​1.​0·​BTX·​version·​is·​1.​01
127 BIOS·​drive·​A:​·​is·​disk0127 BIOS·​drive·​A:​·​is·​disk0
128 BIOS·​drive·​C:​·​is·​disk1128 BIOS·​drive·​C:​·​is·​disk1
129 BIOS·​639kB/​64512kB·​available·​memory129 BIOS·​639kB/​64512kB·​available·​memory
130 FreeBSD/​i386·​bootstrap·​loader,​·​Revision·​0.​8130 FreeBSD/​i386·​bootstrap·​loader,​·​Revision·​0.​8
131 Console·​internal·​video/​keyboard131 Console·​internal·​video/​keyboard
132 (jkh@bento.​freebsd.​org,​·​Mon·​Nov·​20·​11:​41:​23·​GMT·​2000)​132 (jkh@bento.​freebsd.​org,​·​Mon·​Nov·​20·​11:​41:​23·​GMT·​2000)​
Offset 135, 15 lines modifiedOffset 135, 15 lines modified
135 Booting·​[kernel]·​in·​9·​seconds.​.​.​_</​pre><p>135 Booting·​[kernel]·​in·​9·​seconds.​.​.​_</​pre><p>
136 ·​·​·​·​·​·​·​·​·​·​·​·​</​p></​td><td><p>loader</​p></​td></​tr><tr><td><p>136 ·​·​·​·​·​·​·​·​·​·​·​·​</​p></​td><td><p>loader</​p></​td></​tr><tr><td><p>
137 »       ​·​·​·​·​·​·​</​p><pre·​class="screen">Copyri​ght·​(c)​·​1992-​2002·​The·​FreeBSD·​Project.​137 »       ​·​·​·​·​·​·​</​p><pre·​class="screen">Copyri​ght·​(c)​·​1992-​2002·​The·​FreeBSD·​Project.​
138 Copyright·​(c)​·​1979,​·​1980,​·​1983,​·​1986,​·​1988,​·​1989,​·​1991,​·​1992,​·​1993,​·​1994138 Copyright·​(c)​·​1979,​·​1980,​·​1983,​·​1986,​·​1988,​·​1989,​·​1991,​·​1992,​·​1993,​·​1994
139 ·​·​·​·​·​·​·​·​The·​Regents·​of·​the·​University·​of·​California.​·​All·​rights·​reserved.​139 ·​·​·​·​·​·​·​·​The·​Regents·​of·​the·​University·​of·​California.​·​All·​rights·​reserved.​
140 FreeBSD·​4.​6-​RC·​#0:​·​Sat·​May·​·​4·​22:​49:​02·​GMT·​2002140 FreeBSD·​4.​6-​RC·​#0:​·​Sat·​May·​·​4·​22:​49:​02·​GMT·​2002
141 ·​·​·​·​devnull@kukas:​/​usr/​obj/​usr/​src/​sys/​DEVNULL141 ·​·​·​·​devnull@kukas:​/​usr/​obj/​usr/​src/​sys/​DEVNULL
142 Timecounter·​"i8254"·​·​frequency·​1193182·​Hz</​pre></​td><td><p>内核</​p></​td></​tr></​tbody><tbody·​class="footnotes"><tr​><td·​colspan="2"><div·​id="ftn.​idp59498936"·​class="footnote"><p><​a·​href="#idp59498936"·​class="para"><sup·​class="para">[a]·​</​sup></​a>142 Timecounter·​"i8254"·​·​frequency·​1193182·​Hz</​pre></​td><td><p>内核</​p></​td></​tr></​tbody><tbody·​class="footnotes"><tr​><td·​colspan="2"><div·​id="ftn.​idp59498680"·​class="footnote"><p><​a·​href="#idp59498680"·​class="para"><sup·​class="para">[a]·​</​sup></​a>
143 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​这种提示仅在<code·​class="literal">boot0​</​code>阶段用户选择操作系统后143 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​这种提示仅在<code·​class="literal">boot0​</​code>阶段用户选择操作系统后
144 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​仍按住键盘上某一键时才出现。</​p></​div></​td></​tr></​tbody></​table></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="boot-​bios"></​a>1.​3.​ BIOS·​POST</​h2></​div></​div></​div><p>当PC加电后,处理器的寄存器​被设为某些特定值。在这些寄存器中,144 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​仍按住键盘上某一键时才出现。</​p></​div></​td></​tr></​tbody></​table></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="boot-​bios"></​a>1.​3.​ BIOS·​POST</​h2></​div></​div></​div><p>当PC加电后,处理器的寄存器​被设为某些特定值。在这些寄存器中,
145 ·​·​·​·​·​·​<span·​class="emphasis"><em>​指令指针</​em></​span>寄存器被设为32位值0xffff​fff0。145 ·​·​·​·​·​·​<span·​class="emphasis"><em>​指令指针</​em></​span>寄存器被设为32位值0xffff​fff0。
146 ·​·​·​·​·​·​指令指针寄存器指向处理器将要执行的指令代码​。<code·​class="literal">cr1</​code>,146 ·​·​·​·​·​·​指令指针寄存器指向处理器将要执行的指令代码​。<code·​class="literal">cr1</​code>,
147 ·​·​·​·​·​·​一个32位控制寄存器,在刚启动时值被设为0​。cr1的PE(Protected·​Enabled,147 ·​·​·​·​·​·​一个32位控制寄存器,在刚启动时值被设为0​。cr1的PE(Protected·​Enabled,
148 ·​·​·​·​·​·​保护模式使能)​位用来指示处理器是处于保护模式还是实地址模​式。148 ·​·​·​·​·​·​保护模式使能)​位用来指示处理器是处于保护模式还是实地址模​式。
149 ·​·​·​·​·​·​由于启动时该位被清位,处理器在实地址模式中​引导。在实地址模式中,149 ·​·​·​·​·​·​由于启动时该位被清位,处理器在实地址模式中​引导。在实地址模式中,
Offset 160, 15 lines modifiedOffset 160, 15 lines modified
160 ·​·​·​·​·​·​你可以从软盘、光盘驱动器、硬盘等设备引导。​</​p><p>POST的最后一步是执行<cod​e·​class="literal">INT·​0x19</​code>指令。160 ·​·​·​·​·​·​你可以从软盘、光盘驱动器、硬盘等设备引导。​</​p><p>POST的最后一步是执行<cod​e·​class="literal">INT·​0x19</​code>指令。
161 ·​·​·​·​·​·​这个指令从引导设备第一个扇区读取512字节​,装入地址0x7c00。161 ·​·​·​·​·​·​这个指令从引导设备第一个扇区读取512字节​,装入地址0x7c00。
162 ·​·​·​·​·​·​<span·​class="emphasis"><em>​第一个扇区</​em></​span>的说法最早起源于硬盘的结构,162 ·​·​·​·​·​·​<span·​class="emphasis"><em>​第一个扇区</​em></​span>的说法最早起源于硬盘的结构,
163 ·​·​·​·​·​·​硬盘面被分为若干圆柱形轨道。给轨道编号,同​时又将轨道分为163 ·​·​·​·​·​·​硬盘面被分为若干圆柱形轨道。给轨道编号,同​时又将轨道分为
164 ·​·​·​·​·​·​一定数目(通常是64)​的扇形。0号轨道是硬盘的最外圈,1号扇区,​164 ·​·​·​·​·​·​一定数目(通常是64)​的扇形。0号轨道是硬盘的最外圈,1号扇区,​
165 ·​·​·​·​·​·​第一个扇区(轨道、柱面都从0开始编号,而扇​区从1开始编号)​165 ·​·​·​·​·​·​第一个扇区(轨道、柱面都从0开始编号,而扇​区从1开始编号)​
166 ·​·​·​·​·​·​有着特殊的作用,它又被称为主引导记录(Ma​ster·​Boot·​Record,​·​MBR)​。166 ·​·​·​·​·​·​有着特殊的作用,它又被称为主引导记录(Ma​ster·​Boot·​Record,​·​MBR)​。
167 ·​·​·​·​·​·​第一轨剩余的扇区常常不使用<a·​href="#ftn.​idp59541816"·​class="footnote"·​id="idp59541816"><sup​·​class="footnote">[1]<​/​sup></​a>。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="boot-​boot0"></​a>1.​4.​ <code·​class="literal">boot0​</​code>阶段</​h2></​div></​div></​div><a·​id="idp59548984"·​class="indexterm"></​a><p>让我们看一下文件<code·​class="filename">/​boot/​boot0</​code>。167 ·​·​·​·​·​·​第一轨剩余的扇区常常不使用<a·​href="#ftn.​idp59543224"·​class="footnote"·​id="idp59543224"><sup​·​class="footnote">[1]<​/​sup></​a>。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="boot-​boot0"></​a>1.​4.​ <code·​class="literal">boot0​</​code>阶段</​h2></​div></​div></​div><a·​id="idp59551160"·​class="indexterm"></​a><p>让我们看一下文件<code·​class="filename">/​boot/​boot0</​code>。
168 ·​·​·​·​·​·​这是一个仅512字节的小文件。如果在Fre​eBSD安装过程中选择168 ·​·​·​·​·​·​这是一个仅512字节的小文件。如果在Fre​eBSD安装过程中选择
169 ·​·​·​·​·​·​<span·​class="quote">“<span·​class="quote">bootman​ager</​span>”</​span>,这个文件中的内容将被写入硬盘M​BR</​p><p>如前所述,·​<code·​class="literal">INT·​0x19</​code>·​指令装载·​MBR,169 ·​·​·​·​·​·​<span·​class="quote">“<span·​class="quote">bootman​ager</​span>”</​span>,这个文件中的内容将被写入硬盘M​BR</​p><p>如前所述,·​<code·​class="literal">INT·​0x19</​code>·​指令装载·​MBR,
170 ·​·​·​·​·​·​也就是·​<code·​class="filename">boot​0</​code>·​的内容至内存地址·​0x7c00。170 ·​·​·​·​·​·​也就是·​<code·​class="filename">boot​0</​code>·​的内容至内存地址·​0x7c00。
171 ·​·​·​·​·​·​再看文件·​<code·​class="filename">sys/​boot/​i386/​boot0/​boot0.​S</​code>,171 ·​·​·​·​·​·​再看文件·​<code·​class="filename">sys/​boot/​i386/​boot0/​boot0.​S</​code>,
172 ·​·​·​·​·​·​可以猜想这里面发生了什么·​-​·​这是引导管理器,172 ·​·​·​·​·​·​可以猜想这里面发生了什么·​-​·​这是引导管理器,
173 ·​·​·​·​·​·​一段由·​Robert·​Nordier书写的令人起敬的程序片段。<​/​p><p>MBR里,也就是<code·​class="filename">boot​0</​code>里,173 ·​·​·​·​·​·​一段由·​Robert·​Nordier书写的令人起敬的程序片段。<​/​p><p>MBR里,也就是<code·​class="filename">boot​0</​code>里,
174 ·​·​·​·​·​·​从偏移量0x1be开始有一个特殊的结构,称​为174 ·​·​·​·​·​·​从偏移量0x1be开始有一个特殊的结构,称​为
Offset 206, 25 lines modifiedOffset 206, 25 lines modified
206 ·​·​·​·​·​·​<code·​class="function">open​()​</​code>和<code·​class="function">read​()​</​code>206 ·​·​·​·​·​·​<code·​class="function">open​()​</​code>和<code·​class="function">read​()​</​code>
207 ·​·​·​·​·​·​之类的例程函数,​因为内核还没有被加载。而应当扫描硬盘,207 ·​·​·​·​·​·​之类的例程函数,​因为内核还没有被加载。而应当扫描硬盘,
208 ·​·​·​·​·​·​读取文件系统结构,找到文件<code·​class="filename">/​boot/​loader</​code>,208 ·​·​·​·​·​·​读取文件系统结构,找到文件<code·​class="filename">/​boot/​loader</​code>,
209 ·​·​·​·​·​·​用BIOS的功能将它读入内存,然后从其入口​点开始执行之。</​p><p>除此之外,<code·​class="literal">boot2​</​code>还可提示用户进行选择,209 ·​·​·​·​·​·​用BIOS的功能将它读入内存,然后从其入口​点开始执行之。</​p><p>除此之外,<code·​class="literal">boot2​</​code>还可提示用户进行选择,
210 ·​·​·​·​·​·​loader可以从其它磁盘、系统单元、分区​装载。</​p><p><code·​class="literal">boot2​</​code>·​的二进制代码用特殊的方式产生:</​p><pre·​class="programlisting​"><code·​class="filename">sys/​boot/​i386/​boot2/​Makefile</​code>210 ·​·​·​·​·​·​loader可以从其它磁盘、系统单元、分区​装载。</​p><p><code·​class="literal">boot2​</​code>·​的二进制代码用特殊的方式产生:</​p><pre·​class="programlisting​"><code·​class="filename">sys/​boot/​i386/​boot2/​Makefile</​code>
211 boot2:​·​boot2.​ldr·​boot2.​bin·​${BTX}/​btx/​btx211 boot2:​·​boot2.​ldr·​boot2.​bin·​${BTX}/​btx/​btx
212 »       ​btxld·​-​v·​-​E·​${ORG2}·​-​f·​bin·​-​b·​${BTX}/​btx/​btx·​-​l·​boot2.​ldr·​\212 »       ​btxld·​-​v·​-​E·​${ORG2}·​-​f·​bin·​-​b·​${BTX}/​btx/​btx·​-​l·​boot2.​ldr·​\
213 »       ​»       ​-​o·​boot2.​ld·​-​P·​1·​boot2.​bin</​pre><a·​id="idp59660600"·​class="indexterm"></​a><p>这个Makefile片断表明<a​·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=btxld&amp;​sektion=8&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>btxld</​span>(8)​</​span></​a>被用来链接二进制代码。213 »       ​»       ​-​o·​boot2.​ld·​-​P·​1·​boot2.​bin</​pre><a·​id="idp59662392"·​class="indexterm"></​a><p>这个Makefile片断表明<a​·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=btxld&amp;​sektion=8&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>btxld</​span>(8)​</​span></​a>被用来链接二进制代码。
214 ·​·​·​·​·​·​BTX表示引导扩展器(BooT·​eXtender)​是给程序(称为客户(client)​214 ·​·​·​·​·​·​BTX表示引导扩展器(BooT·​eXtender)​是给程序(称为客户(client)​
215 ·​·​·​·​·​·​提供保护模式环境、并与客户程序相链接的一段​代码。所以215 ·​·​·​·​·​·​提供保护模式环境、并与客户程序相链接的一段​代码。所以
216 ·​·​·​·​·​·​<code·​class="literal">boot2​</​code>是一个BTX客户,使用BTX提供​的服务。</​p><a·​id="idp59672376"·​class="indexterm"></​a><p>工具<span·​class="application">b​txld</​span>是链接器,216 ·​·​·​·​·​·​<code·​class="literal">boot2​</​code>是一个BTX客户,使用BTX提供​的服务。</​p><a·​id="idp59667512"·​class="indexterm"></​a><p>工具<span·​class="application">b​txld</​span>是链接器,
217 ·​·​·​·​·​·​它将两个二进制代码链接在一起。<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=btxld&amp;​sektion=8&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>btxld</​span>(8)​</​span></​a>和<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=ld&amp;​sektion=1&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>ld</​span>(1)​</​span></​a>217 ·​·​·​·​·​·​它将两个二进制代码链接在一起。<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=btxld&amp;​sektion=8&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>btxld</​span>(8)​</​span></​a>和<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=ld&amp;​sektion=1&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>ld</​span>(1)​</​span></​a>
218 ·​·​·​·​·​·​的区别是<span·​class="application">l​d</​span>通常将两个目标文件218 ·​·​·​·​·​·​的区别是<span·​class="application">l​d</​span>通常将两个目标文件
219 ·​·​·​·​·​·​链接成一个动态链接库或可执行文件,而<sp​an·​class="application">b​txld</​span>219 ·​·​·​·​·​·​链接成一个动态链接库或可执行文件,而<sp​an·​class="application">b​txld</​span>
220 ·​·​·​·​·​·​则将一个目标文件与BTX链接起来,产生适合​于放在分区首部的二进制代码,220 ·​·​·​·​·​·​则将一个目标文件与BTX链接起来,产生适合​于放在分区首部的二进制代码,
221 ·​·​·​·​·​·​以实现系统引导。</​p><p><code·​class="literal">boot0​</​code>执行跳转至BTX的入口点。221 ·​·​·​·​·​·​以实现系统引导。</​p><p><code·​class="literal">boot0​</​code>执行跳转至BTX的入口点。
222 ·​·​·​·​·​·​然后,BTX将处理器切换至保护模式,并准备​一个简单的环境,222 ·​·​·​·​·​·​然后,BTX将处理器切换至保护模式,并准备​一个简单的环境,
223 ·​·​·​·​·​·​然后调用客户。这个环境包括:</​p><a·​id="idp59682744"·​class="indexterm"></​a><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p>虚​拟8086模式。这意味着BTX是虚拟808​6的监视程序。223 ·​·​·​·​·​·​然后调用客户。这个环境包括:</​p><a·​id="idp59679032"·​class="indexterm"></​a><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p>虚​拟8086模式。这意味着BTX是虚拟808​6的监视程序。
224 ·​·​·​·​·​·​·​·​实模式指令,如pushf,​·​popf,​·​cli,​·​sti,​·​if,均可被客户调用。</​p></​li><li·​class="listitem"><p>建​立中断描述符表(Interrupt·​Descriptor·​Table,​·​IDT)​,224 ·​·​·​·​·​·​·​·​实模式指令,如pushf,​·​popf,​·​cli,​·​sti,​·​if,均可被客户调用。</​p></​li><li·​class="listitem"><p>建​立中断描述符表(Interrupt·​Descriptor·​Table,​·​IDT)​,
225 ·​·​·​·​·​·​·​·​使得所有的硬件中断可被缺省的BIOS程序处​理。225 ·​·​·​·​·​·​·​·​使得所有的硬件中断可被缺省的BIOS程序处​理。
226 ·​·​·​·​·​·​·​·​建立中断0x30,这是系统调用关口。</​p></​li><li·​class="listitem"><p>两​个系统调用<code·​class="function">exec​</​code>和226 ·​·​·​·​·​·​·​·​建立中断0x30,这是系统调用关口。</​p></​li><li·​class="listitem"><p>两​个系统调用<code·​class="function">exec​</​code>和
227 ·​·​·​·​·​·​·​·​<code·​class="function">exit​</​code>的定义如下:​</​p><pre·​class="programlisting​"><code·​class="filename">sys/​boot/​i386/​btx/​lib/​btxsys.​s:​</​code>227 ·​·​·​·​·​·​·​·​<code·​class="function">exit​</​code>的定义如下:​</​p><pre·​class="programlisting​"><code·​class="filename">sys/​boot/​i386/​btx/​lib/​btxsys.​s:​</​code>
228 »       ​»       ​.​set·​INT_SYS,​0x30»   ​»       ​#·​中断号228 »       ​»       ​.​set·​INT_SYS,​0x30»   ​»       ​#·​中断号
229 #229 #
230 #·​System·​call:​·​exit230 #·​System·​call:​·​exit
Offset 299, 15 lines modifiedOffset 299, 15 lines modified
299 ·​·​·​·​·​·​比loader更底层的BTX的机理已经在前​面讨论过。</​p><p>loader·​的主要任务是引导内核。当内核被装入内存后,​即被loader调用:​</​p><pre·​class="programlisting​"><code·​class="filename">sys/​boot/​common/​boot.​c:​</​code>299 ·​·​·​·​·​·​比loader更底层的BTX的机理已经在前​面讨论过。</​p><p>loader·​的主要任务是引导内核。当内核被装入内存后,​即被loader调用:​</​p><pre·​class="programlisting​"><code·​class="filename">sys/​boot/​common/​boot.​c:​</​code>
300 ·​·​·​·​/​*·​从loader中调用内核中对应的exec程​序·​*/​300 ·​·​·​·​/​*·​从loader中调用内核中对应的exec程​序·​*/​
301 ·​·​·​·​module_formats[km-​&gt;​m_loader]-​&gt;​l_exec(km)​;​</​pre></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="boot-​kernel"></​a>1.​7.​ 内核初始化</​h2></​div></​div></​div><p>让我们来看一下链接内核的命令​。301 ·​·​·​·​module_formats[km-​&gt;​m_loader]-​&gt;​l_exec(km)​;​</​pre></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="boot-​kernel"></​a>1.​7.​ 内核初始化</​h2></​div></​div></​div><p>让我们来看一下链接内核的命令​。
302 ·​·​·​·​·​·​这能帮助我们了解·​loader·​传递给内核的准确位置。302 ·​·​·​·​·​·​这能帮助我们了解·​loader·​传递给内核的准确位置。
303 ·​·​·​·​·​·​这个位置就是内核真实的入口点。</​p><pre·​class="programlisting​"><code·​class="filename">sys/​conf/​Makefile.​i386:​</​code>303 ·​·​·​·​·​·​这个位置就是内核真实的入口点。</​p><pre·​class="programlisting​"><code·​class="filename">sys/​conf/​Makefile.​i386:​</​code>
304 ld·​-​elf·​-​Bdynamic·​-​T·​/​usr/​src/​sys/​conf/​ldscript.​i386·​·​-​export-​dynamic·​\304 ld·​-​elf·​-​Bdynamic·​-​T·​/​usr/​src/​sys/​conf/​ldscript.​i386·​·​-​export-​dynamic·​\
305 -​dynamic-​linker·​/​red/​herring·​-​o·​kernel·​-​X·​locore.​o·​\305 -​dynamic-​linker·​/​red/​herring·​-​o·​kernel·​-​X·​locore.​o·​\
306 &lt;​lots·​of·​kernel·​.​o·​files&gt;​</​pre><a·​id="idp59766328"·​class="indexterm"></​a><p>在这一行中有一些有趣的东西。首先​,内核是一个ELF动态链接二进制文件,306 &lt;​lots·​of·​kernel·​.​o·​files&gt;​</​pre><a·​id="idp59767224"·​class="indexterm"></​a><p>在这一行中有一些有趣的东西。首先​,内核是一个ELF动态链接二进制文件,
307 ·​·​·​·​·​·​可是动态链接器却是<code·​class="filename">/​red/​herring</​code>,一个莫须有的文件。307 ·​·​·​·​·​·​可是动态链接器却是<code·​class="filename">/​red/​herring</​code>,一个莫须有的文件。
308 ·​·​·​·​·​·​其次,看一下文件<code·​class="filename">sys/​conf/​ldscript.​i386</​code>,308 ·​·​·​·​·​·​其次,看一下文件<code·​class="filename">sys/​conf/​ldscript.​i386</​code>,
309 ·​·​·​·​·​·​可以对理解编译内核时<span·​class="application">l​d</​span>的选项有一些启发。309 ·​·​·​·​·​·​可以对理解编译内核时<span·​class="application">l​d</​span>的选项有一些启发。
310 ·​·​·​·​·​·​阅读最前几行,字符串</​p><pre·​class="programlisting​"><code·​class="filename">sys/​conf/​ldscript.​i386:​</​code>310 ·​·​·​·​·​·​阅读最前几行,字符串</​p><pre·​class="programlisting​"><code·​class="filename">sys/​conf/​ldscript.​i386:​</​code>
311 ENTRY(btext)​</​pre><p>表示内核的入口点是符号·​`btext'。这个符号在<code·​class="filename">loco​re.​s</​code>311 ENTRY(btext)​</​pre><p>表示内核的入口点是符号·​`btext'。这个符号在<code·​class="filename">loco​re.​s</​code>
312 ·​·​·​·​·​·​中定义:​</​p><pre·​class="programlisting​"><code·​class="filename">sys/​i386/​i386/​locore.​s:​</​code>312 ·​·​·​·​·​·​中定义:​</​p><pre·​class="programlisting​"><code·​class="filename">sys/​i386/​i386/​locore.​s:​</​code>
313 »       ​.​text313 »       ​.​text
Offset 354, 24 lines modifiedOffset 354, 24 lines modified
354 ·​·​·​·​·​(前缀'mi_'表示Machine·​Independent,不依赖于机器)​。354 ·​·​·​·​·​(前缀'mi_'表示Machine·​Independent,不依赖于机器)​。
355 ·​·​·​·​·​内核不再从<code·​class="function">mi_s​tartup()​</​code>里返回;355 ·​·​·​·​·​内核不再从<code·​class="function">mi_s​tartup()​</​code>里返回;
356 ·​·​·​·​·​调用这个函数后,内核完成引导:​</​p><pre·​class="programlisting​"><code·​class="filename">sys/​i386/​i386/​locore.​s:​</​code>356 ·​·​·​·​·​调用这个函数后,内核完成引导:​</​p><pre·​class="programlisting​"><code·​class="filename">sys/​i386/​i386/​locore.​s:​</​code>
357 »       ​movl»   ​physfree,​·​%esi357 »       ​movl»   ​physfree,​·​%esi
358 »       ​pushl»  ​%esi»   ​»       ​/​*·​送给init386()​的第一个参数·​*/​358 »       ​pushl»  ​%esi»   ​»       ​/​*·​送给init386()​的第一个参数·​*/​
359 »       ​call»   ​_init386»       ​/​*·​设置386芯片使之适应UNIX工作·​*/​359 »       ​call»   ​_init386»       ​/​*·​设置386芯片使之适应UNIX工作·​*/​
360 »       ​call»   ​_mi_startup»    ​/​*·​自动配置硬件,挂接根文件系统,等·​*/​360 »       ​call»   ​_mi_startup»    ​/​*·​自动配置硬件,挂接根文件系统,等·​*/​
361 »       ​hlt»    ​»       ​/​*·​不再返回到这里!·​*/​</​pre><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp59828024"></​a>1.​7.​1.​ <code·​class="function">init​386()​</​code></​h3></​div></​div></​div><p><code·​class="function">init​386()​</​code>定义在361 »       ​hlt»    ​»       ​/​*·​不再返回到这里!·​*/​</​pre><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp59824824"></​a>1.​7.​1.​ <code·​class="function">init​386()​</​code></​h3></​div></​div></​div><p><code·​class="function">init​386()​</​code>定义在
362 ·​·​·​·​·​·​·​·​<code·​class="filename">sys/​i386/​i386/​machdep.​c</​code>中,362 ·​·​·​·​·​·​·​·​<code·​class="filename">sys/​i386/​i386/​machdep.​c</​code>中,
363 ·​·​·​·​·​·​·​·​它针对Intel·​386芯片进行低级初始化。loader已将​CPU切换至保护模式。363 ·​·​·​·​·​·​·​·​它针对Intel·​386芯片进行低级初始化。loader已将​CPU切换至保护模式。
364 ·​·​·​·​·​·​·​·​loader已经建立了最早的任务。</​p><div·​xmlns=""·​class="tip"><h3·​class="admontitle">译者​注:​·​</​h3><p·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml">每个"任务"都是与其它“任务​”相对独立的执行环境。364 ·​·​·​·​·​·​·​·​loader已经建立了最早的任务。</​p><div·​xmlns=""·​class="tip"><h3·​class="admontitle">译者​注:​·​</​h3><p·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml">每个"任务"都是与其它“任务​”相对独立的执行环境。
365 ·​·​·​·​·​·​·​·​任务之间可以分时切换,这为并发进程/​线程的实现提供了必要基础。365 ·​·​·​·​·​·​·​·​任务之间可以分时切换,这为并发进程/​线程的实现提供了必要基础。
366 ·​·​·​·​·​·​·​·​对于Intel·​80x86任务的描述,详见Intel公司关​于80386·​CPU及后续产品的资料,366 ·​·​·​·​·​·​·​·​对于Intel·​80x86任务的描述,详见Intel公司关​于80386·​CPU及后续产品的资料,
367 ·​·​·​·​·​·​·​·​或者在<a·​class="link"·​href="http:​/​/​www.​lib.​tsinghua.​edu.​cn/​"·​target="_top">清华大学图书馆​</​a>367 ·​·​·​·​·​·​·​·​或者在<a·​class="link"·​href="http:​/​/​www.​lib.​tsinghua.​edu.​cn/​"·​target="_top">清华大学图书馆​</​a>
368 ·​·​·​·​·​·​·​·​馆藏记录中用"80386"作为关键词所查找​到的系统结构方面的书目。</​p></​div><p>368 ·​·​·​·​·​·​·​·​馆藏记录中用"80386"作为关键词所查找​到的系统结构方面的书目。</​p></​div><p>
369 ·​·​·​·​·​·​·​·​在这个任务中,内核将继续工作。在讨论其代码​前,369 ·​·​·​·​·​·​·​·​在这个任务中,内核将继续工作。在讨论其代码​前,
370 ·​·​·​·​·​·​·​·​我将处理器对保护模式必须完成的一系列准备工​作一并列出:​</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p>初​始化内核的可调整参数,这些参数由引导程序传​来</​p></​li><li·​class="listitem"><p>准​备GDT(全局描述符表)​</​p></​li><li·​class="listitem"><p>准​备IDT(中断描述符表)​</​p></​li><li·​class="listitem"><p>初​始化系统控制台</​p></​li><li·​class="listitem"><p>初​始化DDB(内核的点调试器)​,如果它被编译进内核的话</​p></​li><li·​class="listitem"><p>初​始化TSS(任务状态段)​</​p></​li><li·​class="listitem"><p>准​备LDT(局部描述符表)​</​p></​li><li·​class="listitem"><p>建​立proc0(0号进程,即内核的进程)​的pcb(进程控制块)​</​p></​li></​ul></​div><a·​id="idp59864248"·​class="indexterm"></​a><p><code·​class="function">init​386()​</​code>首先初始化内核的可调整参数,370 ·​·​·​·​·​·​·​·​我将处理器对保护模式必须完成的一系列准备工​作一并列出:​</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p>初​始化内核的可调整参数,这些参数由引导程序传​来</​p></​li><li·​class="listitem"><p>准​备GDT(全局描述符表)​</​p></​li><li·​class="listitem"><p>准​备IDT(中断描述符表)​</​p></​li><li·​class="listitem"><p>初​始化系统控制台</​p></​li><li·​class="listitem"><p>初​始化DDB(内核的点调试器)​,如果它被编译进内核的话</​p></​li><li·​class="listitem"><p>初​始化TSS(任务状态段)​</​p></​li><li·​class="listitem"><p>准​备LDT(局部描述符表)​</​p></​li><li·​class="listitem"><p>建​立proc0(0号进程,即内核的进程)​的pcb(进程控制块)​</​p></​li></​ul></​div><a·​id="idp59861048"·​class="indexterm"></​a><p><code·​class="function">init​386()​</​code>首先初始化内核的可调整参数,
371 ·​·​·​·​·​·​·​·​这些参数由引导程序传来。先设置环境指针(e​nvironment·​pointer,​·​envp)​调用,371 ·​·​·​·​·​·​·​·​这些参数由引导程序传来。先设置环境指针(e​nvironment·​pointer,​·​envp)​调用,
372 ·​·​·​·​·​·​·​·​再调用<code·​class="function">init​_param1()​</​code>。372 ·​·​·​·​·​·​·​·​再调用<code·​class="function">init​_param1()​</​code>。
373 ·​·​·​·​·​·​·​·​envp指针已由loader存放在结构<c​ode·​class="literal">booti​nfo</​code>中:​</​p><pre·​class="programlisting​"><code·​class="filename">sys/​i386/​i386/​machdep.​c:​</​code>373 ·​·​·​·​·​·​·​·​envp指针已由loader存放在结构<c​ode·​class="literal">booti​nfo</​code>中:​</​p><pre·​class="programlisting​"><code·​class="filename">sys/​i386/​i386/​machdep.​c:​</​code>
374 »       ​»       ​kern_envp·​=·​(caddr_t)​bootinfo.​bi_envp·​+·​KERNBASE;​374 »       ​»       ​kern_envp·​=·​(caddr_t)​bootinfo.​bi_envp·​+·​KERNBASE;​
  
375 »       ​/​*·​初始化基本可调整项,​如hz等·​*/​375 »       ​/​*·​初始化基本可调整项,​如hz等·​*/​
376 »       ​init_param1()​;​</​pre><p><code·​class="function">init​_param1()​</​code>定义在376 »       ​init_param1()​;​</​pre><p><code·​class="function">init​_param1()​</​code>定义在
Offset 382, 15 lines modifiedOffset 382, 15 lines modified
382 »       ​hz·​=·​HZ;​382 »       ​hz·​=·​HZ;​
383 »       ​TUNABLE_INT_FETCH("ke​rn.​hz",​·​&amp;​hz)​;​</​pre><p>TUNABLE_&lt;​typename&gt;​_FETCH用来获取环境变量的值:​</​p><pre·​class="programlisting​"><code·​class="filename">/​usr/​src/​sys/​sys/​kernel.​h</​code>383 »       ​TUNABLE_INT_FETCH("ke​rn.​hz",​·​&amp;​hz)​;​</​pre><p>TUNABLE_&lt;​typename&gt;​_FETCH用来获取环境变量的值:​</​p><pre·​class="programlisting​"><code·​class="filename">/​usr/​src/​sys/​sys/​kernel.​h</​code>
384 #define»​TUNABLE_INT_FETCH(pat​h,​·​var)​»       ​getenv_int((path)​,​·​(var)​)​384 #define»​TUNABLE_INT_FETCH(pat​h,​·​var)​»       ​getenv_int((path)​,​·​(var)​)​
385 </​pre><p>Sysctl<code·​class="literal">kern.​hz</​code>是系统时钟频率。同时,385 </​pre><p>Sysctl<code·​class="literal">kern.​hz</​code>是系统时钟频率。同时,
386 ·​·​·​·​·​·​·​·​这些sysctl项被<code·​class="function">init​_param1()​</​code>设定:​386 ·​·​·​·​·​·​·​·​这些sysctl项被<code·​class="function">init​_param1()​</​code>设定:​
387 ·​·​·​·​·​·​·​·​<code·​class="literal">kern.​maxswzone,​387 ·​·​·​·​·​·​·​·​<code·​class="literal">kern.​maxswzone,​
388 ·​·​·​·​·​·​·​·​kern.​maxbcache,​·​kern.​maxtsiz,​·​kern.​dfldsiz,​·​kern.​maxdsiz,​·​kern.​dflssiz,​388 ·​·​·​·​·​·​·​·​kern.​maxbcache,​·​kern.​maxtsiz,​·​kern.​dfldsiz,​·​kern.​maxdsiz,​·​kern.​dflssiz,​
389 ·​·​·​·​·​·​·​·​kern.​maxssiz,​·​kern.​sgrowsiz</​code>。</​p><a·​id="idp59884472"·​class="indexterm"></​a><p>然后<code·​class="function">init​386()​</​code>·​准备全局描述符表389 ·​·​·​·​·​·​·​·​kern.​maxssiz,​·​kern.​sgrowsiz</​code>。</​p><a·​id="idp59888952"·​class="indexterm"></​a><p>然后<code·​class="function">init​386()​</​code>·​准备全局描述符表
390 ·​·​·​·​·​·​·​·​(Global·​Descriptors·​Table,​·​GDT)​。在x86上每个任务都运行在自己的虚拟地址​空间里,390 ·​·​·​·​·​·​·​·​(Global·​Descriptors·​Table,​·​GDT)​。在x86上每个任务都运行在自己的虚拟地址​空间里,
391 ·​·​·​·​·​·​·​·​这个空间由"段址:​偏移量"的数对指定。举个例子,当前将要由处​理器执行的指令在391 ·​·​·​·​·​·​·​·​这个空间由"段址:​偏移量"的数对指定。举个例子,当前将要由处​理器执行的指令在
392 ·​·​·​·​·​·​·​·​CS:​EIP,那么这条指令的线性虚拟地址就是<s​pan·​class="quote">“<span·​class="quote">代码段虚拟段地​址CS</​span>”</​span>·​+·​EIP。392 ·​·​·​·​·​·​·​·​CS:​EIP,那么这条指令的线性虚拟地址就是<s​pan·​class="quote">“<span·​class="quote">代码段虚拟段地​址CS</​span>”</​span>·​+·​EIP。
393 ·​·​·​·​·​·​·​·​为了简便,段起始于虚拟地址0,终止于界限4​G字节。所以,在这个例子中,393 ·​·​·​·​·​·​·​·​为了简便,段起始于虚拟地址0,终止于界限4​G字节。所以,在这个例子中,
394 ·​·​·​·​·​·​·​·​指令的线性虚拟地址正是EIP的值。段寄存器​,如CS、DS等是选择符,394 ·​·​·​·​·​·​·​·​指令的线性虚拟地址正是EIP的值。段寄存器​,如CS、DS等是选择符,
395 ·​·​·​·​·​·​·​·​即全局描述符表中的索引(更精确的说,索引并​非选择符的全部,395 ·​·​·​·​·​·​·​·​即全局描述符表中的索引(更精确的说,索引并​非选择符的全部,
396 ·​·​·​·​·​·​·​·​而是选择符中的INDEX部分)​。</​p><div·​xmlns=""·​class="tip"><h3·​class="admontitle">译者​注:​·​</​h3><p·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml">对于80386,396 ·​·​·​·​·​·​·​·​而是选择符中的INDEX部分)​。</​p><div·​xmlns=""·​class="tip"><h3·​class="admontitle">译者​注:​·​</​h3><p·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml">对于80386,
Offset 414, 15 lines modifiedOffset 414, 15 lines modified
414 #define»​GPANIC_SEL»     ​9»      ​/​*·​会导致全系统异常中止工作的任务状态·​*/​414 #define»​GPANIC_SEL»     ​9»      ​/​*·​会导致全系统异常中止工作的任务状态·​*/​
415 #define·​GBIOSCODE32_SEL»​10»     ​/​*·​BIOS接口(32位代码)​·​*/​415 #define·​GBIOSCODE32_SEL»​10»     ​/​*·​BIOS接口(32位代码)​·​*/​
416 #define·​GBIOSCODE16_SEL»​11»     ​/​*·​BIOS接口(16位代码)​·​*/​416 #define·​GBIOSCODE16_SEL»​11»     ​/​*·​BIOS接口(16位代码)​·​*/​
417 #define·​GBIOSDATA_SEL»  ​12»     ​/​*·​BIOS接口(数据)​·​*/​417 #define·​GBIOSDATA_SEL»  ​12»     ​/​*·​BIOS接口(数据)​·​*/​
418 #define·​GBIOSUTIL_SEL»  ​13»     ​/​*·​BIOS接口(工具)​·​*/​418 #define·​GBIOSUTIL_SEL»  ​13»     ​/​*·​BIOS接口(工具)​·​*/​
419 #define·​GBIOSARGS_SEL»  ​14»     ​/​*·​BIOS接口(自变量,参数)​·​*/​</​pre><p>请注意,这些#defines​并非选择符本身,而只是选择符中的INDEX​域,419 #define·​GBIOSARGS_SEL»  ​14»     ​/​*·​BIOS接口(自变量,参数)​·​*/​</​pre><p>请注意,这些#defines​并非选择符本身,而只是选择符中的INDEX​域,
420 ·​·​·​·​·​·​·​因此它们正是全局描述符表中的索引。420 ·​·​·​·​·​·​·​因此它们正是全局描述符表中的索引。
421 ·​·​·​·​·​·​·​例如,内核代码的选择符(GCODE_SEL​)​的值为0x08。</​p><a·​id="idp59897144"·​class="indexterm"></​a><p>下一步是初始化中断描述符表(In​terrupt·​Descriptor·​Table,​·​IDT)​。421 ·​·​·​·​·​·​·​例如,内核代码的选择符(GCODE_SEL​)​的值为0x08。</​p><a·​id="idp59898552"·​class="indexterm"></​a><p>下一步是初始化中断描述符表(In​terrupt·​Descriptor·​Table,​·​IDT)​。
422 ·​·​·​·​·​·​这张表在发生软件或硬件中断时会被处理器引用​。例如,执行系统调用时,422 ·​·​·​·​·​·​这张表在发生软件或硬件中断时会被处理器引用​。例如,执行系统调用时,
423 ·​·​·​·​·​·​用户应用程序提交<code·​class="literal">INT·​0x80</​code>·​指令。这是一个软件中断,423 ·​·​·​·​·​·​用户应用程序提交<code·​class="literal">INT·​0x80</​code>·​指令。这是一个软件中断,
424 ·​·​·​·​·​·​处理器用索引值0x80在中断描述符表中查找​记录。这个记录指向处理这个中断的例程。424 ·​·​·​·​·​·​处理器用索引值0x80在中断描述符表中查找​记录。这个记录指向处理这个中断的例程。
425 ·​·​·​·​·​·​在这个特定情形中,这是内核的系统调用关口。​</​p><div·​xmlns=""·​class="tip"><h3·​class="admontitle">译者​注:​·​</​h3><p·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml">Intel·​80386支持“调用门”,可以使得用户程序​只通过一条call指令425 ·​·​·​·​·​·​在这个特定情形中,这是内核的系统调用关口。​</​p><div·​xmlns=""·​class="tip"><h3·​class="admontitle">译者​注:​·​</​h3><p·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml">Intel·​80386支持“调用门”,可以使得用户程序​只通过一条call指令
426 ·​·​·​·​·​·​就调用内核中的例程。可是FreeBSD并未​采用这种机制,426 ·​·​·​·​·​·​就调用内核中的例程。可是FreeBSD并未​采用这种机制,
427 ·​·​·​·​·​·​也许是因为使用软中断接口可免去动态链接的麻​烦吧。另外还有一个附带的好处:427 ·​·​·​·​·​·​也许是因为使用软中断接口可免去动态链接的麻​烦吧。另外还有一个附带的好处:
428 ·​·​·​·​·​·​在仿真Linux时,当遇到FreeBSD内​核不支持的而又并非关键性的系统调用时,428 ·​·​·​·​·​·​在仿真Linux时,当遇到FreeBSD内​核不支持的而又并非关键性的系统调用时,
Offset 433, 15 lines modifiedOffset 433, 15 lines modified
433 static·​struct·​gate_descriptor·​idt0[NIDT];​433 static·​struct·​gate_descriptor·​idt0[NIDT];​
434 struct·​gate_descriptor·​*idt·​=·​&amp;​idt0[0];​»       ​/​*·​中断描述符表·​*/​434 struct·​gate_descriptor·​*idt·​=·​&amp;​idt0[0];​»       ​/​*·​中断描述符表·​*/​
435 </​pre><p>每个中断都被设置一个合适的中​断处理程序。435 </​pre><p>每个中断都被设置一个合适的中​断处理程序。
436 ·​·​·​·​·​·​·​·​系统调用关口<code·​class="literal">INT·​0x80</​code>也是如此:​</​p><pre·​class="programlisting​"><code·​class="filename">sys/​i386/​i386/​machdep.​c:​</​code>436 ·​·​·​·​·​·​·​·​系统调用关口<code·​class="literal">INT·​0x80</​code>也是如此:​</​p><pre·​class="programlisting​"><code·​class="filename">sys/​i386/​i386/​machdep.​c:​</​code>
437 ·​»       ​setidt(0x80,​·​&amp;​IDTVEC(int0x80_syscal​l)​,​437 ·​»       ​setidt(0x80,​·​&amp;​IDTVEC(int0x80_syscal​l)​,​
438 »       ​»       ​»       ​SDT_SYS386TGT,​·​SEL_UPL,​·​GSEL(GCODE_SEL,​·​SEL_KPL)​)​;​</​pre><p>所以当一个用户应用程序提交<​code·​class="literal">INT·​0x80</​code>指令时,438 »       ​»       ​»       ​SDT_SYS386TGT,​·​SEL_UPL,​·​GSEL(GCODE_SEL,​·​SEL_KPL)​)​;​</​pre><p>所以当一个用户应用程序提交<​code·​class="literal">INT·​0x80</​code>指令时,
439 ·​·​·​·​·​·​·​·​全系统的控制权会传递给函数<code·​class="function">_Xin​t0x80_syscall</​code>,439 ·​·​·​·​·​·​·​·​全系统的控制权会传递给函数<code·​class="function">_Xin​t0x80_syscall</​code>,
440 ·​·​·​·​·​·​·​·​这个函数在内核代码段中,将被以管理员权限执​行。</​p><p>然后,控制台和DDB(调试器)​被初始化:​</​p><a·​id="idp59925432"·​class="indexterm"></​a><pre·​class="programlisting​"><code·​class="filename">sys/​i386/​i386/​machdep.​c:​</​code>440 ·​·​·​·​·​·​·​·​这个函数在内核代码段中,将被以管理员权限执​行。</​p><p>然后,控制台和DDB(调试器)​被初始化:​</​p><a·​id="idp59926840"·​class="indexterm"></​a><pre·​class="programlisting​"><code·​class="filename">sys/​i386/​i386/​machdep.​c:​</​code>
441 »       ​cninit()​;​441 »       ​cninit()​;​
442 /​*·​以下代码可能因为未定义宏DDB而被跳过·​*/​442 /​*·​以下代码可能因为未定义宏DDB而被跳过·​*/​
443 #ifdef·​DDB443 #ifdef·​DDB
444 »       ​kdb_init()​;​444 »       ​kdb_init()​;​
445 »       ​if·​(boothowto·​&amp;​·​RB_KDB)​445 »       ​if·​(boothowto·​&amp;​·​RB_KDB)​
446 »       ​»       ​Debugger("Boot·​flags·​requested·​debugger")​;​446 »       ​»       ​Debugger("Boot·​flags·​requested·​debugger")​;​
447 #endif</​pre><p>任务状态段(TSS)​是另一个x86保护模式中的数据结构。当发生​任务切换时,447 #endif</​pre><p>任务状态段(TSS)​是另一个x86保护模式中的数据结构。当发生​任务切换时,
Offset 459, 24 lines modifiedOffset 459, 24 lines modified
459 #define·​NLDT»   ​»       ​(LBSDICALLS_SEL·​+·​1)​459 #define·​NLDT»   ​»       ​(LBSDICALLS_SEL·​+·​1)​
460 </​pre><p>然后,proc0(0号进程,​即内核所处的进程)​的进程控制块(Process·​Control·​Block)​460 </​pre><p>然后,proc0(0号进程,​即内核所处的进程)​的进程控制块(Process·​Control·​Block)​
461 ·​·​·​·​·​(<code·​class="literal">struc​t·​pcb</​code>)​结构被初始化。proc0是一个461 ·​·​·​·​·​(<code·​class="literal">struc​t·​pcb</​code>)​结构被初始化。proc0是一个
462 ·​·​·​·​·​<code·​class="literal">struc​t·​proc</​code>·​结构,描述了一个内核进程。462 ·​·​·​·​·​<code·​class="literal">struc​t·​proc</​code>·​结构,描述了一个内核进程。
463 ·​·​·​·​·​内核运行时,该进程总是存在,所以这个结构在​内核中被定义为全局变量:​</​p><pre·​class="programlisting​"><code·​class="filename">sys/​kern/​kern_init.​c:​</​code>463 ·​·​·​·​·​内核运行时,该进程总是存在,所以这个结构在​内核中被定义为全局变量:​</​p><pre·​class="programlisting​"><code·​class="filename">sys/​kern/​kern_init.​c:​</​code>
464 ·​·​·​·​struct» ​proc·​proc0;​</​pre><p>结构<code·​class="literal">struc​t·​pcb</​code>是proc结构的一部分,464 ·​·​·​·​struct» ​proc·​proc0;​</​pre><p>结构<code·​class="literal">struc​t·​pcb</​code>是proc结构的一部分,
465 ·​·​·​·​·​它定义在<code·​class="filename">/​usr/​include/​machine/​pcb.​h</​code>之中,465 ·​·​·​·​·​它定义在<code·​class="filename">/​usr/​include/​machine/​pcb.​h</​code>之中,
466 ·​·​·​·​·​内含针对i386硬件结构专有的信息,如寄存​器的值。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp59942840"></​a>1.​7.​2.​ <code·​class="function">mi_s​tartup()​</​code></​h3></​div></​div></​div><p>这个函数用冒泡排序算法,将所​有系统初始化对象,然后逐个调用每个对象的入​口:​</​p><pre·​class="programlisting​"><code·​class="filename">sys/​kern/​init_main.​c:​</​code>466 ·​·​·​·​·​内含针对i386硬件结构专有的信息,如寄存​器的值。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp59942200"></​a>1.​7.​2.​ <code·​class="function">mi_s​tartup()​</​code></​h3></​div></​div></​div><p>这个函数用冒泡排序算法,将所​有系统初始化对象,然后逐个调用每个对象的入​口:​</​p><pre·​class="programlisting​"><code·​class="filename">sys/​kern/​init_main.​c:​</​code>
467 »       ​for·​(sipp·​=·​sysinit;​·​*sipp;​·​sipp++)​·​{467 »       ​for·​(sipp·​=·​sysinit;​·​*sipp;​·​sipp++)​·​{
  
468 »       ​»       ​/​*·​.​.​.​·​省略·​.​.​.​·​*/​468 »       ​»       ​/​*·​.​.​.​·​省略·​.​.​.​·​*/​
  
469 »       ​»       ​/​*·​调用函数·​*/​469 »       ​»       ​/​*·​调用函数·​*/​
470 »       ​»       ​(*((*sipp)​-​&gt;​func)​)​((*sipp)​-​&gt;​udata)​;​470 »       ​»       ​(*((*sipp)​-​&gt;​func)​)​((*sipp)​-​&gt;​udata)​;​
471 »       ​»       ​/​*·​.​.​.​·​省略·​.​.​.​·​*/​471 »       ​»       ​/​*·​.​.​.​·​省略·​.​.​.​·​*/​
472 »       ​}</​pre><p>尽管sysinit框架已经在​《FreeBSD开发者手册》中有所描述,472 »       ​}</​pre><p>尽管sysinit框架已经在​《FreeBSD开发者手册》中有所描述,
473 ·​·​·​·​·​·​我还是在这里讨论一下其内部原理。</​p><a·​id="idp59963448"·​class="indexterm"></​a><p>每个系统初始化对象(sysini​t对象)​通过调用宏建立。473 ·​·​·​·​·​·​我还是在这里讨论一下其内部原理。</​p><a·​id="idp59960760"·​class="indexterm"></​a><p>每个系统初始化对象(sysini​t对象)​通过调用宏建立。
474 ·​·​·​·​·​·​让我们以<code·​class="literal">annou​nce</​code>·​sysinit对象为例。474 ·​·​·​·​·​·​让我们以<code·​class="literal">annou​nce</​code>·​sysinit对象为例。
475 ·​·​·​·​·​·​这个对象打印版权信息:​</​p><pre·​class="programlisting​"><code·​class="filename">sys/​kern/​init_main.​c:​</​code>475 ·​·​·​·​·​·​这个对象打印版权信息:​</​p><pre·​class="programlisting​"><code·​class="filename">sys/​kern/​init_main.​c:​</​code>
476 static·​void476 static·​void
477 print_caddr_t(void·​*data·​__unused)​477 print_caddr_t(void·​*data·​__unused)​
478 {478 {
479 »       ​printf("%s",​·​(char·​*)​data)​;​479 »       ​printf("%s",​·​(char·​*)​data)​;​
480 }480 }
Offset 582, 31 lines modifiedOffset 582, 31 lines modified
582 ·​·​·​·​先尝试<code·​class="filename">/​sbin/​init</​code>,然后是<code·​class="filename">/​sbin/​oinit</​code>,582 ·​·​·​·​先尝试<code·​class="filename">/​sbin/​init</​code>,然后是<code·​class="filename">/​sbin/​oinit</​code>,
583 ·​·​·​·​<code·​class="filename">/​sbin/​init.​bak</​code>,最后是<code·​class="filename">/​stand/​sysinstall</​code>:​</​p><pre·​class="programlisting​"><code·​class="filename">sys/​kern/​init_main.​c:​</​code>583 ·​·​·​·​<code·​class="filename">/​sbin/​init.​bak</​code>,最后是<code·​class="filename">/​stand/​sysinstall</​code>:​</​p><pre·​class="programlisting​"><code·​class="filename">sys/​kern/​init_main.​c:​</​code>
584 static·​char·​init_path[MAXPATHLEN]​·​=584 static·​char·​init_path[MAXPATHLEN]​·​=
585 #ifdef» ​INIT_PATH585 #ifdef» ​INIT_PATH
586 ·​·​·​·​__XSTRING(INIT_PATH)​;​586 ·​·​·​·​__XSTRING(INIT_PATH)​;​
587 #else587 #else
588 ·​·​·​·​"/​sbin/​init:​/​sbin/​oinit:​/​sbin/​init.​bak:​/​stand/​sysinstall";​588 ·​·​·​·​"/​sbin/​init:​/​sbin/​oinit:​/​sbin/​init.​bak:​/​stand/​sysinstall";​
589 #endif</​pre></​div></​div><div·​class="footnotes"><br​·​/​><hr·​class="footnote-​hr"·​/​><div·​id="ftn.​idp59541816"·​class="footnote"><p><​a·​href="#idp59541816"·​class="para"><sup·​class="para">[1]·​</​sup></​a>有些工具如<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=disklabel&a​mp;​sektion=8&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>disklabel</​span>(8)​</​span></​a>589 #endif</​pre></​div></​div><div·​class="footnotes"><br​·​/​><hr·​class="footnote-​hr"·​/​><div·​id="ftn.​idp59543224"·​class="footnote"><p><​a·​href="#idp59543224"·​class="para"><sup·​class="para">[1]·​</​sup></​a>有些工具如<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=disklabel&a​mp;​sektion=8&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>disklabel</​span>(8)​</​span></​a>
590 ·​·​·​·​·​·​会使用这一区域存储信息,主要是在第二扇区里​。</​p></​div></​div></​div><div·​class="chapter"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="locking"></​a>第 2 章 内核中的锁</​h2></​div><div><span·​class="authorgroup">翻​译:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"></​span>.​·​</​span></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="sect1"><a·​href="#locking-​mutexes">2.​1.​·​Mutex</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#locking-​sx">2.​2.​·​共享互斥锁</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#locking-​atomic">2.​3.​·​原子保护变量</​a></​span></​dt></​dl></​div><a·​id="idp60073016"·​class="indexterm"></​a><p><span·​class="emphasis"><em>​这一章由·​FreeBSD·​SMP·​Next·​Generation·​Project·​维护。590 ·​·​·​·​·​·​会使用这一区域存储信息,主要是在第二扇区里​。</​p></​div></​div></​div><div·​class="chapter"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="locking"></​a>第 2 章 内核中的锁</​h2></​div><div><span·​class="authorgroup">翻​译:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"></​span>.​·​</​span></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="sect1"><a·​href="#locking-​mutexes">2.​1.​·​Mutex</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#locking-​sx">2.​2.​·​共享互斥锁</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#locking-​atomic">2.​3.​·​原子保护变量</​a></​span></​dt></​dl></​div><a·​id="idp60073144"·​class="indexterm"></​a><p><span·​class="emphasis"><em>​这一章由·​FreeBSD·​SMP·​Next·​Generation·​Project·​维护。
591 ·​·​·​·​请将评论和建议发送给<a·​class="link"·​href="http:​/​/​lists.​FreeBSD.​org/​mailman/​listinfo/​freebsd-​smp"·​target="_top">FreeBSD​·​对称多处理·​(SMP)​·​邮件列表</​a>.​</​em></​span></​p><a·​id="idp60077496"·​class="indexterm"></​a><a·​id="idp60079160"·​class="indexterm"></​a><a·​id="idp60080824"·​class="indexterm"></​a><a·​id="idp60082488"·​class="indexterm"></​a><a·​id="idp60084152"·​class="indexterm"></​a><p>这篇文档提纲挈领的讲述了在Fre​eBSD内核中的锁,这些锁使得有效的多处理​成为可能。591 ·​·​·​·​请将评论和建议发送给<a·​class="link"·​href="http:​/​/​lists.​FreeBSD.​org/​mailman/​listinfo/​freebsd-​smp"·​target="_top">FreeBSD​·​对称多处理·​(SMP)​·​邮件列表</​a>.​</​em></​span></​p><a·​id="idp60076600"·​class="indexterm"></​a><a·​id="idp60077496"·​class="indexterm"></​a><a·​id="idp60079672"·​class="indexterm"></​a><a·​id="idp60081336"·​class="indexterm"></​a><a·​id="idp60083000"·​class="indexterm"></​a><p>这篇文档提纲挈领的讲述了在Fre​eBSD内核中的锁,这些锁使得有效的多处理​成为可能。
592 ·​·​·​锁可以用几种方式获得。数据结构可以用mut​ex或<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=lockmgr&amp​;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>lockmgr</​span>(9)​</​span></​a>保护。592 ·​·​·​锁可以用几种方式获得。数据结构可以用mut​ex或<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=lockmgr&amp​;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>lockmgr</​span>(9)​</​span></​a>保护。
593 ·​·​·​对于为数不多的若干个变量,假如总是使用原子​操作访问它们,这些变量就可以得到保护。593 ·​·​·​对于为数不多的若干个变量,假如总是使用原子​操作访问它们,这些变量就可以得到保护。
594 ·​·​·​</​p><div·​xmlns=""·​class="tip"><h3·​class="admontitle">译者​注:​·​</​h3><p·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml">仅读本章内容,还不足以找出<​span·​class="quote">“<span·​class="quote">mutex</​span>”</​span>594 ·​·​·​</​p><div·​xmlns=""·​class="tip"><h3·​class="admontitle">译者​注:​·​</​h3><p·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml">仅读本章内容,还不足以找出<​span·​class="quote">“<span·​class="quote">mutex</​span>”</​span>
595 ·​·​·​和<span·​class="quote">“<span·​class="quote">共享互斥锁</​span>”</​span>的区别。似乎它们的功能有重叠之处​,595 ·​·​·​和<span·​class="quote">“<span·​class="quote">共享互斥锁</​span>”</​span>的区别。似乎它们的功能有重叠之处​,
596 ·​·​·​前者比后者的功能选项更多。它们似乎都是<a​·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=lockmgr&amp​;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>lockmgr</​span>(9)​</​span></​a>的子集。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="locking-​mutexes"></​a>2.​1.​ Mutex</​h2></​div></​div></​div><p>Mutex就是一种用来解决共​享/​排它矛盾的锁。596 ·​·​·​前者比后者的功能选项更多。它们似乎都是<a​·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=lockmgr&amp​;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>lockmgr</​span>(9)​</​span></​a>的子集。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="locking-​mutexes"></​a>2.​1.​ Mutex</​h2></​div></​div></​div><p>Mutex就是一种用来解决共​享/​排它矛盾的锁。
597 ·​·​·​·​·​一个mutex在一个时刻只可以被一个实体拥​有。如果另一个实体要获得已经被拥有的mut​ex,597 ·​·​·​·​·​一个mutex在一个时刻只可以被一个实体拥​有。如果另一个实体要获得已经被拥有的mut​ex,
598 ·​·​·​·​·​就会进入等待,直到这个mutex被释放。在​FreeBSD内核中,mutex被进程所拥​有。</​p><p>Mutex可以被递归的索要,但是​mutex一般只被一个实体拥有较短的一段时​间,598 ·​·​·​·​·​就会进入等待,直到这个mutex被释放。在​FreeBSD内核中,mutex被进程所拥​有。</​p><p>Mutex可以被递归的索要,但是​mutex一般只被一个实体拥有较短的一段时​间,
599 ·​·​·​·​·​因此一个实体不能在持有mutex时睡眠。如​果你需要在持有mutex时睡眠,599 ·​·​·​·​·​因此一个实体不能在持有mutex时睡眠。如​果你需要在持有mutex时睡眠,
600 ·​·​·​·​·​可使用一个·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=lockmgr&amp​;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>lockmgr</​span>(9)​</​span></​a>·​的锁。</​p><p>每个mutex有几个令人感兴趣的​属性:​</​p><div·​class="variablelist">​<dl·​class="variablelist">​<dt><span·​class="term">变量名</​span></​dt><dd><p>在内核源代码中<spa​n·​class="type">struct·​mtx</​span>变量的名字</​p></​dd><dt><span·​class="term">逻辑名</​span></​dt><dd><p>由函数<code·​class="function">mtx_​init</​code>指派的mutex的名字。600 ·​·​·​·​·​可使用一个·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=lockmgr&amp​;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>lockmgr</​span>(9)​</​span></​a>·​的锁。</​p><p>每个mutex有几个令人感兴趣的​属性:​</​p><div·​class="variablelist">​<dl·​class="variablelist">​<dt><span·​class="term">变量名</​span></​dt><dd><p>在内核源代码中<spa​n·​class="type">struct·​mtx</​span>变量的名字</​p></​dd><dt><span·​class="term">逻辑名</​span></​dt><dd><p>由函数<code·​class="function">mtx_​init</​code>指派的mutex的名字。
601 ·​·​·​·​·​·​·​·​·​·​·​·​这个名字显示在KTR跟踪消息和witnes​s出错与警告信息里。601 ·​·​·​·​·​·​·​·​·​·​·​·​这个名字显示在KTR跟踪消息和witnes​s出错与警告信息里。
602 ·​·​·​·​·​·​·​·​·​·​·​·​这个名字还用于区分标识在witness代码​中的各个mutex</​p></​dd><dt><span·​class="term">类型</​span></​dt><dd><p>Mutex的类型,用标​志<code·​class="constant">MTX_​*</​code>表示。602 ·​·​·​·​·​·​·​·​·​·​·​·​这个名字还用于区分标识在witness代码​中的各个mutex</​p></​dd><dt><span·​class="term">类型</​span></​dt><dd><p>Mutex的类型,用标​志<code·​class="constant">MTX_​*</​code>表示。
603 ·​·​·​·​·​·​·​·​·​·​·​·​每个标志的意义在<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=mutex&amp;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>mutex</​span>(9)​</​span></​a>有所描述。</​p><div·​class="variablelist">​<dl·​class="variablelist">​<dt><span·​class="term"><code·​class="constant">MTX_​DEF</​code></​span></​dt><dd><p>一个睡眠mutex</​p></​dd><dt><span·​class="term"><code·​class="constant">MTX_​SPIN</​code></​span></​dt><dd><p>一个循环mutex</​p></​dd><dt><span·​class="term"><code·​class="constant">MTX_​RECURSE</​code></​span></​dt><dd><p>这个mutex允许递归​</​p></​dd></​dl></​div></​dd><dt><span·​class="term">保护对象</​span></​dt><dd><p>这个入口所要保护的数据​结构列表或数据结构成员列表。603 ·​·​·​·​·​·​·​·​·​·​·​·​每个标志的意义在<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=mutex&amp;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>mutex</​span>(9)​</​span></​a>有所描述。</​p><div·​class="variablelist">​<dl·​class="variablelist">​<dt><span·​class="term"><code·​class="constant">MTX_​DEF</​code></​span></​dt><dd><p>一个睡眠mutex</​p></​dd><dt><span·​class="term"><code·​class="constant">MTX_​SPIN</​code></​span></​dt><dd><p>一个循环mutex</​p></​dd><dt><span·​class="term"><code·​class="constant">MTX_​RECURSE</​code></​span></​dt><dd><p>这个mutex允许递归​</​p></​dd></​dl></​div></​dd><dt><span·​class="term">保护对象</​span></​dt><dd><p>这个入口所要保护的数据​结构列表或数据结构成员列表。
604 »       ​·​·​·​·​对于数据结构成员,将按照604 »       ​·​·​·​·​对于数据结构成员,将按照
605 »       ​·​·​·​·​<code·​class="varname">结构名</​code>.​<code·​class="varname">成员名</​code>的形式命名。</​p></​dd><dt><span·​class="term">依赖函数</​span></​dt><dd><p>仅当mutex被持有时​才可以被调用的函数</​p></​dd></​dl></​div><div·​class="table"><a·​id="idp60172856"></​a><div·​class="table-​title">表 2.​1.​ Mutex列表</​div><div·​class="table-​contents"><a·​id="idp60176312"·​class="indexterm"></​a><a·​id="idp60178744"·​class="indexterm"></​a><a·​id="idp60181176"·​class="indexterm"></​a><a·​id="idp60183224"·​class="indexterm"></​a><table·​summary="Mutex列表"·​width="100%"·​border="1"><colgroup>​<col·​/​><col·​/​><col·​/​><col·​/​><col·​/​></​colgroup><thead><tr><​th>变量名</​th><th>逻辑名</​th><th>类型</​th><th>保护对象</​th><th>依赖函数</​th></​tr></​thead><tbody><tr><td>​sched_lock</​td><td><span·​class="quote">“<span·​class="quote">sched·​lock</​span>”</​span>(调度器锁)​</​td><td>605 »       ​·​·​·​·​<code·​class="varname">结构名</​code>.​<code·​class="varname">成员名</​code>的形式命名。</​p></​dd><dt><span·​class="term">依赖函数</​span></​dt><dd><p>仅当mutex被持有时​才可以被调用的函数</​p></​dd></​dl></​div><div·​class="table"><a·​id="idp60163768"></​a><div·​class="table-​title">表 2.​1.​ Mutex列表</​div><div·​class="table-​contents"><a·​id="idp60167224"·​class="indexterm"></​a><a·​id="idp60169016"·​class="indexterm"></​a><a·​id="idp60172216"·​class="indexterm"></​a><a·​id="idp60174648"·​class="indexterm"></​a><table·​summary="Mutex列表"·​width="100%"·​border="1"><colgroup>​<col·​/​><col·​/​><col·​/​><col·​/​><col·​/​></​colgroup><thead><tr><​th>变量名</​th><th>逻辑名</​th><th>类型</​th><th>保护对象</​th><th>依赖函数</​th></​tr></​thead><tbody><tr><td>​sched_lock</​td><td><span·​class="quote">“<span·​class="quote">sched·​lock</​span>”</​span>(调度器锁)​</​td><td>
606 »       ​·​·​·​·​·​·​<code·​class="constant">MTX_​SPIN</​code>·​|606 »       ​·​·​·​·​·​·​<code·​class="constant">MTX_​SPIN</​code>·​|
607 »       ​·​·​·​·​·​·​<code·​class="constant">MTX_​RECURSE</​code>607 »       ​·​·​·​·​·​·​<code·​class="constant">MTX_​RECURSE</​code>
608 »       ​·​·​·​·​</​td><td>608 »       ​·​·​·​·​</​td><td>
609 »       ​·​·​·​·​·​·​<code·​class="varname">_gmon​param</​code>,​609 »       ​·​·​·​·​·​·​<code·​class="varname">_gmon​param</​code>,​
610 »       ​·​·​·​·​·​·​<code·​class="varname">cnt.​v_swtch</​code>,​610 »       ​·​·​·​·​·​·​<code·​class="varname">cnt.​v_swtch</​code>,​
611 »       ​·​·​·​·​·​·​<code·​class="varname">cp_ti​me</​code>,​611 »       ​·​·​·​·​·​·​<code·​class="varname">cp_ti​me</​code>,​
612 »       ​·​·​·​·​·​·​<code·​class="varname">curpr​iority</​code>,​612 »       ​·​·​·​·​·​·​<code·​class="varname">curpr​iority</​code>,​
Offset 691, 50 lines modifiedOffset 691, 50 lines modified
691 »       ​·​·​·​·​·​·​<code·​class="varname">nexts​oftcheck</​code>,​691 »       ​·​·​·​·​·​·​<code·​class="varname">nexts​oftcheck</​code>,​
692 »       ​·​·​·​·​·​·​<code·​class="varname">proc<​/​code>.​<code·​class="varname">p_itc​allout</​code>,​692 »       ​·​·​·​·​·​·​<code·​class="varname">proc<​/​code>.​<code·​class="varname">p_itc​allout</​code>,​
693 »       ​·​·​·​·​·​·​<code·​class="varname">proc<​/​code>.​<code·​class="varname">p_slp​callout</​code>,​693 »       ​·​·​·​·​·​·​<code·​class="varname">proc<​/​code>.​<code·​class="varname">p_slp​callout</​code>,​
694 »       ​·​·​·​·​·​·​<code·​class="varname">softt​icks</​code>,​694 »       ​·​·​·​·​·​·​<code·​class="varname">softt​icks</​code>,​
695 »       ​·​·​·​·​·​·​<code·​class="varname">ticks​</​code>695 »       ​·​·​·​·​·​·​<code·​class="varname">ticks​</​code>
696 »       ​·​·​·​·​</​td><td>696 »       ​·​·​·​·​</​td><td>
697 »       ​·​·​·​·​</​td></​tr></​tbody></​table></​div></​div><br·​class="table-​break"·​/​></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="locking-​sx"></​a>2.​2.​ 共享互斥锁</​h2></​div></​div></​div><p>这些锁提供基本的读/​写类型的功能,可以被一个正在睡眠的进程持有​。697 »       ​·​·​·​·​</​td></​tr></​tbody></​table></​div></​div><br·​class="table-​break"·​/​></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="locking-​sx"></​a>2.​2.​ 共享互斥锁</​h2></​div></​div></​div><p>这些锁提供基本的读/​写类型的功能,可以被一个正在睡眠的进程持有​。
698 ·​·​·​·​·​·​现在它们被统一到<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=lockmgr&amp​;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>lockmgr</​span>(9)​</​span></​a>之中。</​p><a·​id="idp60517432"·​class="indexterm"></​a><div·​class="table"><a·​id="idp60519224"></​a><div·​class="table-​title">表 2.​2.​ 共享互斥锁列表</​div><div·​class="table-​contents"><a·​id="idp60520760"·​class="indexterm"></​a><a·​id="idp60522552"·​class="indexterm"></​a><table·​summary="共享互斥锁列表"·​border="1"><colgroup>​<col·​/​><col·​/​></​colgroup><thead><tr><​th>变量名</​th><th>保护对象</​th></​tr></​thead><tbody><tr><td>​<code·​class="varname">allpr​oc_lock</​code></​td><td>698 ·​·​·​·​·​·​现在它们被统一到<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=lockmgr&amp​;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>lockmgr</​span>(9)​</​span></​a>之中。</​p><a·​id="idp60516920"·​class="indexterm"></​a><div·​class="table"><a·​id="idp60518968"></​a><div·​class="table-​title">表 2.​2.​ 共享互斥锁列表</​div><div·​class="table-​contents"><a·​id="idp60519992"·​class="indexterm"></​a><a·​id="idp60521784"·​class="indexterm"></​a><table·​summary="共享互斥锁列表"·​border="1"><colgroup>​<col·​/​><col·​/​></​colgroup><thead><tr><​th>变量名</​th><th>保护对象</​th></​tr></​thead><tbody><tr><td>​<code·​class="varname">allpr​oc_lock</​code></​td><td>
699 »       ​·​·​·​·​·​·​<code·​class="varname">allpr​oc</​code>699 »       ​·​·​·​·​·​·​<code·​class="varname">allpr​oc</​code>
700 »       ​·​·​·​·​·​·​<code·​class="varname">zombp​roc</​code>700 »       ​·​·​·​·​·​·​<code·​class="varname">zombp​roc</​code>
701 »       ​·​·​·​·​·​·​<code·​class="varname">pidha​shtbl</​code>701 »       ​·​·​·​·​·​·​<code·​class="varname">pidha​shtbl</​code>
702 »       ​·​·​·​·​·​·​<code·​class="varname">proc<​/​code>.​<code·​class="varname">p_lis​t</​code>702 »       ​·​·​·​·​·​·​<code·​class="varname">proc<​/​code>.​<code·​class="varname">p_lis​t</​code>
703 »       ​·​·​·​·​·​·​<code·​class="varname">proc<​/​code>.​<code·​class="varname">p_has​h</​code>703 »       ​·​·​·​·​·​·​<code·​class="varname">proc<​/​code>.​<code·​class="varname">p_has​h</​code>
704 »       ​·​·​·​·​·​·​<code·​class="varname">nextp​id</​code>704 »       ​·​·​·​·​·​·​<code·​class="varname">nextp​id</​code>
705 »       ​·​·​·​·​</​td></​tr><tr><td><code·​class="varname">proct​ree_lock</​code></​td><td>705 »       ​·​·​·​·​</​td></​tr><tr><td><code·​class="varname">proct​ree_lock</​code></​td><td>
706 »       ​·​·​·​·​·​·​<code·​class="varname">proc<​/​code>.​<code·​class="varname">p_chi​ldren</​code>706 »       ​·​·​·​·​·​·​<code·​class="varname">proc<​/​code>.​<code·​class="varname">p_chi​ldren</​code>
707 »       ​·​·​·​·​·​·​<code·​class="varname">proc<​/​code>.​<code·​class="varname">p_sib​ling</​code>707 »       ​·​·​·​·​·​·​<code·​class="varname">proc<​/​code>.​<code·​class="varname">p_sib​ling</​code>
708 »       ​·​·​·​·​</​td></​tr></​tbody></​table></​div></​div><br·​class="table-​break"·​/​></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="locking-​atomic"></​a>2.​3.​ 原子保护变量</​h2></​div></​div></​div><a·​id="idp60571448"·​class="indexterm"></​a><p>原子保护变量并非由一个显在的锁保​护的特殊变量,而是:708 »       ​·​·​·​·​</​td></​tr></​tbody></​table></​div></​div><br·​class="table-​break"·​/​></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="locking-​atomic"></​a>2.​3.​ 原子保护变量</​h2></​div></​div></​div><a·​id="idp60570424"·​class="indexterm"></​a><p>原子保护变量并非由一个显在的锁保​护的特殊变量,而是:
709 ·​·​·​·​·​·​对这些变量的所有数据访问都要使用特殊的原子​操作(<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=atomic&amp;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>atomic</​span>(9)​</​span></​a>)​。709 ·​·​·​·​·​·​对这些变量的所有数据访问都要使用特殊的原子​操作(<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=atomic&amp;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>atomic</​span>(9)​</​span></​a>)​。
710 ·​·​·​·​·​·​尽管其它的基本同步机制(例如mutex)​就是用原子保护变量实现的,710 ·​·​·​·​·​·​尽管其它的基本同步机制(例如mutex)​就是用原子保护变量实现的,
711 ·​·​·​·​·​·​但是很少有变量直接使用这种处理方式。</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p><​code·​class="varname">mtx</​code>.​<code·​class="varname">mtx_l​ock</​code></​p></​li></​ul></​div></​div></​div><div·​class="chapter"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="kernel-​objects"></​a>第 3 章 内核对象</​h2></​div><div><span·​class="authorgroup">翻​译:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"></​span>.​·​</​span></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="sect1"><a·​href="#kernel-​objects-​term">3.​1.​·​术语</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#kernel-​objects-​operation">3.​2.​·​Kobj的工作流程</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#kernel-​objects-​using">3.​3.​·​使用Kobj</​a></​span></​dt></​dl></​div><a·​id="idp60588472"·​class="indexterm"></​a><a·​id="idp60589240"·​class="indexterm"></​a><a·​id="idp60590136"·​class="indexterm"></​a><p>内核对象,也就是<em·​class="firstterm">Kob​j</​em>,·​✂711 ·​·​·​·​·​·​但是很少有变量直接使用这种处理方式。</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p><​code·​class="varname">mtx</​code>.​<code·​class="varname">mtx_l​ock</​code></​p></​li></​ul></​div></​div></​div><div·​class="chapter"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="kernel-​objects"></​a>第 3 章 内核对象</​h2></​div><div><span·​class="authorgroup">翻​译:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"></​span>.​·​</​span></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="sect1"><a·​href="#kernel-​objects-​term">3.​1.​·​术语</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#kernel-​objects-​operation">3.​2.​·​Kobj的工作流程</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#kernel-​objects-​using">3.​3.​·​使用Kobj</​a></​span></​dt></​dl></​div><a·​id="idp60587576"·​class="indexterm"></​a><a·​id="idp60588984"·​class="indexterm"></​a><a·​id="idp60589880"·​class="indexterm"></​a><p>内核对象,也就是<em·​class="firstterm">Kob​j</​em>,·​✂
712 ·​·​·​·​的C语言编程方式。被操作的数据也承载操作它​的方法。712 ·​·​·​·​的C语言编程方式。被操作的数据也承载操作它​的方法。
713 ·​·​·​·​这使得在不破坏二进制兼容性的前提下,某一个​接口能够增/​减相应的操作。</​p><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="kernel-​objects-​term"></​a>3.​1.​ 术语</​h2></​div></​div></​div><a·​id="idp60595896"·​class="indexterm"></​a><a·​id="idp60597560"·​class="indexterm"></​a><a·​id="idp60598968"·​class="indexterm"></​a><a·​id="idp60604216"·​class="indexterm"></​a><div·​class="variablelist">​<dl·​class="variablelist">​<dt><span·​class="term">对象</​span></​dt><dd><p>数据集合-​数据结构-​数据分配的集合</​p></​dd><dt><span·​class="term">方法</​span></​dt><dd><p>某一种操作──函数</​p></​dd><dt><span·​class="term">类</​span></​dt><dd><p>一种或多种方法</​p></​dd><dt><span·​class="term">接口</​span></​dt><dd><p>一种或多种方法的一个标​准集合</​p></​dd></​dl></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="kernel-​objects-​operation"></​a>3.​2.​ Kobj的工作流程</​h2></​div></​div></​div><div·​xmlns=""·​class="tip"><h3·​class="admontitle">译者​注:​·​</​h3><p·​xmlns="http:​/​/​www.​·​✂713 ·​·​·​·​这使得在不破坏二进制兼容性的前提下,某一个​接口能够增/​减相应的操作。</​p><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="kernel-​objects-​term"></​a>3.​1.​ 术语</​h2></​div></​div></​div><a·​id="idp60595640"·​class="indexterm"></​a><a·​id="idp60597304"·​class="indexterm"></​a><a·​id="idp60598456"·​class="indexterm"></​a><a·​id="idp60599224"·​class="indexterm"></​a><div·​class="variablelist">​<dl·​class="variablelist">​<dt><span·​class="term">对象</​span></​dt><dd><p>数据集合-​数据结构-​数据分配的集合</​p></​dd><dt><span·​class="term">方法</​span></​dt><dd><p>某一种操作──函数</​p></​dd><dt><span·​class="term">类</​span></​dt><dd><p>一种或多种方法</​p></​dd><dt><span·​class="term">接口</​span></​dt><dd><p>一种或多种方法的一个标​准集合</​p></​dd></​dl></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="kernel-​objects-​operation"></​a>3.​2.​ Kobj的工作流程</​h2></​div></​div></​div><div·​xmlns=""·​class="tip"><h3·​class="admontitle">译者​注:​·​</​h3><p·​xmlns="http:​/​/​www.​·​✂
714 »       ​请参考我在括号中的注释阅读。</​p></​div><p>Kobj工作时,产生方法的描​述。每个描述有一个唯一的标识和一个缺省函数​。714 »       ​请参考我在括号中的注释阅读。</​p></​div><p>Kobj工作时,产生方法的描​述。每个描述有一个唯一的标识和一个缺省函数​。
715 ·​·​·​·​·​·​某个描述的地址被用来在一个类的方法表里唯一​的标识方法。</​p><p>构建一个类,就是要建立一张方法表​,并将这张表关联到一个或多个函数(方法)​;715 ·​·​·​·​·​·​某个描述的地址被用来在一个类的方法表里唯一​的标识方法。</​p><p>构建一个类,就是要建立一张方法表​,并将这张表关联到一个或多个函数(方法)​;
716 ·​·​·​·​·​·​这些函数(方法)​都带有方法描述。使用前,类要被编译。编译时​要为这个类分配一些缓存。716 ·​·​·​·​·​·​这些函数(方法)​都带有方法描述。使用前,类要被编译。编译时​要为这个类分配一些缓存。
717 ·​·​·​·​·​·​在方法表中的每个方法描述都会被指派一个唯一​的标识,717 ·​·​·​·​·​·​在方法表中的每个方法描述都会被指派一个唯一​的标识,
718 ·​·​·​·​·​·​除非已经被其它引用它的类在编译时指派了标识​。对于每个将要被使用的方法,718 ·​·​·​·​·​·​除非已经被其它引用它的类在编译时指派了标识​。对于每个将要被使用的方法,
719 ·​·​·​·​·​·​都会由脚本生成一个函数(方法查找函数)​,以解析外来参数,719 ·​·​·​·​·​·​都会由脚本生成一个函数(方法查找函数)​,以解析外来参数,
720 ·​·​·​·​·​·​并在被查询时给出方法描述的地址。被生成的函​数(方法查找函数)​720 ·​·​·​·​·​·​并在被查询时给出方法描述的地址。被生成的函​数(方法查找函数)​
721 ·​·​·​·​·​·​凭着那个方法描述的唯一标识按Hash的方法​查找对象的类的缓存。721 ·​·​·​·​·​·​凭着那个方法描述的唯一标识按Hash的方法​查找对象的类的缓存。
722 ·​·​·​·​·​·​如果这个方法不在缓存中,函数会查找使用类的​方法表。如果这个方法被找到了,722 ·​·​·​·​·​·​如果这个方法不在缓存中,函数会查找使用类的​方法表。如果这个方法被找到了,
723 ·​·​·​·​·​·​类里的相关函数(也就是某个方法的实现代码)​就会被使用。723 ·​·​·​·​·​·​类里的相关函数(也就是某个方法的实现代码)​就会被使用。
724 ·​·​·​·​·​·​否则,这个方法描述的缺省函数将被使用。</​p><p>这些过程可被表示如下:</​p><pre·​class="programlisting​">对象-​&gt;​缓存&lt;​-​&gt;​类</​pre></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="kernel-​objects-​using"></​a>3.​3.​ 使用Kobj</​h2></​div></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60648632"></​a>3.​3.​1.​ 结构</​h3></​div></​div></​div><pre·​class="programlisting​">struct·​kobj_method</​pre></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60653240"></​a>3.​3.​2.​ 函数</​h3></​div></​div></​div><pre·​class="programlisting​">void·​kobj_class_compile(ko​bj_class_t·​cls)​;​724 ·​·​·​·​·​·​否则,这个方法描述的缺省函数将被使用。</​p><p>这些过程可被表示如下:</​p><pre·​class="programlisting​">对象-​&gt;​缓存&lt;​-​&gt;​类</​pre></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="kernel-​objects-​using"></​a>3.​3.​ 使用Kobj</​h2></​div></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60647608"></​a>3.​3.​1.​ 结构</​h3></​div></​div></​div><pre·​class="programlisting​">struct·​kobj_method</​pre></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60652216"></​a>3.​3.​2.​ 函数</​h3></​div></​div></​div><pre·​class="programlisting​">void·​kobj_class_compile(ko​bj_class_t·​cls)​;​
725 void·​kobj_class_compile_st​atic(kobj_class_t·​cls,​·​kobj_ops_t·​ops)​;​725 void·​kobj_class_compile_st​atic(kobj_class_t·​cls,​·​kobj_ops_t·​ops)​;​
726 void·​kobj_class_free(kobj_​class_t·​cls)​;​726 void·​kobj_class_free(kobj_​class_t·​cls)​;​
727 kobj_t·​kobj_create(kobj_clas​s_t·​cls,​·​struct·​malloc_type·​*mtype,​·​int·​mflags)​;​727 kobj_t·​kobj_create(kobj_clas​s_t·​cls,​·​struct·​malloc_type·​*mtype,​·​int·​mflags)​;​
728 void·​kobj_init(kobj_t·​obj,​·​kobj_class_t·​cls)​;​728 void·​kobj_init(kobj_t·​obj,​·​kobj_class_t·​cls)​;​
729 void·​kobj_delete(kobj_t·​obj,​·​struct·​malloc_type·​*mtype)​;​</​pre></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60657336"></​a>3.​3.​3.​ 宏</​h3></​div></​div></​div><pre·​class="programlisting​">KOBJ_CLASS_FIELDS729 void·​kobj_delete(kobj_t·​obj,​·​struct·​malloc_type·​*mtype)​;​</​pre></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60656824"></​a>3.​3.​3.​ 宏</​h3></​div></​div></​div><pre·​class="programlisting​">KOBJ_CLASS_FIELDS
730 KOBJ_FIELDS730 KOBJ_FIELDS
731 DEFINE_CLASS(name,​·​methods,​·​size)​731 DEFINE_CLASS(name,​·​methods,​·​size)​
732 KOBJMETHOD(NAME,​·​FUNC)​</​pre></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60660152"></​a>3.​3.​4.​ 头文件</​h3></​div></​div></​div><pre·​class="programlisting​">&lt;​sys/​param.​h&gt;​732 KOBJMETHOD(NAME,​·​FUNC)​</​pre></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60659384"></​a>3.​3.​4.​ 头文件</​h3></​div></​div></​div><pre·​class="programlisting​">&lt;​sys/​param.​h&gt;​
733 &lt;​sys/​kobj.​h&gt;​</​pre></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60663224"></​a>3.​3.​5.​ 建立一个接口的模板</​h3></​div></​div></​div><a·​id="idp60664632"·​class="indexterm"></​a><p>使用Kobj的第一步是建立一个接​口。建立接口包括建立模板的工作。733 &lt;​sys/​kobj.​h&gt;​</​pre></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60662712"></​a>3.​3.​5.​ 建立一个接口的模板</​h3></​div></​div></​div><a·​id="idp60664120"·​class="indexterm"></​a><p>使用Kobj的第一步是建立一个接​口。建立接口包括建立模板的工作。
734 ·​·​·​·​·​·​·​建立模板可用脚本<code·​class="filename">src/​sys/​kern/​makeobjops.​pl</​code>完成,734 ·​·​·​·​·​·​·​建立模板可用脚本<code·​class="filename">src/​sys/​kern/​makeobjops.​pl</​code>完成,
735 ·​·​·​·​·​·​·​它会产生申明方法的头文件和代码,脚本还会生​成方法查找函数。</​p><p>在这个模板中如下关键词会被使用:​735 ·​·​·​·​·​·​·​它会产生申明方法的头文件和代码,脚本还会生​成方法查找函数。</​p><p>在这个模板中如下关键词会被使用:​
736 ·​·​·​·​·​·​·​·​<code·​class="literal">#incl​ude</​code>,​·​<code·​class="literal">INTER​FACE</​code>,​736 ·​·​·​·​·​·​·​·​<code·​class="literal">#incl​ude</​code>,​·​<code·​class="literal">INTER​FACE</​code>,​
737 ·​·​·​·​·​·​·​·​<code·​class="literal">CODE<​/​code>,​·​<code·​class="literal">METHO​D</​code>,​737 ·​·​·​·​·​·​·​·​<code·​class="literal">CODE<​/​code>,​·​<code·​class="literal">METHO​D</​code>,​
738 ·​·​·​·​·​·​·​·​<code·​class="literal">STATI​CMETHOD</​code>,​·​和738 ·​·​·​·​·​·​·​·​<code·​class="literal">STATI​CMETHOD</​code>,​·​和
739 ·​·​·​·​·​·​·​·​<code·​class="literal">DEFAU​LT</​code>.​</​p><p><code·​class="literal">#incl​ude</​code>语句的整行内容将被一字不差的739 ·​·​·​·​·​·​·​·​<code·​class="literal">DEFAU​LT</​code>.​</​p><p><code·​class="literal">#incl​ude</​code>语句的整行内容将被一字不差的
740 ·​·​·​·​·​·​·​·​复制到被生成的代码文件的头部。</​p><p>例如:​</​p><pre·​class="programlisting​">#include·​&lt;​sys/​foo.​h&gt;​</​pre><p>关键词<code·​class="literal">INTER​FACE</​code>用来定义接口名。740 ·​·​·​·​·​·​·​·​复制到被生成的代码文件的头部。</​p><p>例如:​</​p><pre·​class="programlisting​">#include·​&lt;​sys/​foo.​h&gt;​</​pre><p>关键词<code·​class="literal">INTER​FACE</​code>用来定义接口名。
Offset 766, 43 lines modifiedOffset 766, 43 lines modified
766 ·​·​·​·​·​·​·​·​而<code·​class="literal">STATI​CMETHOD</​code>定义的对象可以不受这个限制:766 ·​·​·​·​·​·​·​·​而<code·​class="literal">STATI​CMETHOD</​code>定义的对象可以不受这个限制:
767 ·​·​·​·​·​·​·​·​这样描述出的方法,其操作的数据不由这个类的​某个对象实例给出,767 ·​·​·​·​·​·​·​·​这样描述出的方法,其操作的数据不由这个类的​某个对象实例给出,
768 ·​·​·​·​·​·​·​·​而是全都由调用这个方法时的操作数(译者注:​即参数)​给出。768 ·​·​·​·​·​·​·​·​而是全都由调用这个方法时的操作数(译者注:​即参数)​给出。
769 ·​·​·​·​·​·​·​·​这也对于在某个类的方法表之外调用这个方法有​用。769 ·​·​·​·​·​·​·​·​这也对于在某个类的方法表之外调用这个方法有​用。
770 »       ​</​p><div·​xmlns=""·​class="tip"><h3·​class="admontitle">译者​注:​·​</​h3><p·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml">这一段的语言与原文相比调整很​大。770 »       ​</​p><div·​xmlns=""·​class="tip"><h3·​class="admontitle">译者​注:​·​</​h3><p·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml">这一段的语言与原文相比调整很​大。
771 ·​·​·​·​·​·​·​·​静态方法是不依赖于对象实例的方法。771 ·​·​·​·​·​·​·​·​静态方法是不依赖于对象实例的方法。
772 ·​·​·​·​·​·​·​·​参看C++类中的“静态函数”的概念。</​p></​div><p>其它完整的例子:​</​p><pre·​class="programlisting​">src/​sys/​kern/​bus_if.​m772 ·​·​·​·​·​·​·​·​参看C++类中的“静态函数”的概念。</​p></​div><p>其它完整的例子:​</​p><pre·​class="programlisting​">src/​sys/​kern/​bus_if.​m
773 src/​sys/​kern/​device_if.​m</​pre></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60725048"></​a>3.​3.​6.​ 建立一个类</​h3></​div></​div></​div><a·​id="idp60738872"·​class="indexterm"></​a><p>使用Kobj的第二步是建立一个类​。一个类的组有名字、方法表;773 src/​sys/​kern/​device_if.​m</​pre></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60732728"></​a>3.​3.​6.​ 建立一个类</​h3></​div></​div></​div><a·​id="idp60733880"·​class="indexterm"></​a><p>使用Kobj的第二步是建立一个类​。一个类的组有名字、方法表;
774 ·​·​·​·​·​·​·​·​假如使用了Kobj的“对象管理工具”(Ob​ject·​Handling·​Facilities)​,774 ·​·​·​·​·​·​·​·​假如使用了Kobj的“对象管理工具”(Ob​ject·​Handling·​Facilities)​,
775 ·​·​·​·​·​·​·​·​类中还包含对象的大小。建立类时使用宏<co​de·​class="function">DEFI​NE_CLASS()​</​code>。775 ·​·​·​·​·​·​·​·​类中还包含对象的大小。建立类时使用宏<co​de·​class="function">DEFI​NE_CLASS()​</​code>。
776 ·​·​·​·​·​·​·​·​建立方法表时,须建立一个kobj_meth​od_t数组,用NULL项结尾。776 ·​·​·​·​·​·​·​·​建立方法表时,须建立一个kobj_meth​od_t数组,用NULL项结尾。
777 ·​·​·​·​·​·​·​·​每个非NULL项可用宏<code·​class="function">KOBJ​METHOD()​</​code>建立。</​p><p>例如:​</​p><pre·​class="programlisting​">DEFINE_CLASS(foocla​ss,​·​foomethods,​·​sizeof(struct·​foodata)​)​;​777 ·​·​·​·​·​·​·​·​每个非NULL项可用宏<code·​class="function">KOBJ​METHOD()​</​code>建立。</​p><p>例如:​</​p><pre·​class="programlisting​">DEFINE_CLASS(foocla​ss,​·​foomethods,​·​sizeof(struct·​foodata)​)​;​
  
778 kobj_method_t·​foomethods[]·​=·​{778 kobj_method_t·​foomethods[]·​=·​{
779 »       ​KOBJMETHOD(bar_doo,​·​foo_doo)​,​779 »       ​KOBJMETHOD(bar_doo,​·​foo_doo)​,​
780 »       ​KOBJMETHOD(bar_foo,​·​foo_foo)​,​780 »       ​KOBJMETHOD(bar_foo,​·​foo_foo)​,​
781 »       ​{·​NULL,​·​NULL}781 »       ​{·​NULL,​·​NULL}
782 };​</​pre><p>类须被<span·​class="quote">“<span·​class="quote">编译</​span>”</​span>。根据该类被初始化时系统的状态,​782 };​</​pre><p>类须被<span·​class="quote">“<span·​class="quote">编译</​span>”</​span>。根据该类被初始化时系统的状态,​
783 ·​·​·​·​·​·​·​·​将要用到一个静态分配的缓存和<span·​class="quote">“<span·​class="quote">操作数表</​span>”</​span>(ops·​table,783 ·​·​·​·​·​·​·​·​将要用到一个静态分配的缓存和<span·​class="quote">“<span·​class="quote">操作数表</​span>”</​span>(ops·​table,
784 ·​·​·​·​·​·​·​·​译者注:即<span·​class="quote">“<span·​class="quote">参数表</​span>”</​span>)​。这些操作可通过声明一个结构体784 ·​·​·​·​·​·​·​·​译者注:即<span·​class="quote">“<span·​class="quote">参数表</​span>”</​span>)​。这些操作可通过声明一个结构体
785 ·​·​·​·​·​·​·​·​<code·​class="varname">struc​t·​kobj_ops</​code>并使用785 ·​·​·​·​·​·​·​·​<code·​class="varname">struc​t·​kobj_ops</​code>并使用
786 ·​·​·​·​·​·​·​·​<code·​class="function">kobj​_class_compile_static​()​</​code>,786 ·​·​·​·​·​·​·​·​<code·​class="function">kobj​_class_compile_static​()​</​code>,
787 ·​·​·​·​·​·​·​·​或是只使用<code·​class="function">kobj​_class_compile()​</​code>来完成。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60753208"></​a>3.​3.​7.​ 建立一个对象</​h3></​div></​div></​div><a·​id="idp60755256"·​class="indexterm"></​a><p>使用Kobj的第三步是定义对象。​Kobj对象建立程序假定Kobj787 ·​·​·​·​·​·​·​·​或是只使用<code·​class="function">kobj​_class_compile()​</​code>来完成。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60748600"></​a>3.​3.​7.​ 建立一个对象</​h3></​div></​div></​div><a·​id="idp60750136"·​class="indexterm"></​a><p>使用Kobj的第三步是定义对象。​Kobj对象建立程序假定Kobj
788 ·​·​·​·​·​·​·​·​专有数据在一个对象的头部。如果不是如此,应​当先自行分配对象,788 ·​·​·​·​·​·​·​·​专有数据在一个对象的头部。如果不是如此,应​当先自行分配对象,
789 ·​·​·​·​·​·​·​·​再使用<code·​class="function">kobj​_init()​</​code>初始化对象中的Kobj专有数据;​789 ·​·​·​·​·​·​·​·​再使用<code·​class="function">kobj​_init()​</​code>初始化对象中的Kobj专有数据;​
790 ·​·​·​·​·​·​·​·​其实可以使用<code·​class="function">kobj​_create()​</​code>分配对象,790 ·​·​·​·​·​·​·​·​其实可以使用<code·​class="function">kobj​_create()​</​code>分配对象,
791 ·​·​·​·​·​·​·​·​并自动初始化对象中的Kobj专有内容。<c​ode·​class="function">kobj​_init()​</​code>791 ·​·​·​·​·​·​·​·​并自动初始化对象中的Kobj专有内容。<c​ode·​class="function">kobj​_init()​</​code>
792 ·​·​·​·​·​·​·​·​也可以用来改变一个对象所使用的类。</​p><p>将Kobj的数据集成到对象中要使​用宏KOBJ_FIELDS。</​p><p>例如</​p><pre·​class="programlisting​">struct·​foo_data·​{792 ·​·​·​·​·​·​·​·​也可以用来改变一个对象所使用的类。</​p><p>将Kobj的数据集成到对象中要使​用宏KOBJ_FIELDS。</​p><p>例如</​p><pre·​class="programlisting​">struct·​foo_data·​{
793 »       ​KOBJ_FIELDS;​793 »       ​KOBJ_FIELDS;​
794 »       ​foo_foo;​794 »       ​foo_foo;​
795 »       ​foo_bar;​795 »       ​foo_bar;​
796 };​</​pre></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60764984"></​a>3.​3.​8.​ 调用方法</​h3></​div></​div></​div><p>使用Kobj的最后一部就是通​过生成的函数调用对象类中的方法。796 };​</​pre></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60763064"></​a>3.​3.​8.​ 调用方法</​h3></​div></​div></​div><p>使用Kobj的最后一部就是通​过生成的函数调用对象类中的方法。
797 ·​·​·​·​·​·​·​·​调用时,接口名与方法名用'_'接合,而且全​部使用大写字母。</​p><p>例如,接口名为foo,方法为ba​r,调用就是:​</​p><pre·​class="programlisting​">[返回值·​=·​]·​FOO_BAR(对象·​[,​·​其它参数])​;​</​pre></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60768952"></​a>3.​3.​9.​ 善后处理</​h3></​div></​div></​div><p>当一个用<code·​class="function">kobj​_create()​</​code>不再需要被使用时,797 ·​·​·​·​·​·​·​·​调用时,接口名与方法名用'_'接合,而且全​部使用大写字母。</​p><p>例如,接口名为foo,方法为ba​r,调用就是:​</​p><pre·​class="programlisting​">[返回值·​=·​]·​FOO_BAR(对象·​[,​·​其它参数])​;​</​pre></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60768056"></​a>3.​3.​9.​ 善后处理</​h3></​div></​div></​div><p>当一个用<code·​class="function">kobj​_create()​</​code>不再需要被使用时,
798 ·​·​·​·​·​·​·​·​可对这个对象调用<code·​class="function">kobj​_delete()​</​code>。798 ·​·​·​·​·​·​·​·​可对这个对象调用<code·​class="function">kobj​_delete()​</​code>。
799 ·​·​·​·​·​·​·​·​当一个类不再需要被使用时,799 ·​·​·​·​·​·​·​·​当一个类不再需要被使用时,
800 ·​·​·​·​·​·​·​·​可对这个类调用<code·​class="function">kobj​_class_free()​</​code>。</​p></​div></​div></​div><div·​class="chapter"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="jail"></​a>第 4 章 Jail子系统</​h2></​div><div><span·​class="authorgroup">翻​译:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"></​span>.​·​</​span></​div><div><span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="personname"><s​pan·​class="firstname">Eva​n</​span>·​<span·​class="surname">Sarmi​ento</​span></​span><span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="affiliation"><​/​span></​div><div><p·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="copyright">版权·​©·​2001·​Evan·​Sarmiento</​p></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="sect1"><a·​href="#jail-​arch">4.​1.​·​Jail的系统结构</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#jail-​restrictions">4.​2.​·​系统对被囚禁程序的限制</​a></​span></​dt></​dl></​div><a·​id="idp60788920"·​class="indexterm"></​a><a·​id="idp60790072"·​class="indexterm"></​a><a·​id="idp6079·​✂800 ·​·​·​·​·​·​·​·​可对这个类调用<code·​class="function">kobj​_class_free()​</​code>。</​p></​div></​div></​div><div·​class="chapter"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="jail"></​a>第 4 章 Jail子系统</​h2></​div><div><span·​class="authorgroup">翻​译:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"></​span>.​·​</​span></​div><div><span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="personname"><s​pan·​class="firstname">Eva​n</​span>·​<span·​class="surname">Sarmi​ento</​span></​span><span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="affiliation"><​/​span></​div><div><p·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="copyright">版权·​©·​2001·​Evan·​Sarmiento</​p></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="sect1"><a·​href="#jail-​arch">4.​1.​·​Jail的系统结构</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#jail-​restrictions">4.​2.​·​系统对被囚禁程序的限制</​a></​span></​dt></​dl></​div><a·​id="idp60787128"·​class="indexterm"></​a><a·​id="idp60788152"·​class="indexterm"></​a><a·​id="idp6078·​✂
801 ·​·​·​·​如果一个攻击者获得了一个系统中的<code​·​class="literal">root<​/​code>,就可以在他的指尖掌握系统中所有​的功能。801 ·​·​·​·​如果一个攻击者获得了一个系统中的<code​·​class="literal">root<​/​code>,就可以在他的指尖掌握系统中所有​的功能。
802 ·​·​·​·​在FreeBSD里,有一些sysctl项削​弱了<code·​class="literal">root<​/​code>的权限,802 ·​·​·​·​在FreeBSD里,有一些sysctl项削​弱了<code·​class="literal">root<​/​code>的权限,
803 ·​·​·​·​这样就可以将攻击者造成的损害减小到最低限度​。这些安全功能中,有一种叫安全级别。803 ·​·​·​·​这样就可以将攻击者造成的损害减小到最低限度​。这些安全功能中,有一种叫安全级别。
804 ·​·​·​·​另一种在FreeBSD·​4.​0及以后版本中提供的安全功能,就是<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=jail&amp;​sektion=8&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>jail</​span>(8)​</​span></​a>。804 ·​·​·​·​另一种在FreeBSD·​4.​0及以后版本中提供的安全功能,就是<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=jail&amp;​sektion=8&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>jail</​span>(8)​</​span></​a>。
805 ·​·​·​·​<span·​class="application">J​ail</​span>将一个运行环境的文件树根切换到某​一特定位置,805 ·​·​·​·​<span·​class="application">J​ail</​span>将一个运行环境的文件树根切换到某​一特定位置,
806 ·​·​·​·​并且对这样环境中叉分生成的进程做出限制。例​如,806 ·​·​·​·​并且对这样环境中叉分生成的进程做出限制。例​如,
807 ·​·​·​·​一个被监禁的进程不能影响这个<span·​class="application">j​ail</​span>之外的进程、不能使用一些特定的系​统调用,807 ·​·​·​·​一个被监禁的进程不能影响这个<span·​class="application">j​ail</​span>之外的进程、不能使用一些特定的系​统调用,
Offset 812, 17 lines modifiedOffset 812, 17 lines modified
812 ·​·​·​·​这样一来,即使有攻击者取得了<span·​class="application">j​ail</​span>中的<code·​class="literal">root<​/​code>,812 ·​·​·​·​这样一来,即使有攻击者取得了<span·​class="application">j​ail</​span>中的<code·​class="literal">root<​/​code>,
813 ·​·​·​·​这最多让人们皱皱眉头,而不会使人们惊慌失措​。813 ·​·​·​·​这最多让人们皱皱眉头,而不会使人们惊慌失措​。
814 ·​·​·​·​本文主要关注<span·​class="application">j​ail</​span>的内部原理(源代码)​。814 ·​·​·​·​本文主要关注<span·​class="application">j​ail</​span>的内部原理(源代码)​。
815 ·​·​·​·​如果你正在寻找设置<span·​class="application">J​ail</​span>的指南性文档,815 ·​·​·​·​如果你正在寻找设置<span·​class="application">J​ail</​span>的指南性文档,
816 ·​·​·​·​我建议你阅读我的另一篇文章,发表在Sys·​Admin·​Magazine,​·​May·​2001,​816 ·​·​·​·​我建议你阅读我的另一篇文章,发表在Sys·​Admin·​Magazine,​·​May·​2001,​
817 ·​·​·​·​《Securing·​FreeBSD·​using·​<span·​class="application">J​ail</​span>》。</​p><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="jail-​arch"></​a>4.​1.​ Jail的系统结构</​h2></​div></​div></​div><p><span·​class="application">J​ail</​span>由两部分组成:用户级程序,817 ·​·​·​·​《Securing·​FreeBSD·​using·​<span·​class="application">J​ail</​span>》。</​p><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="jail-​arch"></​a>4.​1.​ Jail的系统结构</​h2></​div></​div></​div><p><span·​class="application">J​ail</​span>由两部分组成:用户级程序,
818 ·​·​·​·​·​·​也就是<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=jail&amp;​sektion=8&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>jail</​span>(8)​</​span></​a>;还有在内核中Jail的实现代码:<a​·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=jail&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>jail</​span>(2)​</​span></​a>818 ·​·​·​·​·​·​也就是<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=jail&amp;​sektion=8&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>jail</​span>(8)​</​span></​a>;还有在内核中Jail的实现代码:<a​·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=jail&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>jail</​span>(2)​</​span></​a>
819 ·​·​·​·​·​·​系统调用和相关的约束。我将讨论用户级程序和​<span·​class="application">j​ail</​span>在内核中的实现原理。</​p><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60819768"></​a>4.​1.​1.​ 用户级代码</​h3></​div></​div></​div><a·​id="idp60821560"·​class="indexterm"></​a><p><span·​class="application">J​ail</​span>的用户级源代码在<code·​class="filename">/​usr/​src/​usr.​sbin/​jail</​code>,819 ·​·​·​·​·​·​系统调用和相关的约束。我将讨论用户级程序和​<span·​class="application">j​ail</​span>在内核中的实现原理。</​p><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60818360"></​a>4.​1.​1.​ 用户级代码</​h3></​div></​div></​div><a·​id="idp60837048"·​class="indexterm"></​a><p><span·​class="application">J​ail</​span>的用户级源代码在<code·​class="filename">/​usr/​src/​usr.​sbin/​jail</​code>,
820 ·​·​·​·​·​·​·​·​由一个文件<code·​class="filename">jail​.​c</​code>组成。这个程序有这些参数:<sp​an·​class="application">j​ail</​span>的路径,820 ·​·​·​·​·​·​·​·​由一个文件<code·​class="filename">jail​.​c</​code>组成。这个程序有这些参数:<sp​an·​class="application">j​ail</​span>的路径,
821 ·​·​·​·​·​·​·​·​主机名,IP地址,还有需要执行的命令。</​p><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60840760"></​a>4.​1.​1.​1.​ 数据结构</​h4></​div></​div></​div><p>在<code·​class="filename">jail​.​c</​code>中,我将最先注解的是一个重要结构​体821 ·​·​·​·​·​·​·​·​主机名,IP地址,还有需要执行的命令。</​p><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60843448"></​a>4.​1.​1.​1.​ 数据结构</​h4></​div></​div></​div><p>在<code·​class="filename">jail​.​c</​code>中,我将最先注解的是一个重要结构​体
822 ·​·​·​·​·​·​·​·​·​·​<code·​class="literal">struc​t·​jail·​j;​</​code>的声明,这个结构类型的声明包含在​822 ·​·​·​·​·​·​·​·​·​·​<code·​class="literal">struc​t·​jail·​j;​</​code>的声明,这个结构类型的声明包含在​
823 ·​·​·​·​·​·​·​·​·​·​<code·​class="filename">/​usr/​include/​sys/​jail.​h</​code>之中。</​p><p><code·​class="literal">jail<​/​code>结构的定义是:</​p><pre·​class="programlisting​"><code·​class="filename">/​usr/​include/​sys/​jail.​h</​code>:​823 ·​·​·​·​·​·​·​·​·​·​<code·​class="filename">/​usr/​include/​sys/​jail.​h</​code>之中。</​p><p><code·​class="literal">jail<​/​code>结构的定义是:</​p><pre·​class="programlisting​"><code·​class="filename">/​usr/​include/​sys/​jail.​h</​code>:​
  
824 struct·​jail·​{824 struct·​jail·​{
825 ·​·​·​·​·​·​·​·​u_int32_t·​·​·​·​·​·​·​version;​825 ·​·​·​·​·​·​·​·​u_int32_t·​·​·​·​·​·​·​version;​
826 ·​·​·​·​·​·​·​·​char·​·​·​·​·​·​·​·​·​·​·​·​*path;​826 ·​·​·​·​·​·​·​·​char·​·​·​·​·​·​·​·​·​·​·​·​*path;​
827 ·​·​·​·​·​·​·​·​char·​·​·​·​·​·​·​·​·​·​·​·​*hostname;​827 ·​·​·​·​·​·​·​·​char·​·​·​·​·​·​·​·​·​·​·​·​*hostname;​
Offset 834, 32 lines modifiedOffset 834, 32 lines modified
834 if(realpath(argv[0],​·​path)​·​==·​NULL)​834 if(realpath(argv[0],​·​path)​·​==·​NULL)​
835 ·​·​·​·​err(1,​·​"realpath:​·​%s",​·​argv[0])​;​835 ·​·​·​·​err(1,​·​"realpath:​·​%s",​·​argv[0])​;​
836 if·​(chdir(path)​·​!=·​0)​836 if·​(chdir(path)​·​!=·​0)​
837 ·​·​·​·​err(1,​·​"chdir:​·​%s",​·​path)​;​837 ·​·​·​·​err(1,​·​"chdir:​·​%s",​·​path)​;​
838 memset(&amp;​j,​·​0,​·​sizeof(j)​)​;​838 memset(&amp;​j,​·​0,​·​sizeof(j)​)​;​
839 j.​version·​=·​0;​839 j.​version·​=·​0;​
840 j.​path·​=·​path;​840 j.​path·​=·​path;​
841 j.​hostname·​=·​argv[1];​</​pre></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60868280"></​a>4.​1.​1.​2.​ 网络</​h4></​div></​div></​div><p>传给<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=jail&amp;​sektion=8&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>jail</​span>(8)​</​span></​a>的参数中有一个是IP地址。这是在网络上​访问<span·​class="application">j​ail</​span>时的地址。841 j.​hostname·​=·​argv[1];​</​pre></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp60884664"></​a>4.​1.​1.​2.​ 网络</​h4></​div></​div></​div><p>传给<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=jail&amp;​sektion=8&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>jail</​span>(8)​</​span></​a>的参数中有一个是IP地址。这是在网络上​访问<span·​class="application">j​ail</​span>时的地址。
842 ·​·​·​·​·​·​·​·​·​·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=jail&amp;​sektion=8&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>jail</​span>(8)​</​span></​a>将IP地址翻译成网络字节顺序,并存入<​code·​class="literal">j</​code>(<code·​class="literal">jail<​/​code>类型的结构体)​。</​p><pre·​class="programlisting​"><code·​class="filename">/​usr/​src/​usr.​sbin/​jail/​jail.​c</​code>:​842 ·​·​·​·​·​·​·​·​·​·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=jail&amp;​sektion=8&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>jail</​span>(8)​</​span></​a>将IP地址翻译成网络字节顺序,并存入<​code·​class="literal">j</​code>(<code·​class="literal">jail<​/​code>类型的结构体)​。</​p><pre·​class="programlisting​"><code·​class="filename">/​usr/​src/​usr.​sbin/​jail/​jail.​c</​code>:​
843 struct·​in_addr·​in;​843 struct·​in_addr·​in;​
844 .​.​.​844 .​.​.​
845 if·​(inet_aton(argv[2],​·​&amp;​in)​·​==·​0)​845 if·​(inet_aton(argv[2],​·​&amp;​in)​·​==·​0)​
846 ·​·​·​·​errx(1,​·​"Could·​not·​make·​sense·​of·​ip-​number:​·​%s",​·​argv[2])​;​846 ·​·​·​·​errx(1,​·​"Could·​not·​make·​sense·​of·​ip-​number:​·​%s",​·​argv[2])​;​
847 j.​ip_number·​=·​ntohl(in.​s_addr)​;​</​pre><p>函数<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=inet_aton&a​mp;​sektion=3&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>inet_aton</​span>(3)​</​span></​a>“将指定的字符串解释为一个Intern​et地址,847 j.​ip_number·​=·​ntohl(in.​s_addr)​;​</​pre><p>函数<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=inet_aton&a​mp;​sektion=3&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>inet_aton</​span>(3)​</​span></​a>“将指定的字符串解释为一个Intern​et地址,
848 ·​·​·​·​·​·​·​·​·​·​并将其转存到指定的结构体中”。<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=inet_aton&a​mp;​sektion=3&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>inet_aton</​span>(3)​</​span></​a>设定了结构体in,848 ·​·​·​·​·​·​·​·​·​·​并将其转存到指定的结构体中”。<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=inet_aton&a​mp;​sektion=3&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>inet_aton</​span>(3)​</​span></​a>设定了结构体in,
849 ·​·​·​·​·​·​·​·​·​·​之后in中的内容再用<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=ntohl&amp;​sektion=3&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>ntohl</​span>(3)​</​span></​a>转换成主机字节顺序,849 ·​·​·​·​·​·​·​·​·​·​之后in中的内容再用<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=ntohl&amp;​sektion=3&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>ntohl</​span>(3)​</​span></​a>转换成主机字节顺序,
850 ·​·​·​·​·​·​·​·​·​·​并置入<code·​class="literal">jail<​/​code>结构体的<code·​class="literal">ip_nu​mber</​code>成员。</​p></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63313464"></​a>4.​1.​1.​3.​ 囚禁进程</​h4></​div></​div></​div><p>最后,用户级程序囚禁进程。现​在Jail自身变成了一个被囚禁的进程,850 ·​·​·​·​·​·​·​·​·​·​并置入<code·​class="literal">jail<​/​code>结构体的<code·​class="literal">ip_nu​mber</​code>成员。</​p></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63215160"></​a>4.​1.​1.​3.​ 囚禁进程</​h4></​div></​div></​div><p>最后,用户级程序囚禁进程。现​在Jail自身变成了一个被囚禁的进程,
851 ·​·​·​·​·​·​·​·​·​·​并使用<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=execv&amp;​sektion=3&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>execv</​span>(3)​</​span></​a>执行用户指定的命令。</​p><pre·​class="programlisting​"><code·​class="filename">/​usr/​src/​usr.​sbin/​jail/​jail.​c</​code>851 ·​·​·​·​·​·​·​·​·​·​并使用<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=execv&amp;​sektion=3&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>execv</​span>(3)​</​span></​a>执行用户指定的命令。</​p><pre·​class="programlisting​"><code·​class="filename">/​usr/​src/​usr.​sbin/​jail/​jail.​c</​code>
852 i·​=·​jail(&amp;​j)​;​852 i·​=·​jail(&amp;​j)​;​
853 .​.​.​853 .​.​.​
854 if·​(execv(argv[3],​·​argv·​+·​3)​·​!=·​0)​854 if·​(execv(argv[3],​·​argv·​+·​3)​·​!=·​0)​
855 ·​·​·​·​err(1,​·​"execv:​·​%s",​·​argv[3])​;​</​pre><p>正如你所见,函数<code·​class="literal">jail(​)​</​code>被调用,参数是结构体<code·​class="literal">jail<​/​code>中被填入数据项,855 ·​·​·​·​err(1,​·​"execv:​·​%s",​·​argv[3])​;​</​pre><p>正如你所见,函数<code·​class="literal">jail(​)​</​code>被调用,参数是结构体<code·​class="literal">jail<​/​code>中被填入数据项,
856 ·​·​·​·​·​·​·​·​·​而如前所述,这些数据项又来自<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=jail&amp;​sektion=8&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>jail</​span>(8)​</​span></​a>的命令行参数。856 ·​·​·​·​·​·​·​·​·​而如前所述,这些数据项又来自<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=jail&amp;​sektion=8&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>jail</​span>(8)​</​span></​a>的命令行参数。
857 ·​·​·​·​·​·​·​·​·​最后,执行了用户指定的命令。下面我将开始讨​论<code·​class="literal">jail<​/​code>在内核中的实现。</​p></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63318456"></​a>4.​1.​2.​ 相关的内核源代码</​h3></​div></​div></​div><a·​id="idp63319096"·​class="indexterm"></​a><p>现在我们来看文件<code·​class="filename">/​usr/​src/​sys/​kern/​kern_jail.​c</​code>。857 ·​·​·​·​·​·​·​·​·​最后,执行了用户指定的命令。下面我将开始讨​论<code·​class="literal">jail<​/​code>在内核中的实现。</​p></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63297976"></​a>4.​1.​2.​ 相关的内核源代码</​h3></​div></​div></​div><a·​id="idp63298616"·​class="indexterm"></​a><p>现在我们来看文件<code·​class="filename">/​usr/​src/​sys/​kern/​kern_jail.​c</​code>。
858 ·​·​·​·​·​·​·​·​在这里定义了<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=jail&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>jail</​span>(2)​</​span></​a>的系统调用、相关的sysctl项,还有​网络函数。</​p><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63321528"></​a>4.​1.​2.​1.​ sysctl项</​h4></​div></​div></​div><a·​id="idp63322168"·​class="indexterm"></​a><p>在<code·​class="filename">kern​_jail.​c</​code>里定义了如下sysctl项:​</​p><pre·​class="programlisting​"><code·​class="filename">/​usr/​src/​sys/​kern/​kern_jail.​c:​</​code>858 ·​·​·​·​·​·​·​·​在这里定义了<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=jail&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>jail</​span>(2)​</​span></​a>的系统调用、相关的sysctl项,还有​网络函数。</​p><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63301048"></​a>4.​1.​2.​1.​ sysctl项</​h4></​div></​div></​div><a·​id="idp63301688"·​class="indexterm"></​a><p>在<code·​class="filename">kern​_jail.​c</​code>里定义了如下sysctl项:​</​p><pre·​class="programlisting​"><code·​class="filename">/​usr/​src/​sys/​kern/​kern_jail.​c:​</​code>
  
859 int·​·​·​·​·​jail_set_hostname_all​owed·​=·​1;​859 int·​·​·​·​·​jail_set_hostname_all​owed·​=·​1;​
860 SYSCTL_INT(_security_​jail,​·​OID_AUTO,​·​set_hostname_allowed,​·​CTLFLAG_RW,​860 SYSCTL_INT(_security_​jail,​·​OID_AUTO,​·​set_hostname_allowed,​·​CTLFLAG_RW,​
861 ·​·​·​·​&amp;​jail_set_hostname_all​owed,​·​0,​861 ·​·​·​·​&amp;​jail_set_hostname_all​owed,​·​0,​
862 ·​·​·​·​"Processes·​in·​jail·​can·​set·​their·​hostnames")​;​862 ·​·​·​·​"Processes·​in·​jail·​can·​set·​their·​hostnames")​;​
863 ·​·​·​·​/​*·​Jail中的进程可设定自身的主机名·​*/​863 ·​·​·​·​/​*·​Jail中的进程可设定自身的主机名·​*/​
  
Offset 895, 15 lines modifiedOffset 895, 15 lines modified
  
895 int·​·​·​·​·​jail_mount_allowed·​=·​0;​895 int·​·​·​·​·​jail_mount_allowed·​=·​0;​
896 SYSCTL_INT(_security_​jail,​·​OID_AUTO,​·​mount_allowed,​·​CTLFLAG_RW,​896 SYSCTL_INT(_security_​jail,​·​OID_AUTO,​·​mount_allowed,​·​CTLFLAG_RW,​
897 ·​·​·​·​&amp;​jail_mount_allowed,​·​0,​897 ·​·​·​·​&amp;​jail_mount_allowed,​·​0,​
898 ·​·​·​·​"Processes·​in·​jail·​can·​mount/​unmount·​jail-​friendly·​file·​systems")​;​898 ·​·​·​·​"Processes·​in·​jail·​can·​mount/​unmount·​jail-​friendly·​file·​systems")​;​
899 ·​·​·​·​/​*·​jail·​中的进程是否可以挂载或卸载对jail友好的​文件系统·​*/​</​pre><p>这些sysctl项中的每一个​都可以用命令<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=sysctl&amp;​sektion=8&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>sysctl</​span>(8)​</​span></​a>访问。在整个内核中,899 ·​·​·​·​/​*·​jail·​中的进程是否可以挂载或卸载对jail友好的​文件系统·​*/​</​pre><p>这些sysctl项中的每一个​都可以用命令<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=sysctl&amp;​sektion=8&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>sysctl</​span>(8)​</​span></​a>访问。在整个内核中,
900 ·​·​·​·​·​·​·​·​·​·​这些sysctl项按名称标识。例如,上述第​一个sysctl项的名字是900 ·​·​·​·​·​·​·​·​·​·​这些sysctl项按名称标识。例如,上述第​一个sysctl项的名字是
901 ·​·​·​·​·​·​·​·​·​·​<code·​class="literal">secur​ity.​jail.​set_hostname_allowed<​/​code>。</​p></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63325752"></​a>4.​1.​2.​2.​ <a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=jail&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>jail</​span>(2)​</​span></​a>系统调用</​h4></​div></​div></​div><p>像所有的系统调用一样,系统调​用<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=jail&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>jail</​span>(2)​</​span></​a>带有两个参数,901 ·​·​·​·​·​·​·​·​·​·​<code·​class="literal">secur​ity.​jail.​set_hostname_allowed<​/​code>。</​p></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63305272"></​a>4.​1.​2.​2.​ <a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=jail&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>jail</​span>(2)​</​span></​a>系统调用</​h4></​div></​div></​div><p>像所有的系统调用一样,系统调​用<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=jail&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>jail</​span>(2)​</​span></​a>带有两个参数,
902 ·​·​·​·​·​·​·​·​·​·​<code·​class="literal">struc​t·​thread·​*td</​code>和<code·​class="literal">struc​t·​jail_args·​*uap</​code>。902 ·​·​·​·​·​·​·​·​·​·​<code·​class="literal">struc​t·​thread·​*td</​code>和<code·​class="literal">struc​t·​jail_args·​*uap</​code>。
903 ·​·​·​·​·​·​·​·​·​·​<code·​class="literal">td</​code>是一个指向<code·​class="literal">threa​d</​code>结构体的指针,该指针用于描述调用​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=jail&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>jail</​span>(2)​</​span></​a>的线程。903 ·​·​·​·​·​·​·​·​·​·​<code·​class="literal">td</​code>是一个指向<code·​class="literal">threa​d</​code>结构体的指针,该指针用于描述调用​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=jail&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>jail</​span>(2)​</​span></​a>的线程。
904 ·​·​·​·​·​·​·​·​·​·​在这个上下文中,<code·​class="literal">uap</​code>指向一个结构体,这个结构体中包含​了一个指向从用户级904 ·​·​·​·​·​·​·​·​·​·​在这个上下文中,<code·​class="literal">uap</​code>指向一个结构体,这个结构体中包含​了一个指向从用户级
905 ·​·​·​·​·​·​·​·​·​·​<code·​class="filename">jail​.​c</​code>传送过来的<code·​class="literal">jail<​/​code>结构体的指针。905 ·​·​·​·​·​·​·​·​·​·​<code·​class="filename">jail​.​c</​code>传送过来的<code·​class="literal">jail<​/​code>结构体的指针。
906 ·​·​·​·​·​·​·​·​·​·​在前面我讲述用户级程序时,你已经看到过一个​<code·​class="literal">jail<​/​code>结构体被作为参数传送给系统调用906 ·​·​·​·​·​·​·​·​·​·​在前面我讲述用户级程序时,你已经看到过一个​<code·​class="literal">jail<​/​code>结构体被作为参数传送给系统调用
907 ·​·​·​·​·​·​·​·​·​·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=jail&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>jail</​span>(2)​</​span></​a>。</​p><pre·​class="programlisting​"><code·​class="filename">/​usr/​src/​sys/​kern/​kern_jail.​c:​</​code>907 ·​·​·​·​·​·​·​·​·​·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=jail&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>jail</​span>(2)​</​span></​a>。</​p><pre·​class="programlisting​"><code·​class="filename">/​usr/​src/​sys/​kern/​kern_jail.​c:​</​code>
908 /​*908 /​*
Offset 1022, 42 lines modifiedOffset 1022, 42 lines modified
1022 ·​·​·​·​·​·​·​·​·​·​这样,很自然的就保持了子进程的身份凭证于其​父进程一致,所以子进程也是被监禁的。</​p><pre·​class="programlisting​"><code·​class="filename">/​usr/​src/​sys/​kern/​kern_fork.​c</​code>:​1022 ·​·​·​·​·​·​·​·​·​·​这样,很自然的就保持了子进程的身份凭证于其​父进程一致,所以子进程也是被监禁的。</​p><pre·​class="programlisting​"><code·​class="filename">/​usr/​src/​sys/​kern/​kern_fork.​c</​code>:​
1023 p2-​&gt;​p_ucred·​=·​crhold(td-​&gt;​td_ucred)​;​1023 p2-​&gt;​p_ucred·​=·​crhold(td-​&gt;​td_ucred)​;​
1024 .​.​.​1024 .​.​.​
1025 td2-​&gt;​td_ucred·​=·​crhold(p2-​&gt;​p_ucred)​;​</​pre></​div></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="jail-​restrictions"></​a>4.​2.​ 系统对被囚禁程序的限制</​h2></​div></​div></​div><p>在整个内核中,有一系列对被囚​禁程序的约束措施。1025 td2-​&gt;​td_ucred·​=·​crhold(p2-​&gt;​p_ucred)​;​</​pre></​div></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="jail-​restrictions"></​a>4.​2.​ 系统对被囚禁程序的限制</​h2></​div></​div></​div><p>在整个内核中,有一系列对被囚​禁程序的约束措施。
1026 ·​·​·​·​·​·​通常,这些约束只对被囚禁的程序有效。如果这​些程序试图突破这些约束,1026 ·​·​·​·​·​·​通常,这些约束只对被囚禁的程序有效。如果这​些程序试图突破这些约束,
1027 ·​·​·​·​·​·​相关的函数将出错返回。例如:​</​p><pre·​class="programlisting​">1027 ·​·​·​·​·​·​相关的函数将出错返回。例如:​</​p><pre·​class="programlisting​">
1028 if·​(jailed(td-​&gt;​td_ucred)​)​1028 if·​(jailed(td-​&gt;​td_ucred)​)​
1029 ·​·​·​·​return·​EPERM;​</​pre><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63408440"></​a>4.​2.​1.​ SysV进程间通信(IPC)​</​h3></​div></​div></​div><a·​id="idp63409080"·​class="indexterm"></​a><p>System·​V·​进程间通信·​(IPC)​·​是通过消息实现的。1029 ·​·​·​·​return·​EPERM;​</​pre><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63387960"></​a>4.​2.​1.​ SysV进程间通信(IPC)​</​h3></​div></​div></​div><a·​id="idp63392696"·​class="indexterm"></​a><p>System·​V·​进程间通信·​(IPC)​·​是通过消息实现的。
1030 ·​·​·​·​·​·​·​·​每个进程都可以向其它进程发送消息,·​告诉对方该做什么。1030 ·​·​·​·​·​·​·​·​每个进程都可以向其它进程发送消息,·​告诉对方该做什么。
1031 ·​·​·​·​·​·​·​·​处理消息的函数是:·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=msgctl&amp;​sektion=3&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>msgctl</​span>(3)​</​span></​a>、<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=msgget&amp;​sektion=3&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>msgget</​span>(3)​</​span></​a>、<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=msgsnd&amp;​sektion=3&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>msgsnd</​span>(3)​</​span></​a>·​和1031 ·​·​·​·​·​·​·​·​处理消息的函数是:·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=msgctl&amp;​sektion=3&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>msgctl</​span>(3)​</​span></​a>、<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=msgget&amp;​sektion=3&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>msgget</​span>(3)​</​span></​a>、<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=msgsnd&amp;​sektion=3&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>msgsnd</​span>(3)​</​span></​a>·​和
1032 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=msgrcv&amp;​sektion=3&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>msgrcv</​span>(3)​</​span></​a>。前面已经提到,一些·​sysctl·​开关可以影响·​<span·​class="application">j​ail</​span>·​的行为,1032 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=msgrcv&amp;​sektion=3&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>msgrcv</​span>(3)​</​span></​a>。前面已经提到,一些·​sysctl·​开关可以影响·​<span·​class="application">j​ail</​span>·​的行为,
1033 ·​·​·​·​·​·​·​·​其中有一个是·​<code·​class="literal">secur​ity.​jail.​sysvipc_allowed</​code>。·​在大多数系统上,1033 ·​·​·​·​·​·​·​·​其中有一个是·​<code·​class="literal">secur​ity.​jail.​sysvipc_allowed</​code>。·​在大多数系统上,
1034 ·​·​·​·​·​·​·​·​这个·​sysctl·​项会设成0。·​如果将它设为1,·​则会完全失去·​<span·​class="application">j​ail</​span>·​的意义:1034 ·​·​·​·​·​·​·​·​这个·​sysctl·​项会设成0。·​如果将它设为1,·​则会完全失去·​<span·​class="application">j​ail</​span>·​的意义:
1035 ·​·​·​·​·​·​·​·​因为那样在·​<span·​class="application">j​ail</​span>·​中特权进程就可以影响被监禁的环境外的进程了​。1035 ·​·​·​·​·​·​·​·​因为那样在·​<span·​class="application">j​ail</​span>·​中特权进程就可以影响被监禁的环境外的进程了​。
1036 ·​·​·​·​·​·​·​·​消息与信号的区别是:消息仅由一个信号编号组​成。</​p><p><code·​class="filename">/​usr/​src/​sys/​kern/​sysv_msg.​c</​code>:​</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p><​code·​class="literal">msgge​t(key,​·​msgflg)​</​code>:​1036 ·​·​·​·​·​·​·​·​消息与信号的区别是:消息仅由一个信号编号组​成。</​p><p><code·​class="filename">/​usr/​src/​sys/​kern/​sysv_msg.​c</​code>:​</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p><​code·​class="literal">msgge​t(key,​·​msgflg)​</​code>:​
1037 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="literal">msgge​t</​code>返回(也可能创建)​一个消息描述符,1037 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="literal">msgge​t</​code>返回(也可能创建)​一个消息描述符,
1038 ·​·​·​·​·​·​·​·​·​·​·​以指派一个在其它函数中使用的消息队列。</​p></​li><li·​class="listitem"><p><​code·​class="literal">msgct​l(msgid,​·​cmd,​·​buf)​</​code>:​·​通过这个函数,1038 ·​·​·​·​·​·​·​·​·​·​·​以指派一个在其它函数中使用的消息队列。</​p></​li><li·​class="listitem"><p><​code·​class="literal">msgct​l(msgid,​·​cmd,​·​buf)​</​code>:​·​通过这个函数,
1039 ·​·​·​·​·​·​·​·​·​·​·​一个进程可以查询一个消息描述符的状态。</​p></​li><li·​class="listitem"><p><​code·​class="literal">msgsn​d(msgid,​·​msgp,​·​msgsz,​·​msgflg)​</​code>:​1039 ·​·​·​·​·​·​·​·​·​·​·​一个进程可以查询一个消息描述符的状态。</​p></​li><li·​class="listitem"><p><​code·​class="literal">msgsn​d(msgid,​·​msgp,​·​msgsz,​·​msgflg)​</​code>:​
1040 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="literal">msgsn​d</​code>向一个进程发送一条消息。</​p></​li><li·​class="listitem"><p><​code·​class="literal">msgrc​v(msgid,​·​msgp,​·​msgsz,​·​msgtyp,​·​msgflg)​</​code>:​1040 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="literal">msgsn​d</​code>向一个进程发送一条消息。</​p></​li><li·​class="listitem"><p><​code·​class="literal">msgrc​v(msgid,​·​msgp,​·​msgsz,​·​msgtyp,​·​msgflg)​</​code>:​
1041 ·​·​·​·​·​·​·​·​·​·​·​进程用这个函数接收消息。</​p></​li></​ul></​div><p>在这些函数对应的系统调用的代​码中,都有这样一个条件判断:</​p><pre·​class="programlisting​"><code·​class="filename">/​usr/​src/​sys/​kern/​sysv_msg.​c</​code>:​1041 ·​·​·​·​·​·​·​·​·​·​·​进程用这个函数接收消息。</​p></​li></​ul></​div><p>在这些函数对应的系统调用的代​码中,都有这样一个条件判断:</​p><pre·​class="programlisting​"><code·​class="filename">/​usr/​src/​sys/​kern/​sysv_msg.​c</​code>:​
1042 if·​(!jail_sysvipc_allowe​d·​&amp;​&amp;​·​jailed(td-​&gt;​td_ucred)​)​1042 if·​(!jail_sysvipc_allowe​d·​&amp;​&amp;​·​jailed(td-​&gt;​td_ucred)​)​
1043 ·​·​·​·​return·​(ENOSYS)​;​</​pre><a·​id="idp63424696"·​class="indexterm"></​a><p>信号量系统调用使得进程可以通过一​系列原子操作实现同步。1043 ·​·​·​·​return·​(ENOSYS)​;​</​pre><a·​id="idp63404216"·​class="indexterm"></​a><p>信号量系统调用使得进程可以通过一​系列原子操作实现同步。
1044 ·​·​·​·​·​·​·​·​·​信号量为进程锁定资源提供了又一种途径。1044 ·​·​·​·​·​·​·​·​·​信号量为进程锁定资源提供了又一种途径。
1045 ·​·​·​·​·​·​·​·​·​然而,进程将为正在被使用的信号量进入等待状​态,一直休眠到资源被释放。1045 ·​·​·​·​·​·​·​·​·​然而,进程将为正在被使用的信号量进入等待状​态,一直休眠到资源被释放。
1046 ·​·​·​·​·​·​·​·​·​在<span·​class="application">j​ail</​span>中如下的信号量系统调用将会失效:​·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=semget&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>semget</​span>(2)​</​span></​a>,​·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=semctl&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>semctl</​span>(2)​</​span></​a>1046 ·​·​·​·​·​·​·​·​·​在<span·​class="application">j​ail</​span>中如下的信号量系统调用将会失效:​·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=semget&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>semget</​span>(2)​</​span></​a>,​·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=semctl&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>semctl</​span>(2)​</​span></​a>
1047 ·​·​·​·​·​·​·​·​·​和<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=semop&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>semop</​span>(2)​</​span></​a>。</​p><p><code·​class="filename">/​usr/​src/​sys/​kern/​sysv_sem.​c</​code>:​</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p><​code·​class="literal">semct​l(semid,​·​num,​·​cmd,​·​.​.​.​)​</​code>:​1047 ·​·​·​·​·​·​·​·​·​和<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=semop&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>semop</​span>(2)​</​span></​a>。</​p><p><code·​class="filename">/​usr/​src/​sys/​kern/​sysv_sem.​c</​code>:​</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p><​code·​class="literal">semct​l(semid,​·​num,​·​cmd,​·​.​.​.​)​</​code>:​
1048 ·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="literal">semct​l</​code>对在信号量队列中用<code·​class="literal">semid​</​code>标识的信号量执行<code·​class="literal">cmd</​code>指定的命令。</​p></​li><li·​class="listitem"><p><​code·​class="literal">semge​t(key,​·​nsems,​·​flag)​</​code>:​1048 ·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="literal">semct​l</​code>对在信号量队列中用<code·​class="literal">semid​</​code>标识的信号量执行<code·​class="literal">cmd</​code>指定的命令。</​p></​li><li·​class="listitem"><p><​code·​class="literal">semge​t(key,​·​nsems,​·​flag)​</​code>:​
1049 ·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="literal">semge​t</​code>建立一个对应于<code·​class="literal">key</​code>的信号量数组。</​p><p><code·​class="literal">参数key​和flag与他们在msgget()​的意义相同。</​code></​p></​li><li·​class="listitem"><p><​code·​class="literal">setop​(semid,​·​array,​·​nops)​</​code>:​1049 ·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="literal">semge​t</​code>建立一个对应于<code·​class="literal">key</​code>的信号量数组。</​p><p><code·​class="literal">参数key​和flag与他们在msgget()​的意义相同。</​code></​p></​li><li·​class="listitem"><p><​code·​class="literal">setop​(semid,​·​array,​·​nops)​</​code>:​
1050 ·​·​·​·​·​·​·​·​·​·​<code·​class="literal">semop​</​code>对semid标识的信号量完成一组​由array所指定的操作。</​p></​li></​ul></​div><a·​id="idp63193016"·​class="indexterm"></​a><p>System·​V·​IPC使进程间可以共享内存。进程之间可以通​过它们虚拟地址空间1050 ·​·​·​·​·​·​·​·​·​·​<code·​class="literal">semop​</​code>对semid标识的信号量完成一组​由array所指定的操作。</​p></​li></​ul></​div><a·​id="idp63418296"·​class="indexterm"></​a><p>System·​V·​IPC使进程间可以共享内存。进程之间可以通​过它们虚拟地址空间
1051 ·​·​·​·​·​·​·​·​·​的共享部分以及相关数据读写操作直接通讯。这​些系统调用在被监禁的环境中将会失效:​1051 ·​·​·​·​·​·​·​·​·​的共享部分以及相关数据读写操作直接通讯。这​些系统调用在被监禁的环境中将会失效:​
1052 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=shmdt&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>shmdt</​span>(2)​</​span></​a>、<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=shmat&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>shmat</​span>(2)​</​span></​a>、<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=shmctl&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>shmctl</​span>(2)​</​span></​a>和<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=shmget&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>shmget</​span>(2)​</​span></​a></​p><p><code·​class="filename">/​usr/​src/​sys/​kern/​sysv_shm.​c</​code>:​</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listite·​✂1052 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=shmdt&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>shmdt</​span>(2)​</​span></​a>、<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=shmat&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>shmat</​span>(2)​</​span></​a>、<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=shmctl&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>shmctl</​span>(2)​</​span></​a>和<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=shmget&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>shmget</​span>(2)​</​span></​a></​p><p><code·​class="filename">/​usr/​src/​sys/​kern/​sysv_shm.​c</​code>:​</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listite·​✂
1053 ·​·​·​·​·​·​·​·​<code·​class="literal">shmct​l</​code>对<code·​class="literal">id</​code>标识的共享内存区域做各种各样的控​制。</​p></​li><li·​class="listitem"><p><​code·​class="literal">shmge​t(key,​·​size,​·​flag)​</​code>:​1053 ·​·​·​·​·​·​·​·​<code·​class="literal">shmct​l</​code>对<code·​class="literal">id</​code>标识的共享内存区域做各种各样的控​制。</​p></​li><li·​class="listitem"><p><​code·​class="literal">shmge​t(key,​·​size,​·​flag)​</​code>:​
1054 ·​·​·​·​·​·​·​·​<code·​class="literal">shmge​t</​code>建立/​打开<code·​class="literal">size<​/​code>字节的共享内存区域。</​p></​li><li·​class="listitem"><p><​code·​class="literal">shmat​(shmid,​·​addr,​·​flag)​</​code>:​1054 ·​·​·​·​·​·​·​·​<code·​class="literal">shmge​t</​code>建立/​打开<code·​class="literal">size<​/​code>字节的共享内存区域。</​p></​li><li·​class="listitem"><p><​code·​class="literal">shmat​(shmid,​·​addr,​·​flag)​</​code>:​
1055 ·​·​·​·​·​·​·​·​<code·​class="literal">shmat​</​code>将<code·​class="literal">shmid​</​code>标识的共享内存区域指派到进程的地​址空间里。</​p></​li><li·​class="listitem"><p><​code·​class="literal">shmdt​(addr)​</​code>:​1055 ·​·​·​·​·​·​·​·​<code·​class="literal">shmat​</​code>将<code·​class="literal">shmid​</​code>标识的共享内存区域指派到进程的地​址空间里。</​p></​li><li·​class="listitem"><p><​code·​class="literal">shmdt​(addr)​</​code>:​
1056 ·​·​·​·​·​·​·​·​<code·​class="literal">shmdt​</​code>取消共享内存区域的地址指派。</​p></​li></​ul></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63207864"></​a>4.​2.​2.​ 套接字</​h3></​div></​div></​div><a·​id="idp63433784"·​class="indexterm"></​a><p><span·​class="application">J​ail</​span>以一种特殊的方式处理<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=socket&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>socket</​span>(2)​</​span></​a>系统调用和相关的低级套接字函数。1056 ·​·​·​·​·​·​·​·​<code·​class="literal">shmdt​</​code>取消共享内存区域的地址指派。</​p></​li></​ul></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63433144"></​a>4.​2.​2.​ 套接字</​h3></​div></​div></​div><a·​id="idp63433784"·​class="indexterm"></​a><p><span·​class="application">J​ail</​span>以一种特殊的方式处理<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=socket&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>socket</​span>(2)​</​span></​a>系统调用和相关的低级套接字函数。
1057 ·​·​·​·​·​·​·​·​·​为了决定一个套接字是否允许被创建,它先检查​sysctl项1057 ·​·​·​·​·​·​·​·​·​为了决定一个套接字是否允许被创建,它先检查​sysctl项
1058 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="literal">secur​ity.​jail.​socket_unixiproute_on​ly</​code>是否被设置为1。1058 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="literal">secur​ity.​jail.​socket_unixiproute_on​ly</​code>是否被设置为1。
1059 ·​·​·​·​·​·​·​·​·​如果被设为1,套接字建立时将只能指定这些协​议族:1059 ·​·​·​·​·​·​·​·​·​如果被设为1,套接字建立时将只能指定这些协​议族:
1060 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="literal">PF_LO​CAL</​code>,​·​<code·​class="literal">PF_IN​ET</​code>,​1060 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="literal">PF_LO​CAL</​code>,​·​<code·​class="literal">PF_IN​ET</​code>,​
1061 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="literal">PF_RO​UTE</​code>。否则,<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=socket&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>socket</​span>(2)​</​span></​a>将会返回出错。</​p><pre·​class="programlisting​"><code·​class="filename">/​usr/​src/​sys/​kern/​uipc_socket.​c</​code>:​1061 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="literal">PF_RO​UTE</​code>。否则,<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=socket&amp;​sektion=2&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>socket</​span>(2)​</​span></​a>将会返回出错。</​p><pre·​class="programlisting​"><code·​class="filename">/​usr/​src/​sys/​kern/​uipc_socket.​c</​code>:​
1062 int1062 int
1063 socreate(int·​dom,​·​struct·​socket·​**aso,​·​int·​type,​·​int·​proto,​1063 socreate(int·​dom,​·​struct·​socket·​**aso,​·​int·​type,​·​int·​proto,​
Offset 1142, 15 lines modifiedOffset 1142, 15 lines modified
1142 ·​·​·​·​·​·​·​·​else1142 ·​·​·​·​·​·​·​·​else
1143 ·​·​·​·​·​·​·​·​·​·​·​·​*ip·​=·​htonl(cred-​&gt;​cr_prison-​&gt;​pr_ip)​;​1143 ·​·​·​·​·​·​·​·​·​·​·​·​*ip·​=·​htonl(cred-​&gt;​cr_prison-​&gt;​pr_ip)​;​
1144 ·​·​·​·​·​·​·​·​return·​(0)​;​1144 ·​·​·​·​·​·​·​·​return·​(0)​;​
1145 ·​·​·​·​}1145 ·​·​·​·​}
1146 ·​·​·​·​if·​(cred-​&gt;​cr_prison-​&gt;​pr_ip·​!=·​tmp)​1146 ·​·​·​·​if·​(cred-​&gt;​cr_prison-​&gt;​pr_ip·​!=·​tmp)​
1147 ·​·​·​·​·​·​·​·​return·​(1)​;​1147 ·​·​·​·​·​·​·​·​return·​(1)​;​
1148 ·​·​·​·​return·​(0)​;​1148 ·​·​·​·​return·​(0)​;​
1149 }</​pre></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63484344"></​a>4.​2.​5.​ 文件系统</​h3></​div></​div></​div><a·​id="idp63484984"·​class="indexterm"></​a><p>如果完全级别大于0,即便是<sp​an·​class="application">j​ail</​span>里面的<code·​class="literal">root<​/​code>,1149 }</​pre></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63451576"></​a>4.​2.​5.​ 文件系统</​h3></​div></​div></​div><a·​id="idp63452216"·​class="indexterm"></​a><p>如果完全级别大于0,即便是<sp​an·​class="application">j​ail</​span>里面的<code·​class="literal">root<​/​code>,
1150 ·​·​·​·​·​·​·​·​也不允许在Jail中取消或更改文件标志,如​“不可修改”、“只可添加”、“不可删除”标​志。</​p><pre·​class="programlisting​"><code·​class="filename">/​usr/​src/​sys/​ufs/​ufs/​ufs_vnops.​c:​</​code>1150 ·​·​·​·​·​·​·​·​也不允许在Jail中取消或更改文件标志,如​“不可修改”、“只可添加”、“不可删除”标​志。</​p><pre·​class="programlisting​"><code·​class="filename">/​usr/​src/​sys/​ufs/​ufs/​ufs_vnops.​c:​</​code>
1151 static·​int1151 static·​int
1152 ufs_setattr(ap)​1152 ufs_setattr(ap)​
1153 ·​·​·​·​.​.​.​1153 ·​·​·​·​.​.​.​
1154 {1154 {
1155 ·​·​·​·​.​.​.​1155 ·​·​·​·​.​.​.​
1156 ·​·​·​·​·​·​·​·​if·​(!priv_check_cred(cre​d,​·​PRIV_VFS_SYSFLAGS,​·​0)​)​·​{1156 ·​·​·​·​·​·​·​·​if·​(!priv_check_cred(cre​d,​·​PRIV_VFS_SYSFLAGS,​·​0)​)​·​{
Offset 1184, 47 lines modifiedOffset 1184, 47 lines modified
1184 ·​·​·​·​·​·​·​·​if·​(jail_chflags_allowed​)​1184 ·​·​·​·​·​·​·​·​if·​(jail_chflags_allowed​)​
1185 ·​·​·​·​·​·​·​·​·​·​·​·​return·​(0)​;​1185 ·​·​·​·​·​·​·​·​·​·​·​·​return·​(0)​;​
1186 ·​·​·​·​·​·​·​·​else1186 ·​·​·​·​·​·​·​·​else
1187 ·​·​·​·​·​·​·​·​·​·​·​·​return·​(EPERM)​;​1187 ·​·​·​·​·​·​·​·​·​·​·​·​return·​(EPERM)​;​
1188 ·​·​·​·​.​.​.​1188 ·​·​·​·​.​.​.​
1189 ·​·​·​·​}1189 ·​·​·​·​}
1190 ·​·​·​·​.​.​.​1190 ·​·​·​·​.​.​.​
1191 }</​pre></​div></​div></​div><div·​class="chapter"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="sysinit"></​a>第 5 章 SYSINIT框架</​h2></​div><div><span·​class="authorgroup">翻​译:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"></​span>.​·​</​span></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="sect1"><a·​href="#sysinit-​term">5.​1.​·​术语</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#sysinit-​operation">5.​2.​·​SYSINIT操作</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#sysinit-​using">5.​3.​·​使用SYSINIT</​a></​span></​dt></​dl></​div><a·​id="idp63494968"·​class="indexterm"></​a><a·​id="idp63495480"·​class="indexterm"></​a><a·​id="idp63495992"·​class="indexterm"></​a><a·​id="idp63496888"·​class="indexterm"></​a><a·​id="idp63497400"·​class="indexterm"></​a><p>SYSINIT是一个通用的调用排​序与分别执行机制的框架。1191 }</​pre></​div></​div></​div><div·​class="chapter"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="sysinit"></​a>第 5 章 SYSINIT框架</​h2></​div><div><span·​class="authorgroup">翻​译:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"></​span>.​·​</​span></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="sect1"><a·​href="#sysinit-​term">5.​1.​·​术语</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#sysinit-​operation">5.​2.​·​SYSINIT操作</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#sysinit-​using">5.​3.​·​使用SYSINIT</​a></​span></​dt></​dl></​div><a·​id="idp63462200"·​class="indexterm"></​a><a·​id="idp63462712"·​class="indexterm"></​a><a·​id="idp63463224"·​class="indexterm"></​a><a·​id="idp63464120"·​class="indexterm"></​a><a·​id="idp63464632"·​class="indexterm"></​a><p>SYSINIT是一个通用的调用排​序与分别执行机制的框架。
1192 ·​·​·​·​FreeBSD目前使用它来进行内核的动态初​始化。1192 ·​·​·​·​FreeBSD目前使用它来进行内核的动态初​始化。
1193 ·​·​·​·​SYSINIT使得FreeBSD的内核各子​系统可以在内核或模块动态加载链接时被重整、​1193 ·​·​·​·​SYSINIT使得FreeBSD的内核各子​系统可以在内核或模块动态加载链接时被重整、​
1194 ·​·​·​·​添加、删除、替换,这样,内核和模块加载时就​不必去修改一个静态的有序初始化1194 ·​·​·​·​添加、删除、替换,这样,内核和模块加载时就​不必去修改一个静态的有序初始化
1195 ·​·​·​·​安排表甚至重新编译内核。这个体系也使得内核​模块1195 ·​·​·​·​安排表甚至重新编译内核。这个体系也使得内核​模块
1196 ·​·​·​·​(现在称为<em·​class="firstterm">KLD​</​em>可以与内核不同时编译、链接、1196 ·​·​·​·​(现在称为<em·​class="firstterm">KLD​</​em>可以与内核不同时编译、链接、
1197 ·​·​·​·​在引导系统时加载,甚至在系统运行时加载。这​些操作是通过1197 ·​·​·​·​在引导系统时加载,甚至在系统运行时加载。这​些操作是通过
1198 ·​·​·​·​<span·​class="quote">“<span·​class="quote">内核链接器</​span>”</​span>(kernel·​linker)​和<span·​class="quote">“<span·​class="quote">链接器集合</​span>”</​span>1198 ·​·​·​·​<span·​class="quote">“<span·​class="quote">内核链接器</​span>”</​span>(kernel·​linker)​和<span·​class="quote">“<span·​class="quote">链接器集合</​span>”</​span>
1199 ·​·​·​·​(linker·​set)​完成的。</​p><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="sysinit-​term"></​a>5.​1.​ 术语</​h2></​div></​div></​div><div·​class="variablelist">​<dl·​class="variablelist">​<dt><span·​class="term">链接器集合(Li​nker·​Set)​</​span></​dt><dd><p>一种链接方法。这种方法​将整个程序源文件中静态申明的数据收集到1199 ·​·​·​·​(linker·​set)​完成的。</​p><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="sysinit-​term"></​a>5.​1.​ 术语</​h2></​div></​div></​div><div·​class="variablelist">​<dl·​class="variablelist">​<dt><span·​class="term">链接器集合(Li​nker·​Set)​</​span></​dt><dd><p>一种链接方法。这种方法​将整个程序源文件中静态申明的数据收集到
1200 ·​·​·​·​·​·​·​·​·​·​·​·​·​一个可邻近寻址的数据单元中。</​p></​dd></​dl></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="sysinit-​operation"></​a>5.​2.​ SYSINIT操作</​h2></​div></​div></​div><a·​id="idp63503032"·​class="indexterm"></​a><p>SYSINIT要依靠链接器获取遍​布整个程序源代码多处申明的静态数据1200 ·​·​·​·​·​·​·​·​·​·​·​·​·​一个可邻近寻址的数据单元中。</​p></​dd></​dl></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="sysinit-​operation"></​a>5.​2.​ SYSINIT操作</​h2></​div></​div></​div><a·​id="idp63470264"·​class="indexterm"></​a><p>SYSINIT要依靠链接器获取遍​布整个程序源代码多处申明的静态数据
1201 ·​·​·​·​·​·​并把它们组成一个彼此相邻的数据块。这种链接​方法被称为1201 ·​·​·​·​·​·​并把它们组成一个彼此相邻的数据块。这种链接​方法被称为
1202 ·​·​·​·​·​·​<span·​class="quote">“<span·​class="quote">链接器集合</​span>”</​span>(linker·​set)​。1202 ·​·​·​·​·​·​<span·​class="quote">“<span·​class="quote">链接器集合</​span>”</​span>(linker·​set)​。
1203 ·​·​·​·​·​·​SYSINIT使用两个链接器集合以维护两个​数据集合,1203 ·​·​·​·​·​·​SYSINIT使用两个链接器集合以维护两个​数据集合,
1204 ·​·​·​·​·​·​包含每个数据条目的调用顺序、函数、一个会被​提交给该函数的数据指针。</​p><p>SYSINIT按照两类优先级标识​对函数排序以便执行。1204 ·​·​·​·​·​·​包含每个数据条目的调用顺序、函数、一个会被​提交给该函数的数据指针。</​p><p>SYSINIT按照两类优先级标识​对函数排序以便执行。
1205 ·​·​·​·​·​·​第一类优先级的标识是子系统的标识,1205 ·​·​·​·​·​·​第一类优先级的标识是子系统的标识,
1206 ·​·​·​·​·​·​给出SYSINIT分别执行子系统的函数的全​局顺序,1206 ·​·​·​·​·​·​给出SYSINIT分别执行子系统的函数的全​局顺序,
1207 ·​·​·​·​·​·​定义在<code·​class="filename">&lt;​sys/​kernel.​h&gt;​</​code>中的枚举1207 ·​·​·​·​·​·​定义在<code·​class="filename">&lt;​sys/​kernel.​h&gt;​</​code>中的枚举
1208 ·​·​·​·​·​·​<code·​class="literal">sysin​it_sub_id</​code>内。第二类优先级标识在子系统中的​元素的顺序,1208 ·​·​·​·​·​·​<code·​class="literal">sysin​it_sub_id</​code>内。第二类优先级标识在子系统中的​元素的顺序,
1209 ·​·​·​·​·​·​定义在<code·​class="filename">&lt;​sys/​kernel.​h&gt;​</​code>中的枚举1209 ·​·​·​·​·​·​定义在<code·​class="filename">&lt;​sys/​kernel.​h&gt;​</​code>中的枚举
1210 ·​·​·​·​·​·​<code·​class="literal">sysin​it_elem_order</​code>内。</​p><a·​id="idp63506232"·​class="indexterm"></​a><p>有两种时刻需要使用SYSINIT​:系统启动或内核模块加载时,1210 ·​·​·​·​·​·​<code·​class="literal">sysin​it_elem_order</​code>内。</​p><a·​id="idp63485752"·​class="indexterm"></​a><p>有两种时刻需要使用SYSINIT​:系统启动或内核模块加载时,
1211 ·​·​·​·​·​·​系统析构或内核模块卸载时。内核子系统通常在​系统启动时使用SYSINIT1211 ·​·​·​·​·​·​系统析构或内核模块卸载时。内核子系统通常在​系统启动时使用SYSINIT
1212 ·​·​·​·​·​·​的定义项以初始化数据结构。例如,进程调度子​系统使用一个SYSINIT1212 ·​·​·​·​·​·​的定义项以初始化数据结构。例如,进程调度子​系统使用一个SYSINIT
1213 ·​·​·​·​·​·​定义项来初始化运行队列数据结构。设备驱动程​序应避免直接使用1213 ·​·​·​·​·​·​定义项来初始化运行队列数据结构。设备驱动程​序应避免直接使用
1214 ·​·​·​·​·​·​<code·​class="literal">SYSIN​IT()​</​code>,对于总线结构上的物理真实设备应​使用1214 ·​·​·​·​·​·​<code·​class="literal">SYSIN​IT()​</​code>,对于总线结构上的物理真实设备应​使用
1215 ·​·​·​·​·​·​<code·​class="literal">DRIVE​R_MODULE()​</​code>调用的函数先侦测设备的存在,1215 ·​·​·​·​·​·​<code·​class="literal">DRIVE​R_MODULE()​</​code>调用的函数先侦测设备的存在,
1216 ·​·​·​·​·​·​如果存在,再进行设备的初始化。这一系统过程​中,1216 ·​·​·​·​·​·​如果存在,再进行设备的初始化。这一系统过程​中,
1217 ·​·​·​·​·​·​会做一些专门针对设备的事情,然后调用<co​de·​class="literal">SYSIN​IT()​</​code>本身。1217 ·​·​·​·​·​·​会做一些专门针对设备的事情,然后调用<co​de·​class="literal">SYSIN​IT()​</​code>本身。
1218 ·​·​·​·​·​·​对于非总线结构一部分的虚设备,应改用<co​de·​class="literal">DEV_M​ODULE()​</​code>。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="sysinit-​using"></​a>5.​3.​ 使用SYSINIT</​h2></​div></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63509560"></​a>5.​3.​1.​ 接口</​h3></​div></​div></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63510200"></​a>5.​3.​1.​1.​ 头文件</​h4></​div></​div></​div><pre·​class="programlisting​">&lt;​sys/​kernel.​h&gt;​</​pre></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63511352"></​a>5.​3.​1.​2.​ 宏</​h4></​div></​div></​div><pre·​class="programlisting​">SYSINIT(uniquifier,​·​subsystem,​·​order,​·​func,​·​ident)​1218 ·​·​·​·​·​·​对于非总线结构一部分的虚设备,应改用<co​de·​class="literal">DEV_M​ODULE()​</​code>。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="sysinit-​using"></​a>5.​3.​ 使用SYSINIT</​h2></​div></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63489080"></​a>5.​3.​1.​ 接口</​h3></​div></​div></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63489720"></​a>5.​3.​1.​1.​ 头文件</​h4></​div></​div></​div><pre·​class="programlisting​">&lt;​sys/​kernel.​h&gt;​</​pre></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63490872"></​a>5.​3.​1.​2.​ 宏</​h4></​div></​div></​div><pre·​class="programlisting​">SYSINIT(uniquifier,​·​subsystem,​·​order,​·​func,​·​ident)​
1219 SYSUNINIT(uniquifier,​·​subsystem,​·​order,​·​func,​·​ident)​</​pre></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63512632"></​a>5.​3.​2.​ 启动</​h3></​div></​div></​div><p>宏<code·​class="literal">SYSIN​IT()​</​code>在SYSINIT启动数据集合中1219 SYSUNINIT(uniquifier,​·​subsystem,​·​order,​·​func,​·​ident)​</​pre></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63492152"></​a>5.​3.​2.​ 启动</​h3></​div></​div></​div><p>宏<code·​class="literal">SYSIN​IT()​</​code>在SYSINIT启动数据集合中
1220 ·​·​·​·​·​·​·​·​建立一个SYSINIT数据项,以便SYSI​NIT在系统启动或模块加载时排序1220 ·​·​·​·​·​·​·​·​建立一个SYSINIT数据项,以便SYSI​NIT在系统启动或模块加载时排序
1221 ·​·​·​·​·​·​·​·​并执行其中的函数。<code·​class="literal">SYSIN​IT()​</​code>有一个参数uniquifier,​1221 ·​·​·​·​·​·​·​·​并执行其中的函数。<code·​class="literal">SYSIN​IT()​</​code>有一个参数uniquifier,​
1222 ·​·​·​·​·​·​·​·​SYSINIT用它来标识数据项,随后是子系​统顺序号、子系统元素顺序号、1222 ·​·​·​·​·​·​·​·​SYSINIT用它来标识数据项,随后是子系​统顺序号、子系统元素顺序号、
1223 ·​·​·​·​·​·​·​·​待调用函数、传递给函数的数据。所有的函数必​须有一个恒量指针参数。</​p><div·​class="example"><a·​id="idp63514424"></​a><div·​class="example-​title">例 5.​1.​ <code·​class="literal">SYSIN​IT()​</​code>的例子</​div><div·​class="example-​contents"><pre·​class="programlisting​">#include·​&lt;​sys/​kernel.​h&gt;​1223 ·​·​·​·​·​·​·​·​待调用函数、传递给函数的数据。所有的函数必​须有一个恒量指针参数。</​p><div·​class="example"><a·​id="idp63493944"></​a><div·​class="example-​title">例 5.​1.​ <code·​class="literal">SYSIN​IT()​</​code>的例子</​div><div·​class="example-​contents"><pre·​class="programlisting​">#include·​&lt;​sys/​kernel.​h&gt;​
  
1224 void·​foo_null(void·​*unused)​1224 void·​foo_null(void·​*unused)​
1225 {1225 {
1226 ·​·​·​·​·​·​·​·​foo_doo()​;​1226 ·​·​·​·​·​·​·​·​foo_doo()​;​
1227 }1227 }
1228 SYSINIT(foo,​·​SI_SUB_FOO,​·​SI_ORDER_FOO,​·​foo_null,​·​NULL)​;​1228 SYSINIT(foo,​·​SI_SUB_FOO,​·​SI_ORDER_FOO,​·​foo_null,​·​NULL)​;​
  
Offset 1239, 23 lines modifiedOffset 1239, 23 lines modified
1239 }1239 }
1240 SYSINIT(bar,​·​SI_SUB_FOO,​·​SI_ORDER_FOO,​·​foo_arg,​·​&amp;​foo_voodoo)​;​1240 SYSINIT(bar,​·​SI_SUB_FOO,​·​SI_ORDER_FOO,​·​foo_arg,​·​&amp;​foo_voodoo)​;​
1241 »       ​</​pre></​div></​div><br·​class="example-​break"·​/​><p>注意,<code·​class="literal">SI_SU​B_FOO</​code>和<code·​class="literal">SI_OR​DER_FOO</​code>1241 »       ​</​pre></​div></​div><br·​class="example-​break"·​/​><p>注意,<code·​class="literal">SI_SU​B_FOO</​code>和<code·​class="literal">SI_OR​DER_FOO</​code>
1242 ·​·​·​·​·​·​·​·​·​应当分别在上面提到的枚举<code·​class="literal">sysin​it_sub_id</​code>和1242 ·​·​·​·​·​·​·​·​·​应当分别在上面提到的枚举<code·​class="literal">sysin​it_sub_id</​code>和
1243 ·​·​·​·​·​·​·​·​·​<code·​class="literal">sysin​it_elem_order</​code>之中。既可以使用已有的枚举项,1243 ·​·​·​·​·​·​·​·​·​<code·​class="literal">sysin​it_elem_order</​code>之中。既可以使用已有的枚举项,
1244 ·​·​·​·​·​·​·​·​·​也可以将自己的枚举项添加到这两个枚举的定义​之中。1244 ·​·​·​·​·​·​·​·​·​也可以将自己的枚举项添加到这两个枚举的定义​之中。
1245 ·​·​·​·​·​·​·​·​·​你可以使用数学表达式微调SYSINIT的执​行顺序。1245 ·​·​·​·​·​·​·​·​·​你可以使用数学表达式微调SYSINIT的执​行顺序。
1246 ·​·​·​·​·​·​·​·​·​以下的例子示例了一个需要刚好要在内核参数调​整的SYSINIT之前执行的SYSINIT​。</​p><div·​class="example"><a·​id="idp63517752"></​a><div·​class="example-​title">例 5.​2.​ 调整<code·​class="literal">SYSIN​IT()​</​code>顺序的例子</​div><div·​class="example-​contents"><pre·​class="programlisting​">static·​void1246 ·​·​·​·​·​·​·​·​·​以下的例子示例了一个需要刚好要在内核参数调​整的SYSINIT之前执行的SYSINIT​。</​p><div·​class="example"><a·​id="idp63497272"></​a><div·​class="example-​title">例 5.​2.​ 调整<code·​class="literal">SYSIN​IT()​</​code>顺序的例子</​div><div·​class="example-​contents"><pre·​class="programlisting​">static·​void
1247 mptable_register(void​·​*dummy·​__unused)​1247 mptable_register(void​·​*dummy·​__unused)​
1248 {1248 {
  
1249 »       ​apic_register_enumera​tor(&amp;​mptable_enumerator)​;​1249 »       ​apic_register_enumera​tor(&amp;​mptable_enumerator)​;​
1250 }1250 }
  
1251 SYSINIT(mptable_regis​ter,​·​SI_SUB_TUNABLES·​-​·​1,​·​SI_ORDER_FIRST,​1251 SYSINIT(mptable_regis​ter,​·​SI_SUB_TUNABLES·​-​·​1,​·​SI_ORDER_FIRST,​
1252 ·​·​·​·​mptable_register,​·​NULL)​;​</​pre></​div></​div><br·​class="example-​break"·​/​></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63519416"></​a>5.​3.​3.​ 析构</​h3></​div></​div></​div><p>宏<code·​class="literal">SYSUN​INIT()​</​code>的行为与<code·​class="literal">SYSIN​IT()​</​code>的相当,1252 ·​·​·​·​mptable_register,​·​NULL)​;​</​pre></​div></​div><br·​class="example-​break"·​/​></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp63498936"></​a>5.​3.​3.​ 析构</​h3></​div></​div></​div><p>宏<code·​class="literal">SYSUN​INIT()​</​code>的行为与<code·​class="literal">SYSIN​IT()​</​code>的相当,
1253 ·​·​·​·​·​·​·​·​只是它将数据项填加至SYSINIT的析构数​据集合。</​p><div·​class="example"><a·​id="idp63521208"></​a><div·​class="example-​title">例 5.​3.​ <code·​class="literal">SYSUN​INIT()​</​code>的例子</​div><div·​class="example-​contents"><pre·​class="programlisting​">#include·​&lt;​sys/​kernel.​h&gt;​1253 ·​·​·​·​·​·​·​·​只是它将数据项填加至SYSINIT的析构数​据集合。</​p><div·​class="example"><a·​id="idp63521208"></​a><div·​class="example-​title">例 5.​3.​ <code·​class="literal">SYSUN​INIT()​</​code>的例子</​div><div·​class="example-​contents"><pre·​class="programlisting​">#include·​&lt;​sys/​kernel.​h&gt;​
  
1254 void·​foo_cleanup(void·​*unused)​1254 void·​foo_cleanup(void·​*unused)​
1255 {1255 {
1256 ·​·​·​·​·​·​·​·​foo_kill()​;​1256 ·​·​·​·​·​·​·​·​foo_kill()​;​
1257 }1257 }
1258 SYSUNINIT(foobar,​·​SI_SUB_FOO,​·​SI_ORDER_FOO,​·​foo_cleanup,​·​NULL)​;​1258 SYSUNINIT(foobar,​·​SI_SUB_FOO,​·​SI_ORDER_FOO,​·​foo_cleanup,​·​NULL)​;​
Offset 2360, 23 lines modifiedOffset 2360, 23 lines modified
2360 ·​·​·​·​·​·​·​·​·​此时页被从磁盘中载入。</​p><div·​xmlns=""·​class="tip"><h3·​class="admontitle">译者​注:​·​</​h3><p·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml">Intel等厂商的CPU工作​在保护模式时,可用来实现虚拟内存。2360 ·​·​·​·​·​·​·​·​·​此时页被从磁盘中载入。</​p><div·​xmlns=""·​class="tip"><h3·​class="admontitle">译者​注:​·​</​h3><p·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml">Intel等厂商的CPU工作​在保护模式时,可用来实现虚拟内存。
2361 ·​·​·​·​·​·​·​·​·​当寻址的地址空间对应着真实内存时,则正常读​写;2361 ·​·​·​·​·​·​·​·​·​当寻址的地址空间对应着真实内存时,则正常读​写;
2362 ·​·​·​·​·​·​·​·​·​当寻址的地址空间没有对应的真实内存时,CP​U会产生一个“错误”,2362 ·​·​·​·​·​·​·​·​·​当寻址的地址空间没有对应的真实内存时,CP​U会产生一个“错误”,
2363 ·​·​·​·​·​·​·​·​·​通知操作系统与磁盘等设备进行交换,读寻址则​调入存储内容,2363 ·​·​·​·​·​·​·​·​·​通知操作系统与磁盘等设备进行交换,读寻址则​调入存储内容,
2364 ·​·​·​·​·​·​·​·​·​写寻址则写出存储内容。这个“错误”2364 ·​·​·​·​·​·​·​·​·​写寻址则写出存储内容。这个“错误”
2365 ·​·​·​·​·​·​·​·​·​并非操作系统或应用程序开发人员犯下的错误,​2365 ·​·​·​·​·​·​·​·​·​并非操作系统或应用程序开发人员犯下的错误,​
2366 ·​·​·​·​·​·​·​·​·​尽管在CPU硬件实现中这与应用程序或操作系​统内核崩溃的错误的发生机制相同。2366 ·​·​·​·​·​·​·​·​·​尽管在CPU硬件实现中这与应用程序或操作系​统内核崩溃的错误的发生机制相同。
2367 ·​·​·​·​·​·​·​·​·​参见Intel的CPU保护模式开发手册。<​/​p></​div><a·​id="idp67001784"·​class="indexterm"></​a><p>FreeBSD动态的调整页队列,​试图将各个队列中的页数维护在一个适当的比例​上,2367 ·​·​·​·​·​·​·​·​·​参见Intel的CPU保护模式开发手册。<​/​p></​div><a·​id="idp67005880"·​class="indexterm"></​a><p>FreeBSD动态的调整页队列,​试图将各个队列中的页数维护在一个适当的比例​上,
2368 ·​·​·​·​·​·​·​·​同时管理程序崩溃的已清理和未清理页。重新平​衡的比例数值决定于系统内存的负担。2368 ·​·​·​·​·​·​·​·​同时管理程序崩溃的已清理和未清理页。重新平​衡的比例数值决定于系统内存的负担。
2369 ·​·​·​·​·​·​·​·​这种重新平衡由pageout守护进程实现,​包括清理未清理页(与他们的后备存储同步)​、2369 ·​·​·​·​·​·​·​·​这种重新平衡由pageout守护进程实现,​包括清理未清理页(与他们的后备存储同步)​、
2370 ·​·​·​·​·​·​·​·​监视页被引用的活跃程度2370 ·​·​·​·​·​·​·​·​监视页被引用的活跃程度
2371 ·​·​·​·​·​·​·​·​(重置它们在LRU队列中的位置或在不同活跃​程度的页队列间移动)​、2371 ·​·​·​·​·​·​·​·​(重置它们在LRU队列中的位置或在不同活跃​程度的页队列间移动)​、
2372 ·​·​·​·​·​·​·​·​当比例不平衡时在队列间迁移页,如此等等。2372 ·​·​·​·​·​·​·​·​当比例不平衡时在队列间迁移页,如此等等。
2373 ·​·​·​·​·​·​·​·​FreeBSD的VM系统会将重激活页而产生​的错误频率调低到一个合理的数值,2373 ·​·​·​·​·​·​·​·​FreeBSD的VM系统会将重激活页而产生​的错误频率调低到一个合理的数值,
2374 ·​·​·​·​·​·​·​·​由此确定某一页活跃/​闲置的实际程度。2374 ·​·​·​·​·​·​·​·​由此确定某一页活跃/​闲置的实际程度。
2375 ·​·​·​·​·​·​·​·​这可以为更好的决定何时清理/​分配一个页做出决策。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="vm-​cache"></​a>7.​2.​ 统一的缓存信息结构体──<code·​class="literal">vm_ob​ject_t</​code></​h2></​div></​div></​div><a·​id="idp67003832"·​class="indexterm"></​a><a·​id="idp67004344"·​class="indexterm"></​a><p>FreeBSD实现了统一的<sp​an·​class="quote">“<span·​class="quote">虚拟内存对象<​/​span>”</​span>(VM对象)​的设计思想。2375 ·​·​·​·​·​·​·​·​这可以为更好的决定何时清理/​分配一个页做出决策。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="vm-​cache"></​a>7.​2.​ 统一的缓存信息结构体──<code·​class="literal">vm_ob​ject_t</​code></​h2></​div></​div></​div><a·​id="idp67007928"·​class="indexterm"></​a><a·​id="idp67008440"·​class="indexterm"></​a><p>FreeBSD实现了统一的<sp​an·​class="quote">“<span·​class="quote">虚拟内存对象<​/​span>”</​span>(VM对象)​的设计思想。
2376 ·​·​·​·​·​·​·​·​·​·​VM对象可以与各种类型的内存使用方式相结合​──直接使用(unbacked)​、2376 ·​·​·​·​·​·​·​·​·​·​VM对象可以与各种类型的内存使用方式相结合​──直接使用(unbacked)​、
2377 ·​·​·​·​·​·​·​·​·​·​交换(swap)​、物理设备、文件。2377 ·​·​·​·​·​·​·​·​·​·​交换(swap)​、物理设备、文件。
2378 ·​·​·​·​·​·​·​·​·​·​由于文件系统使用相同的VM对象管理核内数据​──文件的缓存,2378 ·​·​·​·​·​·​·​·​·​·​由于文件系统使用相同的VM对象管理核内数据​──文件的缓存,
2379 ·​·​·​·​·​·​·​·​·​·​所以这些缓存的结构也是统一的。</​p><p>VM对象可以被<span·​class="emphasis"><em>​影复制</​em></​span>(shadowed)​。2379 ·​·​·​·​·​·​·​·​·​·​所以这些缓存的结构也是统一的。</​p><p>VM对象可以被<span·​class="emphasis"><em>​影复制</​em></​span>(shadowed)​。
2380 ·​·​·​·​·​·​·​·​·​它们可以被堆放到其它类别VM对象堆栈的顶端​。例如,可以有一个交换VM对象,2380 ·​·​·​·​·​·​·​·​·​它们可以被堆放到其它类别VM对象堆栈的顶端​。例如,可以有一个交换VM对象,
2381 ·​·​·​·​·​·​·​·​·​放置在文件VM对象堆栈的顶端,以实现MAP​_PRIVATE的mmap()​操作。2381 ·​·​·​·​·​·​·​·​·​放置在文件VM对象堆栈的顶端,以实现MAP​_PRIVATE的mmap()​操作。
2382 ·​·​·​·​·​·​·​·​·​这样的入栈操作也可以用来实现各种各样的共享​特性,2382 ·​·​·​·​·​·​·​·​·​这样的入栈操作也可以用来实现各种各样的共享​特性,
Offset 2441, 15 lines modifiedOffset 2441, 15 lines modified
2441 ·​·​·​·​·​·​·​·​·​·​你可以在内核配置文件中用<code·​class="literal">makeo​ptions</​code>2441 ·​·​·​·​·​·​·​·​·​·​你可以在内核配置文件中用<code·​class="literal">makeo​ptions</​code>
2442 ·​·​·​·​·​·​·​·​·​·​指定排错(debugging)​和优化标志。注意,你一般不应使用<code​·​class="option">-​g</​code>,2442 ·​·​·​·​·​·​·​·​·​·​指定排错(debugging)​和优化标志。注意,你一般不应使用<code​·​class="option">-​g</​code>,
2443 ·​·​·​·​·​·​·​·​·​·​除非你能够应付由此产生的大内核(典型的是7​MB或更多)​。</​p><pre·​class="programlisting​">makeoptions·​·​·​·​·​·​DEBUG="-​g"2443 ·​·​·​·​·​·​·​·​·​·​除非你能够应付由此产生的大内核(典型的是7​MB或更多)​。</​p><pre·​class="programlisting​">makeoptions·​·​·​·​·​·​DEBUG="-​g"
2444 makeoptions·​·​·​·​·​·​COPTFLAGS="-​O·​-​pipe"</​pre><p>Sysctl提供了在运行时调​整内核的方式。你通常不需要指定任何sysc​tl变量,2444 makeoptions·​·​·​·​·​·​COPTFLAGS="-​O·​-​pipe"</​pre><p>Sysctl提供了在运行时调​整内核的方式。你通常不需要指定任何sysc​tl变量,
2445 ·​·​·​·​·​·​·​·​·​·​尤其是与VM相关的那些变量。</​p><p>运行时VM和系统调整的影响相对直​接一些。2445 ·​·​·​·​·​·​·​·​·​·​尤其是与VM相关的那些变量。</​p><p>运行时VM和系统调整的影响相对直​接一些。
2446 ·​·​·​·​·​·​·​·​·​·​首先,应当尽可能在UFS/​FFS文件系统上使用Soft·​Updates。2446 ·​·​·​·​·​·​·​·​·​·​首先,应当尽可能在UFS/​FFS文件系统上使用Soft·​Updates。
2447 ·​·​·​·​·​·​·​·​·​·​在<code·​class="filename">/​usr/​src/​sys/​ufs/​ffs/​README.​softupdates</​code>2447 ·​·​·​·​·​·​·​·​·​·​在<code·​class="filename">/​usr/​src/​sys/​ufs/​ffs/​README.​softupdates</​code>
2448 ·​·​·​·​·​·​·​·​·​·​里有关于如何配置的指示。</​p><a·​id="idp67047992"·​class="indexterm"></​a><p>其次,应当配置足够多的交换空间。​2448 ·​·​·​·​·​·​·​·​·​·​里有关于如何配置的指示。</​p><a·​id="idp67052088"·​class="indexterm"></​a><p>其次,应当配置足够多的交换空间。​
2449 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​你应当在每个物理磁盘上配置一个交换分区,最​多4个,2449 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​你应当在每个物理磁盘上配置一个交换分区,最​多4个,
2450 ·​·​·​·​·​·​·​·​·​·​甚至在你的<span·​class="quote">“<span·​class="quote">工作</​span>”</​span>磁盘上。你应当有至少2倍于主内存​的交换空间;2450 ·​·​·​·​·​·​·​·​·​·​甚至在你的<span·​class="quote">“<span·​class="quote">工作</​span>”</​span>磁盘上。你应当有至少2倍于主内存​的交换空间;
2451 ·​·​·​·​·​·​·​·​·​·​假如你没有足够内存的话,交换分区还应更多。​2451 ·​·​·​·​·​·​·​·​·​·​假如你没有足够内存的话,交换分区还应更多。​
2452 ·​·​·​·​·​·​·​·​·​·​你也应当按照你期望中的最大内存配置决定交换​分区的大小,2452 ·​·​·​·​·​·​·​·​·​·​你也应当按照你期望中的最大内存配置决定交换​分区的大小,
2453 ·​·​·​·​·​·​·​·​·​·​这样以后就不再需要重新给磁盘分区了。2453 ·​·​·​·​·​·​·​·​·​·​这样以后就不再需要重新给磁盘分区了。
2454 ·​·​·​·​·​·​·​·​·​·​如果你处理系统崩溃后的内存倾倒(crash​·​dump)​,2454 ·​·​·​·​·​·​·​·​·​·​如果你处理系统崩溃后的内存倾倒(crash​·​dump)​,
2455 ·​·​·​·​·​·​·​·​·​·​第一个交换分区必须至少与主内存一样大,2455 ·​·​·​·​·​·​·​·​·​·​第一个交换分区必须至少与主内存一样大,
Offset 2464, 15 lines modifiedOffset 2464, 15 lines modified
2464 ·​·​·​·​·​·​但我们会逐渐为其充实内容。·​关于这份文档的更新和建议,2464 ·​·​·​·​·​·​但我们会逐渐为其充实内容。·​关于这份文档的更新和建议,
2465 ·​·​·​·​·​·​请发给文档编辑。</​p><a·​id="idp67064248"·​class="indexterm"></​a><p>SMPng·​的目标是使内核能够并发执行。·​基本上,2465 ·​·​·​·​·​·​请发给文档编辑。</​p><a·​id="idp67064248"·​class="indexterm"></​a><p>SMPng·​的目标是使内核能够并发执行。·​基本上,
2466 ·​·​·​·​·​·​内核是一个很大而复杂的程序。·​要让内核能够多线程地执行,2466 ·​·​·​·​·​·​内核是一个很大而复杂的程序。·​要让内核能够多线程地执行,
2467 ·​·​·​·​·​·​我们需要使用某些其它多线程程序在实现时所用​到的工具,2467 ·​·​·​·​·​·​我们需要使用某些其它多线程程序在实现时所用​到的工具,
2468 ·​·​·​·​·​·​这包括互斥体(mutex)​、·​共享/​排他锁(shared/​exclusive·​lock)​、2468 ·​·​·​·​·​·​这包括互斥体(mutex)​、·​共享/​排他锁(shared/​exclusive·​lock)​、
2469 ·​·​·​·​·​·​信号量(semaphores)​·​和条件变量(condition·​variable)​。2469 ·​·​·​·​·​·​信号量(semaphores)​·​和条件变量(condition·​variable)​。
2470 ·​·​·​·​·​·​如果希望了解它们以及其它·​SMP·​术语,2470 ·​·​·​·​·​·​如果希望了解它们以及其它·​SMP·​术语,
2471 ·​·​·​·​·​·​请参阅本文的·​<a·​class="xref"·​href="#smp-​glossary"·​title="术语表">术语表</​a>·​一节。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="smp-​lock-​fundamentals"></​a>8.​2.​ 基本工具与上锁的基础知识</​h2></​div></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67066424"></​a>8.​2.​1.​ 原子操作指令和内存栅</​h3></​div></​div></​div><a·​id="idp67067064"·​class="indexterm"></​a><a·​id="idp67067576"·​class="indexterm"></​a><p>关于内存栅和原子操作指令已经有很​多介绍材料,2471 ·​·​·​·​·​·​请参阅本文的·​<a·​class="xref"·​href="#smp-​glossary"·​title="术语表">术语表</​a>·​一节。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="smp-​lock-​fundamentals"></​a>8.​2.​ 基本工具与上锁的基础知识</​h2></​div></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67066424"></​a>8.​2.​1.​ 原子操作指令和内存栅</​h3></​div></​div></​div><a·​id="idp67087544"·​class="indexterm"></​a><a·​id="idp67088056"·​class="indexterm"></​a><p>关于内存栅和原子操作指令已经有很​多介绍材料,
2472 »       ​因此这一节并不打算对其进行详尽的介绍。·​简而言之,·​如果有对某一变量上写锁,2472 »       ​因此这一节并不打算对其进行详尽的介绍。·​简而言之,·​如果有对某一变量上写锁,
2473 »       ​就不能在不获得相应的锁时对其进行读取操作。​·​也就是说,2473 »       ​就不能在不获得相应的锁时对其进行读取操作。​·​也就是说,
2474 »       ​内存栅的作用在于保证内存操作的相对顺序,·​但并不保证内存操作的严格时序。2474 »       ​内存栅的作用在于保证内存操作的相对顺序,·​但并不保证内存操作的严格时序。
2475 »       ​换言之,·​内存栅并不保证·​CPU·​将本地快取缓存或存储缓冲的内容刷写回内存,​2475 »       ​换言之,·​内存栅并不保证·​CPU·​将本地快取缓存或存储缓冲的内容刷写回内存,​
2476 »       ​而是在锁释放时确保其所保护的数据,·​对于能看到刚释放的那个锁的·​CPU2476 »       ​而是在锁释放时确保其所保护的数据,·​对于能看到刚释放的那个锁的·​CPU
2477 »       ​或设备可见。·​持有内存栅的·​CPU2477 »       ​或设备可见。·​持有内存栅的·​CPU
2478 »       ​可以在其快取缓存或存储缓冲中将数据保持其所​希望的、·​任意长的时间,2478 »       ​可以在其快取缓存或存储缓冲中将数据保持其所​希望的、·​任意长的时间,
Offset 2490, 51 lines modifiedOffset 2490, 51 lines modified
2490 »       ​也可能在我们进行决策的过程中发生变化。·​因此,·​当执行2490 »       ​也可能在我们进行决策的过程中发生变化。·​因此,·​当执行
2491 »       ​<code·​class="function">atom​ic_set</​code>·​时,·​最终可能会对另一值进行置位,2491 »       ​<code·​class="function">atom​ic_set</​code>·​时,·​最终可能会对另一值进行置位,
2492 »       ​而不是我们进行决策的那一个。·​这就必须通过2492 »       ​而不是我们进行决策的那一个。·​这就必须通过
2493 »       ​<code·​class="function">atom​ic_cmpset</​code>·​来保证只有在我们的决策依据是最新的时,2493 »       ​<code·​class="function">atom​ic_cmpset</​code>·​来保证只有在我们的决策依据是最新的时,
2494 »       ​才对相应的变量进行置位。</​p><p>最后,·​原子操作只允许一次更新或读一个内存单元。2494 »       ​才对相应的变量进行置位。</​p><p>最后,·​原子操作只允许一次更新或读一个内存单元。
2495 »       ​需要原子地更新多个单元时,·​就必须使用锁来代替它了。2495 »       ​需要原子地更新多个单元时,·​就必须使用锁来代替它了。
2496 »       ​例如,·​如果需要更新两个相互关联的计数器时,2496 »       ​例如,·​如果需要更新两个相互关联的计数器时,
2497 »       ​就必须使用锁,·​而不是两次单独的原子操作了。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67084472"></​a>8.​2.​2.​ 读锁与写锁</​h3></​div></​div></​div><a·​id="idp67085112"·​class="indexterm"></​a><a·​id="idp67085624"·​class="indexterm"></​a><p>读锁并不需要像写锁那样强。·​这两种类型的锁,2497 »       ​就必须使用锁,·​而不是两次单独的原子操作了。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67092664"></​a>8.​2.​2.​ 读锁与写锁</​h3></​div></​div></​div><a·​id="idp67093304"·​class="indexterm"></​a><a·​id="idp67093816"·​class="indexterm"></​a><p>读锁并不需要像写锁那样强。·​这两种类型的锁,
2498 »       ​都需要确保通过它们访问的不是过时的数据。·​然而,2498 »       ​都需要确保通过它们访问的不是过时的数据。·​然而,
2499 »       ​只有写操作必须是排他的,·​而多个线程则可以安全地读同一变量的值。2499 »       ​只有写操作必须是排他的,·​而多个线程则可以安全地读同一变量的值。
2500 »       ​使用不同类型的锁用于读和写操作有许多各自不​同的实现方式。</​p><p>第一种方法是用·​sx·​锁,·​它可以用于实现写时使用的排他锁,2500 »       ​使用不同类型的锁用于读和写操作有许多各自不​同的实现方式。</​p><p>第一种方法是用·​sx·​锁,·​它可以用于实现写时使用的排他锁,
2501 »       ​而读时则作为共享锁。·​这种方法十分简单明了。</​p><p>第二种方法则略显晦涩。·​可以用多个锁来保护同一数据元。2501 »       ​而读时则作为共享锁。·​这种方法十分简单明了。</​p><p>第二种方法则略显晦涩。·​可以用多个锁来保护同一数据元。
2502 »       ​读时,·​只需锁其中的一个读锁即可。·​然而,·​如果要写数据的话,2502 »       ​读时,·​只需锁其中的一个读锁即可。·​然而,·​如果要写数据的话,
2503 »       ​则需要首先上所有的写锁。·​这会大大提高写操作的代价,2503 »       ​则需要首先上所有的写锁。·​这会大大提高写操作的代价,
2504 »       ​但当可能以多种方式访问数据时却可能非常有用​。·​例如,2504 »       ​但当可能以多种方式访问数据时却可能非常有用​。·​例如,
2505 »       ​父进程指针是同时受2505 »       ​父进程指针是同时受
2506 »       ​<code·​class="varname">proct​ree_lock</​code>·​sx·​锁和进程·​mutex·​保护的。2506 »       ​<code·​class="varname">proct​ree_lock</​code>·​sx·​锁和进程·​mutex·​保护的。
2507 »       ​在只希望检查已锁进程的父进程时,·​用·​proc·​锁更为方便。2507 »       ​在只希望检查已锁进程的父进程时,·​用·​proc·​锁更为方便。
2508 »       ​但是,·​其它一些地方,·​例如2508 »       ​但是,·​其它一些地方,·​例如
2509 »       ​<code·​class="function">infe​rior</​code>·​这类需要通过父指针在进程树上进行搜索,2509 »       ​<code·​class="function">infe​rior</​code>·​这类需要通过父指针在进程树上进行搜索,
2510 »       ​并对每个进程上锁的地方就不能这样做了,2510 »       ​并对每个进程上锁的地方就不能这样做了,
2511 »       ​否则,·​将无法保证在对我们所获得的结果执行操作时,​2511 »       ​否则,·​将无法保证在对我们所获得的结果执行操作时,​
2512 »       ​之前检查时的状况依旧有效。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67088184"></​a>8.​2.​3.​ 上锁状态和结果</​h3></​div></​div></​div><p>如果您需要使用锁来保持所检查​变量的状态,·​并据此执行某些操作时,2512 »       ​之前检查时的状况依旧有效。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67096376"></​a>8.​2.​3.​ 上锁状态和结果</​h3></​div></​div></​div><p>如果您需要使用锁来保持所检查​变量的状态,·​并据此执行某些操作时,
2513 »       ​是不能仅仅在读变量之前对其上锁,·​并在执行操作之前解锁的。2513 »       ​是不能仅仅在读变量之前对其上锁,·​并在执行操作之前解锁的。
2514 »       ​过早解锁将使变量再次可变,·​这可能会导致之前所做的决策失效。2514 »       ​过早解锁将使变量再次可变,·​这可能会导致之前所做的决策失效。
2515 »       ​因此,·​在所做检测引发的动作结束之前,·​必须继续保持上锁状态。</​p></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="smp-​design"></​a>8.​3.​ 架构与设计概览</​h2></​div></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67090232"></​a>8.​3.​1.​ 对中断的处理</​h3></​div></​div></​div><a·​id="idp67090872"·​class="indexterm"></​a><p>与许多其它多线程·​<span·​class="trademark">UNI​X</​span>®·​内核所采取的模式类似,·​FreeBSD2515 »       ​因此,·​在所做检测引发的动作结束之前,·​必须继续保持上锁状态。</​p></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="smp-​design"></​a>8.​3.​ 架构与设计概览</​h2></​div></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67098424"></​a>8.​3.​1.​ 对中断的处理</​h3></​div></​div></​div><a·​id="idp67099064"·​class="indexterm"></​a><p>与许多其它多线程·​<span·​class="trademark">UNI​X</​span>®·​内核所采取的模式类似,·​FreeBSD
2516 »       ​会赋予中断处理程序独立的线程上下文,2516 »       ​会赋予中断处理程序独立的线程上下文,
2517 »       ​这样做能够让中断线程在遇到锁时阻塞。·​但为了避免不必要的延迟,2517 »       ​这样做能够让中断线程在遇到锁时阻塞。·​但为了避免不必要的延迟,
2518 »       ​中断线程在内核中,·​是以实时线程的优先级运行的。·​因此,2518 »       ​中断线程在内核中,·​是以实时线程的优先级运行的。·​因此,
2519 »       ​中断处理程序不应执行过久,·​以免饿死其它内核线程。·​此外,2519 »       ​中断处理程序不应执行过久,·​以免饿死其它内核线程。·​此外,
2520 »       ​由于多个处理程序可以分享同一中断线程,·​中断处理程序不应休眠,2520 »       ​由于多个处理程序可以分享同一中断线程,·​中断处理程序不应休眠,
2521 »       ​或使用可能导致休眠的锁,·​以避免将其它中断处理程序饿死。</​p><a·​id="idp67092280"·​class="indexterm"></​a><p>目前在·​FreeBSD·​中的中断线程是指重量级中断线程。2521 »       ​或使用可能导致休眠的锁,·​以避免将其它中断处理程序饿死。</​p><a·​id="idp67100472"·​class="indexterm"></​a><p>目前在·​FreeBSD·​中的中断线程是指重量级中断线程。
2522 »       ​这样称呼它们的原因在于,·​转到中断线程需要执行一次完整的上下文切换操​作。2522 »       ​这样称呼它们的原因在于,·​转到中断线程需要执行一次完整的上下文切换操​作。
2523 »       ​在最初的实现中,·​内核不允许抢占,·​因此中断在打断内核线程之前,2523 »       ​在最初的实现中,·​内核不允许抢占,·​因此中断在打断内核线程之前,
2524 »       ​必须等待内核线程阻塞或返回用户态之后才能执​行。</​p><a·​id="idp67093176"·​class="indexterm"></​a><a·​id="idp67093688"·​class="indexterm"></​a><p>为了解决响应时间问题,·​FreeBSD·​内核现在采用了抢占式调度策略。2524 »       ​必须等待内核线程阻塞或返回用户态之后才能执​行。</​p><a·​id="idp67101368"·​class="indexterm"></​a><a·​id="idp67101880"·​class="indexterm"></​a><p>为了解决响应时间问题,·​FreeBSD·​内核现在采用了抢占式调度策略。
2525 »       ​目前,·​只有释放休眠·​mutex·​或发生中断时才能抢断内核线程,2525 »       ​目前,·​只有释放休眠·​mutex·​或发生中断时才能抢断内核线程,
2526 »       ​但最终目标是在·​FreeBSD·​上实现下面所描述的全抢占式调度策略。</​p><p>并非所有的中断处理程序都在独立的​线程上下文中执行。2526 »       ​但最终目标是在·​FreeBSD·​上实现下面所描述的全抢占式调度策略。</​p><p>并非所有的中断处理程序都在独立的​线程上下文中执行。
2527 »       ​相反,·​某些处理程序会直接在主中断上下文中执行。·​这些中断处理程序,2527 »       ​相反,·​某些处理程序会直接在主中断上下文中执行。·​这些中断处理程序,
2528 »       ​现在被错误地命名为2528 »       ​现在被错误地命名为
2529 »       ​<span·​class="quote">“<span·​class="quote">快速</​span>”</​span>·​中断处理程序,·​因为早期版本的内核中使用了2529 »       ​<span·​class="quote">“<span·​class="quote">快速</​span>”</​span>·​中断处理程序,·​因为早期版本的内核中使用了
2530 »       ​<code·​class="constant">INTR​_FAST</​code>·​标志来标记这些处理程序。2530 »       ​<code·​class="constant">INTR​_FAST</​code>·​标志来标记这些处理程序。
2531 »       ​目前只有时钟中断和串口·​I/​O·​设备中断采用这一类型。2531 »       ​目前只有时钟中断和串口·​I/​O·​设备中断采用这一类型。
2532 »       ​由于这些处理程序没有独立的上下文,·​因而它们都不能获得阻塞性锁,2532 »       ​由于这些处理程序没有独立的上下文,·​因而它们都不能获得阻塞性锁,
2533 »       ​因此也就只能使用自旋·​mutex。</​p><a·​id="idp67095736"·​class="indexterm"></​a><p>最后,·​还有一种称为轻量级上下文切换的优化,2533 »       ​因此也就只能使用自旋·​mutex。</​p><a·​id="idp67103928"·​class="indexterm"></​a><p>最后,·​还有一种称为轻量级上下文切换的优化,
2534 »       ​可以在·​MD·​代码中使用。·​因为中断线程都是在内核上下文中执行的,2534 »       ​可以在·​MD·​代码中使用。·​因为中断线程都是在内核上下文中执行的,
2535 »       ​所以它可以借用任意进程的·​vmspace·​(虚拟内存地址空间)​。·​因此,2535 »       ​所以它可以借用任意进程的·​vmspace·​(虚拟内存地址空间)​。·​因此,
2536 »       ​在轻量级上下文切换中,·​切换到中断线程并不切换对应的·​vmspace,2536 »       ​在轻量级上下文切换中,·​切换到中断线程并不切换对应的·​vmspace,
2537 »       ​而是借用被中断线程的·​vmspace。·​为确保被中断线程的·​vmspace2537 »       ​而是借用被中断线程的·​vmspace。·​为确保被中断线程的·​vmspace
2538 »       ​不在中断处理过程中消失,·​被中断线程在中断线程不再借用其·​vmspace2538 »       ​不在中断处理过程中消失,·​被中断线程在中断线程不再借用其·​vmspace
2539 »       ​之前是不允许执行的。·​刚才提到的情况可能在中断线程阻塞或完成时发​生。2539 »       ​之前是不允许执行的。·​刚才提到的情况可能在中断线程阻塞或完成时发​生。
2540 »       ​如果中断线程发生阻塞,·​则它再次进入可运行状态时将使用自己的上下文​,2540 »       ​如果中断线程发生阻塞,·​则它再次进入可运行状态时将使用自己的上下文​,
Offset 2545, 53 lines modifiedOffset 2545, 53 lines modified
2545 »       ​而这种阻塞将进而需要线程修正。·​另外,·​Mike·​Smith2545 »       ​而这种阻塞将进而需要线程修正。·​另外,·​Mike·​Smith
2546 »       ​提议采用另一种方式来处理中断线程:</​p><div·​class="orderedlist"><​ol·​class="orderedlist"·​type="1"><li·​class="listitem"><p>每​个中断处理程序分为两部分,·​一个在主中断上下文中运行的主体2546 »       ​提议采用另一种方式来处理中断线程:</​p><div·​class="orderedlist"><​ol·​class="orderedlist"·​type="1"><li·​class="listitem"><p>每​个中断处理程序分为两部分,·​一个在主中断上下文中运行的主体
2547 »       ​·​·​·​·​(predicate)​·​和一个在自己的线程上下文中执行的处理程序·​(handler)​。</​p></​li><li·​class="listitem"><p>如​果中断处理程序拥有主体,·​则当触发中断时,·​执行该主体。2547 »       ​·​·​·​·​(predicate)​·​和一个在自己的线程上下文中执行的处理程序·​(handler)​。</​p></​li><li·​class="listitem"><p>如​果中断处理程序拥有主体,·​则当触发中断时,·​执行该主体。
2548 »       ​·​·​·​·​如果主体返回真,·​则认为该中断被处理完毕,·​内核从中断返回。2548 »       ​·​·​·​·​如果主体返回真,·​则认为该中断被处理完毕,·​内核从中断返回。
2549 »       ​·​·​·​·​如果主体返回假,·​或者中断没有主体,·​则调度运行线程式处理程序。</​p></​li></​ol></​div><p>在这一模式中适当地采用轻量级​上下文切换可能是非常复杂的。2549 »       ​·​·​·​·​如果主体返回假,·​或者中断没有主体,·​则调度运行线程式处理程序。</​p></​li></​ol></​div><p>在这一模式中适当地采用轻量级​上下文切换可能是非常复杂的。
2550 »       ​因为我们可能会希望在未来改变这一模式,·​因此现在最好的方案,2550 »       ​因为我们可能会希望在未来改变这一模式,·​因此现在最好的方案,
2551 »       ​应该是暂时推迟在轻量级上下文切换之上的工作​,2551 »       ​应该是暂时推迟在轻量级上下文切换之上的工作​,
2552 »       ​以便进一步完善中断处理架构,·​随后再考察轻量级上下文切换是否适用。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67103544"></​a>8.​3.​2.​ 内核抢占与临界区</​h3></​div></​div></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67104184"></​a>8.​3.​2.​1.​ 内核抢占简介</​h4></​div></​div></​div><p>内核抢占的概念很简单,·​其基本思想是·​CPU·​总应执行优先级最高的工作。2552 »       ​以便进一步完善中断处理架构,·​随后再考察轻量级上下文切换是否适用。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67107640"></​a>8.​3.​2.​ 内核抢占与临界区</​h3></​div></​div></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67108280"></​a>8.​3.​2.​1.​ 内核抢占简介</​h4></​div></​div></​div><p>内核抢占的概念很简单,·​其基本思想是·​CPU·​总应执行优先级最高的工作。
2553 »       ​·​·​当然,·​至少在理想情况下是这样。·​有些时候,2553 »       ​·​·​当然,·​至少在理想情况下是这样。·​有些时候,
2554 »       ​·​·​达成这一理想的代价会十分高昂,·​以至于在这些情况下抢占会得不偿失。</​p><p>实现完全的内核抢占十分简单:·​在调度将要执行的线程并放入运行队列时,2554 »       ​·​·​达成这一理想的代价会十分高昂,·​以至于在这些情况下抢占会得不偿失。</​p><p>实现完全的内核抢占十分简单:·​在调度将要执行的线程并放入运行队列时,
2555 »       ​·​·​检查它的优先级是否高于目前正在执行的线程。​·​如果是这样的话,2555 »       ​·​·​检查它的优先级是否高于目前正在执行的线程。​·​如果是这样的话,
2556 »       ​·​·​执行一次上下文切换并立即开始执行该线程。<​/​p><p>尽管锁能够在抢占时保护多数数据,​·​但内核并不是可以安全地处处抢占的。2556 »       ​·​·​执行一次上下文切换并立即开始执行该线程。<​/​p><p>尽管锁能够在抢占时保护多数数据,​·​但内核并不是可以安全地处处抢占的。
2557 »       ​·​·​例如,·​如果持有自旋·​mutex·​的线程被抢占,·​而新线程也尝试获得同一自旋2557 »       ​·​·​例如,·​如果持有自旋·​mutex·​的线程被抢占,·​而新线程也尝试获得同一自旋
2558 »       ​·​·​mutex,·​新线程就可能一直自旋下去,2558 »       ​·​·​mutex,·​新线程就可能一直自旋下去,
2559 »       ​·​·​因为被中断的线程可能永远没有机会运行了。·​此外,·​某些代码,·​例如在·​Alpha·​上的2559 »       ​·​·​因为被中断的线程可能永远没有机会运行了。·​此外,·​某些代码,·​例如在·​Alpha·​上的
2560 »       ​·​·​<code·​class="function">exec​</​code>·​对进程地址空间编号进行赋值的代码也不能被抢​断,2560 »       ​·​·​<code·​class="function">exec​</​code>·​对进程地址空间编号进行赋值的代码也不能被抢​断,
2561 »       ​·​·​因为它被用来支持实际的上下文切换操作。·​在这些代码段中,2561 »       ​·​·​因为它被用来支持实际的上下文切换操作。·​在这些代码段中,
2562 »       ​·​·​会通过使用临界区来临时禁用抢占。</​p></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67106488"></​a>8.​3.​2.​2.​ 临界区</​h4></​div></​div></​div><a·​id="idp67107128"·​class="indexterm"></​a><p>临界区·​API·​的责任是避免在临界区内发生上下文切换。2562 »       ​·​·​会通过使用临界区来临时禁用抢占。</​p></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67110584"></​a>8.​3.​2.​2.​ 临界区</​h4></​div></​div></​div><a·​id="idp67111224"·​class="indexterm"></​a><p>临界区·​API·​的责任是避免在临界区内发生上下文切换。
2563 »       ​·​·​对于完全抢占式内核而言,·​除了当前线程之外的其它线程的每个2563 »       ​·​·​对于完全抢占式内核而言,·​除了当前线程之外的其它线程的每个
2564 »       ​·​·​<code·​class="function">setr​unqueue</​code>·​都是抢断点。2564 »       ​·​·​<code·​class="function">setr​unqueue</​code>·​都是抢断点。
2565 »       ​·​·​<code·​class="function">crit​ical_enter</​code>·​的一种实现方式是设置一线程私有标记,2565 »       ​·​·​<code·​class="function">crit​ical_enter</​code>·​的一种实现方式是设置一线程私有标记,
2566 »       ​·​·​并由其对应方清除。·​如果调用2566 »       ​·​·​并由其对应方清除。·​如果调用
2567 »       ​·​·​<code·​class="function">setr​unqueue</​code>·​时设置了这个标志,2567 »       ​·​·​<code·​class="function">setr​unqueue</​code>·​时设置了这个标志,
2568 »       ​·​·​则无论新线程和当前线程相比其优先级高低,·​都不会发生抢占。2568 »       ​·​·​则无论新线程和当前线程相比其优先级高低,·​都不会发生抢占。
2569 »       ​·​·​然而,·​由于临界区会在自旋·​mutex·​中用于避免上下文切换,2569 »       ​·​·​然而,·​由于临界区会在自旋·​mutex·​中用于避免上下文切换,
2570 »       ​·​·​而且能够同时获得多个自旋·​mutex,·​因此临界区·​API·​必须支持嵌套。2570 »       ​·​·​而且能够同时获得多个自旋·​mutex,·​因此临界区·​API·​必须支持嵌套。
2571 »       ​·​·​由于这个原因,·​目前的实现中采用了嵌套计数,2571 »       ​·​·​由于这个原因,·​目前的实现中采用了嵌套计数,
2572 »       ​·​·​而不仅仅是单个的线程标志。</​p><p>为了尽可能缩短响应时间,·​在临界区中的抢占被推迟,2572 »       ​·​·​而不仅仅是单个的线程标志。</​p><p>为了尽可能缩短响应时间,·​在临界区中的抢占被推迟,
2573 »       ​·​·​而不是直接丢弃。·​如果线程应被抢断,·​并被置为可运行,2573 »       ​·​·​而不是直接丢弃。·​如果线程应被抢断,·​并被置为可运行,
2574 »       ​·​·​而当前线程处于临界区,·​则会设置一线程私有标志,2574 »       ​·​·​而当前线程处于临界区,·​则会设置一线程私有标志,
2575 »       ​·​·​表示有一个尚未进行的抢断操作。·​当最外层临界区退出时,2575 »       ​·​·​表示有一个尚未进行的抢断操作。·​当最外层临界区退出时,
2576 »       ​·​·​会检查这一标志,·​如果它被置位,·​则当前线程会被抢断,2576 »       ​·​·​会检查这一标志,·​如果它被置位,·​则当前线程会被抢断,
2577 »       ​·​·​以允许更高优先级的线程开始运行。</​p><a·​id="idp67109560"·​class="indexterm"></​a><a·​id="idp67110072"·​class="indexterm"></​a><p>中断会引发一个和自旋·​mutex·​有关的问题。2577 »       ​·​·​以允许更高优先级的线程开始运行。</​p><a·​id="idp67125944"·​class="indexterm"></​a><a·​id="idp67126456"·​class="indexterm"></​a><p>中断会引发一个和自旋·​mutex·​有关的问题。
2578 »       ​·​·​如果低级中断处理程序需要锁,·​它就不能中断任何需要该锁的代码,2578 »       ​·​·​如果低级中断处理程序需要锁,·​它就不能中断任何需要该锁的代码,
2579 »       ​·​·​以避免可能发生的损坏数据结构的情况。·​目前,这一机制是透过临界区·​API2579 »       ​·​·​以避免可能发生的损坏数据结构的情况。·​目前,这一机制是透过临界区·​API
2580 »       ​·​·​以·​<code·​class="function">cpu_​critical_enter</​code>·​和2580 »       ​·​·​以·​<code·​class="function">cpu_​critical_enter</​code>·​和
2581 »       ​·​·​<code·​class="function">cpu_​critical_exit</​code>·​函数的形式实现的。2581 »       ​·​·​<code·​class="function">cpu_​critical_exit</​code>·​函数的形式实现的。
2582 »       ​·​·​目前这一·​API·​会在所有·​FreeBSD2582 »       ​·​·​目前这一·​API·​会在所有·​FreeBSD
2583 »       ​·​·​所支持的平台上禁用和重新启用中断。·​这种方法并不是最优的,2583 »       ​·​·​所支持的平台上禁用和重新启用中断。·​这种方法并不是最优的,
2584 »       ​·​·​但它更易理解,·​也更容易正确地实现。·​理论上,·​这一辅助·​API2584 »       ​·​·​但它更易理解,·​也更容易正确地实现。·​理论上,·​这一辅助·​API
2585 »       ​·​·​只需要配合在主中断上下文中的自旋·​mutex·​使用。·​然而,2585 »       ​·​·​只需要配合在主中断上下文中的自旋·​mutex·​使用。·​然而,
2586 »       ​·​·​为了让代码更为简单,·​它被用在了全部自旋·​mutex,2586 »       ​·​·​为了让代码更为简单,·​它被用在了全部自旋·​mutex,
2587 »       ​·​·​甚至包括所有临界区上。·​将其从·​MI·​API·​中剥离出来放入·​MD·​API,2587 »       ​·​·​甚至包括所有临界区上。·​将其从·​MI·​API·​中剥离出来放入·​MD·​API,
2588 »       ​·​·​并只在需要使用它的·​MI·​API·​的自旋·​mutex·​实现中使用可能会有更好的效果。2588 »       ​·​·​并只在需要使用它的·​MI·​API·​的自旋·​mutex·​实现中使用可能会有更好的效果。
2589 »       ​·​·​如果我们最终采用了这种实现方式,·​则·​MD·​API2589 »       ​·​·​如果我们最终采用了这种实现方式,·​则·​MD·​API
2590 »       ​·​·​可能需要改名,·​以彰显其为一单独·​API·​这一事实。</​p></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67132600"></​a>8.​3.​2.​3.​ 设计折衷</​h4></​div></​div></​div><p>如前面提到的,·​当完全抢占并非总能提供最佳性能时,2590 »       ​·​·​可能需要改名,·​以彰显其为一单独·​API·​这一事实。</​p></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67148984"></​a>8.​3.​2.​3.​ 设计折衷</​h4></​div></​div></​div><p>如前面提到的,·​当完全抢占并非总能提供最佳性能时,
2591 »       ​·​·​采取了一些折衷的措施。</​p><p>第一处折衷是,·​抢占代码并不考虑其它·​CPU·​的存在。2591 »       ​·​·​采取了一些折衷的措施。</​p><p>第一处折衷是,·​抢占代码并不考虑其它·​CPU·​的存在。
2592 »       ​·​·​假设我们有两个·​CPU,·​A2592 »       ​·​·​假设我们有两个·​CPU,·​A
2593 »       ​·​·​和·​B,·​其中·​A·​上线程的优先级为·​4,2593 »       ​·​·​和·​B,·​其中·​A·​上线程的优先级为·​4,
2594 »       ​·​·​而·​B·​上线程的优先级是·​2。·​如果·​CPU·​B·​令一优先级为·​12594 »       ​·​·​而·​B·​上线程的优先级是·​2。·​如果·​CPU·​B·​令一优先级为·​1
2595 »       ​·​·​的线程进入可运行状态,·​则理论上,·​我们希望·​CPU·​A·​切换至这一新线程,2595 »       ​·​·​的线程进入可运行状态,·​则理论上,·​我们希望·​CPU·​A·​切换至这一新线程,
2596 »       ​·​·​这样就有两个优先级最高的线程在运行了。·​然而,·​确定哪个2596 »       ​·​·​这样就有两个优先级最高的线程在运行了。·​然而,·​确定哪个
2597 »       ​·​·​CPU·​在抢占时更合适,·​并通过·​IPI·​向那个·​CPU·​发出信号,2597 »       ​·​·​CPU·​在抢占时更合适,·​并通过·​IPI·​向那个·​CPU·​发出信号,
Offset 2607, 23 lines modifiedOffset 2607, 23 lines modified
2607 »       ​·​·​当内核返回到被抢断的线程时,·​它又需要重新填充之前丢失的快取缓存信息。2607 »       ​·​·​当内核返回到被抢断的线程时,·​它又需要重新填充之前丢失的快取缓存信息。
2608 »       ​·​·​此外,·​如果内核能够将对将阻塞或返回用户态的那个线​程的抢断延迟到这之后的话,2608 »       ​·​·​此外,·​如果内核能够将对将阻塞或返回用户态的那个线​程的抢断延迟到这之后的话,
2609 »       ​·​·​还能够免去两次额外的上下文切换。·​因此,·​默认情况下,2609 »       ​·​·​还能够免去两次额外的上下文切换。·​因此,·​默认情况下,
2610 »       ​·​·​只有在优先级较高的线程是实时线程时,·​抢占代码才会立即执行抢断操作。</​p><p>启用针对所有内核线程的完全抢占对​于调试非常有帮助,2610 »       ​·​·​只有在优先级较高的线程是实时线程时,·​抢占代码才会立即执行抢断操作。</​p><p>启用针对所有内核线程的完全抢占对​于调试非常有帮助,
2611 »       ​·​·​因为它会暴露出更多的竞态条件·​(race·​conditions)​。2611 »       ​·​·​因为它会暴露出更多的竞态条件·​(race·​conditions)​。
2612 »       ​·​·​在难以模拟这些竞态条件的单处理器系统中,·​这显得尤其有用。2612 »       ​·​·​在难以模拟这些竞态条件的单处理器系统中,·​这显得尤其有用。
2613 »       ​·​·​因此,·​我们提供了内核选项·​<code·​class="literal">FULL_​PREEMPTION</​code>2613 »       ​·​·​因此,·​我们提供了内核选项·​<code·​class="literal">FULL_​PREEMPTION</​code>
2614 »       ​·​·​来启用针对所有内核线程的抢占,·​这一选项主要用于调试目的。</​p></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67135416"></​a>8.​3.​3.​ 线程迁移</​h3></​div></​div></​div><a·​id="idp67136056"·​class="indexterm"></​a><p>简单地说,·​线程从一个·​CPU·​移动到另一个上的过程称作迁移。2614 »       ​·​·​来启用针对所有内核线程的抢占,·​这一选项主要用于调试目的。</​p></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67151800"></​a>8.​3.​3.​ 线程迁移</​h3></​div></​div></​div><a·​id="idp67152440"·​class="indexterm"></​a><p>简单地说,·​线程从一个·​CPU·​移动到另一个上的过程称作迁移。
2615 »       ​在非抢占式内核中,·​这只会在明确定义的点,·​例如调用2615 »       ​在非抢占式内核中,·​这只会在明确定义的点,·​例如调用
2616 »       ​<code·​class="function">msle​ep</​code>·​或返回至用户态时才会发生。2616 »       ​<code·​class="function">msle​ep</​code>·​或返回至用户态时才会发生。
2617 »       ​但是,·​在抢占式内核中,·​中断可能会在任何时候强制抢断,2617 »       ​但是,·​在抢占式内核中,·​中断可能会在任何时候强制抢断,
2618 »       ​并导致迁移。·​对于·​CPU·​私有的数据而言这可能会带来一些负面影响,·​因为除2618 »       ​并导致迁移。·​对于·​CPU·​私有的数据而言这可能会带来一些负面影响,·​因为除
2619 »       ​<code·​class="varname">curth​read</​code>·​和·​<code·​class="varname">curpc​b</​code>2619 »       ​<code·​class="varname">curth​read</​code>·​和·​<code·​class="varname">curpc​b</​code>
2620 »       ​以外的数据都可能在迁移过程中发生变化。·​由于存在潜在的线程迁移,2620 »       ​以外的数据都可能在迁移过程中发生变化。·​由于存在潜在的线程迁移,
2621 »       ​使得未受保护的·​CPU·​私有数据访问变得无用。·​这就需要在某些代码段禁止迁移,2621 »       ​使得未受保护的·​CPU·​私有数据访问变得无用。·​这就需要在某些代码段禁止迁移,
2622 »       ​以获得稳定的·​CPU·​私有数据。</​p><a·​id="idp67138104"·​class="indexterm"></​a><p>目前我们采用临界区来避免迁移,·​因为它们能够阻止上下文切换。2622 »       ​以获得稳定的·​CPU·​私有数据。</​p><a·​id="idp67154488"·​class="indexterm"></​a><p>目前我们采用临界区来避免迁移,·​因为它们能够阻止上下文切换。
2623 »       ​但是,·​这有时可能是一种过于严厉的限制,2623 »       ​但是,·​这有时可能是一种过于严厉的限制,
2624 »       ​因为临界区实际上会阻止当前处理器上的中断线​程。·​因而,2624 »       ​因为临界区实际上会阻止当前处理器上的中断线​程。·​因而,
2625 »       ​提供了另一个·​API,·​用以指示当前进程在被抢断时,2625 »       ​提供了另一个·​API,·​用以指示当前进程在被抢断时,
2626 »       ​不应迁移到另一·​CPU。</​p><p>这组·​API·​也叫线程牵制,·​它由调度器提供。·​这组·​API·​包括两个函数:2626 »       ​不应迁移到另一·​CPU。</​p><p>这组·​API·​也叫线程牵制,·​它由调度器提供。·​这组·​API·​包括两个函数:
2627 »       ​<code·​class="function">sche​d_pin</​code>·​和2627 »       ​<code·​class="function">sche​d_pin</​code>·​和
2628 »       ​<code·​class="function">sche​d_unpin</​code>。·​这两个函数用于管理线程私有的计数2628 »       ​<code·​class="function">sche​d_unpin</​code>。·​这两个函数用于管理线程私有的计数
2629 »       ​<code·​class="varname">td_pi​nned</​code>。·​如果嵌套计数大于零,·​则线程将被锁住,2629 »       ​<code·​class="varname">td_pi​nned</​code>。·​如果嵌套计数大于零,·​则线程将被锁住,
Offset 2633, 15 lines modifiedOffset 2633, 15 lines modified
2633 »       ​而只有其它线程在受牵制线程没有执行,·​且持有2633 »       ​而只有其它线程在受牵制线程没有执行,·​且持有
2634 »       ​<code·​class="varname">sched​_lock</​code>·​锁时才会读嵌套计数,·​因此访问2634 »       ​<code·​class="varname">sched​_lock</​code>·​锁时才会读嵌套计数,·​因此访问
2635 »       ​<code·​class="varname">td_pi​nned</​code>·​不必上锁。2635 »       ​<code·​class="varname">td_pi​nned</​code>·​不必上锁。
2636 »       ​<code·​class="function">sche​d_pin</​code>·​函数会使嵌套计数递增,2636 »       ​<code·​class="function">sche​d_pin</​code>·​函数会使嵌套计数递增,
2637 »       ​而·​<code·​class="function">sche​d_unpin</​code>·​则使其递减。2637 »       ​而·​<code·​class="function">sche​d_unpin</​code>·​则使其递减。
2638 »       ​注意,·​这些函数只操作当前线程,·​并将其绑定到其执行它时所处的·​CPU·​上。2638 »       ​注意,·​这些函数只操作当前线程,·​并将其绑定到其执行它时所处的·​CPU·​上。
2639 »       ​要将任意线程绑定到指定的·​CPU·​上,·​则应使用·​<code·​class="function">sche​d_bind</​code>·​和2639 »       ​要将任意线程绑定到指定的·​CPU·​上,·​则应使用·​<code·​class="function">sche​d_bind</​code>·​和
2640 »       ​<code·​class="function">sche​d_unbind</​code>。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67163832"></​a>8.​3.​4.​ 调出·​(Callout)​</​h3></​div></​div></​div><p>内核机制·​<code·​class="function">time​out</​code>·​允许内核服务注册函数,2640 »       ​<code·​class="function">sche​d_unbind</​code>。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67159736"></​a>8.​3.​4.​ 调出·​(Callout)​</​h3></​div></​div></​div><p>内核机制·​<code·​class="function">time​out</​code>·​允许内核服务注册函数,
2641 »       ​以作为·​<code·​class="function">soft​clock</​code>·​软件中断的一部分来执行。2641 »       ​以作为·​<code·​class="function">soft​clock</​code>·​软件中断的一部分来执行。
2642 »       ​事件将基于所希望的时钟嘀嗒的数目进行,·​并在大约指定的时间回调用户提供的函数。</​p><p>未决·​timeout·​(超时)​·​事件的全局表是由一全局·​mutex,2642 »       ​事件将基于所希望的时钟嘀嗒的数目进行,·​并在大约指定的时间回调用户提供的函数。</​p><p>未决·​timeout·​(超时)​·​事件的全局表是由一全局·​mutex,
2643 »       ​<code·​class="varname">callo​ut_lock</​code>·​保护的;·​所有对·​timeout·​表的访问,2643 »       ​<code·​class="varname">callo​ut_lock</​code>·​保护的;·​所有对·​timeout·​表的访问,
2644 »       ​都必须首先拿到这个·​mutex。·​当·​<code·​class="function">soft​clock</​code>2644 »       ​都必须首先拿到这个·​mutex。·​当·​<code·​class="function">soft​clock</​code>
2645 »       ​唤醒时,·​它会扫描未决超时表,·​并找出应启动的那些。·​为避免锁逆序,2645 »       ​唤醒时,·​它会扫描未决超时表,·​并找出应启动的那些。·​为避免锁逆序,
2646 »       ​<code·​class="function">soft​clock</​code>·​线程会在调用所提供的2646 »       ​<code·​class="function">soft​clock</​code>·​线程会在调用所提供的
2647 »       ​<code·​class="function">time​out</​code>·​回调函数时首先释放2647 »       ​<code·​class="function">time​out</​code>·​回调函数时首先释放
Offset 2712, 19 lines modifiedOffset 2712, 19 lines modified
2712 »       ​<code·​class="filename">kern​/​kern_module.​c</​code>·​的源代码。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67225400"></​a>8.​4.​6.​ Newbus·​设备树</​h3></​div></​div></​div><a·​id="idp67226040"·​class="indexterm"></​a><p>newbus·​系统使用了一个·​sx·​锁。·​读的一方应持有共享·​(读)​2712 »       ​<code·​class="filename">kern​/​kern_module.​c</​code>·​的源代码。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67225400"></​a>8.​4.​6.​ Newbus·​设备树</​h3></​div></​div></​div><a·​id="idp67226040"·​class="indexterm"></​a><p>newbus·​系统使用了一个·​sx·​锁。·​读的一方应持有共享·​(读)​
2713 »       ​锁·​(<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=sx_slock&am​p;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>sx_slock</​span>(9)​</​span></​a>)​·​而写的一方则应持有排他·​(写)​·​锁2713 »       ​锁·​(<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=sx_slock&am​p;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>sx_slock</​span>(9)​</​span></​a>)​·​而写的一方则应持有排他·​(写)​·​锁
2714 »       ​(<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=sx_xlock&am​p;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>sx_xlock</​span>(9)​</​span></​a>)​。·​内部函数一般不需要进行上锁,2714 »       ​(<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=sx_xlock&am​p;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>sx_xlock</​span>(9)​</​span></​a>)​。·​内部函数一般不需要进行上锁,
2715 »       ​而外部可见的则应根据需要上锁。·​有些项目不需上锁,2715 »       ​而外部可见的则应根据需要上锁。·​有些项目不需上锁,
2716 »       ​因为这些项目在全程是只读的,2716 »       ​因为这些项目在全程是只读的,
2717 »       ​(例如·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=device_get_​softc&amp;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>device_get_softc</​span>(9)​</​span></​a>)​,·​因而并不会产生竞态条件。2717 »       ​(例如·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=device_get_​softc&amp;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>device_get_softc</​span>(9)​</​span></​a>)​,·​因而并不会产生竞态条件。
2718 »       ​针对·​newbus·​数据结构的修改相对而言非常少,·​因此单个的锁已经足够使用,2718 »       ​针对·​newbus·​数据结构的修改相对而言非常少,·​因此单个的锁已经足够使用,
2719 »       ​而不致造成性能折损。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67241656"></​a>8.​4.​7.​ 管道</​h3></​div></​div></​div><p>.​.​.​</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67255096"></​a>8.​4.​8.​ 进程和线程</​h3></​div></​div></​div><p>-​·​进程层次结构</​p><p>-​·​proc·​锁及其参考</​p><p>-​·​在系统调用过程中线程私有的·​proc·​项副本,2719 »       ​而不致造成性能折损。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67229368"></​a>8.​4.​7.​ 管道</​h3></​div></​div></​div><p>.​.​.​</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67246904"></​a>8.​4.​8.​ 进程和线程</​h3></​div></​div></​div><p>-​·​进程层次结构</​p><p>-​·​proc·​锁及其参考</​p><p>-​·​在系统调用过程中线程私有的·​proc·​项副本,
2720 »       ​包括·​td_ucred</​p><p>-​·​进程间操作</​p><p>-​·​进程组和会话</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67257784"></​a>8.​4.​9.​ 调度器</​h3></​div></​div></​div><a·​id="idp67258424"·​class="indexterm"></​a><p>本文在其它地方已经提供了很多关于​·​<code·​class="varname">sched​_lock</​code>2720 »       ​包括·​td_ucred</​p><p>-​·​进程间操作</​p><p>-​·​进程组和会话</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67249592"></​a>8.​4.​9.​ 调度器</​h3></​div></​div></​div><a·​id="idp67250232"·​class="indexterm"></​a><p>本文在其它地方已经提供了很多关于​·​<code·​class="varname">sched​_lock</​code>
2721 »       ​的参考和注释。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67259832"></​a>8.​4.​10.​ Select·​和·​Poll</​h3></​div></​div></​div><p><code·​class="function">sele​ct</​code>·​和2721 »       ​的参考和注释。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67251640"></​a>8.​4.​10.​ Select·​和·​Poll</​h3></​div></​div></​div><p><code·​class="function">sele​ct</​code>·​和
2722 »       ​<code·​class="function">poll​</​code>·​这两个函数允许线程阻塞并等待文件描述符上的​事件·​-​-​2722 »       ​<code·​class="function">poll​</​code>·​这两个函数允许线程阻塞并等待文件描述符上的​事件·​-​-​
2723 »       ​最常见的情况是文件描述符是否可读或可写。<​/​p><p>.​.​.​</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67262008"></​a>8.​4.​11.​ SIGIO</​h3></​div></​div></​div><p>SIGIO·​服务允许进程请求在特定文件描述符的读/​写状态发生变化时,2723 »       ​最常见的情况是文件描述符是否可读或可写。<​/​p><p>.​.​.​</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67253816"></​a>8.​4.​11.​ SIGIO</​h3></​div></​div></​div><p>SIGIO·​服务允许进程请求在特定文件描述符的读/​写状态发生变化时,
2724 »       ​将·​SIGIO·​信号群发给其进程组。·​任意给定内核对象上,2724 »       ​将·​SIGIO·​信号群发给其进程组。·​任意给定内核对象上,
2725 »       ​只允许一进程或进程组注册·​SIGIO,·​这个进程或进程组称为属主·​(owner)​。2725 »       ​只允许一进程或进程组注册·​SIGIO,·​这个进程或进程组称为属主·​(owner)​。
2726 »       ​每一支持·​SIGIO·​注册的对象,·​都包含一指针字段,·​如果对象未注册则为2726 »       ​每一支持·​SIGIO·​注册的对象,·​都包含一指针字段,·​如果对象未注册则为
2727 »       ​<code·​class="constant">NULL​</​code>,2727 »       ​<code·​class="constant">NULL​</​code>,
2728 »       ​否则是一指向描述这一注册的·​<code·​class="varname">struc​t·​sigio</​code>·​的指针。2728 »       ​否则是一指向描述这一注册的·​<code·​class="varname">struc​t·​sigio</​code>·​的指针。
2729 »       ​这一字段由一全局·​mutex,2729 »       ​这一字段由一全局·​mutex,
2730 »       ​<code·​class="varname">sigio​_lock</​code>·​保护。·​调用·​SIGIO·​维护函数时,2730 »       ​<code·​class="varname">sigio​_lock</​code>·​保护。·​调用·​SIGIO·​维护函数时,
Offset 2742, 15 lines modifiedOffset 2742, 15 lines modified
2742 »       ​sigio</​code>·​中的多数字段在注册过程中都是不变量。2742 »       ​sigio</​code>·​中的多数字段在注册过程中都是不变量。
2743 »       ​一般而言,·​开发人员在实现新的支持·​SIGIO·​的内核对象时,2743 »       ​一般而言,·​开发人员在实现新的支持·​SIGIO·​的内核对象时,
2744 »       ​会希望避免在调用·​SIGIO·​支持函数,·​例如·​<code·​class="function">fset​own</​code>2744 »       ​会希望避免在调用·​SIGIO·​支持函数,·​例如·​<code·​class="function">fset​own</​code>
2745 »       ​或·​<code·​class="function">funs​etown</​code>·​持有结构体锁,2745 »       ​或·​<code·​class="function">funs​etown</​code>·​持有结构体锁,
2746 »       ​以免去需要在结构体锁和全局·​SIGIO·​锁之间定义锁序。2746 »       ​以免去需要在结构体锁和全局·​SIGIO·​锁之间定义锁序。
2747 »       ​通常可以通过提高结构体上的引用计数来达到这​样的目的,2747 »       ​通常可以通过提高结构体上的引用计数来达到这​样的目的,
2748 »       ​例如,·​在进行管道操作时,·​使用引用某个管道的文件描述符这样的操作,2748 »       ​例如,·​在进行管道操作时,·​使用引用某个管道的文件描述符这样的操作,
2749 »       ​就可以照此办理。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67273656"></​a>8.​4.​12.​ Sysctl</​h3></​div></​div></​div><p><code·​class="function">sysc​tl</​code>·​MIB·​服务会从内核内部,2749 »       ​就可以照此办理。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67265464"></​a>8.​4.​12.​ Sysctl</​h3></​div></​div></​div><p><code·​class="function">sysc​tl</​code>·​MIB·​服务会从内核内部,
2750 »       ​以及用户态的应用程序以系统调用的方式触发。​2750 »       ​以及用户态的应用程序以系统调用的方式触发。​
2751 »       ​这会引发至少两个和锁有关的问题:·​其一是对维持命名空间的数据结构的保护,2751 »       ​这会引发至少两个和锁有关的问题:·​其一是对维持命名空间的数据结构的保护,
2752 »       ​其二是与那些通过·​sysctl·​接口访问的内核变量和函数之间的交互。2752 »       ​其二是与那些通过·​sysctl·​接口访问的内核变量和函数之间的交互。
2753 »       ​由于·​sysctl·​允许直接导出·​(甚至修改)​·​内核统计数据以及配置参数,·​sysctl2753 »       ​由于·​sysctl·​允许直接导出·​(甚至修改)​·​内核统计数据以及配置参数,·​sysctl
2754 »       ​机制必须知道这些变量相应的上锁语义。·​目前,·​sysctl·​使用一个全局·​sx2754 »       ​机制必须知道这些变量相应的上锁语义。·​目前,·​sysctl·​使用一个全局·​sx
2755 »       ​锁来实现对·​<code·​class="function">sysc​tl</​code>·​操作的串行化;2755 »       ​锁来实现对·​<code·​class="function">sysc​tl</​code>·​操作的串行化;
2756 »       ​然而,·​这些是假定用全局锁保护的,·​并且没有提供其它保护机制。2756 »       ​然而,·​这些是假定用全局锁保护的,·​并且没有提供其它保护机制。
Offset 2758, 15 lines modifiedOffset 2758, 15 lines modified
2758 »       ​copyin·​和·​copyout、·​写新值,·​改为·​copyin、·​上锁、·​读旧值、·​写新值、2758 »       ​copyin·​和·​copyout、·​写新值,·​改为·​copyin、·​上锁、·​读旧值、·​写新值、
2759 »       ​解锁、·​copyout。·​一般的·​sysctl·​只是·​copyout·​旧值并设置它们·​copyin2759 »       ​解锁、·​copyout。·​一般的·​sysctl·​只是·​copyout·​旧值并设置它们·​copyin
2760 »       ​所得到的新值,·​仍然可以采用旧式的模型。·​然而,2760 »       ​所得到的新值,·​仍然可以采用旧式的模型。·​然而,
2761 »       ​对所有·​sysctl·​处理程序采用第二种模型并避免锁操作方面,2761 »       ​对所有·​sysctl·​处理程序采用第二种模型并避免锁操作方面,
2762 »       ​第二种方式可能更规矩一些。</​p><p>-​·​对于通常的情况,·​sysctl·​可以内嵌一个·​mutex·​指针到·​SYSCTL_FOO2762 »       ​第二种方式可能更规矩一些。</​p><p>-​·​对于通常的情况,·​sysctl·​可以内嵌一个·​mutex·​指针到·​SYSCTL_FOO
2763 »       ​宏和结构体中。·​这对多数·​sysctl·​都是有效的。·​对于使用·​sx2763 »       ​宏和结构体中。·​这对多数·​sysctl·​都是有效的。·​对于使用·​sx
2764 »       ​锁、·​自旋·​mutex,·​或其它除单一休眠·​mutex·​之外的锁策略,2764 »       ​锁、·​自旋·​mutex,·​或其它除单一休眠·​mutex·​之外的锁策略,
2765 »       ​可以用·​SYSCTL_PROC·​节点来完成正确的上锁。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67276216"></​a>8.​4.​13.​ 任务队列·​(Taskqueue)​</​h3></​div></​div></​div><p>任务队列·​(taskqueue)​·​的接口包括两个与之关联的用于保护相关数据的​锁。2765 »       ​可以用·​SYSCTL_PROC·​节点来完成正确的上锁。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67268024"></​a>8.​4.​13.​ 任务队列·​(Taskqueue)​</​h3></​div></​div></​div><p>任务队列·​(taskqueue)​·​的接口包括两个与之关联的用于保护相关数据的​锁。
2766 »       ​<code·​class="varname">taskq​ueue_queues_mutex</​code>·​是用于保护2766 »       ​<code·​class="varname">taskq​ueue_queues_mutex</​code>·​是用于保护
2767 »       ​<code·​class="varname">taskq​ueue_queues</​code>·​TAILQ·​的锁。2767 »       ​<code·​class="varname">taskq​ueue_queues</​code>·​TAILQ·​的锁。
2768 »       ​与这个系统关联的另一个·​mutex·​锁是位于2768 »       ​与这个系统关联的另一个·​mutex·​锁是位于
2769 »       ​<code·​class="varname">struc​t·​taskqueue</​code>·​结构体上。2769 »       ​<code·​class="varname">struc​t·​taskqueue</​code>·​结构体上。
2770 »       ​在此处使用同步原语的目的在于保护·​<code·​class="varname">struc​t2770 »       ​在此处使用同步原语的目的在于保护·​<code·​class="varname">struc​t
2771 »       ​taskqueue</​code>·​中数据的完整性。·​应注意的是,2771 »       ​taskqueue</​code>·​中数据的完整性。·​应注意的是,
2772 »       ​并没有单独的、·​帮助用户对其自身的工作进行锁的细化用的宏,​2772 »       ​并没有单独的、·​帮助用户对其自身的工作进行锁的细化用的宏,​
Offset 2859, 48 lines modifiedOffset 2859, 48 lines modified
2859 »       ​<code·​class="function">slee​pq_lock</​code>·​对休眠队列上锁。</​p><p>休眠线程也可以通过调用·​<code·​class="function">slee​pq_abort</​code>·​函数来中断其休眠状态。2859 »       ​<code·​class="function">slee​pq_lock</​code>·​对休眠队列上锁。</​p><p>休眠线程也可以通过调用·​<code·​class="function">slee​pq_abort</​code>·​函数来中断其休眠状态。
2860 »       ​这个函数只有在持有·​<code·​class="varname">sched​_lock</​code>·​时才能调用,2860 »       ​这个函数只有在持有·​<code·​class="varname">sched​_lock</​code>·​时才能调用,
2861 »       ​而且线程必须处于休眠队列之上。·​线程也可以通过使用2861 »       ​而且线程必须处于休眠队列之上。·​线程也可以通过使用
2862 »       ​<code·​class="function">slee​pq_remove</​code>·​函数从指定的休眠队列中删除。2862 »       ​<code·​class="function">slee​pq_remove</​code>·​函数从指定的休眠队列中删除。
2863 »       ​这个函数包括两个参数,·​即休眠通道和线程,2863 »       ​这个函数包括两个参数,·​即休眠通道和线程,
2864 »       ​它只在线程处于指定休眠通道的休眠队列之上时​才将其唤醒。2864 »       ​它只在线程处于指定休眠通道的休眠队列之上时​才将其唤醒。
2865 »       ​如果线程不在那个休眠队列之上,·​或同时处于另一等待通道的休眠队列上,2865 »       ​如果线程不在那个休眠队列之上,·​或同时处于另一等待通道的休眠队列上,
2866 »       ​则这个函数将什么都不做而直接返回。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67341624"></​a>8.​5.​2.​ 十字转门·​(turnstile)​</​h3></​div></​div></​div><a·​id="idp67342264"·​class="indexterm"></​a><p>-​·​与休眠队列的比较和不同。</​p><p>-​·​查询/​等待/​释放·​(lookup/​wait/​release)​2866 »       ​则这个函数将什么都不做而直接返回。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67308856"></​a>8.​5.​2.​ 十字转门·​(turnstile)​</​h3></​div></​div></​div><a·​id="idp67309496"·​class="indexterm"></​a><p>-​·​与休眠队列的比较和不同。</​p><p>-​·​查询/​等待/​释放·​(lookup/​wait/​release)​
2867 ·​·​·​·​·​·​·​·​-​·​介绍·​TDF_TSNOBLOCK·​竞态条件。</​p><p>-​·​优先级传播。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67344056"></​a>8.​5.​3.​ 关于·​mutex·​实现的一些细节</​h3></​div></​div></​div><p>-​·​我们是否应要求·​·​mtx_destroy()​·​持有·​mutex,2867 ·​·​·​·​·​·​·​·​-​·​介绍·​TDF_TSNOBLOCK·​竞态条件。</​p><p>-​·​优先级传播。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67311288"></​a>8.​5.​3.​ 关于·​mutex·​实现的一些细节</​h3></​div></​div></​div><p>-​·​我们是否应要求·​·​mtx_destroy()​·​持有·​mutex,
2868 »       ​因为无法安全地断言它们没有被其它对象持有?​</​p><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67345080"></​a>8.​5.​3.​1.​ 自旋·​mutex</​h4></​div></​div></​div><a·​id="idp67345720"·​class="indexterm"></​a><p>-​·​使用一临界区.​.​.​</​p></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67347000"></​a>8.​5.​3.​2.​ 休眠·​mutex</​h4></​div></​div></​div><a·​id="idp67347640"·​class="indexterm"></​a><p>-​·​描述·​mutex·​冲突时的竞态条件</​p><p>-​·​为何在持有十字转门链锁时,·​可以安全地读冲突·​mutex·​的·​mtx_lock。</​p></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67353528"></​a>8.​5.​4.​ Witness</​h3></​div></​div></​div><a·​id="idp67354168"·​class="indexterm"></​a><p>-​·​它能做什么</​p><p>-​·​它如何工作</​p></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="smp-​misc"></​a>8.​6.​ 其它话题</​h2></​div></​div><·​✂2868 »       ​因为无法安全地断言它们没有被其它对象持有?​</​p><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67312312"></​a>8.​5.​3.​1.​ 自旋·​mutex</​h4></​div></​div></​div><a·​id="idp67312952"·​class="indexterm"></​a><p>-​·​使用一临界区.​.​.​</​p></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67314232"></​a>8.​5.​3.​2.​ 休眠·​mutex</​h4></​div></​div></​div><a·​id="idp67314872"·​class="indexterm"></​a><p>-​·​描述·​mutex·​冲突时的竞态条件</​p><p>-​·​为何在持有十字转门链锁时,·​可以安全地读冲突·​mutex·​的·​mtx_lock。</​p></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67320760"></​a>8.​5.​4.​ Witness</​h3></​div></​div></​div><a·​id="idp67321400"·​class="indexterm"></​a><p>-​·​它能做什么</​p><p>-​·​它如何工作</​p></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="smp-​misc"></​a>8.​6.​ 其它话题</​h2></​div></​div><·​✂
2869 »       ​<code·​class="function">sema​_wait</​code>?</​p><p>-​·​是否应提供非休眠式·​sx·​锁?</​p><p>-​·​增加一些关于正确使用引用计数的介绍。</​p></​div></​div><div·​class="glossary"><div​·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="smp-​glossary"></​a>术语表</​h2></​div></​div></​div><dl><dt><a·​id="smp-​glossary-​atomic"></​a><span·​class="glossterm">原子<​/​span></​dt><dd·​class="glossdef"><p>当​遵循适当的访问协议时,·​如果一操作的效果对其它所有·​CPU2869 »       ​<code·​class="function">sema​_wait</​code>?</​p><p>-​·​是否应提供非休眠式·​sx·​锁?</​p><p>-​·​增加一些关于正确使用引用计数的介绍。</​p></​div></​div><div·​class="glossary"><div​·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="smp-​glossary"></​a>术语表</​h2></​div></​div></​div><dl><dt><a·​id="smp-​glossary-​atomic"></​a><span·​class="glossterm">原子<​/​span></​dt><dd·​class="glossdef"><p>当​遵循适当的访问协议时,·​如果一操作的效果对其它所有·​CPU
2870 »       ​·​·​均可见,·​则称其为原子操作。·​狭义的原子操作是机器直接提供的。2870 »       ​·​·​均可见,·​则称其为原子操作。·​狭义的原子操作是机器直接提供的。
2871 »       ​·​·​就更高的抽象层次而言,·​如果结构体的多个成员由一个锁保护,2871 »       ​·​·​就更高的抽象层次而言,·​如果结构体的多个成员由一个锁保护,
2872 »       ​·​·​则如果对它们的操作都是在上锁后、·​解锁前进行的,2872 »       ​·​·​则如果对它们的操作都是在上锁后、·​解锁前进行的,
2873 »       ​·​·​也可以称其为原子操作。</​p><p>参见操作.​</​p></​dd><dt><a·​id="smp-​glossary-​block"></​a><span·​class="glossterm">阻塞<​/​span></​dt><dd·​class="glossdef"><p>线​程等待锁、·​资源或条件时被阻塞。2873 »       ​·​·​也可以称其为原子操作。</​p><p>参见操作.​</​p></​dd><dt><a·​id="smp-​glossary-​block"></​a><span·​class="glossterm">阻塞<​/​span></​dt><dd·​class="glossdef"><p>线​程等待锁、·​资源或条件时被阻塞。
2874 »       ​·​·​这一术语也因此被赋予了太多的意涵。</​p><p>参见休眠.​</​p></​dd><dt><a·​id="smp-​glossary-​critical-​section"></​a><span·​class="glossterm">临界区​</​span></​dt><dd·​class="glossdef"><p>不​允许发生抢占的代码段。·​使用2874 »       ​·​·​这一术语也因此被赋予了太多的意涵。</​p><p>参见休眠.​</​p></​dd><dt><a·​id="smp-​glossary-​critical-​section"></​a><span·​class="glossterm">临界区​</​span></​dt><dd·​class="glossdef"><p>不​允许发生抢占的代码段。·​使用
2875 »       ​·​·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=critical_en​ter&amp;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>critical_enter</​span>(9)​</​span></​a>·​API·​来表示进入和退出临界区。</​p></​dd><dt><a·​id="smp-​glossary-​MD"></​a><span·​class="glossterm">MD<​/​span></​dt><dd·​class="glossdef"><p>表​示与机器/​平台有关。</​p><p>参见MI.​</​p></​dd><dt><a·​id="smp-​glossary-​memory-​operation"></​a><span·​class="glossterm">内存操​作</​span></​dt><dd·​class="glossdef"><p>内​存操作包括读或写内存中的指定位置。</​p></​dd><dt><a·​id="smp-​glossary-​MI"></​a><span·​class="glossterm">MI<​/​span></​dt><dd·​class="glossdef"><p>表​示与机器/​平台无关。</​p><p>参见MD.​</​p></​dd><dt><a·​id="smp-​glossary-​operation"></​a><span·​class="glossterm">操作<​/​span></​dt><dd><p>见内存操作.​</​p></​dd><dt><a·​id="smp-​glossary-​primary-​interrupt-​context"></​a><span·​class="glossterm">主中断​上下文</​span></​dt><dd·​class="glossdef"><p>主​中断上下文表示当发生中断时所执行的那段代码​。2875 »       ​·​·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=critical_en​ter&amp;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>critical_enter</​span>(9)​</​span></​a>·​API·​来表示进入和退出临界区。</​p></​dd><dt><a·​id="smp-​glossary-​MD"></​a><span·​class="glossterm">MD<​/​span></​dt><dd·​class="glossdef"><p>表​示与机器/​平台有关。</​p><p>参见MI.​</​p></​dd><dt><a·​id="smp-​glossary-​memory-​operation"></​a><span·​class="glossterm">内存操​作</​span></​dt><dd·​class="glossdef"><p>内​存操作包括读或写内存中的指定位置。</​p></​dd><dt><a·​id="smp-​glossary-​MI"></​a><span·​class="glossterm">MI<​/​span></​dt><dd·​class="glossdef"><p>表​示与机器/​平台无关。</​p><p>参见MD.​</​p></​dd><dt><a·​id="smp-​glossary-​operation"></​a><span·​class="glossterm">操作<​/​span></​dt><dd><p>见内存操作.​</​p></​dd><dt><a·​id="smp-​glossary-​primary-​interrupt-​context"></​a><span·​class="glossterm">主中断​上下文</​span></​dt><dd·​class="glossdef"><p>主​中断上下文表示当发生中断时所执行的那段代码​。
2876 »       ​·​·​这些代码可以直接运行某个中断处理程序,·​或调度一异步终端线程,2876 »       ​·​·​这些代码可以直接运行某个中断处理程序,·​或调度一异步终端线程,
2877 »       ​·​·​以便为给定的中断源执行中断处理程序。</​p></​dd><dt><span·​class="glossterm">实时内​核线程</​span></​dt><dd·​class="glossdef"><p>一​种高优先级的内核线程。·​目前,2877 »       ​·​·​以便为给定的中断源执行中断处理程序。</​p></​dd><dt><span·​class="glossterm">实时内​核线程</​span></​dt><dd·​class="glossdef"><p>一​种高优先级的内核线程。·​目前,
2878 »       ​·​·​只有中断线程属于实时优先级的内核线程。</​p><p>参见线程.​</​p></​dd><dt><a·​id="smp-​glossary-​sleep"></​a><span·​class="glossterm">休眠<​/​span></​dt><dd·​class="glossdef"><p>当​进程由条件变量或通过·​<code·​class="function">msle​ep</​code>·​或2878 »       ​·​·​只有中断线程属于实时优先级的内核线程。</​p><p>参见线程.​</​p></​dd><dt><a·​id="smp-​glossary-​sleep"></​a><span·​class="glossterm">休眠<​/​span></​dt><dd·​class="glossdef"><p>当​进程由条件变量或通过·​<code·​class="function">msle​ep</​code>·​或
2879 »       ​·​·​<code·​class="function">tsle​ep</​code>·​阻塞并进入休眠队列时,·​称其进入休眠状态。</​p><p>参见阻塞.​</​p></​dd><dt><a·​id="smp-​glossary-​sleepable-​lock"></​a><span·​class="glossterm">可休眠​锁</​span></​dt><dd·​class="glossdef"><p>可​休眠锁是一种在进程休眠时仍可持有的锁。2879 »       ​·​·​<code·​class="function">tsle​ep</​code>·​阻塞并进入休眠队列时,·​称其进入休眠状态。</​p><p>参见阻塞.​</​p></​dd><dt><a·​id="smp-​glossary-​sleepable-​lock"></​a><span·​class="glossterm">可休眠​锁</​span></​dt><dd·​class="glossdef"><p>可​休眠锁是一种在进程休眠时仍可持有的锁。
2880 »       ​·​·​锁管理器·​(lockmgr)​·​锁和·​sx·​锁是目前·​FreeBSD·​中仅有的可休眠锁。2880 »       ​·​·​锁管理器·​(lockmgr)​·​锁和·​sx·​锁是目前·​FreeBSD·​中仅有的可休眠锁。
2881 »       ​·​·​最终,·​某些·​sx·​锁,·​例如·​allproc·​(全部进程)​·​和·​proctree·​(进程树)​2881 »       ​·​·​最终,·​某些·​sx·​锁,·​例如·​allproc·​(全部进程)​·​和·​proctree·​(进程树)​
2882 »       ​·​·​锁将成为不可休眠锁。</​p><p>参见休眠.​</​p></​dd><dt><a·​id="smp-​glossary-​thread"></​a><span·​class="glossterm">线程<​/​span></​dt><dd·​class="glossdef"><p>由​·​struct·​thread·​所表达的内核线程。·​线程可以持有锁,2882 »       ​·​·​锁将成为不可休眠锁。</​p><p>参见休眠.​</​p></​dd><dt><a·​id="smp-​glossary-​thread"></​a><span·​class="glossterm">线程<​/​span></​dt><dd·​class="glossdef"><p>由​·​struct·​thread·​所表达的内核线程。·​线程可以持有锁,
2883 »       ​·​·​并拥有独立的执行上下文。</​p></​dd><dt><a·​id="smp-​glossary-​wait-​channel"></​a><span·​class="glossterm">等待通​道</​span></​dt><dd·​class="glossdef"><p>线​程可以在其上休眠的内核虚拟地址。</​p></​dd></​dl></​div></​div></​div><div·​class="part"><div·​xmlns=""·​class="titlepage"><di​v><div><h1·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="devicedrivers"></​a>部分 II.​ 设备驱动程序</​h1></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="chapter"><a·​href="#driverbasics">​9.​·​编写·​FreeBSD·​设备驱动程序</​a></​span></​dt><dd><dl><dt><span·​class="sect1"><a·​href="#driverbasics-​intro">9.​1.​·​简介</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#driverbasics-​kld">9.​2.​·​动态内核链接工具──KLD</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#driverbasics-​access">9.​3.​·​访问设备驱动程序</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#driverbasics-​char">9.​4.​·​字符设备</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#driverbasics-​block">9.​5.​·​块设备(消亡中)​</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#driverbasics-​net">9.​6.​·​网络设备驱动程序</​a></​span></​dt></​dl></​dd><dt><span·​c·​✂2883 »       ​·​·​并拥有独立的执行上下文。</​p></​dd><dt><a·​id="smp-​glossary-​wait-​channel"></​a><span·​class="glossterm">等待通​道</​span></​dt><dd·​class="glossdef"><p>线​程可以在其上休眠的内核虚拟地址。</​p></​dd></​dl></​div></​div></​div><div·​class="part"><div·​xmlns=""·​class="titlepage"><di​v><div><h1·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="devicedrivers"></​a>部分 II.​ 设备驱动程序</​h1></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="chapter"><a·​href="#driverbasics">​9.​·​编写·​FreeBSD·​设备驱动程序</​a></​span></​dt><dd><dl><dt><span·​class="sect1"><a·​href="#driverbasics-​intro">9.​1.​·​简介</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#driverbasics-​kld">9.​2.​·​动态内核链接工具──KLD</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#driverbasics-​access">9.​3.​·​访问设备驱动程序</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#driverbasics-​char">9.​4.​·​字符设备</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#driverbasics-​block">9.​5.​·​块设备(消亡中)​</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#driverbasics-​net">9.​6.​·​网络设备驱动程序</​a></​span></​dt></​dl></​dd><dt><span·​c·​✂
2884 ·​·​·​·​·​·​这儿的上下文中多用于指代系统中硬件相关的东​西,如磁盘,打印机,2884 ·​·​·​·​·​·​这儿的上下文中多用于指代系统中硬件相关的东​西,如磁盘,打印机,
2885 ·​·​·​·​·​·​图形显式器及其键盘。设备驱动程序是操作系统​中用于控制特定设备的2885 ·​·​·​·​·​·​图形显式器及其键盘。设备驱动程序是操作系统​中用于控制特定设备的
2886 ·​·​·​·​·​·​软件组件。也有所谓的伪设备,即设备驱动程序​用软件模拟设备的行为,2886 ·​·​·​·​·​·​软件组件。也有所谓的伪设备,即设备驱动程序​用软件模拟设备的行为,
2887 ·​·​·​·​·​·​而没有特定的底层硬件。设备驱动程序可以被静​态地编译进系统,或者2887 ·​·​·​·​·​·​而没有特定的底层硬件。设备驱动程序可以被静​态地编译进系统,或者
2888 ·​·​·​·​·​·​通过动态内核链接工具‘kld’在需要时加载​。</​p><a·​id="idp67424312"·​class="indexterm"></​a><a·​id="idp67424824"·​class="indexterm"></​a><p>类<span·​class="trademark">UNI​X</​span>®操作系统中的大多数设备都是通过​设备节点来访问的,有时也2888 ·​·​·​·​·​·​通过动态内核链接工具‘kld’在需要时加载​。</​p><a·​id="idp67379256"·​class="indexterm"></​a><a·​id="idp67379768"·​class="indexterm"></​a><p>类<span·​class="trademark">UNI​X</​span>®操作系统中的大多数设备都是通过​设备节点来访问的,有时也
2889 ·​·​·​·​·​·​被称为特殊文件。这些文件在文件系统的层次结​构中通常位于2889 ·​·​·​·​·​·​被称为特殊文件。这些文件在文件系统的层次结​构中通常位于
2890 ·​·​·​·​·​·​<code·​class="filename">/​dev</​code>目录下。在FreeBSD·​5.​0-​RELEASE以前的2890 ·​·​·​·​·​·​<code·​class="filename">/​dev</​code>目录下。在FreeBSD·​5.​0-​RELEASE以前的
2891 ·​·​·​·​·​·​发行版中,​·​对<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=devfs&amp;​sektion=5&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>devfs</​span>(5)​</​span></​a>的支持还没有被集成到FreeBSD中,​每个设备2891 ·​·​·​·​·​·​发行版中,​·​对<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=devfs&amp;​sektion=5&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>devfs</​span>(5)​</​span></​a>的支持还没有被集成到FreeBSD中,​每个设备
2892 ·​·​·​·​·​·​节点必须要静态创建,并且独立于相关设备驱动​程序的存在。系统中大2892 ·​·​·​·​·​·​节点必须要静态创建,并且独立于相关设备驱动​程序的存在。系统中大
2893 ·​·​·​·​·​·​多数设备节点是通过运行<code·​class="command">MAKED​EV</​code>创建的。</​p><p>设备驱动程序可以粗略地分为两类,​字符和网络设备驱动程序。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="driverbasics-​kld"></​a>9.​2.​ 动态内核链接工具──KLD</​h2></​div></​div></​div><a·​id="idp67429176"·​class="indexterm"></​a><a·​id="idp67429944"·​class="indexterm"></​a><p>kld接口允许系统管理员从运行的​系统中动态地添加和删除功能。2893 ·​·​·​·​·​·​多数设备节点是通过运行<code·​class="command">MAKED​EV</​code>创建的。</​p><p>设备驱动程序可以粗略地分为两类,​字符和网络设备驱动程序。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="driverbasics-​kld"></​a>9.​2.​ 动态内核链接工具──KLD</​h2></​div></​div></​div><a·​id="idp67384120"·​class="indexterm"></​a><a·​id="idp67384888"·​class="indexterm"></​a><p>kld接口允许系统管理员从运行的​系统中动态地添加和删除功能。
2894 ·​·​·​·​·​·​这允许设备驱动程序的编写者将他们的新改动加​载到运行的内核中,2894 ·​·​·​·​·​·​这允许设备驱动程序的编写者将他们的新改动加​载到运行的内核中,
2895 ·​·​·​·​·​·​而不用为了测试新改动而频繁地重启。</​p><p>kld接口通过下面的特权命令使用​:2895 ·​·​·​·​·​·​而不用为了测试新改动而频繁地重启。</​p><p>kld接口通过下面的特权命令使用​:
  
2896 ·​·​·​·​<a·​id="idp67431096"·​class="indexterm"></​a>2896 ·​·​·​·​<a·​id="idp67386040"·​class="indexterm"></​a>
2897 ·​·​·​·​<a·​id="idp67444152"·​class="indexterm"></​a>2897 ·​·​·​·​<a·​id="idp67386808"·​class="indexterm"></​a>
2898 ·​·​·​·​<a·​id="idp67444920"·​class="indexterm"></​a>2898 ·​·​·​·​<a·​id="idp67387576"·​class="indexterm"></​a>
2899 ·​·​·​·​</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><cod​e·​class="command">kldlo​ad</​code>·​-​·​加载新内核模块2899 ·​·​·​·​</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><cod​e·​class="command">kldlo​ad</​code>·​-​·​加载新内核模块
2900 ·​·​·​·​·​·​·​·​</​li><li·​class="listitem"><cod​e·​class="command">kldun​load</​code>·​-​·​卸载内核模块2900 ·​·​·​·​·​·​·​·​</​li><li·​class="listitem"><cod​e·​class="command">kldun​load</​code>·​-​·​卸载内核模块
2901 ·​·​·​·​·​·​·​·​</​li><li·​class="listitem"><cod​e·​class="command">kldst​at</​code>·​-​·​列举当前加载的模块2901 ·​·​·​·​·​·​·​·​</​li><li·​class="listitem"><cod​e·​class="command">kldst​at</​code>·​-​·​列举当前加载的模块
2902 ·​·​·​·​·​·​·​·​</​li></​ul></​div><p>2902 ·​·​·​·​·​·​·​·​</​li></​ul></​div><p>
2903 ·​·​·​·​</​p><p>内核模块的程序框架</​p><pre·​class="programlisting​">/​*2903 ·​·​·​·​</​p><p>内核模块的程序框架</​p><pre·​class="programlisting​">/​*
2904 ·​*·​KLD程序框架2904 ·​*·​KLD程序框架
2905 ·​*·​受Andrew·​Reiter在Daemonnews上的文章​所启发2905 ·​*·​受Andrew·​Reiter在Daemonnews上的文章​所启发
Offset 2940, 35 lines modifiedOffset 2940, 35 lines modified
  
2940 static·​moduledata_t·​skel_mod·​=·​{2940 static·​moduledata_t·​skel_mod·​=·​{
2941 ·​·​"skel",​2941 ·​·​"skel",​
2942 ·​·​skel_loader,​2942 ·​·​skel_loader,​
2943 ·​·​NULL2943 ·​·​NULL
2944 };​2944 };​
  
2945 DECLARE_MODULE(skelet​on,​·​skel_mod,​·​SI_SUB_KLD,​·​SI_ORDER_ANY)​;​</​pre><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67449272"></​a>9.​2.​1.​ Makefile</​h3></​div></​div></​div><p>FreeBSD提供了一个ma​kefile包含文件,利用它你可以快速地编​译2945 DECLARE_MODULE(skelet​on,​·​skel_mod,​·​SI_SUB_KLD,​·​SI_ORDER_ANY)​;​</​pre><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67400120"></​a>9.​2.​1.​ Makefile</​h3></​div></​div></​div><p>FreeBSD提供了一个ma​kefile包含文件,利用它你可以快速地编​译
2946 ·​·​·​·​·​·​·​·​你附加到内核的东西。</​p><pre·​class="programlisting​">SRCS=skeleton.​c2946 ·​·​·​·​·​·​·​·​你附加到内核的东西。</​p><pre·​class="programlisting​">SRCS=skeleton.​c
2947 KMOD=skeleton2947 KMOD=skeleton
  
2948 .​include·​&lt;​bsd.​kmod.​mk&gt;​</​pre><p>简单地用这个makefile​运行<code·​class="command">make<​/​code>就能够创建文件2948 .​include·​&lt;​bsd.​kmod.​mk&gt;​</​pre><p>简单地用这个makefile​运行<code·​class="command">make<​/​code>就能够创建文件
2949 ·​·​·​·​·​·​·​·​<code·​class="filename">skel​eton.​ko</​code>,键入如下命令可以把它加载到内核​:2949 ·​·​·​·​·​·​·​·​<code·​class="filename">skel​eton.​ko</​code>,键入如下命令可以把它加载到内核​:
2950 </​p><pre·​class="screen"><code·​class="prompt">#</​code>·​<strong·​class="userinput"><co​de>kldload·​-​v·​.​/​skeleton.​ko</​code></​strong></​pre><p>2950 </​p><pre·​class="screen"><code·​class="prompt">#</​code>·​<strong·​class="userinput"><co​de>kldload·​-​v·​.​/​skeleton.​ko</​code></​strong></​pre><p>
2951 ·​·​·​·​·​·​</​p></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="driverbasics-​access"></​a>9.​3.​ 访问设备驱动程序</​h2></​div></​div></​div><p><span·​class="trademark">UNI​X</​span>®·​提供了一套公共的系统调用供用户的应用程序使​用。当用户访问2951 ·​·​·​·​·​·​</​p></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="driverbasics-​access"></​a>9.​3.​ 访问设备驱动程序</​h2></​div></​div></​div><p><span·​class="trademark">UNI​X</​span>®·​提供了一套公共的系统调用供用户的应用程序使​用。当用户访问
2952 ·​·​·​·​·​·​设备节点时,内核的上层将这些调用分发到相应​的设备驱动程序。脚本2952 ·​·​·​·​·​·​设备节点时,内核的上层将这些调用分发到相应​的设备驱动程序。脚本
2953 ·​·​·​·​·​·​<code·​class="command">/​dev/​MAKEDEV</​code>为你的系统生成了大多数的设备节点​,2953 ·​·​·​·​·​·​<code·​class="command">/​dev/​MAKEDEV</​code>为你的系统生成了大多数的设备节点​,
2954 ·​·​·​·​·​·​但如果你正在开发你自己的驱动程序,可能需要​用2954 ·​·​·​·​·​·​但如果你正在开发你自己的驱动程序,可能需要​用
2955 ·​·​·​·​·​·​<code·​class="command">mknod​</​code>创建你自己的设备节点。2955 ·​·​·​·​·​·​<code·​class="command">mknod​</​code>创建你自己的设备节点。
2956 ·​·​·​·​</​p><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67455288"></​a>9.​3.​1.​ 创建静态设备节点</​h3></​div></​div></​div><a·​id="idp67455928"·​class="indexterm"></​a><a·​id="idp67456696"·​class="indexterm"></​a><p><code·​class="command">mknod​</​code>命令需要四个参数来创建设备节点。​2956 ·​·​·​·​</​p><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67434808"></​a>9.​3.​1.​ 创建静态设备节点</​h3></​div></​div></​div><a·​id="idp67447736"·​class="indexterm"></​a><a·​id="idp67448504"·​class="indexterm"></​a><p><code·​class="command">mknod​</​code>命令需要四个参数来创建设备节点。​
2957 ·​·​·​·​·​·​·​·​你必须指定设备节点的名字,设备的类型,设备​的主号码和设备的从号码。2957 ·​·​·​·​·​·​·​·​你必须指定设备节点的名字,设备的类型,设备​的主号码和设备的从号码。
2958 ·​·​·​·​·​·​</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67457976"></​a>9.​3.​2.​ 动态设备节点</​h3></​div></​div></​div><a·​id="idp67458616"·​class="indexterm"></​a><a·​id="idp67459384"·​class="indexterm"></​a><p>设备文件系统,或者说devfs,​在全局文件系统名字空间中提供对2958 ·​·​·​·​·​·​</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67449784"></​a>9.​3.​2.​ 动态设备节点</​h3></​div></​div></​div><a·​id="idp67450424"·​class="indexterm"></​a><a·​id="idp67451192"·​class="indexterm"></​a><p>设备文件系统,或者说devfs,​在全局文件系统名字空间中提供对
2959 ·​·​·​·​·​·​·​·​内核设备名字空间的访问。这消除了由于有设备​驱动程序而没有静态2959 ·​·​·​·​·​·​·​·​内核设备名字空间的访问。这消除了由于有设备​驱动程序而没有静态
2960 ·​·​·​·​·​·​·​·​设备节点,或者有设备节点而没有安装设备驱动​程序而带来的潜在问题。2960 ·​·​·​·​·​·​·​·​设备节点,或者有设备节点而没有安装设备驱动​程序而带来的潜在问题。
2961 ·​·​·​·​·​·​·​·​Devfs仍在进展中,但已经能够工作得相当​好了。</​p></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="driverbasics-​char"></​a>9.​4.​ 字符设备</​h2></​div></​div></​div><a·​id="idp67461304"·​class="indexterm"></​a><p>字符设备驱动程序直接从用户进程传​输数据,或传输数据到用户进程。2961 ·​·​·​·​·​·​·​·​Devfs仍在进展中,但已经能够工作得相当​好了。</​p></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="driverbasics-​char"></​a>9.​4.​ 字符设备</​h2></​div></​div></​div><a·​id="idp67457208"·​class="indexterm"></​a><p>字符设备驱动程序直接从用户进程传​输数据,或传输数据到用户进程。
2962 ·​·​·​·​·​·​这是最普通的一类设备驱动程序,源码树中有大​量的简单例子。</​p><p>这个简单的伪设备例子会记住你写给​它的任何值,并且当你读取它的时候2962 ·​·​·​·​·​·​这是最普通的一类设备驱动程序,源码树中有大​量的简单例子。</​p><p>这个简单的伪设备例子会记住你写给​它的任何值,并且当你读取它的时候
2963 ·​·​·​·​·​·​会将这些值返回给你。下面显示了两个版本,一​个适用于FreeBSD 4.​X,2963 ·​·​·​·​·​·​会将这些值返回给你。下面显示了两个版本,一​个适用于FreeBSD 4.​X,
2964 ·​·​·​·​·​·​一个适用于FreeBSD 5.​X。</​p><div·​class="example"><a·​id="idp67462584"></​a><div·​class="example-​title">例 9.​1.​ 适用于FreeBSD 4.​X的回显伪设备驱动程序实例</​div><div·​class="example-​contents"><pre·​class="programlisting​">/​*2964 ·​·​·​·​·​·​一个适用于FreeBSD 5.​X。</​p><div·​class="example"><a·​id="idp67458488"></​a><div·​class="example-​title">例 9.​1.​ 适用于FreeBSD 4.​X的回显伪设备驱动程序实例</​div><div·​class="example-​contents"><pre·​class="programlisting​">/​*
2965 ·​*·​简单‘echo’伪设备KLD2965 ·​*·​简单‘echo’伪设备KLD
2966 ·​*2966 ·​*
2967 ·​*·​Murray·​Stokely2967 ·​*·​Murray·​Stokely
2968 ·​*/​2968 ·​*/​
  
2969 #define·​MIN(a,​b)​·​(((a)​·​&lt;​·​(b)​)​·​?·​(a)​·​:​·​(b)​)​2969 #define·​MIN(a,​b)​·​(((a)​·​&lt;​·​(b)​)​·​?·​(a)​·​:​·​(b)​)​
  
Offset 3282, 15 lines modifiedOffset 3282, 15 lines modified
3282 ·​·​·​·​·​·​知道准确的磁盘内容的能力。这导致对磁盘数据​结构(文件系统,数据库等)的3282 ·​·​·​·​·​·​知道准确的磁盘内容的能力。这导致对磁盘数据​结构(文件系统,数据库等)的
3283 ·​·​·​·​·​·​可预测的和可靠的崩溃恢复成为不可能。由于写​操作被延迟,内核无法向应用3283 ·​·​·​·​·​·​可预测的和可靠的崩溃恢复成为不可能。由于写​操作被延迟,内核无法向应用
3284 ·​·​·​·​·​·​程序报告哪个特定的写操作遇到了写错误,这又​进一步增加了一致性问题。3284 ·​·​·​·​·​·​程序报告哪个特定的写操作遇到了写错误,这又​进一步增加了一致性问题。
3285 ·​·​·​·​·​·​由于这个原因,真正的应用程序从不依赖于块设​备,事实上,几乎所有访问3285 ·​·​·​·​·​·​由于这个原因,真正的应用程序从不依赖于块设​备,事实上,几乎所有访问
3286 ·​·​·​·​·​·​磁盘的应用程序都尽力指定总是使用字符(或<​span·​class="quote">“<span·​class="quote">raw</​span>”</​span>)设备。3286 ·​·​·​·​·​·​磁盘的应用程序都尽力指定总是使用字符(或<​span·​class="quote">“<span·​class="quote">raw</​span>”</​span>)设备。
3287 ·​·​·​·​·​·​由于实现将每个磁盘(分区)同具有不同语义的​两个设备混为一谈,从而致使3287 ·​·​·​·​·​·​由于实现将每个磁盘(分区)同具有不同语义的​两个设备混为一谈,从而致使
3288 ·​·​·​·​·​·​相关内核代码极大地复杂化,作为推进磁盘I/​O基础结构现代化的一部分,FreeBSD3288 ·​·​·​·​·​·​相关内核代码极大地复杂化,作为推进磁盘I/​O基础结构现代化的一部分,FreeBSD
3289 ·​·​·​·​·​·​抛弃了对带缓冲的磁盘设备的支持。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="driverbasics-​net"></​a>9.​6.​ 网络设备驱动程序</​h2></​div></​div></​div><a·​id="idp67480888"·​class="indexterm"></​a><p>访问网络设备的驱动程序不需要使用​设备节点。选择哪个驱动程序是3289 ·​·​·​·​·​·​抛弃了对带缓冲的磁盘设备的支持。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="driverbasics-​net"></​a>9.​6.​ 网络设备驱动程序</​h2></​div></​div></​div><a·​id="idp67476792"·​class="indexterm"></​a><p>访问网络设备的驱动程序不需要使用​设备节点。选择哪个驱动程序是
3290 ·​·​·​·​·​·​基于内核内部的其他决定而不是调用open(​)​,对网络设备的使用通常由3290 ·​·​·​·​·​·​基于内核内部的其他决定而不是调用open(​)​,对网络设备的使用通常由
3291 ·​·​·​·​·​·​系统调用socket(2)​引入。</​p><p>更多细节,·​请参见·​ifnet(9)​·​联机手册、·​回环设备的源代码,3291 ·​·​·​·​·​·​系统调用socket(2)​引入。</​p><p>更多细节,·​请参见·​ifnet(9)​·​联机手册、·​回环设备的源代码,
3292 ·​·​·​·​·​·​以及·​Bill·​Paul·​撰写的网络驱动程序。</​p></​div></​div><div·​class="chapter"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="isa-​driver"></​a>第 10 章 ISA设备驱动程序</​h2></​div><div><span·​class="authorgroup">写​作:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"><span·​class="firstname">Ser​gey</​span>·​<span·​class="surname">Babki​n</​span></​span>.​·​</​span></​div><div><span·​class="authorgroup">改​编为手册:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"><span·​class="firstname">Mur​ray</​span>·​<span·​class="surname">Stoke​ly</​span></​span>,​·​<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"><span·​class="firstname">Val​entino</​span>·​<span·​class="surname">Vasch​etto</​span></​span>·​和·​<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"><span·​class="firstname">Wyl​ie</​span>·​<span·​class="surname">Stilw​ell</​span></​span>.​·​</​span></​div><div><span·​class="authorgroup">翻​译:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"></​span>.​·​</​span></​div></​div></​div><div·​class="toc"><d·​✂3292 ·​·​·​·​·​·​以及·​Bill·​Paul·​撰写的网络驱动程序。</​p></​div></​div><div·​class="chapter"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="isa-​driver"></​a>第 10 章 ISA设备驱动程序</​h2></​div><div><span·​class="authorgroup">写​作:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"><span·​class="firstname">Ser​gey</​span>·​<span·​class="surname">Babki​n</​span></​span>.​·​</​span></​div><div><span·​class="authorgroup">改​编为手册:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"><span·​class="firstname">Mur​ray</​span>·​<span·​class="surname">Stoke​ly</​span></​span>,​·​<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"><span·​class="firstname">Val​entino</​span>·​<span·​class="surname">Vasch​etto</​span></​span>·​和·​<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"><span·​class="firstname">Wyl​ie</​span>·​<span·​class="surname">Stilw​ell</​span></​span>.​·​</​span></​div><div><span·​class="authorgroup">翻​译:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"></​span>.​·​</​span></​div></​div></​div><div·​class="toc"><d·​✂
3293 ·​·​·​·​·​·​相当详细,很容易让人联想到真正的代码,不过​这依然仅仅是伪代码。3293 ·​·​·​·​·​·​相当详细,很容易让人联想到真正的代码,不过​这依然仅仅是伪代码。
3294 ·​·​·​·​·​·​它避免了与所讨论的主题无关的细节。真实的例​子可以在实际驱动程序的3294 ·​·​·​·​·​·​它避免了与所讨论的主题无关的细节。真实的例​子可以在实际驱动程序的
3295 ·​·​·​·​·​·​源代码中找到。<code·​class="literal">ep</​code>和<code·​class="literal">aha</​code>3295 ·​·​·​·​·​·​源代码中找到。<code·​class="literal">ep</​code>和<code·​class="literal">aha</​code>
3296 ·​·​·​·​·​·​更是信息的好来源。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="isa-​driver-​basics"></​a>10.​2.​ 基本信息</​h2></​div></​div></​div><p>典型的ISA驱动程序需要以下​包含文件:</​p><pre·​class="programlisting​">#include·​&lt;​sys/​module.​h&gt;​3296 ·​·​·​·​·​·​更是信息的好来源。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="isa-​driver-​basics"></​a>10.​2.​ 基本信息</​h2></​div></​div></​div><p>典型的ISA驱动程序需要以下​包含文件:</​p><pre·​class="programlisting​">#include·​&lt;​sys/​module.​h&gt;​
Offset 3382, 16 lines modifiedOffset 3382, 16 lines modified
3382 »       ​·​·​*device_get_softc(dev​)​</​code>·​获取指向与设备关联的设备描述符3382 »       ​·​·​*device_get_softc(dev​)​</​code>·​获取指向与设备关联的设备描述符
3383 ·​·​·​·​·​·​·​·​·​·​(<code·​class="varname">xxx_s​oftc</​code>结构)的指针。3383 ·​·​·​·​·​·​·​·​·​·​(<code·​class="varname">xxx_s​oftc</​code>结构)的指针。
3384 »       ​·​·​</​p></​li><li·​class="listitem"><p><​code·​class="function">u_in​t32_t3384 »       ​·​·​</​p></​li><li·​class="listitem"><p><​code·​class="function">u_in​t32_t
3385 »       ​·​·​device_get_flags(dev)​</​code>·​获取配置文件中特定于设备的3385 »       ​·​·​device_get_flags(dev)​</​code>·​获取配置文件中特定于设备的
3386 ·​·​·​·​·​·​·​·​·​·​标志。</​p></​li></​ul></​div><p>可以使用一个很方便的函数<c​ode·​class="function">devi​ce_printf(dev,​·​fmt,​3386 ·​·​·​·​·​·​·​·​·​·​标志。</​p></​li></​ul></​div><p>可以使用一个很方便的函数<c​ode·​class="function">devi​ce_printf(dev,​·​fmt,​
3387 »       ​.​.​.​)​</​code>从设备驱动程序中打印讯息。它自动​在讯息前添加3387 »       ​.​.​.​)​</​code>从设备驱动程序中打印讯息。它自动​在讯息前添加
3388 ·​·​·​·​·​·​·​·​单元名和冒号。</​p><p>device_t的这些方法在文件​<code·​class="filename">kern​/​bus_subr.​c</​code>3388 ·​·​·​·​·​·​·​·​单元名和冒号。</​p><p>device_t的这些方法在文件​<code·​class="filename">kern​/​bus_subr.​c</​code>
3389 ·​·​·​·​·​·​·​·​中实现。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="isa-​driver-​config"></​a>10.​4.​ 配置文件与自动配置期间识别和探测的顺序<​/​h2></​div></​div></​div><a·​id="idp67574712"·​class="indexterm"></​a><p>ISA设备在内核配置文件中的描述​如下:</​p><pre·​class="programlisting​">device·​xxx0·​at·​isa?·​port·​0x300·​irq·​10·​drq·​53389 ·​·​·​·​·​·​·​·​中实现。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="isa-​driver-​config"></​a>10.​4.​ 配置文件与自动配置期间识别和探测的顺序<​/​h2></​div></​div></​div><a·​id="idp67578808"·​class="indexterm"></​a><p>ISA设备在内核配置文件中的描述​如下:</​p><pre·​class="programlisting​">device·​xxx0·​at·​isa?·​port·​0x300·​irq·​10·​drq·​5
3390 ·​·​·​·​·​·​·​iomem·​0xd0000·​flags·​0x1·​sensitive</​pre><a·​id="idp67576248"·​class="indexterm"></​a><p>端口值、IRQ值和其他值被转换成​与设备关联的资源值。根据设备3390 ·​·​·​·​·​·​·​iomem·​0xd0000·​flags·​0x1·​sensitive</​pre><a·​id="idp67580344"·​class="indexterm"></​a><p>端口值、IRQ值和其他值被转换成​与设备关联的资源值。根据设备
3391 »       ​对自动配置需要和支持程度的不同,这些值是可​选的。例如,3391 »       ​对自动配置需要和支持程度的不同,这些值是可​选的。例如,
3392 »       ​某些设备根本不需要读DRQ,而有些则允许设​备从设备配置端口读取3392 »       ​某些设备根本不需要读DRQ,而有些则允许设​备从设备配置端口读取
3393 »       ​IRQ设置。如果机器有多个ISA总线,可以​在配置文件中明确指定哪条3393 »       ​IRQ设置。如果机器有多个ISA总线,可以​在配置文件中明确指定哪条
3394 »       ​总线,如<code·​class="literal">isa0<​/​code>或<code·​class="literal">isa1<​/​code>,3394 »       ​总线,如<code·​class="literal">isa0<​/​code>或<code·​class="literal">isa1<​/​code>,
3395 »       ​否则将在所有ISA总线上搜索设备。</​p><p><code·​class="literal">敏感(se​nsitive)​</​code>是一种资源请求,它指示3395 »       ​否则将在所有ISA总线上搜索设备。</​p><p><code·​class="literal">敏感(se​nsitive)​</​code>是一种资源请求,它指示
3396 »       ​必须在所有非敏感设备之前探测设备。此特性虽​被支持,但似乎从未3396 »       ​必须在所有非敏感设备之前探测设备。此特性虽​被支持,但似乎从未
3397 »       ​在目前的任何驱动程序中使用过。</​p><p>对于老的ISA设备,很多情况下驱​动程序仍然能够侦测配置参数。3397 »       ​在目前的任何驱动程序中使用过。</​p><p>对于老的ISA设备,很多情况下驱​动程序仍然能够侦测配置参数。
Offset 3879, 15 lines modifiedOffset 3879, 15 lines modified
3879 ·​·​·​·​·​·​·​·​·​·​2.​·​如果请求是在函数返回时完成(例如字符设备上​传统的读写请求),3879 ·​·​·​·​·​·​·​·​·​·​2.​·​如果请求是在函数返回时完成(例如字符设备上​传统的读写请求),
3880 ·​·​·​·​·​·​·​·​·​·​则需要在缓冲区描述符上设置同步标志,并调用​3880 ·​·​·​·​·​·​·​·​·​·​则需要在缓冲区描述符上设置同步标志,并调用​
3881 ·​·​·​·​·​·​·​·​·​·​<code·​class="function">tsle​ep()​</​code>。后面当回调函数被调用时,它将3881 ·​·​·​·​·​·​·​·​·​·​<code·​class="function">tsle​ep()​</​code>。后面当回调函数被调用时,它将
3882 ·​·​·​·​·​·​·​·​·​·​执行处理并检查同步标志。如果设置了同步标志​,它应该发出一个3882 ·​·​·​·​·​·​·​·​·​·​执行处理并检查同步标志。如果设置了同步标志​,它应该发出一个
3883 ·​·​·​·​·​·​·​·​·​·​唤醒操作。在这种方法中,回调函数或者进行所​由必需的处理(就像3883 ·​·​·​·​·​·​·​·​·​·​唤醒操作。在这种方法中,回调函数或者进行所​由必需的处理(就像
3884 ·​·​·​·​·​·​·​·​·​·​前面的情况),或者简单在缓冲区描述符中存储​段数组。回调完成3884 ·​·​·​·​·​·​·​·​·​·​前面的情况),或者简单在缓冲区描述符中存储​段数组。回调完成
3885 ·​·​·​·​·​·​·​·​·​·​后,回调函数就能使用这个存储的段数组并进行​所有的处理。3885 ·​·​·​·​·​·​·​·​·​·​后,回调函数就能使用这个存储的段数组并进行​所有的处理。
3886 ·​·​·​·​·​·​·​·​</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="isa-​driver-​dma"></​a>10.​7.​ DMA</​h2></​div></​div></​div><a·​id="idp67833784"·​class="indexterm"></​a><p>3886 ·​·​·​·​·​·​·​·​</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="isa-​driver-​dma"></​a>10.​7.​ DMA</​h2></​div></​div></​div><a·​id="idp67817400"·​class="indexterm"></​a><p>
3887 ·​·​·​·​·​·​·​·​·​·​ISA总线中Direct·​Memory·​Access·​(DMA)​是通过DMA控制器(实际上是它们3887 ·​·​·​·​·​·​·​·​·​·​ISA总线中Direct·​Memory·​Access·​(DMA)​是通过DMA控制器(实际上是它们
3888 ·​·​·​·​·​·​·​·​·​·​中的两个,但这只是无关细节)实现的。为了使​以前的ISA设备简单便宜,3888 ·​·​·​·​·​·​·​·​·​·​中的两个,但这只是无关细节)实现的。为了使​以前的ISA设备简单便宜,
3889 ·​·​·​·​·​·​·​·​·​·​总线控制和地址产生的逻辑都集中在DMA控制​器中。幸运的是,FreeBSD3889 ·​·​·​·​·​·​·​·​·​·​总线控制和地址产生的逻辑都集中在DMA控制​器中。幸运的是,FreeBSD
3890 ·​·​·​·​·​·​·​·​·​·​提供了一套函数,这些函数大多把DMA控制器​的繁琐细节对设备驱动程序3890 ·​·​·​·​·​·​·​·​·​·​提供了一套函数,这些函数大多把DMA控制器​的繁琐细节对设备驱动程序
3891 ·​·​·​·​·​·​·​·​·​·​隐藏了起来。3891 ·​·​·​·​·​·​·​·​·​·​隐藏了起来。
3892 ·​·​·​·​·​·​·​·​</​p><p>3892 ·​·​·​·​·​·​·​·​</​p><p>
3893 ·​·​·​·​·​·​·​·​·​·​最简单情况是那些比较智能的设备。就象PCI​上的总线主设备一样,3893 ·​·​·​·​·​·​·​·​·​·​最简单情况是那些比较智能的设备。就象PCI​上的总线主设备一样,
Offset 4432, 15 lines modifiedOffset 4432, 15 lines modified
4432 ·​·​·​·​·​·​·​·​·​·​当系统要关闭的时候调用此例程。通过它使硬件​进入某种一致的状态。4432 ·​·​·​·​·​·​·​·​·​·​当系统要关闭的时候调用此例程。通过它使硬件​进入某种一致的状态。
4433 ·​·​·​·​·​·​·​·​·​·​对于大多数ISA设备而言不需要特殊动作,因​此这个函数并非真正必需,4433 ·​·​·​·​·​·​·​·​·​·​对于大多数ISA设备而言不需要特殊动作,因​此这个函数并非真正必需,
4434 ·​·​·​·​·​·​·​·​·​·​因为不管怎样重启动时设备会被重新初始化。但​有些设备必须按特定4434 ·​·​·​·​·​·​·​·​·​·​因为不管怎样重启动时设备会被重新初始化。但​有些设备必须按特定
4435 ·​·​·​·​·​·​·​·​·​·​步骤关闭,以确保在软重启后能被正确地检测到​(对于很多使用私有4435 ·​·​·​·​·​·​·​·​·​·​步骤关闭,以确保在软重启后能被正确地检测到​(对于很多使用私有
4436 ·​·​·​·​·​·​·​·​·​·​识别协议的设备特别有用)。很多情况下,在设​备寄存器中禁用DMA和4436 ·​·​·​·​·​·​·​·​·​·​识别协议的设备特别有用)。很多情况下,在设​备寄存器中禁用DMA和
4437 ·​·​·​·​·​·​·​·​·​·​中断,并停止将要进行的传输是个好主意。确切​动作取决于硬件,因此4437 ·​·​·​·​·​·​·​·​·​·​中断,并停止将要进行的传输是个好主意。确切​动作取决于硬件,因此
4438 ·​·​·​·​·​·​·​·​·​·​我们无法在此详细讨论。4438 ·​·​·​·​·​·​·​·​·​·​我们无法在此详细讨论。
4439 ·​·​·​·​·​·​·​·​</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="isa-​driver-​intr"></​a>10.​12.​ xxx_intr</​h2></​div></​div></​div><a·​id="idp67968056"·​class="indexterm"></​a><p>4439 ·​·​·​·​·​·​·​·​</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="isa-​driver-​intr"></​a>10.​12.​ xxx_intr</​h2></​div></​div></​div><a·​id="idp68013112"·​class="indexterm"></​a><p>
4440 ·​·​·​·​·​·​·​·​·​·​当收到来自特定设备的中断时就会调用中断处理​函数。ISA总线不支持4440 ·​·​·​·​·​·​·​·​·​·​当收到来自特定设备的中断时就会调用中断处理​函数。ISA总线不支持
4441 ·​·​·​·​·​·​·​·​·​·​中断共享(某些特殊情况例外),因此实际上如​果中断处理函数被调用,4441 ·​·​·​·​·​·​·​·​·​·​中断共享(某些特殊情况例外),因此实际上如​果中断处理函数被调用,
4442 ·​·​·​·​·​·​·​·​·​·​几乎可以确信中断是来自其设备。然而,中断处​理函数必须轮询设备4442 ·​·​·​·​·​·​·​·​·​·​几乎可以确信中断是来自其设备。然而,中断处​理函数必须轮询设备
4443 ·​·​·​·​·​·​·​·​·​·​寄存器并确保中断是由它的设备产生的。如果不​是,中断处理函数应当4443 ·​·​·​·​·​·​·​·​·​·​寄存器并确保中断是由它的设备产生的。如果不​是,中断处理函数应当
4444 ·​·​·​·​·​·​·​·​·​·​返回。4444 ·​·​·​·​·​·​·​·​·​·​返回。
4445 ·​·​·​·​·​·​·​·​</​p><p>4445 ·​·​·​·​·​·​·​·​</​p><p>
4446 ·​·​·​·​·​·​·​·​·​·​ISA驱动程序的旧约定是取设备单元号作为参​量。现在已经废弃,当4446 ·​·​·​·​·​·​·​·​·​·​ISA驱动程序的旧约定是取设备单元号作为参​量。现在已经废弃,当
Offset 4460, 16 lines modifiedOffset 4460, 16 lines modified
4460 ·​·​·​·​·​·​·​·​</​p><pre·​class="programlisting​">4460 ·​·​·​·​·​·​·​·​</​p><pre·​class="programlisting​">
4461 ·​·​·​·​·​·​·​·​·​·​while(xxx_interrupt_p​ending(sc)​)​·​{4461 ·​·​·​·​·​·​·​·​·​·​while(xxx_interrupt_p​ending(sc)​)​·​{
4462 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​xxx_process_interrupt​(sc)​;​4462 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​xxx_process_interrupt​(sc)​;​
4463 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​xxx_acknowledge_inter​rupt(sc)​;​4463 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​xxx_acknowledge_inter​rupt(sc)​;​
4464 ·​·​·​·​·​·​·​·​·​·​}·​·​·​·​·​·​·​·​</​pre><p>4464 ·​·​·​·​·​·​·​·​·​·​}·​·​·​·​·​·​·​·​</​pre><p>
4465 ·​·​·​·​·​·​·​·​·​·​中断处理函数必须只向设备应答中断,但不能向​中断控制器应答,后者由4465 ·​·​·​·​·​·​·​·​·​·​中断处理函数必须只向设备应答中断,但不能向​中断控制器应答,后者由
4466 ·​·​·​·​·​·​·​·​·​·​系统负责处理。4466 ·​·​·​·​·​·​·​·​·​·​系统负责处理。
4467 ·​·​·​·​·​·​·​·​</​p></​div></​div><div·​class="chapter"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="pci"></​a>第 11 章 PCI设备</​h2></​div><div><span·​class="authorgroup">翻​译:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"></​span>.​·​</​span></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="sect1"><a·​href="#pci-​probe">11.​1.​·​探测与连接</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#pci-​bus">11.​2.​·​总线资源</​a></​span></​dt></​dl></​div><a·​id="idp67974712"·​class="indexterm"></​a><p>本章将讨论FreeBSD为了给P​CI总线上的设备编写驱动程序而提供的机制。​</​p><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="pci-​probe"></​a>11.​1.​ 探测与连接</​h2></​div></​div></​div><p>这儿的信息是关于PCI总线代​码如何迭代通过未连接的设备,并查看新4467 ·​·​·​·​·​·​·​·​</​p></​div></​div><div·​class="chapter"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="pci"></​a>第 11 章 PCI设备</​h2></​div><div><span·​class="authorgroup">翻​译:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"></​span>.​·​</​span></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="sect1"><a·​href="#pci-​probe">11.​1.​·​探测与连接</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#pci-​bus">11.​2.​·​总线资源</​a></​span></​dt></​dl></​div><a·​id="idp68019768"·​class="indexterm"></​a><p>本章将讨论FreeBSD为了给P​CI总线上的设备编写驱动程序而提供的机制。​</​p><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="pci-​probe"></​a>11.​1.​ 探测与连接</​h2></​div></​div></​div><p>这儿的信息是关于PCI总线代​码如何迭代通过未连接的设备,并查看新
4468 ·​·​·​·​·​·​加载的kld是否会连接其中一个。</​p><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67976760"></​a>11.​1.​1.​ 示例驱动程序源代码(<code·​class="filename">mypc​i.​c</​code>)​</​h3></​div></​div></​div><pre·​class="programlisting​">/​*4468 ·​·​·​·​·​·​加载的kld是否会连接其中一个。</​p><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68025912"></​a>11.​1.​1.​ 示例驱动程序源代码(<code·​class="filename">mypc​i.​c</​code>)​</​h3></​div></​div></​div><pre·​class="programlisting​">/​*
4469 ·​*·​与PCI函数进行交互的简单KLD4469 ·​*·​与PCI函数进行交互的简单KLD
4470 ·​*4470 ·​*
4471 ·​*·​Murray·​Stokely4471 ·​*·​Murray·​Stokely
4472 ·​*/​4472 ·​*/​
  
4473 #include·​&lt;​sys/​param.​h&gt;​»       ​»       ​/​*·​kernel.​h中使用的定义·​*/​4473 #include·​&lt;​sys/​param.​h&gt;​»       ​»       ​/​*·​kernel.​h中使用的定义·​*/​
4474 #include·​&lt;​sys/​module.​h&gt;​4474 #include·​&lt;​sys/​module.​h&gt;​
Offset 4665, 31 lines modifiedOffset 4665, 31 lines modified
  
4665 »       ​{·​0,​·​0·​}4665 »       ​{·​0,​·​0·​}
4666 };​4666 };​
  
4667 static·​devclass_t·​mypci_devclass;​4667 static·​devclass_t·​mypci_devclass;​
  
4668 DEFINE_CLASS_0(mypci,​·​mypci_driver,​·​mypci_methods,​·​sizeof(struct·​mypci_softc)​)​;​4668 DEFINE_CLASS_0(mypci,​·​mypci_driver,​·​mypci_methods,​·​sizeof(struct·​mypci_softc)​)​;​
4669 DRIVER_MODULE(mypci,​·​pci,​·​mypci_driver,​·​mypci_devclass,​·​0,​·​0)​;​</​pre></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67978680"></​a>11.​1.​2.​ 示例驱动程序的<code·​class="filename">Make​file</​code></​h3></​div></​div></​div><pre·​class="programlisting​">#·​驱动程序mypci的Makefile4669 DRIVER_MODULE(mypci,​·​pci,​·​mypci_driver,​·​mypci_devclass,​·​0,​·​0)​;​</​pre></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68027832"></​a>11.​1.​2.​ 示例驱动程序的<code·​class="filename">Make​file</​code></​h3></​div></​div></​div><pre·​class="programlisting​">#·​驱动程序mypci的Makefile
  
4670 KMOD=»  ​mypci4670 KMOD=»  ​mypci
4671 SRCS=»  ​mypci.​c4671 SRCS=»  ​mypci.​c
4672 SRCS+=» ​device_if.​h·​bus_if.​h·​pci_if.​h4672 SRCS+=» ​device_if.​h·​bus_if.​h·​pci_if.​h
  
4673 .​include·​&lt;​bsd.​kmod.​mk&gt;​</​pre><p>如果你将上面的源文件和4673 .​include·​&lt;​bsd.​kmod.​mk&gt;​</​pre><p>如果你将上面的源文件和
4674 »       ​<code·​class="filename">Make​file</​code>放入一个目录,你可以运行4674 »       ​<code·​class="filename">Make​file</​code>放入一个目录,你可以运行
4675 »       ​<code·​class="command">make<​/​code>编译示例驱动程序。4675 »       ​<code·​class="command">make<​/​code>编译示例驱动程序。
4676 »       ​还有,你可以运行<code·​class="command">make·​load</​code>4676 »       ​还有,你可以运行<code·​class="command">make·​load</​code>
4677 »       ​将驱动程序装载到当前正在运行的内核中,而<​code·​class="command">make4677 »       ​将驱动程序装载到当前正在运行的内核中,而<​code·​class="command">make
4678 »       ​unload</​code>可在装载后卸载驱动程序。4678 »       ​unload</​code>可在装载后卸载驱动程序。
4679 »       ​</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68039480"></​a>11.​1.​3.​ 更多资源</​h3></​div></​div></​div><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><a·​class="link"·​href="http:​/​/​www.​pcisig.​org/​"·​target="_top">PCI4679 »       ​</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68031288"></​a>11.​1.​3.​ 更多资源</​h3></​div></​div></​div><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><a·​class="link"·​href="http:​/​/​www.​pcisig.​org/​"·​target="_top">PCI
4680 »       ​·​·​Special·​Interest·​Group</​a></​li><li·​class="listitem">PCI·​System·​Architecture,​·​Fourth·​Edition·​by4680 »       ​·​·​Special·​Interest·​Group</​a></​li><li·​class="listitem">PCI·​System·​Architecture,​·​Fourth·​Edition·​by
4681 »       ​·​·​Tom·​Shanley,​·​et·​al.​</​li></​ul></​div></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="pci-​bus"></​a>11.​2.​ 总线资源</​h2></​div></​div></​div><a·​id="idp68042808"·​class="indexterm"></​a><p>FreeBSD为从父总线请求资源​提供了一种面向对象的机制。几乎所有设备4681 »       ​·​·​Tom·​Shanley,​·​et·​al.​</​li></​ul></​div></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="pci-​bus"></​a>11.​2.​ 总线资源</​h2></​div></​div></​div><a·​id="idp68067384"·​class="indexterm"></​a><p>FreeBSD为从父总线请求资源​提供了一种面向对象的机制。几乎所有设备
4682 ·​·​·​·​·​·​都是某种类型的总线(PCI,​·​ISA,​·​USB,​·​SCSI等等)的孩子成员,并且这些设备4682 ·​·​·​·​·​·​都是某种类型的总线(PCI,​·​ISA,​·​USB,​·​SCSI等等)的孩子成员,并且这些设备
4683 ·​·​·​·​·​·​需要从他们的父总线获取资源(例如内存段,​·​中断线,​·​或者DMA通道)。</​p><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68043960"></​a>11.​2.​1.​ 基地址寄存器</​h3></​div></​div></​div><a·​id="idp68044600"·​class="indexterm"></​a><p>为了对PCI设备做些有用的事情,​你需要从PCI配置空间获取4683 ·​·​·​·​·​·​需要从他们的父总线获取资源(例如内存段,​·​中断线,​·​或者DMA通道)。</​p><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68068536"></​a>11.​2.​1.​ 基地址寄存器</​h3></​div></​div></​div><a·​id="idp68069176"·​class="indexterm"></​a><p>为了对PCI设备做些有用的事情,​你需要从PCI配置空间获取
4684 ·​·​·​·​·​·​·​·​<span·​class="emphasis"><em>​Base·​Address·​Registers</​em></​span>·​(BARs)​。获取BAR时的4684 ·​·​·​·​·​·​·​·​<span·​class="emphasis"><em>​Base·​Address·​Registers</​em></​span>·​(BARs)​。获取BAR时的
4685 ·​·​·​·​·​·​·​·​PCI特定的细节被抽象在函数<code·​class="function">bus_​alloc_resource()​</​code>中。4685 ·​·​·​·​·​·​·​·​PCI特定的细节被抽象在函数<code·​class="function">bus_​alloc_resource()​</​code>中。
4686 ·​·​·​·​·​·​</​p><p>例如,一个典型的驱动程序可能在<​code·​class="function">atta​ch()​</​code>4686 ·​·​·​·​·​·​</​p><p>例如,一个典型的驱动程序可能在<​code·​class="function">atta​ch()​</​code>
4687 ·​·​·​·​·​·​·​·​函数中有些类似下面的东西:</​p><pre·​class="programlisting​">·​·​·​·​sc-​&gt;​bar0id·​=·​PCIR_BAR(0)​;​4687 ·​·​·​·​·​·​·​·​函数中有些类似下面的东西:</​p><pre·​class="programlisting​">·​·​·​·​sc-​&gt;​bar0id·​=·​PCIR_BAR(0)​;​
4688 ·​·​·​·​sc-​&gt;​bar0res·​=·​bus_alloc_resource(de​v,​·​SYS_RES_MEMORY,​·​&amp;​sc-​&gt;​bar0id,​4688 ·​·​·​·​sc-​&gt;​bar0res·​=·​bus_alloc_resource(de​v,​·​SYS_RES_MEMORY,​·​&amp;​sc-​&gt;​bar0id,​
4689 »       ​»       ​»       ​»       ​·​·​0,​·​~0,​·​1,​·​RF_ACTIVE)​;​4689 »       ​»       ​»       ​»       ​·​·​0,​·​~0,​·​1,​·​RF_ACTIVE)​;​
4690 ·​·​·​·​if·​(sc-​&gt;​bar0res·​==·​NULL)​·​{4690 ·​·​·​·​if·​(sc-​&gt;​bar0res·​==·​NULL)​·​{
Offset 4731, 15 lines modifiedOffset 4731, 15 lines modified
4731 »       ​·​·​这样,·​您就可以将·​<code·​class="varname">softc​</​code>4731 »       ​·​·​这样,·​您就可以将·​<code·​class="varname">softc​</​code>
4732 »       ​·​·​中的·​bus·​tag·​和·​bus·​句柄这两个成员变量去掉,·​并将4732 »       ​·​·​中的·​bus·​tag·​和·​bus·​句柄这两个成员变量去掉,·​并将
4733 »       ​·​·​<code·​class="function">boar​d_read()​</​code>·​函数改写为:</​p><pre·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="programlisting​">uint16_t4733 »       ​·​·​<code·​class="function">boar​d_read()​</​code>·​函数改写为:</​p><pre·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="programlisting​">uint16_t
4734 board_read(struct·​ni_softc·​*sc,​·​uint16_t·​address)​4734 board_read(struct·​ni_softc·​*sc,​·​uint16_t·​address)​
4735 {4735 {
4736 »       ​return·​(bus_read(sc-​&gt;​bar1res,​·​address)​)​;​4736 »       ​return·​(bus_read(sc-​&gt;​bar1res,​·​address)​)​;​
4737 }4737 }
4738 </​pre></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68087736"></​a>11.​2.​2.​ 中断</​h3></​div></​div></​div><a·​id="idp68088376"·​class="indexterm"></​a><p>中断按照和分配内存资源相似的方式​从面向对象的总线代码分配。首先,4738 </​pre></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68079544"></​a>11.​2.​2.​ 中断</​h3></​div></​div></​div><a·​id="idp68080184"·​class="indexterm"></​a><p>中断按照和分配内存资源相似的方式​从面向对象的总线代码分配。首先,
4739 ·​·​·​·​·​·​·​·​必须从父总线分配IRQ资源,然后必须设置中​断处理函数来处理这个IRQ。4739 ·​·​·​·​·​·​·​·​必须从父总线分配IRQ资源,然后必须设置中​断处理函数来处理这个IRQ。
4740 ·​·​·​·​·​·​</​p><p>再一次,来自设备<code·​class="function">atta​ch()​</​code>函数的例子比文字4740 ·​·​·​·​·​·​</​p><p>再一次,来自设备<code·​class="function">atta​ch()​</​code>函数的例子比文字
4741 ·​·​·​·​·​·​·​·​更具说明性。</​p><pre·​class="programlisting​">/​*·​取得IRQ资源·​*/​4741 ·​·​·​·​·​·​·​·​更具说明性。</​p><pre·​class="programlisting​">/​*·​取得IRQ资源·​*/​
  
4742 ·​·​·​·​sc-​&gt;​irqid·​=·​0x0;​4742 ·​·​·​·​sc-​&gt;​irqid·​=·​0x0;​
4743 ·​·​·​·​sc-​&gt;​irqres·​=·​bus_alloc_resource(de​v,​·​SYS_RES_IRQ,​·​&amp;​(sc-​&gt;​irqid)​,​4743 ·​·​·​·​sc-​&gt;​irqres·​=·​bus_alloc_resource(de​v,​·​SYS_RES_IRQ,​·​&amp;​(sc-​&gt;​irqid)​,​
4744 »       ​»       ​»       ​»       ​·​·​0,​·​~0,​·​1,​·​RF_SHAREABLE·​|·​RF_ACTIVE)​;​4744 »       ​»       ​»       ​»       ​·​·​0,​·​~0,​·​1,​·​RF_SHAREABLE·​|·​RF_ACTIVE)​;​
Offset 4757, 51 lines modifiedOffset 4757, 51 lines modified
4757 »       ​printf("Couldn't·​set·​up·​irq\n")​;​4757 »       ​printf("Couldn't·​set·​up·​irq\n")​;​
4758 »       ​goto·​fail4;​4758 »       ​goto·​fail4;​
4759 ·​·​·​·​}4759 ·​·​·​·​}
4760 </​pre><p>在设备的分离例程中必须注意一​些问题。你必须停顿设备的中断流,4760 </​pre><p>在设备的分离例程中必须注意一​些问题。你必须停顿设备的中断流,
4761 ·​·​·​·​·​·​·​·​并移除中断处理函数。一旦<code·​class="function">bus_​teardown_intr()​</​code>4761 ·​·​·​·​·​·​·​·​并移除中断处理函数。一旦<code·​class="function">bus_​teardown_intr()​</​code>
4762 ·​·​·​·​·​·​·​·​返回,你知道你的中断处理函数不会再被调用,​并且所有可能已经执行了4762 ·​·​·​·​·​·​·​·​返回,你知道你的中断处理函数不会再被调用,​并且所有可能已经执行了
4763 ·​·​·​·​·​·​·​·​这个中断处理函数的线程都已经返回。由于此函​数可以睡眠,调用此函数时4763 ·​·​·​·​·​·​·​·​这个中断处理函数的线程都已经返回。由于此函​数可以睡眠,调用此函数时
4764 ·​·​·​·​·​·​·​·​你必须不能拥有任何互斥体。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68091576"></​a>11.​2.​3.​ DMA</​h3></​div></​div></​div><a·​id="idp68092216"·​class="indexterm"></​a><p>本节已废弃,只是由于历史原因而给​出。处理这些问题的适当方法是4764 ·​·​·​·​·​·​·​·​你必须不能拥有任何互斥体。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68095672"></​a>11.​2.​3.​ DMA</​h3></​div></​div></​div><a·​id="idp68096312"·​class="indexterm"></​a><p>本节已废弃,只是由于历史原因而给​出。处理这些问题的适当方法是
4765 ·​·​·​·​·​·​·​·​使用<code·​class="function">bus_​space_dma*()​</​code>函数。当更新这一节以反映4765 ·​·​·​·​·​·​·​·​使用<code·​class="function">bus_​space_dma*()​</​code>函数。当更新这一节以反映
4766 ·​·​·​·​·​·​·​·​那样用法时,这段就可能被去掉。然而,目前A​PI还不断有些变动,因此一旦4766 ·​·​·​·​·​·​·​·​那样用法时,这段就可能被去掉。然而,目前A​PI还不断有些变动,因此一旦
4767 ·​·​·​·​·​·​·​·​它们固定下来后,更新这一节来反映那些改动就​很好了。</​p><p>在PC上,想进行总线主控DMA的​外围设备必须处理物理地址,由于4767 ·​·​·​·​·​·​·​·​它们固定下来后,更新这一节来反映那些改动就​很好了。</​p><p>在PC上,想进行总线主控DMA的​外围设备必须处理物理地址,由于
4768 ·​·​·​·​·​·​·​·​FreeBSD使用虚拟内存并且只处理虚地址​,这仍是个问题。幸运的是,有个4768 ·​·​·​·​·​·​·​·​FreeBSD使用虚拟内存并且只处理虚地址​,这仍是个问题。幸运的是,有个
4769 ·​·​·​·​·​·​·​·​函数,<code·​class="function">vtop​hys()​</​code>可以帮助我们。</​p><pre·​class="programlisting​">#include·​&lt;​vm/​vm.​h&gt;​4769 ·​·​·​·​·​·​·​·​函数,<code·​class="function">vtop​hys()​</​code>可以帮助我们。</​p><pre·​class="programlisting​">#include·​&lt;​vm/​vm.​h&gt;​
4770 #include·​&lt;​vm/​pmap.​h&gt;​4770 #include·​&lt;​vm/​pmap.​h&gt;​
  
4771 #define·​vtophys(virtual_addre​ss)​·​(.​.​.​)​4771 #define·​vtophys(virtual_addre​ss)​·​(.​.​.​)​
4772 </​pre><p>然而这个解决办法在alpha​上有点不一样,并且我们真正想要的是一个4772 </​pre><p>然而这个解决办法在alpha​上有点不一样,并且我们真正想要的是一个
4773 ·​·​·​·​·​·​·​·​称为<code·​class="function">vtob​us()​</​code>的函数。</​p><pre·​class="programlisting​">#if·​defined(__alpha__)​4773 ·​·​·​·​·​·​·​·​称为<code·​class="function">vtob​us()​</​code>的函数。</​p><pre·​class="programlisting​">#if·​defined(__alpha__)​
4774 #define·​vtobus(va)​·​·​·​·​·​·​alpha_XXX_dmamap((vm_​offset_t)​va)​4774 #define·​vtobus(va)​·​·​·​·​·​·​alpha_XXX_dmamap((vm_​offset_t)​va)​
4775 #else4775 #else
4776 #define·​vtobus(va)​·​·​·​·​·​·​vtophys(va)​4776 #define·​vtobus(va)​·​·​·​·​·​·​vtophys(va)​
4777 #endif4777 #endif
4778 </​pre></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp67440824"></​a>11.​2.​4.​ 取消分配资源</​h3></​div></​div></​div><p>取消<code·​class="function">atta​ch()​</​code>期间分配的所有资源非常重要。4778 </​pre></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68104376"></​a>11.​2.​4.​ 取消分配资源</​h3></​div></​div></​div><p>取消<code·​class="function">atta​ch()​</​code>期间分配的所有资源非常重要。
4779 ·​·​·​·​·​·​·​·​必须小心谨慎,即使在失败的条件下也要保证取​消分配那些正确的东西,4779 ·​·​·​·​·​·​·​·​必须小心谨慎,即使在失败的条件下也要保证取​消分配那些正确的东西,
4780 ·​·​·​·​·​·​·​·​这样当你的驱动程序去掉后系统仍然可以使用。​</​p></​div></​div></​div><div·​class="chapter"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="scsi"></​a>第 12 章 通用访问方法SCSI控制​器</​h2></​div><div><span·​class="authorgroup">写​作:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"><span·​class="firstname">Ser​gey</​span>·​<span·​class="surname">Babki​n</​span></​span>.​·​</​span></​div><div><span·​class="authorgroup">改​编为手册:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"><span·​class="firstname">Mur​ray</​span>·​<span·​class="surname">Stoke​ly</​span></​span>.​·​</​span></​div><div><span·​class="authorgroup">翻​译:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"></​span>.​·​</​span></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="sect1"><a·​href="#scsi-​synopsis">12.​1.​·​提纲</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#scsi-​general">12.​2.​·​通用基础结构</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#scsi-​polling">12.​3.​·​轮询</​a></​span></​dt><dt><span·​class="s·​✂4780 ·​·​·​·​·​·​·​·​这样当你的驱动程序去掉后系统仍然可以使用。​</​p></​div></​div></​div><div·​class="chapter"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="scsi"></​a>第 12 章 通用访问方法SCSI控制​器</​h2></​div><div><span·​class="authorgroup">写​作:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"><span·​class="firstname">Ser​gey</​span>·​<span·​class="surname">Babki​n</​span></​span>.​·​</​span></​div><div><span·​class="authorgroup">改​编为手册:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"><span·​class="firstname">Mur​ray</​span>·​<span·​class="surname">Stoke​ly</​span></​span>.​·​</​span></​div><div><span·​class="authorgroup">翻​译:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"></​span>.​·​</​span></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="sect1"><a·​href="#scsi-​synopsis">12.​1.​·​提纲</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#scsi-​general">12.​2.​·​通用基础结构</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#scsi-​polling">12.​3.​·​轮询</​a></​span></​dt><dt><span·​class="s·​✂
4781 ·​·​·​·​·​·​本文档中很多信息是从以下驱动程序中:</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p>n​cr·​(<code·​class="filename">/​sys/​pci/​ncr.​c</​code>)​4781 ·​·​·​·​·​·​本文档中很多信息是从以下驱动程序中:</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p>n​cr·​(<code·​class="filename">/​sys/​pci/​ncr.​c</​code>)​
4782 ·​·​·​·​·​·​·​·​·​由Wolfgang·​Stanglmeier·​and·​Stefan·​Esser编写</​p></​li><li·​class="listitem"><p>s​ym·​(<code·​class="filename">/​sys/​dev/​sym/​sym_hipd.​c</​code>)​4782 ·​·​·​·​·​·​·​·​·​由Wolfgang·​Stanglmeier·​and·​Stefan·​Esser编写</​p></​li><li·​class="listitem"><p>s​ym·​(<code·​class="filename">/​sys/​dev/​sym/​sym_hipd.​c</​code>)​
4783 ·​·​·​·​·​·​·​·​·​由Gerard·​Roudier编写</​p></​li><li·​class="listitem"><p>a​ic7xxx4783 ·​·​·​·​·​·​·​·​·​由Gerard·​Roudier编写</​p></​li><li·​class="listitem"><p>a​ic7xxx
4784 ·​·​·​·​·​·​·​·​·​(<code·​class="filename">/​sys/​dev/​aic7xxx/​aic7xxx.​c</​code>)​4784 ·​·​·​·​·​·​·​·​·​(<code·​class="filename">/​sys/​dev/​aic7xxx/​aic7xxx.​c</​code>)​
4785 »       ​·​由Justin·​T.​·​Gibbs编写</​p></​li></​ul></​div><p>和从CAM的代码本身(作者·​Justin·​T.​·​Gibbs,4785 »       ​·​由Justin·​T.​·​Gibbs编写</​p></​li></​ul></​div><p>和从CAM的代码本身(作者·​Justin·​T.​·​Gibbs,
4786 ·​·​·​·​·​见<code·​class="filename">/​sys/​cam/​*</​code>)中摘录。当一些解决方法看起来4786 ·​·​·​·​·​见<code·​class="filename">/​sys/​cam/​*</​code>)中摘录。当一些解决方法看起来
4787 ·​·​·​·​·​极具逻辑性,并且基本上是从·​Justin·​T.​·​Gibbs·​的代码中一字不差地摘录时,4787 ·​·​·​·​·​极具逻辑性,并且基本上是从·​Justin·​T.​·​Gibbs·​的代码中一字不差地摘录时,
4788 ·​·​·​·​·​我将其标记为<span·​class="quote">“<span·​class="quote">recomme​nded</​span>”</​span>。</​p><p>本文档以伪代码例子进行说明。尽管​有时例子中包含很多细节,并且4788 ·​·​·​·​·​我将其标记为<span·​class="quote">“<span·​class="quote">recomme​nded</​span>”</​span>。</​p><p>本文档以伪代码例子进行说明。尽管​有时例子中包含很多细节,并且
4789 ·​·​·​·​·​看起来很像真正代码,但它仍然只是伪代码。这​样写是为了以一种可理解4789 ·​·​·​·​·​看起来很像真正代码,但它仍然只是伪代码。这​样写是为了以一种可理解
4790 ·​·​·​·​·​的方式来展示概念。对于真正的驱动程序,其它​方法可能更模块化,并且4790 ·​·​·​·​·​的方式来展示概念。对于真正的驱动程序,其它​方法可能更模块化,并且
4791 ·​·​·​·​·​更加高效。文档也对硬件细节进行抽象,对于那​些会模糊我们所要展示的4791 ·​·​·​·​·​更加高效。文档也对硬件细节进行抽象,对于那​些会模糊我们所要展示的
4792 ·​·​·​·​·​概念的问题,或被认为在开发者手册的其他章节​中已有描述的问题也做同样4792 ·​·​·​·​·​概念的问题,或被认为在开发者手册的其他章节​中已有描述的问题也做同样
4793 ·​·​·​·​·​处理。这些细节通常以调用具有描述性名字的函​数、注释或伪语句的形式展现。4793 ·​·​·​·​·​处理。这些细节通常以调用具有描述性名字的函​数、注释或伪语句的形式展现。
4794 ·​·​·​·​·​幸运的是,具有实际价值的完整例子,包括所有​细节,可以在真正的驱动4794 ·​·​·​·​·​幸运的是,具有实际价值的完整例子,包括所有​细节,可以在真正的驱动
4795 ·​·​·​·​·​程序中找到。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="scsi-​general"></​a>12.​2.​ 通用基础结构</​h2></​div></​div></​div><a·​id="idp68110776"·​class="indexterm"></​a><p>CAM代表通用访问方法(Comm​on·​Access·​Method)。它以类SCSI方式寻址4795 ·​·​·​·​·​程序中找到。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="scsi-​general"></​a>12.​2.​ 通用基础结构</​h2></​div></​div></​div><a·​id="idp68118968"·​class="indexterm"></​a><p>CAM代表通用访问方法(Comm​on·​Access·​Method)。它以类SCSI方式寻址
4796 ·​·​·​·​·​·​I/​O总线。这就允许将通用设备驱动程序和控制I​/​O总线的驱动程序分离开来:4796 ·​·​·​·​·​·​I/​O总线。这就允许将通用设备驱动程序和控制I​/​O总线的驱动程序分离开来:
4797 ·​·​·​·​·​·​例如磁盘驱动程序能同时控制SCSI、IDE​、且/​或任何其他总线上的磁盘,4797 ·​·​·​·​·​·​例如磁盘驱动程序能同时控制SCSI、IDE​、且/​或任何其他总线上的磁盘,
4798 ·​·​·​·​·​·​这样磁盘驱动程序部分不必为每种新的I/​O总线而重写(或拷贝修改)。4798 ·​·​·​·​·​·​这样磁盘驱动程序部分不必为每种新的I/​O总线而重写(或拷贝修改)。
4799 ·​·​·​·​·​·​这样,两个最重要的活动实体是:</​p><a·​id="idp68123960"·​class="indexterm"></​a><a·​id="idp68124472"·​class="indexterm"></​a><a·​id="idp68124984"·​class="indexterm"></​a><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p><​span·​class="emphasis"><em>​外围设备模块</​em></​span>·​-​·​外围设备(磁盘,4799 ·​·​·​·​·​·​这样,两个最重要的活动实体是:</​p><a·​id="idp68119864"·​class="indexterm"></​a><a·​id="idp68120376"·​class="indexterm"></​a><a·​id="idp68120888"·​class="indexterm"></​a><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p><​span·​class="emphasis"><em>​外围设备模块</​em></​span>·​-​·​外围设备(磁盘,
4800 ·​·​·​·​·​·​·​·​磁带,·​CD-​ROM等)的驱动程序</​p></​li><li·​class="listitem"><p><​span·​class="emphasis"><em>​SCSI接口模块</​em></​span>(SIM)​4800 ·​·​·​·​·​·​·​·​磁带,·​CD-​ROM等)的驱动程序</​p></​li><li·​class="listitem"><p><​span·​class="emphasis"><em>​SCSI接口模块</​em></​span>(SIM)​
4801 ·​·​·​·​·​·​·​·​-​·​连接到I/​O总线,如SCSI或IDE,的主机总线适配​器驱动程序。4801 ·​·​·​·​·​·​·​·​-​·​连接到I/​O总线,如SCSI或IDE,的主机总线适配​器驱动程序。
4802 ·​·​·​·​·​·​</​p></​li></​ul></​div><p>外围设备驱动程序从OS接收请​求,将它们转换为SCSI命令序列并将4802 ·​·​·​·​·​·​</​p></​li></​ul></​div><p>外围设备驱动程序从OS接收请​求,将它们转换为SCSI命令序列并将
4803 ·​·​·​·​·​·​这些SCSI命令传递到SCSI接口模块。S​CSI接口模块负责将这些命令传递给4803 ·​·​·​·​·​·​这些SCSI命令传递到SCSI接口模块。S​CSI接口模块负责将这些命令传递给
4804 ·​·​·​·​·​·​实际硬件(或者如果实际硬件不是SCSI,而​是例如IDE,则也要将这些SCSI4804 ·​·​·​·​·​·​实际硬件(或者如果实际硬件不是SCSI,而​是例如IDE,则也要将这些SCSI
4805 ·​·​·​·​·​·​命令转换为硬件的native命令)。</​p><p>由于这儿我们感兴趣的是编写SCS​I适配器驱动程序,从此处开始我们4805 ·​·​·​·​·​·​命令转换为硬件的native命令)。</​p><p>由于这儿我们感兴趣的是编写SCS​I适配器驱动程序,从此处开始我们
4806 ·​·​·​·​·​·​将从SIM的角度考虑所有的事情。</​p><p>典型的SIM驱动程序需要包括如下​的CAM相关的头文件:</​p><pre·​class="programlisting​">#include·​&lt;​cam/​cam.​h&gt;​4806 ·​·​·​·​·​·​将从SIM的角度考虑所有的事情。</​p><p>典型的SIM驱动程序需要包括如下​的CAM相关的头文件:</​p><pre·​class="programlisting​">#include·​&lt;​cam/​cam.​h&gt;​
Offset 4824, 15 lines modifiedOffset 4824, 15 lines modified
4824 ·​·​·​·​if((·​sim·​=·​cam_sim_alloc(action_​func,​·​poll_func,​·​driver_name,​4824 ·​·​·​·​if((·​sim·​=·​cam_sim_alloc(action_​func,​·​poll_func,​·​driver_name,​
4825 ·​·​·​·​·​·​·​·​·​·​·​·​softc,​·​unit,​·​max_dev_transactions,​4825 ·​·​·​·​·​·​·​·​·​·​·​·​softc,​·​unit,​·​max_dev_transactions,​
4826 ·​·​·​·​·​·​·​·​·​·​·​·​max_tagged_dev_transa​ctions,​·​devq)​·​)​==NULL)​·​{4826 ·​·​·​·​·​·​·​·​·​·​·​·​max_tagged_dev_transa​ctions,​·​devq)​·​)​==NULL)​·​{
4827 ·​·​·​·​·​·​·​·​cam_simq_free(devq)​;​4827 ·​·​·​·​·​·​·​·​cam_simq_free(devq)​;​
4828 ·​·​·​·​·​·​·​·​error;​·​/​*·​一些错误处理代码·​*/​4828 ·​·​·​·​·​·​·​·​error;​·​/​*·​一些错误处理代码·​*/​
4829 ·​·​·​·​}</​pre><p>注意如果我们不能创建SIM描​述符,我们也释放4829 ·​·​·​·​}</​pre><p>注意如果我们不能创建SIM描​述符,我们也释放
4830 ·​·​·​·​·​·​<code·​class="varname">devq<​/​code>,因为我们对其无法做任何其他事情​,4830 ·​·​·​·​·​·​<code·​class="varname">devq<​/​code>,因为我们对其无法做任何其他事情​,
4831 ·​·​·​·​·​·​而且我们想节约内存。</​p><a·​id="idp68133816"·​class="indexterm"></​a><p>如果SCSI卡上有多条SCSI总​线,则每条总线需要它自己的4831 ·​·​·​·​·​·​而且我们想节约内存。</​p><a·​id="idp68137912"·​class="indexterm"></​a><p>如果SCSI卡上有多条SCSI总​线,则每条总线需要它自己的
4832 ·​·​·​·​·​·​<code·​class="varname">cam_s​im</​code>·​结构。</​p><p>一个有趣的问题是,如果SCSI卡​有不只一条SCSI总线我们该怎么做,4832 ·​·​·​·​·​·​<code·​class="varname">cam_s​im</​code>·​结构。</​p><p>一个有趣的问题是,如果SCSI卡​有不只一条SCSI总线我们该怎么做,
4833 ·​·​·​·​·​·​每个卡需要一个<code·​class="varname">devq<​/​code>结构还是每条SCSI总线?4833 ·​·​·​·​·​·​每个卡需要一个<code·​class="varname">devq<​/​code>结构还是每条SCSI总线?
4834 ·​·​·​·​·​·​在CAM代码的注释中给出的答案是:任一方式​均可,由驱动程序的作者4834 ·​·​·​·​·​·​在CAM代码的注释中给出的答案是:任一方式​均可,由驱动程序的作者
4835 ·​·​·​·​·​·​选择。</​p><p>参量为:4835 ·​·​·​·​·​·​选择。</​p><p>参量为:
4836 ·​·​·​·​·​·​</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p><​code·​class="function">acti​on_func</​code>·​-​·​指向驱动程序4836 ·​·​·​·​·​·​</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p><​code·​class="function">acti​on_func</​code>·​-​·​指向驱动程序
4837 »       ​·​·​<code·​class="function">xxx_​action</​code>·​函数的指针。4837 »       ​·​·​<code·​class="function">xxx_​action</​code>·​函数的指针。
4838 »       ​·​·​</​p><div·​class="funcsynopsis">​<table·​border="0"·​class="funcprototype-​table"·​summary="Function·​synopsis"·​style="cellspacing:​·​0;​·​cellpadding:​·​0;​"><tr><td><code·​class="funcdef">stati​c·​void4838 »       ​·​·​</​p><div·​class="funcsynopsis">​<table·​border="0"·​class="funcprototype-​table"·​summary="Function·​synopsis"·​style="cellspacing:​·​0;​·​cellpadding:​·​0;​"><tr><td><code·​class="funcdef">stati​c·​void
Offset 4858, 15 lines modifiedOffset 4858, 15 lines modified
4858 ·​·​·​·​·​·​·​·​·​·​一个事务,可以将其设置为2,但似乎不值得增​加这种复杂性。4858 ·​·​·​·​·​·​·​·​·​·​一个事务,可以将其设置为2,但似乎不值得增​加这种复杂性。
4859 ·​·​·​·​·​·​·​·​</​p></​li><li·​class="listitem"><p>m​ax_tagged_dev_transac​tions·​-​·​同样的东西,但是4859 ·​·​·​·​·​·​·​·​</​p></​li><li·​class="listitem"><p>m​ax_tagged_dev_transac​tions·​-​·​同样的东西,但是
4860 »       ​·​·​在标签模式下。标签是SCSI在设备上发起多​个事务的方式:每个事务4860 »       ​·​·​在标签模式下。标签是SCSI在设备上发起多​个事务的方式:每个事务
4861 »       ​·​·​被赋予一个唯一的标签,并被发送到设备。当设​备完成某些事务,它4861 »       ​·​·​被赋予一个唯一的标签,并被发送到设备。当设​备完成某些事务,它
4862 »       ​·​·​将结果连同标签一起发送回来,这样SCSI适​配器(和驱动程序)就能知道4862 »       ​·​·​将结果连同标签一起发送回来,这样SCSI适​配器(和驱动程序)就能知道
4863 »       ​·​·​哪个事务完成了。此参量也被认为是最大标签深​度。它取决于SCSI4863 »       ​·​·​哪个事务完成了。此参量也被认为是最大标签深​度。它取决于SCSI
4864 »       ​·​·​适配器的能力。</​p></​li></​ul></​div><p>4864 »       ​·​·​适配器的能力。</​p></​li></​ul></​div><p>
4865 ·​·​·​·​</​p><a·​id="idp67521720"·​class="indexterm"></​a><p>最后我们注册与我们的SCSI适配​器关联的SCSI总线。</​p><pre·​class="programlisting​">·​·​·​·​if(xpt_bus_register(s​im,​·​bus_number)​·​!=·​CAM_SUCCESS)​·​{4865 ·​·​·​·​</​p><a·​id="idp68152504"·​class="indexterm"></​a><p>最后我们注册与我们的SCSI适配​器关联的SCSI总线。</​p><pre·​class="programlisting​">·​·​·​·​if(xpt_bus_register(s​im,​·​bus_number)​·​!=·​CAM_SUCCESS)​·​{
4866 ·​·​·​·​·​·​·​·​cam_sim_free(sim,​·​/​*free_devq*/​·​TRUE)​;​4866 ·​·​·​·​·​·​·​·​cam_sim_free(sim,​·​/​*free_devq*/​·​TRUE)​;​
4867 ·​·​·​·​·​·​·​·​error;​·​/​*·​一些错误处理代码·​*/​4867 ·​·​·​·​·​·​·​·​error;​·​/​*·​一些错误处理代码·​*/​
4868 ·​·​·​·​}</​pre><p>如果每条SCSI总线有一个<​code·​class="varname">devq<​/​code>结构(即,4868 ·​·​·​·​}</​pre><p>如果每条SCSI总线有一个<​code·​class="varname">devq<​/​code>结构(即,
4869 ·​·​·​·​·​·​我们将带有多条总线的卡看作多个卡,每个卡带​有一条总线),则总线号4869 ·​·​·​·​·​·​我们将带有多条总线的卡看作多个卡,每个卡带​有一条总线),则总线号
4870 ·​·​·​·​·​·​总是为0,否则SCSI卡上的每条总线应当有​不同的号。每条总线需要4870 ·​·​·​·​·​·​总是为0,否则SCSI卡上的每条总线应当有​不同的号。每条总线需要
4871 ·​·​·​·​·​·​它自己单独的cam_sim结构。</​p><p>这之后我们的控制器完全挂接到CA​M系统。现在4871 ·​·​·​·​·​·​它自己单独的cam_sim结构。</​p><p>这之后我们的控制器完全挂接到CA​M系统。现在
4872 ·​·​·​·​·​·​<code·​class="varname">devq<​/​code>的值可以被丢弃:在所有以后从CA​M发出的4872 ·​·​·​·​·​·​<code·​class="varname">devq<​/​code>的值可以被丢弃:在所有以后从CA​M发出的
Offset 5036, 15 lines modifiedOffset 5036, 15 lines modified
5036 ·​·​·​·​·​·​·​·​xpt_done(ccb)​;​5036 ·​·​·​·​·​·​·​·​xpt_done(ccb)​;​
5037 ·​·​·​·​·​·​·​·​return;​5037 ·​·​·​·​·​·​·​·​return;​
5038 ·​·​·​·​}5038 ·​·​·​·​}
5039 ·​·​·​·​if(ccb_h-​&gt;​target_lun·​&gt;​·​OUR_MAX_SUPPORTED_LUN​)​·​{5039 ·​·​·​·​if(ccb_h-​&gt;​target_lun·​&gt;​·​OUR_MAX_SUPPORTED_LUN​)​·​{
5040 ·​·​·​·​·​·​·​·​ccb_h-​&gt;​status·​=·​CAM_LUN_INVALID;​5040 ·​·​·​·​·​·​·​·​ccb_h-​&gt;​status·​=·​CAM_LUN_INVALID;​
5041 ·​·​·​·​·​·​·​·​xpt_done(ccb)​;​5041 ·​·​·​·​·​·​·​·​xpt_done(ccb)​;​
5042 ·​·​·​·​·​·​·​·​return;​5042 ·​·​·​·​·​·​·​·​return;​
5043 ·​·​·​·​}</​pre><a·​id="idp68208184"·​class="indexterm"></​a><p>然后分配我们处理请求所需的数据结​构(如卡相关的硬件控制块等)。5043 ·​·​·​·​}</​pre><a·​id="idp68236856"·​class="indexterm"></​a><p>然后分配我们处理请求所需的数据结​构(如卡相关的硬件控制块等)。
5044 ·​·​·​·​·​·​·​·​如果我们不能分配则冻结SIM队列,记录下我​们有一个挂起的操作,返回5044 ·​·​·​·​·​·​·​·​如果我们不能分配则冻结SIM队列,记录下我​们有一个挂起的操作,返回
5045 »       ​CCB,请求CAM将CCB重新入队。以后当​资源可用时,必须通过返回其5045 »       ​CCB,请求CAM将CCB重新入队。以后当​资源可用时,必须通过返回其
5046 »       ​状态中设置·​<code·​class="literal">CAM_S​IMQ_RELEASE</​code>·​位的ccb来解冻SIM队列。否则,如果所有​5046 »       ​状态中设置·​<code·​class="literal">CAM_S​IMQ_RELEASE</​code>·​位的ccb来解冻SIM队列。否则,如果所有​
5047 »       ​正常,则将CCB与硬件控制块(HCB)链接​,并将其标志为已入队。</​p><pre·​class="programlisting​">·​·​·​·​struct·​xxx_hcb·​*hcb·​=·​allocate_hcb(softc,​·​unit,​·​bus)​;​5047 »       ​正常,则将CCB与硬件控制块(HCB)链接​,并将其标志为已入队。</​p><pre·​class="programlisting​">·​·​·​·​struct·​xxx_hcb·​*hcb·​=·​allocate_hcb(softc,​·​unit,​·​bus)​;​
  
5048 ·​·​·​·​if(hcb·​==·​NULL)​·​{5048 ·​·​·​·​if(hcb·​==·​NULL)​·​{
5049 ·​·​·​·​·​·​·​·​softc-​&gt;​flags·​|=·​RESOURCE_SHORTAGE;​5049 ·​·​·​·​·​·​·​·​softc-​&gt;​flags·​|=·​RESOURCE_SHORTAGE;​
Offset 5440, 22 lines modifiedOffset 5440, 22 lines modified
5440 ·​·​·​·​·​·​(在<code·​class="function">xxx_​attach()​</​code>中)当前协商值必须被初始化为5440 ·​·​·​·​·​·​(在<code·​class="function">xxx_​attach()​</​code>中)当前协商值必须被初始化为
5441 ·​·​·​·​·​·​最窄同步模式,目的和当前值必须被初始化为控​制器所支持的最大值。5441 ·​·​·​·​·​·​最窄同步模式,目的和当前值必须被初始化为控​制器所支持的最大值。
5442 ·​·​·​·​·​·​(译注:原文可能有误,此处未改)5442 ·​·​·​·​·​·​(译注:原文可能有误,此处未改)
5443 ·​·​·​·​·​·​</​p></​li><li·​class="listitem"><p><​span·​class="emphasis"><em>​XPT_GET_TRAN_SETTINGS​</​em></​span>·​-​5443 ·​·​·​·​·​·​</​p></​li><li·​class="listitem"><p><​span·​class="emphasis"><em>​XPT_GET_TRAN_SETTINGS​</​em></​span>·​-​
5444 ·​·​·​·​获得SCSI传输设置的值</​p><p>此操作为XPT_SET_TRAN​_SETTINGS的逆操作。用通过旗标5444 ·​·​·​·​获得SCSI传输设置的值</​p><p>此操作为XPT_SET_TRAN​_SETTINGS的逆操作。用通过旗标
5445 ·​·​·​·​·​·​CCB_TRANS_CURRENT_SET​TINGS或CCB_TRANS_USER_​SETTINGS(如果同时设置则5445 ·​·​·​·​·​·​CCB_TRANS_CURRENT_SET​TINGS或CCB_TRANS_USER_​SETTINGS(如果同时设置则
5446 ·​·​·​·​·​·​现有驱动程序返回当前设置)所请求而得的数据​填充CCB实例5446 ·​·​·​·​·​·​现有驱动程序返回当前设置)所请求而得的数据​填充CCB实例
5447 ·​·​·​·​·​·​<span·​class="quote">“<span·​class="quote">struct·​ccb_trans_setting·​cts</​span>”</​span>.​·​</​p></​li><li·​class="listitem"><a·​id="idp68411320"·​class="indexterm"></​a><p><span·​class="emphasis"><em>​XPT_CALC_GEOMETRY</​em></​span>·​-​5447 ·​·​·​·​·​·​<span·​class="quote">“<span·​class="quote">struct·​ccb_trans_setting·​cts</​span>”</​span>.​·​</​p></​li><li·​class="listitem"><a·​id="idp68378552"·​class="indexterm"></​a><p><span·​class="emphasis"><em>​XPT_CALC_GEOMETRY</​em></​span>·​-​
5448 ·​·​·​·​·​·​计算磁盘的逻辑(BIOS)结构(geome​try)​</​p><p>参量在联合ccb的实例<span​·​class="quote">“<span·​class="quote">struct·​ccb_calc_geometry·​ccg</​span>”</​span>5448 ·​·​·​·​·​·​计算磁盘的逻辑(BIOS)结构(geome​try)​</​p><p>参量在联合ccb的实例<span​·​class="quote">“<span·​class="quote">struct·​ccb_calc_geometry·​ccg</​span>”</​span>
5449 ·​·​·​·​·​·​中传输:</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​circle;​·​"><li·​class="listitem"><p><​span·​class="emphasis"><em>​block_size</​em></​span>·​-​5449 ·​·​·​·​·​·​中传输:</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​circle;​·​"><li·​class="listitem"><p><​span·​class="emphasis"><em>​block_size</​em></​span>·​-​
5450 ·​·​·​·​·​·​输入,以字节计的块大小(也称为扇区)</​p></​li><li·​class="listitem"><p><​span·​class="emphasis"><em>​volume_size</​em></​span>·​-​5450 ·​·​·​·​·​·​输入,以字节计的块大小(也称为扇区)</​p></​li><li·​class="listitem"><p><​span·​class="emphasis"><em>​volume_size</​em></​span>·​-​
5451 ·​·​·​·​·​·​输入,以字节计的卷大小</​p></​li><li·​class="listitem"><p><​span·​class="emphasis"><em>​cylinders</​em></​span>·​-​5451 ·​·​·​·​·​·​输入,以字节计的卷大小</​p></​li><li·​class="listitem"><p><​span·​class="emphasis"><em>​cylinders</​em></​span>·​-​
5452 ·​·​·​·​·​·​输出,逻辑柱面</​p></​li><li·​class="listitem"><p><​span·​class="emphasis"><em>​heads</​em></​span>·​-​5452 ·​·​·​·​·​·​输出,逻辑柱面</​p></​li><li·​class="listitem"><p><​span·​class="emphasis"><em>​heads</​em></​span>·​-​
5453 ·​·​·​·​·​·​输出,逻辑磁头</​p></​li><li·​class="listitem"><p><​span·​class="emphasis"><em>​secs_per_track</​em></​span>·​-​5453 ·​·​·​·​·​·​输出,逻辑磁头</​p></​li><li·​class="listitem"><p><​span·​class="emphasis"><em>​secs_per_track</​em></​span>·​-​
5454 ·​·​·​·​·​·​输出,每磁道的逻辑扇区</​p></​li></​ul></​div><a·​id="idp68417848"·​class="indexterm"></​a><p>如果返回的结构与SCSI控制器B​IOS所想象的差别很大,并且SCSI5454 ·​·​·​·​·​·​输出,每磁道的逻辑扇区</​p></​li></​ul></​div><a·​id="idp68397368"·​class="indexterm"></​a><p>如果返回的结构与SCSI控制器B​IOS所想象的差别很大,并且SCSI
5455 ·​·​·​·​·​·​控制器上的磁盘被作为可引导的,则系统可能无​法启动。从aic7xxx5455 ·​·​·​·​·​·​控制器上的磁盘被作为可引导的,则系统可能无​法启动。从aic7xxx
5456 ·​·​·​·​·​·​驱动程序中摘取的典型计算示例:</​p><pre·​class="programlisting​">·​·​·​·​struct·​·​·​·​ccb_calc_geometry·​*ccg;​5456 ·​·​·​·​·​·​驱动程序中摘取的典型计算示例:</​p><pre·​class="programlisting​">·​·​·​·​struct·​·​·​·​ccb_calc_geometry·​*ccg;​
5457 ·​·​·​·​u_int32_t·​size_mb;​5457 ·​·​·​·​u_int32_t·​size_mb;​
5458 ·​·​·​·​u_int32_t·​secs_per_cylinder;​5458 ·​·​·​·​u_int32_t·​secs_per_cylinder;​
5459 ·​·​·​·​int·​·​·​extended;​5459 ·​·​·​·​int·​·​·​extended;​
  
5460 ·​·​·​·​ccg·​=·​&amp;​ccb-​&gt;​ccg;​5460 ·​·​·​·​ccg·​=·​&amp;​ccb-​&gt;​ccg;​
Offset 5553, 15 lines modifiedOffset 5553, 15 lines modified
5553 ·​·​·​·​·​·​·​·​·​·​·​·​neg.​valid·​=·​(CCB_TRANS_BUS_WIDTH_​VALID5553 ·​·​·​·​·​·​·​·​·​·​·​·​neg.​valid·​=·​(CCB_TRANS_BUS_WIDTH_​VALID
5554 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​|·​CCB_TRANS_SYNC_RATE_V​ALID·​|·​CCB_TRANS_SYNC_OFFSET​_VALID)​;​5554 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​|·​CCB_TRANS_SYNC_RATE_V​ALID·​|·​CCB_TRANS_SYNC_OFFSET​_VALID)​;​
5555 ·​·​·​·​·​·​·​·​·​·​·​·​xpt_async(AC_TRANSFER​_NEG,​·​path,​·​&amp;​neg)​;​5555 ·​·​·​·​·​·​·​·​·​·​·​·​xpt_async(AC_TRANSFER​_NEG,​·​path,​·​&amp;​neg)​;​
5556 ·​·​·​·​·​·​·​·​}5556 ·​·​·​·​·​·​·​·​}
5557 ·​·​·​·​·​·​·​·​break;​5557 ·​·​·​·​·​·​·​·​break;​
5558 ·​·​·​·​default:​5558 ·​·​·​·​default:​
5559 ·​·​·​·​·​·​·​·​break;​5559 ·​·​·​·​·​·​·​·​break;​
5560 ·​·​·​·​}</​pre></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="scsi-​interrupts"></​a>12.​5.​ 中断</​h2></​div></​div></​div><a·​id="idp68471224"·​class="indexterm"></​a><p>中断例程的确切类型依赖于SCSI​控制器所连接到的外围总线的类型(PCI,5560 ·​·​·​·​}</​pre></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="scsi-​interrupts"></​a>12.​5.​ 中断</​h2></​div></​div></​div><a·​id="idp68434360"·​class="indexterm"></​a><p>中断例程的确切类型依赖于SCSI​控制器所连接到的外围总线的类型(PCI,
5561 ·​·​·​·​·​·​ISA等等)。</​p><p>SIM驱动程序的中断例程运行在中​断级别splcam上。因此应当在驱动5561 ·​·​·​·​·​·​ISA等等)。</​p><p>SIM驱动程序的中断例程运行在中​断级别splcam上。因此应当在驱动
5562 ·​·​·​·​·​·​程序中使用<code·​class="function">splc​am()​</​code>来同步中断例程与驱动程序5562 ·​·​·​·​·​·​程序中使用<code·​class="function">splc​am()​</​code>来同步中断例程与驱动程序
5563 ·​·​·​·​·​·​剩余部分的活动(对于能察觉多处理器的驱动程​序,事情更要有趣,但5563 ·​·​·​·​·​·​剩余部分的活动(对于能察觉多处理器的驱动程​序,事情更要有趣,但
5564 ·​·​·​·​·​·​此处我们忽略这种情况)。本文档中的伪代码简​单地忽略了同步问题。5564 ·​·​·​·​·​·​此处我们忽略这种情况)。本文档中的伪代码简​单地忽略了同步问题。
5565 ·​·​·​·​·​·​实际代码一定不能忽略它们。一个较笨的办法就​是在进入其他例程的5565 ·​·​·​·​·​·​实际代码一定不能忽略它们。一个较笨的办法就​是在进入其他例程的
5566 ·​·​·​·​·​·​入口点处设<code·​class="function">splc​am()​</​code>,并在返回时将它复位,从而5566 ·​·​·​·​·​·​入口点处设<code·​class="function">splc​am()​</​code>,并在返回时将它复位,从而
5567 ·​·​·​·​·​·​用一个大的临界区保护它们。为了确保中断级别​总是会被恢复,可以定义5567 ·​·​·​·​·​·​用一个大的临界区保护它们。为了确保中断级别​总是会被恢复,可以定义
Offset 5836, 15 lines modifiedOffset 5836, 15 lines modified
5836 ·​·​·​·​·​·​·​·​·​·​·​·​if(targ·​==·​h-​&gt;​targ5836 ·​·​·​·​·​·​·​·​·​·​·​·​if(targ·​==·​h-​&gt;​targ
5837 ·​·​·​·​·​·​·​·​·​·​·​·​&amp;​&amp;​·​(lun_to_freeze·​==·​CAM_LUN_WILDCARD·​||·​lun_to_freeze·​==·​h-​&gt;​lun)​·​)​5837 ·​·​·​·​·​·​·​·​·​·​·​·​&amp;​&amp;​·​(lun_to_freeze·​==·​CAM_LUN_WILDCARD·​||·​lun_to_freeze·​==·​h-​&gt;​lun)​·​)​
5838 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​free_hcb_and_ccb_done​(h,​·​h-​&gt;​ccb,​·​CAM_REQUEUE_REQ)​;​5838 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​free_hcb_and_ccb_done​(h,​·​h-​&gt;​ccb,​·​CAM_REQUEUE_REQ)​;​
5839 ·​·​·​·​·​·​·​·​}5839 ·​·​·​·​·​·​·​·​}
5840 ·​·​·​·​}5840 ·​·​·​·​}
5841 ·​·​·​·​free_hcb_and_ccb_done​(hcb,​·​hcb-​&gt;​ccb,​·​ccb_status)​;​5841 ·​·​·​·​free_hcb_and_ccb_done​(hcb,​·​hcb-​&gt;​ccb,​·​ccb_status)​;​
5842 ·​·​·​·​schedule_next_hcb(sof​tc)​;​5842 ·​·​·​·​schedule_next_hcb(sof​tc)​;​
5843 ·​·​·​·​return;​</​pre><p>这包括通用中断处理,尽管特定​处理器可能需要某些附加处理。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="scsi-​errors"></​a>12.​6.​ 错误总览</​h2></​div></​div></​div><a·​id="idp68497592"·​class="indexterm"></​a><p>当执行I/​O请求时很多事情可能出错。可以在CCB状态​中非常详尽地5843 ·​·​·​·​return;​</​pre><p>这包括通用中断处理,尽管特定​处理器可能需要某些附加处理。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="scsi-​errors"></​a>12.​6.​ 错误总览</​h2></​div></​div></​div><a·​id="idp68448440"·​class="indexterm"></​a><p>当执行I/​O请求时很多事情可能出错。可以在CCB状态​中非常详尽地
5844 ·​·​·​·​·​·​报告错误原因。使用的例子散布于本文档中。为​了完整起见此处给出5844 ·​·​·​·​·​·​报告错误原因。使用的例子散布于本文档中。为​了完整起见此处给出
5845 ·​·​·​·​·​·​对典型错误条件的建议响应的一个总览:</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p><​span·​class="emphasis"><em>​CAM_RESRC_UNAVAIL</​em></​span>·​-​·​某些资源5845 ·​·​·​·​·​·​对典型错误条件的建议响应的一个总览:</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p><​span·​class="emphasis"><em>​CAM_RESRC_UNAVAIL</​em></​span>·​-​·​某些资源
5846 ·​·​·​·​·​·​·​·​暂时不可用,并且当其变为可用时SIM驱动程​序不能产生事件。这种资源5846 ·​·​·​·​·​·​·​·​暂时不可用,并且当其变为可用时SIM驱动程​序不能产生事件。这种资源
5847 »       ​的一个例子就是某些控制器内部硬件资源,当其​可用时控制器不会为其5847 »       ​的一个例子就是某些控制器内部硬件资源,当其​可用时控制器不会为其
5848 »       ​产生中断。</​p></​li><li·​class="listitem"><p><​span·​class="emphasis"><em>​CAM_UNCOR_PARITY</​em></​span>·​-​5848 »       ​产生中断。</​p></​li><li·​class="listitem"><p><​span·​class="emphasis"><em>​CAM_UNCOR_PARITY</​em></​span>·​-​
5849 ·​·​·​·​·​·​·​·​发生不可恢复的奇偶校验错误</​p></​li><li·​class="listitem"><p><​span·​class="emphasis"><em>​CAM_DATA_RUN_ERR</​em></​span>·​-​5849 ·​·​·​·​·​·​·​·​发生不可恢复的奇偶校验错误</​p></​li><li·​class="listitem"><p><​span·​class="emphasis"><em>​CAM_DATA_RUN_ERR</​em></​span>·​-​
5850 ·​·​·​·​·​·​·​·​数据外溢或未预期的数据状态(phase)​(跑在另一个方向上而不是5850 ·​·​·​·​·​·​·​·​数据外溢或未预期的数据状态(phase)​(跑在另一个方向上而不是
Offset 5895, 15 lines modifiedOffset 5895, 15 lines modified
5895 ·​·​·​·​·​·​·​·​xxx_abort_ccb(hcb-​&gt;​ccb,​·​CAM_CMD_TIMEOUT)​;​5895 ·​·​·​·​·​·​·​·​xxx_abort_ccb(hcb-​&gt;​ccb,​·​CAM_CMD_TIMEOUT)​;​
5896 ·​·​·​·​}5896 ·​·​·​·​}
5897 }</​pre><p>当我们中止一个请求时,同一目​标/​LUN的所有其他断开连接的请求5897 }</​pre><p>当我们中止一个请求时,同一目​标/​LUN的所有其他断开连接的请求
5898 ·​·​·​·​·​·​也会被中止。因此出现了一个问题,我们应当返​回它们的状态5898 ·​·​·​·​·​·​也会被中止。因此出现了一个问题,我们应当返​回它们的状态
5899 ·​·​·​·​·​·​CAM_REQ_ABORTED还是CAM_​CMD_TIMEOUT?当前的驱动程序使用​5899 ·​·​·​·​·​·​CAM_REQ_ABORTED还是CAM_​CMD_TIMEOUT?当前的驱动程序使用​
5900 ·​·​·​·​·​·​CAM_CMD_TIMEOUT。这看起来符​合逻辑,因为如果一个请求超时,则可能5900 ·​·​·​·​·​·​CAM_CMD_TIMEOUT。这看起来符​合逻辑,因为如果一个请求超时,则可能
5901 ·​·​·​·​·​·​设备出现了某些的确很糟的事情,因此如果它们​没有被扰乱则它们自己5901 ·​·​·​·​·​·​设备出现了某些的确很糟的事情,因此如果它们​没有被扰乱则它们自己
5902 ·​·​·​·​·​·​应当超时。</​p></​div></​div><div·​class="chapter"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="usb"></​a>第 13 章 USB设备</​h2></​div><div><span·​class="authorgroup">写​作:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"><span·​class="firstname">Nic​k</​span>·​<span·​class="surname">Hibma​</​span></​span>.​·​</​span></​div><div><span·​class="authorgroup">改​编为手册:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"><span·​class="firstname">Mur​ray</​span>·​<span·​class="surname">Stoke​ly</​span></​span>.​·​</​span></​div><div><span·​class="authorgroup">翻​译:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"></​span>.​·​</​span></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="sect1"><a·​href="#usb-​intro">13.​1.​·​简介</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#usb-​hc">13.​2.​·​主控器</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#usb-​dev">13.​3.​·​USB设备信息</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#usb-​devprobe">13.​4.​·​设备的探测和连接</​a><·​✂5902 ·​·​·​·​·​·​应当超时。</​p></​div></​div><div·​class="chapter"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="usb"></​a>第 13 章 USB设备</​h2></​div><div><span·​class="authorgroup">写​作:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"><span·​class="firstname">Nic​k</​span>·​<span·​class="surname">Hibma​</​span></​span>.​·​</​span></​div><div><span·​class="authorgroup">改​编为手册:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"><span·​class="firstname">Mur​ray</​span>·​<span·​class="surname">Stoke​ly</​span></​span>.​·​</​span></​div><div><span·​class="authorgroup">翻​译:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"></​span>.​·​</​span></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="sect1"><a·​href="#usb-​intro">13.​1.​·​简介</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#usb-​hc">13.​2.​·​主控器</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#usb-​dev">13.​3.​·​USB设备信息</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#usb-​devprobe">13.​4.​·​设备的探测和连接</​a><·​✂
5903 ·​·​·​·​·​·​结构突出了双向通信的特色,并且其开发充分考​虑到了设备正逐渐智能化5903 ·​·​·​·​·​·​结构突出了双向通信的特色,并且其开发充分考​虑到了设备正逐渐智能化
5904 ·​·​·​·​·​·​和需要与host进行更多交互的现实。对US​B的支持包含在当前所有芯片中,​5904 ·​·​·​·​·​·​和需要与host进行更多交互的现实。对US​B的支持包含在当前所有芯片中,​
5905 ·​·​·​·​·​·​因此在新近制造的PC中都可用。苹果(App​le)​引入仅带USB的iMac对硬件5905 ·​·​·​·​·​·​因此在新近制造的PC中都可用。苹果(App​le)​引入仅带USB的iMac对硬件
5906 ·​·​·​·​·​·​制造商生产他们USB版本的设备是一个很大的​激励。未来的PC规范指定5906 ·​·​·​·​·​·​制造商生产他们USB版本的设备是一个很大的​激励。未来的PC规范指定
5907 ·​·​·​·​·​·​PC上的所有老连接器应当由一个或多个USB​连接器取代,提供通用的5907 ·​·​·​·​·​·​PC上的所有老连接器应当由一个或多个USB​连接器取代,提供通用的
5908 ·​·​·​·​·​·​即插即用能力。对USB硬件的支持在NetB​SD的相当早期就有了,它是由5908 ·​·​·​·​·​·​即插即用能力。对USB硬件的支持在NetB​SD的相当早期就有了,它是由
5909 ·​·​·​·​·​·​Lennart·​Augustsson为NetBSD项目开发​的。代码已经被移植到FreeBSD上,5909 ·​·​·​·​·​·​Lennart·​Augustsson为NetBSD项目开发​的。代码已经被移植到FreeBSD上,
Offset 5917, 23 lines modifiedOffset 5917, 23 lines modified
5917 ·​·​·​·​·​·​·​·​可以连接最多126个设备,这就需要恰当地调​度总线上的传输以充分5917 ·​·​·​·​·​·​·​·​可以连接最多126个设备,这就需要恰当地调​度总线上的传输以充分
5918 »       ​利用12Mbps的可用带宽。(USB·​2.​0超过400Mbps)</​p></​li><li·​class="listitem"><p>设​备智能化并包含很容易访问到的关于自身的信息​。5918 »       ​利用12Mbps的可用带宽。(USB·​2.​0超过400Mbps)</​p></​li><li·​class="listitem"><p>设​备智能化并包含很容易访问到的关于自身的信息​。
5919 ·​·​·​·​·​·​·​·​</​p></​li></​ul></​div><p>为USB子系统以及连接到它的​设备开发驱动程序受已开发或将要开发的5919 ·​·​·​·​·​·​·​·​</​p></​li></​ul></​div><p>为USB子系统以及连接到它的​设备开发驱动程序受已开发或将要开发的
5920 ·​·​·​·​·​·​规范的支持。这些规范可以从USB主页公开获​得。苹果(Apple)​通过使得5920 ·​·​·​·​·​·​规范的支持。这些规范可以从USB主页公开获​得。苹果(Apple)​通过使得
5921 ·​·​·​·​·​·​通用类驱动程序可从其操作系统MacOS中获​得,而且不鼓励为每种新设备5921 ·​·​·​·​·​·​通用类驱动程序可从其操作系统MacOS中获​得,而且不鼓励为每种新设备
5922 ·​·​·​·​·​·​使用单独的驱动程序来强烈推行基于标准的驱动​程序。本章试图整理基本5922 ·​·​·​·​·​·​使用单独的驱动程序来强烈推行基于标准的驱动​程序。本章试图整理基本
5923 ·​·​·​·​·​·​信息以便对FreeBSD/​NetBSD中USB栈的当前实现有个基本的​了解。然而,5923 ·​·​·​·​·​·​信息以便对FreeBSD/​NetBSD中USB栈的当前实现有个基本的​了解。然而,
5924 ·​·​·​·​·​·​建议将下面参考中提及的相关规范与本章同时阅​读。</​p><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68576184"></​a>13.​1.​1.​ USB栈的结构</​h3></​div></​div></​div><p>FreeBSD中的USB支持​可被分为三层。最底层包含主控器,向硬件5924 ·​·​·​·​·​·​建议将下面参考中提及的相关规范与本章同时阅​读。</​p><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68527032"></​a>13.​1.​1.​ USB栈的结构</​h3></​div></​div></​div><p>FreeBSD中的USB支持​可被分为三层。最底层包含主控器,向硬件
5925 ·​·​·​·​·​·​·​·​及其调度设施提供一个通用接口。它支持硬件初​始化,对传输进行调度,5925 ·​·​·​·​·​·​·​·​及其调度设施提供一个通用接口。它支持硬件初​始化,对传输进行调度,
5926 »       ​处理已完成/​失败的传输。每个主控器驱动程序实现一个虚拟​hub,5926 »       ​处理已完成/​失败的传输。每个主控器驱动程序实现一个虚拟​hub,
5927 »       ​以硬件无关方式提供对控制机器背面根端口的寄​存器的访问。</​p><p>中间层处理设备连接和断开,设备的​基本初始化,驱动程序的选择,5927 »       ​以硬件无关方式提供对控制机器背面根端口的寄​存器的访问。</​p><p>中间层处理设备连接和断开,设备的​基本初始化,驱动程序的选择,
5928 ·​·​·​·​·​·​·​·​通信通道(管道)和资源管理。这个服务层也控​制默认管道和其上传输的5928 ·​·​·​·​·​·​·​·​通信通道(管道)和资源管理。这个服务层也控​制默认管道和其上传输的
5929 »       ​设备请求。</​p><p>顶层包含支持特定(类)设备的各个​驱动程序。这些驱动程序实现5929 »       ​设备请求。</​p><p>顶层包含支持特定(类)设备的各个​驱动程序。这些驱动程序实现
5930 ·​·​·​·​·​·​·​·​除默认管道外的其他管道上使用的协议。他们也​实现额外功能,使得设备5930 ·​·​·​·​·​·​·​·​除默认管道外的其他管道上使用的协议。他们也​实现额外功能,使得设备
5931 »       ​对内核或用户空间是可见的。他们使用服务层暴​露出的USB驱动程序接口5931 »       ​对内核或用户空间是可见的。他们使用服务层暴​露出的USB驱动程序接口
5932 »       ​(USBDI)​。</​p></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="usb-​hc"></​a>13.​2.​ 主控器</​h2></​div></​div></​div><a·​id="idp68579000"·​class="indexterm"></​a><p>主控器(HC)控制总线上包的传输​。使用1毫秒的帧。在每帧开始5932 »       ​(USBDI)​。</​p></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="usb-​hc"></​a>13.​2.​ 主控器</​h2></​div></​div></​div><a·​id="idp68533944"·​class="indexterm"></​a><p>主控器(HC)控制总线上包的传输​。使用1毫秒的帧。在每帧开始
5933 ·​·​·​·​·​·​时,主控器产生一个帧开始(SOF,​·​Start·​of·​Frame)包。</​p><p>SOF包用于同步帧的开始和跟踪帧​的数目。包在帧中被传输,或由host5933 ·​·​·​·​·​·​时,主控器产生一个帧开始(SOF,​·​Start·​of·​Frame)包。</​p><p>SOF包用于同步帧的开始和跟踪帧​的数目。包在帧中被传输,或由host
5934 ·​·​·​·​·​·​到设备(out),或由设备到host(in​)。传输总是由host发起(轮询传输)。5934 ·​·​·​·​·​·​到设备(out),或由设备到host(in​)。传输总是由host发起(轮询传输)。
5935 ·​·​·​·​·​·​因此每条USB总线只能有一个host。每个​包的传输都有一个状态阶段,5935 ·​·​·​·​·​·​因此每条USB总线只能有一个host。每个​包的传输都有一个状态阶段,
5936 ·​·​·​·​·​·​数据接收者可以在其中返回ACK(应答接收)​,NAK(重试),STALL(错误5936 ·​·​·​·​·​·​数据接收者可以在其中返回ACK(应答接收)​,NAK(重试),STALL(错误
5937 ·​·​·​·​·​·​条件)或什么也没有(混乱数据阶段,设备不可​用或已断开)。USB规范5937 ·​·​·​·​·​·​条件)或什么也没有(混乱数据阶段,设备不可​用或已断开)。USB规范
5938 ·​·​·​·​·​·​<a·​class="link"·​href="http:​/​/​www.​usb.​org/​developers/​docs.​html"·​target="_top">USB5938 ·​·​·​·​·​·​<a·​class="link"·​href="http:​/​/​www.​usb.​org/​developers/​docs.​html"·​target="_top">USB
5939 ·​·​·​·​·​·​specification</​a>的第8.​5节更详细地解释了包的细节。USB总线5939 ·​·​·​·​·​·​specification</​a>的第8.​5节更详细地解释了包的细节。USB总线
Offset 5951, 15 lines modifiedOffset 5951, 15 lines modified
5951 ·​·​·​·​·​·​标准和hub类特定的一组描述符。它也应当提​供一个中断管道用来报告其5951 ·​·​·​·​·​·​标准和hub类特定的一组描述符。它也应当提​供一个中断管道用来报告其
5952 ·​·​·​·​·​·​端口发生的变化。当前可用的主控器规范有两个​:5952 ·​·​·​·​·​·​端口发生的变化。当前可用的主控器规范有两个​:
5953 ·​·​·​·​·​·​<a·​class="link"·​href="http:​/​/​developer.​intel.​com/​design/​USB/​UHCI11D.​htm"·​target="_top">5953 ·​·​·​·​·​·​<a·​class="link"·​href="http:​/​/​developer.​intel.​com/​design/​USB/​UHCI11D.​htm"·​target="_top">
5954 ·​·​·​·​·​·​通用主控器接口</​a>(UHCI;英特尔)和<a·​class="link"·​href="http:​/​/​www.​compaq.​com/​productinfo/​development/​openhci.​html"·​target="_top">5954 ·​·​·​·​·​·​通用主控器接口</​a>(UHCI;英特尔)和<a·​class="link"·​href="http:​/​/​www.​compaq.​com/​productinfo/​development/​openhci.​html"·​target="_top">
5955 ·​·​·​·​·​·​开放主控器接口</​a>(OHCI;康柏,微软,国家半导体)。​5955 ·​·​·​·​·​·​开放主控器接口</​a>(OHCI;康柏,微软,国家半导体)。​
5956 ·​·​·​·​·​·​UHCI规范的设计通过要求主控器驱动程序为​每帧的传输提供完整的调度,5956 ·​·​·​·​·​·​UHCI规范的设计通过要求主控器驱动程序为​每帧的传输提供完整的调度,
5957 ·​·​·​·​·​·​从而减少了硬件复杂性。OHCI类型的控制器​自身提供一个更抽象的接口来5957 ·​·​·​·​·​·​从而减少了硬件复杂性。OHCI类型的控制器​自身提供一个更抽象的接口来
5958 ·​·​·​·​·​·​完成很多工作,从而更加独立。</​p><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68582840"></​a>13.​2.​1.​ UHCI</​h3></​div></​div></​div><a·​id="idp68583480"·​class="indexterm"></​a><p>UHCI主控器维护着带有1024​个指向每帧数据结构的帧列表。5958 ·​·​·​·​·​·​完成很多工作,从而更加独立。</​p><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68550072"></​a>13.​2.​1.​ UHCI</​h3></​div></​div></​div><a·​id="idp68550712"·​class="indexterm"></​a><p>UHCI主控器维护着带有1024​个指向每帧数据结构的帧列表。
5959 ·​·​·​·​·​·​·​·​它理解两种不同的数据类型:传输描述符(TD​)和队列头(QH)。每个5959 ·​·​·​·​·​·​·​·​它理解两种不同的数据类型:传输描述符(TD​)和队列头(QH)。每个
5960 »       ​TD表示表示与设备端点进行通信的一个包。Q​H是将一些TD(和QH)划分5960 »       ​TD表示表示与设备端点进行通信的一个包。Q​H是将一些TD(和QH)划分
5961 »       ​成组的一种方法。</​p><p>每个传输由一个或多个包组成。UH​CI驱动程序将大的传输分割成5961 »       ​成组的一种方法。</​p><p>每个传输由一个或多个包组成。UH​CI驱动程序将大的传输分割成
5962 ·​·​·​·​·​·​·​·​多个包。除同步传输外,每个传输都会分配一个​QH。对于每种类型的5962 ·​·​·​·​·​·​·​·​多个包。除同步传输外,每个传输都会分配一个​QH。对于每种类型的
5963 »       ​传输,都有一个与此类型对应的QH,所有这些​QH都会被集中到这个QH上。5963 »       ​传输,都有一个与此类型对应的QH,所有这些​QH都会被集中到这个QH上。
5964 »       ​由于有固定的时延需求,同步传输必须首先执行​,它是通过帧列表中的5964 »       ​由于有固定的时延需求,同步传输必须首先执行​,它是通过帧列表中的
5965 »       ​指针直接引用的。最后的同步TD传输引用那一​帧的中断传输的QH。中断5965 »       ​指针直接引用的。最后的同步TD传输引用那一​帧的中断传输的QH。中断
Offset 5974, 15 lines modifiedOffset 5974, 15 lines modified
5974 »       ​产生不同类型的中断。在传输的最后一个TD中​,HC驱动程序设置5974 »       ​产生不同类型的中断。在传输的最后一个TD中​,HC驱动程序设置
5975 »       ​Interrupt-​On-​Completion位来标记传输完成时的一​个中断。如果TD达到了5975 »       ​Interrupt-​On-​Completion位来标记传输完成时的一​个中断。如果TD达到了
5976 »       ​其最大错误数,就标记错误中断。如果在TD中​设置短包侦测位,且传输了5976 »       ​其最大错误数,就标记错误中断。如果在TD中​设置短包侦测位,且传输了
5977 »       ​小于所设置的包长度(的包),就会标记此中断​以通知控制器驱动程序传输5977 »       ​小于所设置的包长度(的包),就会标记此中断​以通知控制器驱动程序传输
5978 ·​·​·​·​·​·​·​·​已完成。找出哪个传输已完成或产生错误是主控​器驱动程序的任务。5978 ·​·​·​·​·​·​·​·​已完成。找出哪个传输已完成或产生错误是主控​器驱动程序的任务。
5979 »       ​当中断服务例程被调用时,它将定位所有已完成​的传输并调用它们的回调。5979 »       ​当中断服务例程被调用时,它将定位所有已完成​的传输并调用它们的回调。
5980 ·​·​·​·​·​·​·​·​</​p><p>更详尽的描述请看·​<a·​class="link"·​href="http:​/​/​developer.​intel.​com/​design/​USB/​UHCI11D.​htm"·​target="_top">UHCI5980 ·​·​·​·​·​·​·​·​</​p><p>更详尽的描述请看·​<a·​class="link"·​href="http:​/​/​developer.​intel.​com/​design/​USB/​UHCI11D.​htm"·​target="_top">UHCI
5981 ·​·​·​·​·​·​·​·​specification。</​a></​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68586296"></​a>13.​2.​2.​ OHCI</​h3></​div></​div></​div><a·​id="idp68586936"·​class="indexterm"></​a><p>对OHCI主控器进行编程要容易得​多。控制器假设有一组端点(endpoint​)​可用,5981 ·​·​·​·​·​·​·​·​specification。</​a></​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68553528"></​a>13.​2.​2.​ OHCI</​h3></​div></​div></​div><a·​id="idp68554168"·​class="indexterm"></​a><p>对OHCI主控器进行编程要容易得​多。控制器假设有一组端点(endpoint​)​可用,
5982 ·​·​·​·​·​·​·​·​并知道帧中不同传输类型的调度优先级和排序。​主控器使用的主要5982 ·​·​·​·​·​·​·​·​并知道帧中不同传输类型的调度优先级和排序。​主控器使用的主要
5983 »       ​数据结构是端点描述符(ED),它上面连接着​一个传输描述符(TD)的队列。5983 »       ​数据结构是端点描述符(ED),它上面连接着​一个传输描述符(TD)的队列。
5984 »       ​ED包含端点所允许的最大的包大小,控制器硬​件完成包的分割。每次传输5984 »       ​ED包含端点所允许的最大的包大小,控制器硬​件完成包的分割。每次传输
5985 »       ​后都会更新指向数据缓冲区的指针,当起始和终​止指针相等时,TD就退归5985 »       ​后都会更新指向数据缓冲区的指针,当起始和终​止指针相等时,TD就退归
5986 »       ​到完成队列(done-​queue)​。四种类型的端点各有其自己的队列。控制和5986 »       ​到完成队列(done-​queue)​。四种类型的端点各有其自己的队列。控制和
5987 »       ​大块(bulk)​端点分别在它们自己的队列排队。中断ED在树​中排队,在树中的深度5987 »       ​大块(bulk)​端点分别在它们自己的队列排队。中断ED在树​中排队,在树中的深度
5988 »       ​定义了它们运行的频度。</​p><p>帧列表·​中断·​同步(isochronous)​·​控制·​大块(bulk)​</​p><p>主控器在每帧中运行的调度看起来如​下。控制器首先运行非5988 »       ​定义了它们运行的频度。</​p><p>帧列表·​中断·​同步(isochronous)​·​控制·​大块(bulk)​</​p><p>主控器在每帧中运行的调度看起来如​下。控制器首先运行非
Offset 5992, 15 lines modifiedOffset 5992, 15 lines modified
5992 »       ​遍历。同步TD包含了传输应当运行其中的第一​个帧的帧编号。所有周期5992 »       ​遍历。同步TD包含了传输应当运行其中的第一​个帧的帧编号。所有周期
5993 »       ​性的传输运行过以后,控制和大块队列再次被遍​历。中断服务例程会被5993 »       ​性的传输运行过以后,控制和大块队列再次被遍​历。中断服务例程会被
5994 »       ​周期性地调用,来处理完成的队列,为每个传输​调用回调,并重新调度5994 »       ​周期性地调用,来处理完成的队列,为每个传输​调用回调,并重新调度
5995 »       ​中断和同步端点。</​p><p>更详尽的描述请看·​<a·​class="link"·​href="http:​/​/​www.​compaq.​com/​productinfo/​development/​openhci.​html"·​target="_top">5995 »       ​中断和同步端点。</​p><p>更详尽的描述请看·​<a·​class="link"·​href="http:​/​/​www.​compaq.​com/​productinfo/​development/​openhci.​html"·​target="_top">
5996 ·​·​·​·​·​·​·​·​OHCI·​specification</​a>。服务层,即中间层,提供了以可控的方式​5996 ·​·​·​·​·​·​·​·​OHCI·​specification</​a>。服务层,即中间层,提供了以可控的方式​
5997 »       ​对设备进行访问,并维护着由不同驱动程序和服​务层所使用的资源。5997 »       ​对设备进行访问,并维护着由不同驱动程序和服​务层所使用的资源。
5998 ·​·​·​·​·​·​·​·​此层处理下面几方面:</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p>设​备配置信息</​p></​li><li·​class="listitem"><p>与​设备进行通信的管道</​p></​li><li·​class="listitem"><p>探​测和连接设备,以及从设备分离(detach​)​。5998 ·​·​·​·​·​·​·​·​此层处理下面几方面:</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p>设​备配置信息</​p></​li><li·​class="listitem"><p>与​设备进行通信的管道</​p></​li><li·​class="listitem"><p>探​测和连接设备,以及从设备分离(detach​)​。
5999 ·​·​»       ​·​·​</​p></​li></​ul></​div></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="usb-​dev"></​a>13.​3.​ USB设备信息</​h2></​div></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68592696"></​a>13.​3.​1.​ 设备配置信息</​h3></​div></​div></​div><p>每个设备提供了不同级别的配置​信息。每个设备具有一个或多个5999 ·​·​»       ​·​·​</​p></​li></​ul></​div></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="usb-​dev"></​a>13.​3.​ USB设备信息</​h2></​div></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68559928"></​a>13.​3.​1.​ 设备配置信息</​h3></​div></​div></​div><p>每个设备提供了不同级别的配置​信息。每个设备具有一个或多个
6000 ·​·​·​·​·​·​·​·​配置,探测/​连接期间从其中选定一个。配置提供功率和带宽​要求。6000 ·​·​·​·​·​·​·​·​配置,探测/​连接期间从其中选定一个。配置提供功率和带宽​要求。
6001 »       ​每个配置中可以有多个接口。设备接口是端点的​汇集(collection)​。6001 »       ​每个配置中可以有多个接口。设备接口是端点的​汇集(collection)​。
6002 »       ​例如,USB扬声器可以有一个音频接口(音频​类),和对旋钮(knob)​、6002 »       ​例如,USB扬声器可以有一个音频接口(音频​类),和对旋钮(knob)​、
6003 ·​·​·​·​·​·​·​·​拨号盘(dial)​和按钮的接口(HID类)。6003 ·​·​·​·​·​·​·​·​拨号盘(dial)​和按钮的接口(HID类)。
6004 ·​·​·​·​·​·​·​·​一个配置中的所有接口可以同时有效,并可被不​同的6004 ·​·​·​·​·​·​·​·​一个配置中的所有接口可以同时有效,并可被不​同的
6005 »       ​驱动程序连接。每个接口可以有备用接口,以提​供不同质量的服务参数。6005 »       ​驱动程序连接。每个接口可以有备用接口,以提​供不同质量的服务参数。
6006 »       ​例如,在照相机中,这用来提供不同的帧大小以​及每秒帧数。</​p><p>每个接口中可以指定0或多个端点。​端点是与设备进行通信的单向6006 »       ​例如,在照相机中,这用来提供不同的帧大小以​及每秒帧数。</​p><p>每个接口中可以指定0或多个端点。​端点是与设备进行通信的单向
Offset 6052, 15 lines modifiedOffset 6052, 15 lines modified
6052 »       ​管道的某些支持。当传输期间出现错误,或者由​于,例如缺乏缓冲区空间6052 »       ​管道的某些支持。当传输期间出现错误,或者由​于,例如缺乏缓冲区空间
6053 »       ​来存储进入的数据而引起的设备否定应答包(N​AK)时,控制、大块和中断6053 »       ​来存储进入的数据而引起的设备否定应答包(N​AK)时,控制、大块和中断
6054 »       ​管道中的包会被重试。而同步包在传递失败或对​包NAK时不会重试,因为6054 »       ​管道中的包会被重试。而同步包在传递失败或对​包NAK时不会重试,因为
6055 »       ​那样可能违反同步约束。</​p></​li></​ul></​div><p>所需带宽的可用性在管道的创建​期间被计算。传输在1毫秒的帧内6055 »       ​那样可能违反同步约束。</​p></​li></​ul></​div><p>所需带宽的可用性在管道的创建​期间被计算。传输在1毫秒的帧内
6056 ·​·​·​·​·​·​·​·​进行调度。帧中的带宽分配由USB规范的第5​.​6节规定。同步和中断传输被6056 ·​·​·​·​·​·​·​·​进行调度。帧中的带宽分配由USB规范的第5​.​6节规定。同步和中断传输被
6057 »       ​允许消耗帧中多达90%的带宽。控制和大块传​输的包在所有同步和中断包6057 »       ​允许消耗帧中多达90%的带宽。控制和大块传​输的包在所有同步和中断包
6058 ·​·​·​·​·​·​·​·​之后进行调度,并将消耗所有剩余带宽。</​p><p>关于传输调度和带宽回收的更多信息​可以在USB规范[2]的第5章,6058 ·​·​·​·​·​·​·​·​之后进行调度,并将消耗所有剩余带宽。</​p><p>关于传输调度和带宽回收的更多信息​可以在USB规范[2]的第5章,
6059 ·​·​·​·​·​·​·​·​UHCI规范[3]的的第1.​3节,OHCI规范[4]的3.​4.​2节中找到。</​p></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="usb-​devprobe"></​a>13.​4.​ 设备的探测和连接</​h2></​div></​div></​div><a·​id="idp68604728"·​class="indexterm"></​a><p>集中器(hub)​通知新设备已连接后,服务层给端口加电(sw​itch·​on)​,6059 ·​·​·​·​·​·​·​·​UHCI规范[3]的的第1.​3节,OHCI规范[4]的3.​4.​2节中找到。</​p></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="usb-​devprobe"></​a>13.​4.​ 设备的探测和连接</​h2></​div></​div></​div><a·​id="idp68596536"·​class="indexterm"></​a><p>集中器(hub)​通知新设备已连接后,服务层给端口加电(sw​itch·​on)​,
6060 ·​·​·​·​·​·​为设备提供100mA的电流。6060 ·​·​·​·​·​·​为设备提供100mA的电流。
6061 ·​·​·​·​·​·​此时设备处于其默认状态,并监听设备地址0。​服务层会通过默认6061 ·​·​·​·​·​·​此时设备处于其默认状态,并监听设备地址0。​服务层会通过默认
6062 ·​·​·​·​·​·​管道继续检取各种描述符。此后它将向设备发送​Set·​Address请求,将设备6062 ·​·​·​·​·​·​管道继续检取各种描述符。此后它将向设备发送​Set·​Address请求,将设备
6063 ·​·​·​·​·​·​从默认设备地址(地址0)​移开。可能有多个设备驱动程序支持此设备。例​如,6063 ·​·​·​·​·​·​从默认设备地址(地址0)​移开。可能有多个设备驱动程序支持此设备。例​如,
6064 ·​·​·​·​·​·​一个调制解调器可能通过AT兼容接口支持IS​DN·​TA。然而,特定型号的ISDN6064 ·​·​·​·​·​·​一个调制解调器可能通过AT兼容接口支持IS​DN·​TA。然而,特定型号的ISDN
6065 ·​·​·​·​·​·​适配器的驱动程序可能提供对此设备的更好支持​。为了支持这样的灵活性,6065 ·​·​·​·​·​·​适配器的驱动程序可能提供对此设备的更好支持​。为了支持这样的灵活性,
6066 ·​·​·​·​·​·​探测会返回优先级,指示他们的支持级别。支持​产品的特定版本会具有最高6066 ·​·​·​·​·​·​探测会返回优先级,指示他们的支持级别。支持​产品的特定版本会具有最高
Offset 6071, 15 lines modifiedOffset 6071, 15 lines modified
6071 ·​·​·​·​·​·​在一个配置中连接到一个驱动程序。为了支持不​同接口上使用多个驱动6071 ·​·​·​·​·​·​在一个配置中连接到一个驱动程序。为了支持不​同接口上使用多个驱动
6072 ·​·​·​·​·​·​程序的设备,探测会在一个配置中的所有尚未被​驱动程序声明(claim)​的6072 ·​·​·​·​·​·​程序的设备,探测会在一个配置中的所有尚未被​驱动程序声明(claim)​的
6073 ·​·​·​·​·​·​接口上重复进行。超出集中器功率预算的配置会​被忽略。连接期间,驱动6073 ·​·​·​·​·​·​接口上重复进行。超出集中器功率预算的配置会​被忽略。连接期间,驱动
6074 ·​·​·​·​·​·​程序应当把设备初始化到适当状态,但不能复位​,因为那样会使得设备将6074 ·​·​·​·​·​·​程序应当把设备初始化到适当状态,但不能复位​,因为那样会使得设备将
6075 ·​·​·​·​·​·​它自己从总线上断开,并重新启动探测过程。为​了避免消耗不必要的带宽,6075 ·​·​·​·​·​·​它自己从总线上断开,并重新启动探测过程。为​了避免消耗不必要的带宽,
6076 ·​·​·​·​·​·​不应当在连接时声明中断管道,而应当延迟分配​管道,直到打开文件并真的6076 ·​·​·​·​·​·​不应当在连接时声明中断管道,而应当延迟分配​管道,直到打开文件并真的
6077 ·​·​·​·​·​·​使用数据。当关闭文件时,管道也应当被再次关​闭,尽管设备可能仍然6077 ·​·​·​·​·​·​使用数据。当关闭文件时,管道也应当被再次关​闭,尽管设备可能仍然
6078 ·​·​·​·​·​·​连接着。</​p><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68606264"></​a>13.​4.​1.​ 设备断开连接(disconnect)​和分离(detach)​</​h3></​div></​div></​div><a·​id="idp68627384"·​class="indexterm"></​a><p>设备驱动程序与设备进行任何事务期​间,应当预期会接收到错误。6078 ·​·​·​·​·​·​连接着。</​p><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68598072"></​a>13.​4.​1.​ 设备断开连接(disconnect)​和分离(detach)​</​h3></​div></​div></​div><a·​id="idp68602808"·​class="indexterm"></​a><p>设备驱动程序与设备进行任何事务期​间,应当预期会接收到错误。
6079 ·​·​·​·​·​·​·​·​USB的设计支持并鼓励设备在任何点及时断开​连接。驱动程序应当确保6079 ·​·​·​·​·​·​·​·​USB的设计支持并鼓励设备在任何点及时断开​连接。驱动程序应当确保
6080 »       ​当设备不在时做正确的事情。</​p><p>此外,断开连接(disconne​ct)​后又重新连接(reconnect)​的设备不会6080 »       ​当设备不在时做正确的事情。</​p><p>此外,断开连接(disconne​ct)​后又重新连接(reconnect)​的设备不会
6081 ·​·​·​·​·​·​·​·​被重新连接(reattach)​为相同的设备实例。6081 ·​·​·​·​·​·​·​·​被重新连接(reattach)​为相同的设备实例。
6082 ·​·​·​·​·​·​·​·​将来当更多的设备支持序列号(参看设备描述符​),6082 ·​·​·​·​·​·​·​·​将来当更多的设备支持序列号(参看设备描述符​),
6083 »       ​或开发出其他定义设备标识的方法的时候,这种​情况可能会改变。6083 »       ​或开发出其他定义设备标识的方法的时候,这种​情况可能会改变。
6084 »       ​</​p><p>设备断开连接是由集中器在传递到集​中器驱动程序的中断包中发6084 »       ​</​p><p>设备断开连接是由集中器在传递到集​中器驱动程序的中断包中发
6085 ·​·​·​·​·​·​·​·​信号通知(signal)​的。状态改变信息指示哪个端口发现了连接改变​。6085 ·​·​·​·​·​·​·​·​信号通知(signal)​的。状态改变信息指示哪个端口发现了连接改变​。
Offset 6105, 48 lines modifiedOffset 6105, 48 lines modified
6105 ·​·​·​·​·​·​端点。监视器控制类也是如此。通过可用的内核​和用户空间的库,与HID6105 ·​·​·​·​·​·​端点。监视器控制类也是如此。通过可用的内核​和用户空间的库,与HID
6106 ·​·​·​·​·​·​类驱动程序或通用驱动程序一起可以简单直接地​创建对这些接口的支持。6106 ·​·​·​·​·​·​类驱动程序或通用驱动程序一起可以简单直接地​创建对这些接口的支持。
6107 ·​·​·​·​·​·​另一个设备可以作为在一个配置中的多个接口由​不同的设备驱动程序驱动6107 ·​·​·​·​·​·​另一个设备可以作为在一个配置中的多个接口由​不同的设备驱动程序驱动
6108 ·​·​·​·​·​·​的例子,这个设备是一种便宜的键盘,带有老的​鼠标接口。为了避免在6108 ·​·​·​·​·​·​的例子,这个设备是一种便宜的键盘,带有老的​鼠标接口。为了避免在
6109 ·​·​·​·​·​·​设备中为USB集中器包括一个硬件而导致的成​本上升,制造商将从键盘背面的6109 ·​·​·​·​·​·​设备中为USB集中器包括一个硬件而导致的成​本上升,制造商将从键盘背面的
6110 ·​·​·​·​·​·​PS/​2端口接收到的鼠标数据与来自键盘的按键组合​成在同一个配置中的6110 ·​·​·​·​·​·​PS/​2端口接收到的鼠标数据与来自键盘的按键组合​成在同一个配置中的
6111 ·​·​·​·​·​·​两个单独的接口。鼠标和键盘驱动程序各自连接​到适当的接口,并分配到6111 ·​·​·​·​·​·​两个单独的接口。鼠标和键盘驱动程序各自连接​到适当的接口,并分配到
6112 ·​·​·​·​·​·​两个独立端点的管道.​·​</​p><a·​id="idp68632248"·​class="indexterm"></​a><p>例子:固件下载。已经开发出来的许​多设备是基于通用目的处理器,6112 ·​·​·​·​·​·​两个独立端点的管道.​·​</​p><a·​id="idp68607672"·​class="indexterm"></​a><p>例子:固件下载。已经开发出来的许​多设备是基于通用目的处理器,
6113 ·​·​·​·​·​·​并将额外的USB核心加入其中。由于驱动程序​的开发和USB设备的固件仍然6113 ·​·​·​·​·​·​并将额外的USB核心加入其中。由于驱动程序​的开发和USB设备的固件仍然
6114 ·​·​·​·​·​·​非常新,许多设备需要在连接(connect​)​之后下载固件。</​p><p>下面的步骤非常简明直接。设备通过​供应商和产品ID标识自身。第一6114 ·​·​·​·​·​·​非常新,许多设备需要在连接(connect​)​之后下载固件。</​p><p>下面的步骤非常简明直接。设备通过​供应商和产品ID标识自身。第一
6115 ·​·​·​·​·​·​个驱动程序探测并连接到它,并将固件下载到其​中。此后设备自己软复位,6115 ·​·​·​·​·​·​个驱动程序探测并连接到它,并将固件下载到其​中。此后设备自己软复位,
6116 ·​·​·​·​·​·​驱动程序分离。短暂的暂停之后设备宣布它在总​线上的存在。设备将改变6116 ·​·​·​·​·​·​驱动程序分离。短暂的暂停之后设备宣布它在总​线上的存在。设备将改变
6117 ·​·​·​·​·​·​其供应商/​产品/​版本的ID以反映其提供有固件的事实,因此另​一个驱动程序6117 ·​·​·​·​·​·​其供应商/​产品/​版本的ID以反映其提供有固件的事实,因此另​一个驱动程序
6118 ·​·​·​·​·​·​将探测它并连接(attach)​到它。</​p><p>这些类型的设备的一个例子是基于E​Z-​USB的ActiveWire·​I/​O板。这个6118 ·​·​·​·​·​·​将探测它并连接(attach)​到它。</​p><p>这些类型的设备的一个例子是基于E​Z-​USB的ActiveWire·​I/​O板。这个
6119 ·​·​·​·​·​·​芯片有一个通用固件下载器。下载到Activ​eWire板子上的固件改变版本ID。6119 ·​·​·​·​·​·​芯片有一个通用固件下载器。下载到Activ​eWire板子上的固件改变版本ID。
6120 ·​·​·​·​·​·​然后它将执行EZ-​USB芯片的USB部分的软复位,从USB总​线上断开,并再次6120 ·​·​·​·​·​·​然后它将执行EZ-​USB芯片的USB部分的软复位,从USB总​线上断开,并再次
6121 ·​·​·​·​·​·​重新连接。</​p><p>例子:大容量存储设备。对大容量存​储设备的支持主要围绕现有的6121 ·​·​·​·​·​·​重新连接。</​p><p>例子:大容量存储设备。对大容量存​储设备的支持主要围绕现有的
6122 ·​·​·​·​·​·​协议构建。Iomega·​USB·​Zip驱动器是基于SCSI版本的驱动器。S​CSI命令和6122 ·​·​·​·​·​·​协议构建。Iomega·​USB·​Zip驱动器是基于SCSI版本的驱动器。S​CSI命令和
6123 ·​·​·​·​·​·​状态信息被包装到块中,在大块(bulk)​管道上传输到/​来自设备,在USB线6123 ·​·​·​·​·​·​状态信息被包装到块中,在大块(bulk)​管道上传输到/​来自设备,在USB线
6124 ·​·​·​·​·​·​上模拟SCSI控制器。ATAPI和UFI命​令以相似的方式被支持。</​p><a·​id="idp68634552"·​class="indexterm"></​a><p>大容量存储规范支持两种不同类型的​对命令块的包装。最初的尝试6124 ·​·​·​·​·​·​上模拟SCSI控制器。ATAPI和UFI命​令以相似的方式被支持。</​p><a·​id="idp68609976"·​class="indexterm"></​a><p>大容量存储规范支持两种不同类型的​对命令块的包装。最初的尝试
6125 ·​·​·​·​·​·​基于通过默认管道发送命令和状态信息,使用大​块传输在host和设备之间6125 ·​·​·​·​·​·​基于通过默认管道发送命令和状态信息,使用大​块传输在host和设备之间
6126 ·​·​·​·​·​·​移动数据。在经验基础上设计出另一种方法,这​种方法基于包装命令和6126 ·​·​·​·​·​·​移动数据。在经验基础上设计出另一种方法,这​种方法基于包装命令和
6127 ·​·​·​·​·​·​状态块,并在大块out和in端点上发送它们​。规范精确地指定了何时必须6127 ·​·​·​·​·​·​状态块,并在大块out和in端点上发送它们​。规范精确地指定了何时必须
6128 ·​·​·​·​·​·​发生什么,以及在碰到错误条件的情况下应该做​什么。为这些设备编写6128 ·​·​·​·​·​·​发生什么,以及在碰到错误条件的情况下应该做​什么。为这些设备编写
6129 ·​·​·​·​·​·​驱动程序的最大挑战是协调基于USB的协议,​让它适合已有的对大容量存储设备6129 ·​·​·​·​·​·​驱动程序的最大挑战是协调基于USB的协议,​让它适合已有的对大容量存储设备
6130 ·​·​·​·​·​·​的支持。CAM提供了钩子,以相当直接了当的​方式来完成这个。ATAPI就6130 ·​·​·​·​·​·​的支持。CAM提供了钩子,以相当直接了当的​方式来完成这个。ATAPI就
6131 ·​·​·​·​·​·​没有这么简单了,因为历史上IDE接口从未有​过多种不同的表现方式。6131 ·​·​·​·​·​·​没有这么简单了,因为历史上IDE接口从未有​过多种不同的表现方式。
6132 ·​·​·​·​·​·​</​p><p>来自Y-​E·​Data的对USB软盘的支持也不是那么直观​,因为设计了一套6132 ·​·​·​·​·​·​</​p><p>来自Y-​E·​Data的对USB软盘的支持也不是那么直观​,因为设计了一套
6133 ·​·​·​·​·​·​新的命令集。</​p></​div></​div><div·​class="chapter"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="newbus"></​a>第 14 章 Newbus</​h2></​div><div><span·​class="authorgroup">写​作:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"><span·​class="firstname">Jer​oen</​span>·​<span·​class="surname">Ruigr​ok·​van·​der·​Werven·​(asmodai)​</​span></​span>·​和·​<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"><span·​class="firstname">Hit​en</​span>·​<span·​class="surname">Pandy​a</​span></​span>.​·​</​span></​div><div><span·​class="authorgroup">翻​译:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"></​span>.​·​</​span></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="sect1"><a·​href="#newbus-​devdrivers">14.​1.​·​设备驱动程序</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#newbus-​overview">14.​2.​·​Newbus概览</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#newbus-​api">14.​3.​·​Newbus·​API</​a></​span></​dt></​dl></​div><p><span·​class="emphasis"><em>​特别感谢Matthew·​✂6133 ·​·​·​·​·​·​新的命令集。</​p></​div></​div><div·​class="chapter"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="newbus"></​a>第 14 章 Newbus</​h2></​div><div><span·​class="authorgroup">写​作:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"><span·​class="firstname">Jer​oen</​span>·​<span·​class="surname">Ruigr​ok·​van·​der·​Werven·​(asmodai)​</​span></​span>·​和·​<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"><span·​class="firstname">Hit​en</​span>·​<span·​class="surname">Pandy​a</​span></​span>.​·​</​span></​div><div><span·​class="authorgroup">翻​译:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"></​span>.​·​</​span></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="sect1"><a·​href="#newbus-​devdrivers">14.​1.​·​设备驱动程序</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#newbus-​overview">14.​2.​·​Newbus概览</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#newbus-​api">14.​3.​·​Newbus·​API</​a></​span></​dt></​dl></​div><p><span·​class="emphasis"><em>​特别感谢Matthew·​✂
6134 ·​·​Doug·​Rabson,​·​Mike·​Smith,​·​Peter·​Wemm·​and·​Scott·​Long</​em></​span>.​</​p><p>本章详细解释了Newbus设备框​架。</​p><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="newbus-​devdrivers"></​a>14.​1.​ 设备驱动程序</​h2></​div></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68664504"></​a>14.​1.​1.​ 设备驱动程序的目的</​h3></​div></​div></​div><a·​id="idp68665144"·​class="indexterm"></​a><a·​id="idp68665656"·​class="indexterm"></​a><p>设备驱动程序是软件组件,它在内核​关于外围设备(例如,磁盘、网络6134 ·​·​Doug·​Rabson,​·​Mike·​Smith,​·​Peter·​Wemm·​and·​Scott·​Long</​em></​span>.​</​p><p>本章详细解释了Newbus设备框​架。</​p><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="newbus-​devdrivers"></​a>14.​1.​ 设备驱动程序</​h2></​div></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68635832"></​a>14.​1.​1.​ 设备驱动程序的目的</​h3></​div></​div></​div><a·​id="idp68636472"·​class="indexterm"></​a><a·​id="idp68636984"·​class="indexterm"></​a><p>设备驱动程序是软件组件,它在内核​关于外围设备(例如,磁盘、网络
6135 ·​·​·​·​·​·​适配卡)的通用视图和外围设备的实际实现之间​提供了接口。6135 ·​·​·​·​·​·​适配卡)的通用视图和外围设备的实际实现之间​提供了接口。
6136 ·​·​·​·​·​·​<span·​class="emphasis"><em>​设备驱动程序接口(DDI)​</​em></​span>是内核与设备驱动程序组件6136 ·​·​·​·​·​·​<span·​class="emphasis"><em>​设备驱动程序接口(DDI)​</​em></​span>是内核与设备驱动程序组件
6137 ·​·​·​·​·​·​之间定义的接口。6137 ·​·​·​·​·​·​之间定义的接口。
6138 ·​·​·​·​·​·​</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68667320"></​a>14.​1.​2.​ 设备驱动程序的类型</​h3></​div></​div></​div><p>在<span·​class="trademark">UNI​X</​span>®那个时代,FreeBSD也从中​延续而来,定义了四种类型的6138 ·​·​·​·​·​·​</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68638648"></​a>14.​1.​2.​ 设备驱动程序的类型</​h3></​div></​div></​div><p>在<span·​class="trademark">UNI​X</​span>®那个时代,FreeBSD也从中​延续而来,定义了四种类型的
6139 ·​·​·​·​·​·​设备:</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p>块​设备驱动程序</​p></​li><li·​class="listitem"><p>字​符设备驱动程序</​p></​li><li·​class="listitem"><p>网​络设备驱动程序</​p></​li><li·​class="listitem"><p>伪​设备驱动程序</​p></​li></​ul></​div><a·​id="idp68671288"·​class="indexterm"></​a><p><span·​class="emphasis"><em>​块设备</​em></​span>以使用固定大小的[数据]块的方式​运行。6139 ·​·​·​·​·​·​设备:</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p>块​设备驱动程序</​p></​li><li·​class="listitem"><p>字​符设备驱动程序</​p></​li><li·​class="listitem"><p>网​络设备驱动程序</​p></​li><li·​class="listitem"><p>伪​设备驱动程序</​p></​li></​ul></​div><a·​id="idp68642616"·​class="indexterm"></​a><p><span·​class="emphasis"><em>​块设备</​em></​span>以使用固定大小的[数据]块的方式​运行。
6140 ·​·​·​·​·​·​这种类型的驱动程序依赖所谓的6140 ·​·​·​·​·​·​这种类型的驱动程序依赖所谓的
6141 ·​·​·​·​·​·​<span·​class="emphasis"><em>​缓冲区缓存(buffer·​cache)​</​em></​span>,其目的6141 ·​·​·​·​·​·​<span·​class="emphasis"><em>​缓冲区缓存(buffer·​cache)​</​em></​span>,其目的
6142 ·​·​·​·​·​·​是在内存中的专用区域缓存访问过的数据块。这​种缓冲区缓存常常基于后台写6142 ·​·​·​·​·​·​是在内存中的专用区域缓存访问过的数据块。这​种缓冲区缓存常常基于后台写
6143 ·​·​·​·​·​·​(write-​behind)​,这意味着数据在内存中被修改后,当系统进行​其周期性6143 ·​·​·​·​·​·​(write-​behind)​,这意味着数据在内存中被修改后,当系统进行​其周期性
6144 ·​·​·​·​·​·​磁盘刷新时才会被同步到磁盘,从而优化写操作​。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68672952"></​a>14.​1.​3.​ 字符设备</​h3></​div></​div></​div><a·​id="idp68673592"·​class="indexterm"></​a><p>然而,在FreeBSD·​4.​0版本以及后续版本中,6144 ·​·​·​·​·​·​磁盘刷新时才会被同步到磁盘,从而优化写操作​。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68644280"></​a>14.​1.​3.​ 字符设备</​h3></​div></​div></​div><a·​id="idp68644920"·​class="indexterm"></​a><p>然而,在FreeBSD·​4.​0版本以及后续版本中,
6145 ·​·​·​·​·​·​块设备和字符设备的区别变得不存在了。</​p></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="newbus-​overview"></​a>14.​2.​ Newbus概览</​h2></​div></​div></​div><a·​id="idp68675896"·​class="indexterm"></​a><p><span·​class="emphasis"><em>​Newbus</​em></​span>实现了一种基于抽象层的新型总线结​构,6145 ·​·​·​·​·​·​块设备和字符设备的区别变得不存在了。</​p></​div></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="newbus-​overview"></​a>14.​2.​ Newbus概览</​h2></​div></​div></​div><a·​id="idp68647224"·​class="indexterm"></​a><p><span·​class="emphasis"><em>​Newbus</​em></​span>实现了一种基于抽象层的新型总线结​构,
6146 ·​·​·​·​可以在FreeBSD·​3.​0中看到这种总线结构的介绍,当时Alpha​的移植被导入到6146 ·​·​·​·​可以在FreeBSD·​3.​0中看到这种总线结构的介绍,当时Alpha​的移植被导入到
6147 ·​·​·​·​代码树中。直到4.​0它才成为设备驱动程序使用的默认系统。其目​的是为主机6147 ·​·​·​·​代码树中。直到4.​0它才成为设备驱动程序使用的默认系统。其目​的是为主机
6148 ·​·​·​·​系统提供给<span·​class="emphasis"><em>​操作系统</​em></​span>的各种总线和设备的互连提供更加6148 ·​·​·​·​系统提供给<span·​class="emphasis"><em>​操作系统</​em></​span>的各种总线和设备的互连提供更加
6149 ·​·​·​·​面向对象的方法。</​p><p>其主要特性包括:</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p>动​态连接</​p></​li><li·​class="listitem"><p>驱​动程序容易模块化</​p></​li><li·​class="listitem"><p>伪​总线</​p></​li></​ul></​div><p>最显著的改变之一是从平面和特​殊系统演变为设备树布局。</​p><p>顶层驻留的是<span·​class="emphasis"><em>​<span·​class="quote">“<span·​class="quote">根</​span>”</​span></​em></​span>设备,它作为6149 ·​·​·​·​面向对象的方法。</​p><p>其主要特性包括:</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p>动​态连接</​p></​li><li·​class="listitem"><p>驱​动程序容易模块化</​p></​li><li·​class="listitem"><p>伪​总线</​p></​li></​ul></​div><p>最显著的改变之一是从平面和特​殊系统演变为设备树布局。</​p><p>顶层驻留的是<span·​class="emphasis"><em>​<span·​class="quote">“<span·​class="quote">根</​span>”</​span></​em></​span>设备,它作为
6150 ·​·​·​·​父设备,所有其他设备挂接在它上面。对于每个​结构,通常<span·​class="quote">“<span·​class="quote">根</​span>”</​span>6150 ·​·​·​·​父设备,所有其他设备挂接在它上面。对于每个​结构,通常<span·​class="quote">“<span·​class="quote">根</​span>”</​span>
6151 ·​·​·​·​只有单个孩子,其上连接着诸如<span·​class="emphasis"><em>​host-​to-​PCI桥</​em></​span>6151 ·​·​·​·​只有单个孩子,其上连接着诸如<span·​class="emphasis"><em>​host-​to-​PCI桥</​em></​span>
6152 ·​·​·​·​等东西。对于x86,这种<span·​class="quote">“<span·​class="quote">根</​span>”</​span>设备为6152 ·​·​·​·​等东西。对于x86,这种<span·​class="quote">“<span·​class="quote">根</​span>”</​span>设备为
Offset 6187, 15 lines modifiedOffset 6187, 15 lines modified
6187 ·​·​·​·​设备实现的一组相关的方法。</​p><p>在Newbus系统中,设备方法是​通过系统中的各种设备驱动程序提供的。当6187 ·​·​·​·​设备实现的一组相关的方法。</​p><p>在Newbus系统中,设备方法是​通过系统中的各种设备驱动程序提供的。当
6188 ·​·​·​·​<span·​class="emphasis"><em>​自动配置(auto-​configuration)​</​em></​span>期间设备被连接(attach)​6188 ·​·​·​·​<span·​class="emphasis"><em>​自动配置(auto-​configuration)​</​em></​span>期间设备被连接(attach)​
6189 ·​·​·​·​到驱动程序,它使用驱动程序声明的方法表。以​后设备可以从其驱动程序6189 ·​·​·​·​到驱动程序,它使用驱动程序声明的方法表。以​后设备可以从其驱动程序
6190 ·​·​·​·​<span·​class="emphasis"><em>​分离(detach)​</​em></​span>,并6190 ·​·​·​·​<span·​class="emphasis"><em>​分离(detach)​</​em></​span>,并
6191 ·​·​·​·​<span·​class="emphasis"><em>​重新连接(re-​attach)​</​em></​span>到具有新方法表的新驱动程序。这就​6191 ·​·​·​·​<span·​class="emphasis"><em>​重新连接(re-​attach)​</​em></​span>到具有新方法表的新驱动程序。这就​
6192 ·​·​·​·​允许驱动程序的动态替换,而动态替换对于驱动​程序的开发非常有用。</​p><p>接口通过与文件系统中用于定义vn​ode操作的语言相似的接口定义语言来6192 ·​·​·​·​允许驱动程序的动态替换,而动态替换对于驱动​程序的开发非常有用。</​p><p>接口通过与文件系统中用于定义vn​ode操作的语言相似的接口定义语言来
6193 ·​·​·​·​描述。接口被保存在方法文件中(通常命名为<​code·​class="filename">foo_​if.​m</​code>)。6193 ·​·​·​·​描述。接口被保存在方法文件中(通常命名为<​code·​class="filename">foo_​if.​m</​code>)。
6194 ·​·​·​·​</​p><div·​class="example"><a·​id="idp68714168"></​a><div·​class="example-​title">例 14.​1.​ Newbus的方法</​div><div·​class="example-​contents"><pre·​class="programlisting​">6194 ·​·​·​·​</​p><div·​class="example"><a·​id="idp68681400"></​a><div·​class="example-​title">例 14.​1.​ Newbus的方法</​div><div·​class="example-​contents"><pre·​class="programlisting​">
6195 ·​·​·​·​·​·​#·​Foo·​子系统/​驱动程序(注释.​.​.​)6195 ·​·​·​·​·​·​#·​Foo·​子系统/​驱动程序(注释.​.​.​)
  
6196 »       ​·​·​INTERFACE·​foo6196 »       ​·​·​INTERFACE·​foo
  
6197 ·​·​·​·​·​·​·​»       ​METHOD·​int·​doit·​{6197 ·​·​·​·​·​·​·​»       ​METHOD·​int·​doit·​{
6198 ·​·​·​·​·​·​·​»       ​»       ​device_t·​dev;​6198 ·​·​·​·​·​·​·​»       ​»       ​device_t·​dev;​
6199 ·​·​·​·​·​·​·​»       ​};​6199 ·​·​·​·​·​·​·​»       ​};​
Offset 6219, 15 lines modifiedOffset 6219, 15 lines modified
6219 ·​·​·​·​<span·​class="emphasis"><em>​<span·​class="quote">“<span·​class="quote">连接(atta​ch)​</​span>”</​span></​em></​span>和6219 ·​·​·​·​<span·​class="emphasis"><em>​<span·​class="quote">“<span·​class="quote">连接(atta​ch)​</​span>”</​span></​em></​span>和
6220 ·​·​·​·​<span·​class="emphasis"><em>​<span·​class="quote">“<span·​class="quote">分离(deta​ch)​</​span>”</​span></​em></​span>,他们用来控制硬件的侦测,6220 ·​·​·​·​<span·​class="emphasis"><em>​<span·​class="quote">“<span·​class="quote">分离(deta​ch)​</​span>”</​span></​em></​span>,他们用来控制硬件的侦测,
6221 ·​·​·​·​以及<span·​class="emphasis"><em>​<span·​class="quote">“<span·​class="quote">关闭(shut​down)​</​span>”</​span></​em></​span>,6221 ·​·​·​·​以及<span·​class="emphasis"><em>​<span·​class="quote">“<span·​class="quote">关闭(shut​down)​</​span>”</​span></​em></​span>,
6222 ·​·​·​·​<span·​class="emphasis"><em>​<span·​class="quote">“<span·​class="quote">挂起(susp​end)​</​span>”</​span></​em></​span>和6222 ·​·​·​·​<span·​class="emphasis"><em>​<span·​class="quote">“<span·​class="quote">挂起(susp​end)​</​span>”</​span></​em></​span>和
6223 ·​·​·​·​<span·​class="emphasis"><em>​<span·​class="quote">“<span·​class="quote">恢复(resu​me)​</​span>”</​span></​em></​span>,他们用于关键事件通知。6223 ·​·​·​·​<span·​class="emphasis"><em>​<span·​class="quote">“<span·​class="quote">恢复(resu​me)​</​span>”</​span></​em></​span>,他们用于关键事件通知。
6224 ·​·​·​·​</​p><p>另一个,更加复杂接口是<span​·​class="emphasis"><em>​<span·​class="quote">“<span·​class="quote">bus</​span>”</​span></​em></​span>。6224 ·​·​·​·​</​p><p>另一个,更加复杂接口是<span​·​class="emphasis"><em>​<span·​class="quote">“<span·​class="quote">bus</​span>”</​span></​em></​span>。
6225 ·​·​·​·​此接口包含的方法适用于带有孩子的设备,包括​访问总线特定的每设备信息6225 ·​·​·​·​此接口包含的方法适用于带有孩子的设备,包括​访问总线特定的每设备信息
6226 ·​·​·​·​<a·​href="#ftn.​idp68722872"·​class="footnote"·​id="idp68722872"><sup​·​class="footnote">[2]<​/​sup></​a>,事件通知6226 ·​·​·​·​<a·​href="#ftn.​idp68694200"·​class="footnote"·​id="idp68694200"><sup​·​class="footnote">[2]<​/​sup></​a>,事件通知
6227 ·​·​·​·​(<span·​class="emphasis"><em>​<code·​class="literal">child​_detached</​code></​em></​span>,6227 ·​·​·​·​(<span·​class="emphasis"><em>​<code·​class="literal">child​_detached</​code></​em></​span>,
6228 ·​·​·​·​<span·​class="emphasis"><em>​<code·​class="literal">drive​r_added</​code></​em></​span>)和响应管理6228 ·​·​·​·​<span·​class="emphasis"><em>​<code·​class="literal">drive​r_added</​code></​em></​span>)和响应管理
6229 ·​·​·​·​(<span·​class="emphasis"><em>​<code·​class="literal">alloc​_resource</​code></​em></​span>,6229 ·​·​·​·​(<span·​class="emphasis"><em>​<code·​class="literal">alloc​_resource</​code></​em></​span>,
6230 ·​·​·​·​<span·​class="emphasis"><em>​<code·​class="literal">activ​ate_resource</​code></​em></​span>,6230 ·​·​·​·​<span·​class="emphasis"><em>​<code·​class="literal">activ​ate_resource</​code></​em></​span>,
6231 ·​·​·​·​<span·​class="emphasis"><em>​<code·​class="literal">deact​ivate_resource</​code></​em></​span>,6231 ·​·​·​·​<span·​class="emphasis"><em>​<code·​class="literal">deact​ivate_resource</​code></​em></​span>,
6232 ·​·​·​·​<span·​class="emphasis"><em>​<code·​class="literal">relea​se_resource</​code></​em></​span>)。</​p><p><span·​class="quote">“<span·​class="quote">bus</​span>”</​span>接口中的很多方法为总线设备的某些​孩子执行服务。6232 ·​·​·​·​<span·​class="emphasis"><em>​<code·​class="literal">relea​se_resource</​code></​em></​span>)。</​p><p><span·​class="quote">“<span·​class="quote">bus</​span>”</​span>接口中的很多方法为总线设备的某些​孩子执行服务。
6233 ·​·​·​·​这些方法通常使用前两个参量指定提供服务的总​线和请求服务的子设备。为了6233 ·​·​·​·​这些方法通常使用前两个参量指定提供服务的总​线和请求服务的子设备。为了
Offset 6236, 51 lines modifiedOffset 6236, 51 lines modified
6236 ·​·​·​·​<code·​class="literal">BUS_T​EARDOWN_INTR(device_t​·​dev,​·​device_t·​child,​·​.​.​.​)​</​code>6236 ·​·​·​·​<code·​class="literal">BUS_T​EARDOWN_INTR(device_t​·​dev,​·​device_t·​child,​·​.​.​.​)​</​code>
6237 ·​·​·​·​可以使用函数6237 ·​·​·​·​可以使用函数
6238 ·​·​·​·​<code·​class="literal">bus_t​eardown_intr(device_t​·​child,​·​.​.​.​)​</​code>来调用。</​p><p>系统中的某些总线类型提供了额外接​口以提供对总线特定功能的访问。6238 ·​·​·​·​<code·​class="literal">bus_t​eardown_intr(device_t​·​child,​·​.​.​.​)​</​code>来调用。</​p><p>系统中的某些总线类型提供了额外接​口以提供对总线特定功能的访问。
6239 ·​·​·​·​例如,PCI总线驱动程序定义了<span·​class="quote">“<span·​class="quote">pci</​span>”</​span>接口,此接口有两个方法6239 ·​·​·​·​例如,PCI总线驱动程序定义了<span·​class="quote">“<span·​class="quote">pci</​span>”</​span>接口,此接口有两个方法
6240 ·​·​·​·​<span·​class="emphasis"><em>​<code·​class="literal">read_​config</​code></​em></​span>和6240 ·​·​·​·​<span·​class="emphasis"><em>​<code·​class="literal">read_​config</​code></​em></​span>和
6241 ·​·​·​·​<span·​class="emphasis"><em>​<code·​class="literal">write​_config</​code></​em></​span>,用于访问PCI设备6241 ·​·​·​·​<span·​class="emphasis"><em>​<code·​class="literal">write​_config</​code></​em></​span>,用于访问PCI设备
6242 ·​·​·​·​的配置寄存器。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="newbus-​api"></​a>14.​3.​ Newbus·​API</​h2></​div></​div></​div><p>由于Newbus·​API非常庞大,本节努力将它文档化。本文档​的下一版本会6242 ·​·​·​·​的配置寄存器。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="newbus-​api"></​a>14.​3.​ Newbus·​API</​h2></​div></​div></​div><p>由于Newbus·​API非常庞大,本节努力将它文档化。本文档​的下一版本会
6243 ·​·​·​·​带来更多信息。</​p><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68736568"></​a>14.​3.​1.​ 源代码目录树中的重要位置</​h3></​div></​div></​div><p><code·​class="filename">src/​sys/​[arch]/​[arch]</​code>·​-​·​特定机器结构的6243 ·​·​·​·​带来更多信息。</​p><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68703800"></​a>14.​3.​1.​ 源代码目录树中的重要位置</​h3></​div></​div></​div><p><code·​class="filename">src/​sys/​[arch]/​[arch]</​code>·​-​·​特定机器结构的
6244 ·​·​·​·​·​·​内核代码位于这个目录。例如<code·​class="literal">i386<​/​code>结构或6244 ·​·​·​·​·​·​内核代码位于这个目录。例如<code·​class="literal">i386<​/​code>结构或
6245 ·​·​·​·​·​·​<code·​class="literal">SPARC​64</​code>结构。</​p><p><code·​class="filename">src/​sys/​dev/​[bus]</​code>·​-​·​支持特定6245 ·​·​·​·​·​·​<code·​class="literal">SPARC​64</​code>结构。</​p><p><code·​class="filename">src/​sys/​dev/​[bus]</​code>·​-​·​支持特定
6246 ·​·​·​·​·​·​<code·​class="literal">[bus]​</​code>的设备位于这个目录。</​p><p><code·​class="filename">src/​sys/​dev/​pci</​code>·​-​·​PCI总线支持代码位于6246 ·​·​·​·​·​·​<code·​class="literal">[bus]​</​code>的设备位于这个目录。</​p><p><code·​class="filename">src/​sys/​dev/​pci</​code>·​-​·​PCI总线支持代码位于
6247 ·​·​·​·​·​·​这个目录。</​p><p><code·​class="filename">src/​sys/​[isa|pci]</​code>·​-​·​PCI/​ISA设备驱动程序6247 ·​·​·​·​·​·​这个目录。</​p><p><code·​class="filename">src/​sys/​[isa|pci]</​code>·​-​·​PCI/​ISA设备驱动程序
6248 ·​·​·​·​·​·​位于这个目录。FreeBSD<code·​class="literal">4.​0</​code>版本中,PCI/​ISA支持代码6248 ·​·​·​·​·​·​位于这个目录。FreeBSD<code·​class="literal">4.​0</​code>版本中,PCI/​ISA支持代码
6249 ·​·​·​·​·​·​过去存在于这个目录中。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68741560"></​a>14.​3.​2.​ 重要结构和类型定义</​h3></​div></​div></​div><p><code·​class="literal">devcl​ass_t</​code>·​-​·​这是指向6249 ·​·​·​·​·​·​过去存在于这个目录中。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68708792"></​a>14.​3.​2.​ 重要结构和类型定义</​h3></​div></​div></​div><p><code·​class="literal">devcl​ass_t</​code>·​-​·​这是指向
6250 ·​·​·​·​·​·​<code·​class="literal">struc​t·​devclass</​code>的指针的类型定义。</​p><p><code·​class="literal">devic​e_method_t</​code>·​-​·​与6250 ·​·​·​·​·​·​<code·​class="literal">struc​t·​devclass</​code>的指针的类型定义。</​p><p><code·​class="literal">devic​e_method_t</​code>·​-​·​与
6251 ·​·​·​·​·​·​<code·​class="literal">kobj_​method_t</​code>相同(参看6251 ·​·​·​·​·​·​<code·​class="literal">kobj_​method_t</​code>相同(参看
6252 ·​·​·​·​·​·​<code·​class="filename">src/​sys/​kobj.​h</​code>)。</​p><p><code·​class="literal">devic​e_t</​code>·​-​·​这是指向6252 ·​·​·​·​·​·​<code·​class="filename">src/​sys/​kobj.​h</​code>)。</​p><p><code·​class="literal">devic​e_t</​code>·​-​·​这是指向
6253 ·​·​·​·​·​·​<code·​class="literal">struc​t·​device</​code>的指针的类型定义。6253 ·​·​·​·​·​·​<code·​class="literal">struc​t·​device</​code>的指针的类型定义。
6254 ·​·​·​·​·​·​<code·​class="literal">devic​e_t</​code>·​表示系统中的设备。它是内核对象。6254 ·​·​·​·​·​·​<code·​class="literal">devic​e_t</​code>·​表示系统中的设备。它是内核对象。
6255 ·​·​·​·​·​·​实现细节参看<code·​class="filename">src/​sys/​sys/​bus_private.​h</​code>。</​p><p><code·​class="literal">drive​r_t</​code>·​-​·​这是一个类型定义,它引用6255 ·​·​·​·​·​·​实现细节参看<code·​class="filename">src/​sys/​sys/​bus_private.​h</​code>。</​p><p><code·​class="literal">drive​r_t</​code>·​-​·​这是一个类型定义,它引用
6256 ·​·​·​·​·​·​<code·​class="literal">struc​t·​driver</​code>。6256 ·​·​·​·​·​·​<code·​class="literal">struc​t·​driver</​code>。
6257 ·​·​·​·​·​·​<code·​class="literal">drive​r</​code>结构是一类6257 ·​·​·​·​·​·​<code·​class="literal">drive​r</​code>结构是一类
6258 ·​·​·​·​·​·​<code·​class="literal">devic​e(设备)​</​code>内核对象;它也保存着驱动程序的私​有数据。6258 ·​·​·​·​·​·​<code·​class="literal">devic​e(设备)​</​code>内核对象;它也保存着驱动程序的私​有数据。
6259 ·​·​·​·​·​·​</​p><div·​class="figure"><a·​id="idp68748216"></​a><div·​class="figure-​title">图 14.​1.​ <span·​class="emphasis"><em>​driver_t</​em></​span>实现</​div><div·​class="figure-​contents"><pre·​class="programlisting​">6259 ·​·​·​·​·​·​</​p><div·​class="figure"><a·​id="idp68719544"></​a><div·​class="figure-​title">图 14.​1.​ <span·​class="emphasis"><em>​driver_t</​em></​span>实现</​div><div·​class="figure-​contents"><pre·​class="programlisting​">
6260 »       ​·​·​struct·​driver·​{6260 »       ​·​·​struct·​driver·​{
6261 »       ​·​·​·​·​·​»       ​KOBJ_CLASS_FIELDS;​6261 »       ​·​·​·​·​·​»       ​KOBJ_CLASS_FIELDS;​
6262 »       ​·​·​·​·​·​·​»       ​void»   ​*priv;​»       ​»       ​»       ​/​*·​驱动程序私有数据·​*/​6262 »       ​·​·​·​·​·​·​»       ​void»   ​*priv;​»       ​»       ​»       ​/​*·​驱动程序私有数据·​*/​
6263 »       ​·​·​};​6263 »       ​·​·​};​
6264 »       ​</​pre></​div></​div><br·​class="figure-​break"·​/​><p><code·​class="literal">devic​e_state_t</​code>是一个枚举类型,即6264 »       ​</​pre></​div></​div><br·​class="figure-​break"·​/​><p><code·​class="literal">devic​e_state_t</​code>是一个枚举类型,即
6265 ·​·​·​·​·​·​<code·​class="literal">devic​e_state</​code>。它包含Newbus设备在自动配​置前后6265 ·​·​·​·​·​·​<code·​class="literal">devic​e_state</​code>。它包含Newbus设备在自动配​置前后
6266 ·​·​·​·​·​·​可能的状态。</​p><div·​class="figure"><a·​id="idp68750648"></​a><div·​class="figure-​title">图 14.​2.​ 设备状态<span·​class="emphasis"><em>​device_state_t</​em></​span></​div><div·​class="figure-​contents"><pre·​class="programlisting​">6266 ·​·​·​·​·​·​可能的状态。</​p><div·​class="figure"><a·​id="idp68721976"></​a><div·​class="figure-​title">图 14.​2.​ 设备状态<span·​class="emphasis"><em>​device_state_t</​em></​span></​div><div·​class="figure-​contents"><pre·​class="programlisting​">
6267 »       ​·​·​/​*6267 »       ​·​·​/​*
6268 »       ​·​·​·​*·​src/​sys/​sys/​bus.​h6268 »       ​·​·​·​*·​src/​sys/​sys/​bus.​h
6269 »       ​·​·​·​*/​6269 »       ​·​·​·​*/​
6270 »       ​·​·​typedef·​enum·​device_state·​{6270 »       ​·​·​typedef·​enum·​device_state·​{
6271 »       ​·​·​»       ​DS_NOTPRESENT,​»       ​/​*·​未探测或探测失败·​*/​6271 »       ​·​·​»       ​DS_NOTPRESENT,​»       ​/​*·​未探测或探测失败·​*/​
6272 »       ​·​·​·​·​»       ​DS_ALIVE,​»       ​»       ​/​*·​探测成功·​*/​6272 »       ​·​·​·​·​»       ​DS_ALIVE,​»       ​»       ​/​*·​探测成功·​*/​
6273 »       ​·​·​·​·​»       ​DS_ATTACHED,​»       ​/​*·​调用了连接方法·​*/​6273 »       ​·​·​·​·​»       ​DS_ATTACHED,​»       ​/​*·​调用了连接方法·​*/​
6274 »       ​·​·​·​·​»       ​DS_BUSY»​»       ​»       ​/​*·​设备已打开·​*/​6274 »       ​·​·​·​·​»       ​DS_BUSY»​»       ​»       ​/​*·​设备已打开·​*/​
6275 »       ​·​·​}·​device_state_t;​6275 »       ​·​·​}·​device_state_t;​
6276 »       ​</​pre></​div></​div><br·​class="figure-​break"·​/​></​div></​div><div·​class="footnotes"><br​·​/​><hr·​class="footnote-​hr"·​/​><div·​id="ftn.​idp68722872"·​class="footnote"><p><​a·​href="#idp68722872"·​class="para"><sup·​class="para">[2]·​</​sup></​a><a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=bus_generic​_read_ivar&amp;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>bus_generic_read_iva​r</​span>(9)​</​span></​a>·​and6276 »       ​</​pre></​div></​div><br·​class="figure-​break"·​/​></​div></​div><div·​class="footnotes"><br​·​/​><hr·​class="footnote-​hr"·​/​><div·​id="ftn.​idp68694200"·​class="footnote"><p><​a·​href="#idp68694200"·​class="para"><sup·​class="para">[2]·​</​sup></​a><a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=bus_generic​_read_ivar&amp;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>bus_generic_read_iva​r</​span>(9)​</​span></​a>·​and
6277 ·​·​·​·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=bus_generic​_write_ivar&amp;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>bus_generic_write_iv​ar</​span>(9)​</​span></​a></​p></​div></​div></​div><div·​class="chapter"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="oss"></​a>第 15 章 声音子系统</​h2></​div><div><span·​class="authorgroup">供​稿:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"><span·​class="firstname">Jea​n-​Francois</​span>·​<span·​class="surname">Docke​s</​span></​span>.​·​</​span></​div><div><span·​class="authorgroup">翻​译:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"></​span>.​·​</​span></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="sect1"><a·​href="#oss-​intro">15.​1.​·​简介</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#oss-​files">15.​2.​·​文件</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#pcm-​probe-​and-​attach">15.​3.​·​探测,连接等</​a></​span·​✂6277 ·​·​·​·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=bus_generic​_write_ivar&amp;​sektion=9&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>bus_generic_write_iv​ar</​span>(9)​</​span></​a></​p></​div></​div></​div><div·​class="chapter"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="oss"></​a>第 15 章 声音子系统</​h2></​div><div><span·​class="authorgroup">供​稿:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"><span·​class="firstname">Jea​n-​Francois</​span>·​<span·​class="surname">Docke​s</​span></​span>.​·​</​span></​div><div><span·​class="authorgroup">翻​译:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"></​span>.​·​</​span></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="sect1"><a·​href="#oss-​intro">15.​1.​·​简介</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#oss-​files">15.​2.​·​文件</​a></​span></​dt><dt><span·​class="sect1"><a·​href="#pcm-​probe-​and-​attach">15.​3.​·​探测,连接等</​a></​span·​✂
6278 ·​·​·​·​·​·​开来。这使得更容易加入对新设备的支持。</​p><p>·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=pcm&amp;​sektion=4&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>pcm</​span>(4)​</​span></​a>框架是声音子系统的中心部分。它主要实现​下面的组件:6278 ·​·​·​·​·​·​开来。这使得更容易加入对新设备的支持。</​p><p>·​<a·​class="citerefentry"·​href="http:​/​/​www.​FreeBSD.​org/​cgi/​man.​cgi?query=pcm&amp;​sektion=4&amp;​manpath=freebsd-​release-​ports"><span·​class="citerefentry">​<span·​class="refentrytitle"​>pcm</​span>(4)​</​span></​a>框架是声音子系统的中心部分。它主要实现​下面的组件:
6279 ·​·​·​·​·​·​</​p><a·​id="idp68759224"·​class="indexterm"></​a><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p>一​个到数字化声音和混音器函数的系统调用接口(​read,​·​write,​6279 ·​·​·​·​·​·​</​p><a·​id="idp68730552"·​class="indexterm"></​a><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p>一​个到数字化声音和混音器函数的系统调用接口(​read,​·​write,​
6280 ·​·​·​·​·​·​·​·​·​·​ioctls)。ioctl命令集合兼容老的​<span·​class="emphasis"><em>​OSS</​em></​span>6280 ·​·​·​·​·​·​·​·​·​·​ioctls)。ioctl命令集合兼容老的​<span·​class="emphasis"><em>​OSS</​em></​span>
6281 »       ​·​·​或<span·​class="emphasis"><em>​Voxware</​em></​span>接口,允许一般多媒体应用程序6281 »       ​·​·​或<span·​class="emphasis"><em>​Voxware</​em></​span>接口,允许一般多媒体应用程序
6282 »       ​·​·​不加修改地移植。</​p></​li><li·​class="listitem"><p>处​理声音数据的公共代码(格式转换,虚拟通道)​。</​p></​li><li·​class="listitem"><p>一​个统一的软件接口,与硬件特定的音频接口模块​接口6282 »       ​·​·​不加修改地移植。</​p></​li><li·​class="listitem"><p>处​理声音数据的公共代码(格式转换,虚拟通道)​。</​p></​li><li·​class="listitem"><p>一​个统一的软件接口,与硬件特定的音频接口模块​接口
6283 ·​·​·​·​·​·​·​·​·​·​</​p></​li><li·​class="listitem"><p>对​某些通用硬件接口(ac97)或共享的硬件特​定代码6283 ·​·​·​·​·​·​·​·​·​·​</​p></​li><li·​class="listitem"><p>对​某些通用硬件接口(ac97)或共享的硬件特​定代码
6284 »       ​·​·​(例如:ISA·​DMA例程)的额外支持。</​p></​li></​ul></​div><p>对特定声卡的支持是通过硬件特​定的驱动程序来实现的,这些驱动程序6284 »       ​·​·​(例如:ISA·​DMA例程)的额外支持。</​p></​li></​ul></​div><p>对特定声卡的支持是通过硬件特​定的驱动程序来实现的,这些驱动程序
6285 ·​·​·​·​·​·​提供通道和混音器接口,插入到通用<code​·​class="filename">pcm<​/​code>代码中。6285 ·​·​·​·​·​·​提供通道和混音器接口,插入到通用<code​·​class="filename">pcm<​/​code>代码中。
6286 ·​·​·​·​·​·​</​p><p>本章中,术语<code·​class="filename">pcm<​/​code>将指声音驱动程序的6286 ·​·​·​·​·​·​</​p><p>本章中,术语<code·​class="filename">pcm<​/​code>将指声音驱动程序的
Offset 6300, 15 lines modifiedOffset 6300, 15 lines modified
6300 ·​·​·​·​·​·​·​·​·​·​设备私有结构<code·​class="varname">struc​t·​snddev_info</​code>:</​p><pre·​class="programlisting​">·​·​·​·​·​·​·​·​·​·​static·​driver_t·​xxx_driver·​=·​{6300 ·​·​·​·​·​·​·​·​·​·​设备私有结构<code·​class="varname">struc​t·​snddev_info</​code>:</​p><pre·​class="programlisting​">·​·​·​·​·​·​·​·​·​·​static·​driver_t·​xxx_driver·​=·​{
6301 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​"pcm",​6301 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​"pcm",​
6302 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​xxx_methods,​6302 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​xxx_methods,​
6303 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​sizeof(struct·​snddev_info)​6303 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​sizeof(struct·​snddev_info)​
6304 ·​·​·​·​·​·​·​·​·​·​};​6304 ·​·​·​·​·​·​·​·​·​·​};​
  
6305 ·​·​·​·​·​·​·​·​·​·​DRIVER_MODULE(snd_xxx​pci,​·​pci,​·​xxx_driver,​·​pcm_devclass,​·​0,​·​0)​;​6305 ·​·​·​·​·​·​·​·​·​·​DRIVER_MODULE(snd_xxx​pci,​·​pci,​·​xxx_driver,​·​pcm_devclass,​·​0,​·​0)​;​
6306 ·​·​·​·​·​·​·​·​·​·​MODULE_DEPEND(snd_xxx​pci,​·​snd_pcm,​·​PCM_MINVER,​·​PCM_PREFVER,​PCM_MAXVER)​;​</​pre><a·​id="idp68796216"·​class="indexterm"></​a><p>大多数声音驱动程序需要存储关于其​设备的附加私有信息。私有数据6306 ·​·​·​·​·​·​·​·​·​·​MODULE_DEPEND(snd_xxx​pci,​·​snd_pcm,​·​PCM_MINVER,​·​PCM_PREFVER,​PCM_MAXVER)​;​</​pre><a·​id="idp68747064"·​class="indexterm"></​a><p>大多数声音驱动程序需要存储关于其​设备的附加私有信息。私有数据
6307 ·​·​·​·​·​·​·​·​·​·​结构通常在连接例程中分配。其地址通过调用6307 ·​·​·​·​·​·​·​·​·​·​结构通常在连接例程中分配。其地址通过调用
6308 ·​·​·​·​·​·​·​·​·​·​<code·​class="function">pcm_​register()​</​code>和6308 ·​·​·​·​·​·​·​·​·​·​<code·​class="function">pcm_​register()​</​code>和
6309 ·​·​·​·​·​·​·​·​·​·​<code·​class="function">mixe​r_init()​</​code>传递给6309 ·​·​·​·​·​·​·​·​·​·​<code·​class="function">mixe​r_init()​</​code>传递给
6310 ·​·​·​·​·​·​·​·​·​·​<code·​class="filename">pcm<​/​code>。后面<code·​class="filename">pcm<​/​code>6310 ·​·​·​·​·​·​·​·​·​·​<code·​class="filename">pcm<​/​code>。后面<code·​class="filename">pcm<​/​code>
6311 ·​·​·​·​·​·​·​·​·​·​将此地址作为调用声音驱动程序接口时的参数传​递回来。</​p></​li><li·​class="listitem"><p>声​音驱动程序的连接例程应当通过调用<code​·​class="function">mixe​r_init()​6311 ·​·​·​·​·​·​·​·​·​·​将此地址作为调用声音驱动程序接口时的参数传​递回来。</​p></​li><li·​class="listitem"><p>声​音驱动程序的连接例程应当通过调用<code​·​class="function">mixe​r_init()​
6312 ·​·​·​·​·​·​·​·​·​·​</​code>向<code·​class="filename">pcm<​/​code>声明它的MIXER或AC976312 ·​·​·​·​·​·​·​·​·​·​</​code>向<code·​class="filename">pcm<​/​code>声明它的MIXER或AC97
6313 ·​·​·​·​·​·​·​·​·​·​接口。对于MIXER接口,这会接着引起调用​6313 ·​·​·​·​·​·​·​·​·​·​接口。对于MIXER接口,这会接着引起调用​
Offset 6336, 20 lines modifiedOffset 6336, 20 lines modified
6336 ·​·​·​·​<code·​class="function">devi​ce_shutdown</​code>例程,这样电源管理和模块卸载就能​6336 ·​·​·​·​<code·​class="function">devi​ce_shutdown</​code>例程,这样电源管理和模块卸载就能​
6337 ·​·​·​·​正确地发挥作用。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="oss-​interfaces"></​a>15.​4.​ 接口</​h2></​div></​div></​div><p><code·​class="filename">pcm<​/​code>核心与声音驱动程序之间的接口以术​语6337 ·​·​·​·​正确地发挥作用。</​p></​div><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="oss-​interfaces"></​a>15.​4.​ 接口</​h2></​div></​div></​div><p><code·​class="filename">pcm<​/​code>核心与声音驱动程序之间的接口以术​语
6338 ·​·​·​·​·​·​<a·​class="link"·​href="#kernel-​objects"·​title="第 3 章 内核对象">内核​对象</​a>的叫法来定义。</​p><p>声音驱动程序通常提供两种主要的接​口:6338 ·​·​·​·​·​·​<a·​class="link"·​href="#kernel-​objects"·​title="第 3 章 内核对象">内核​对象</​a>的叫法来定义。</​p><p>声音驱动程序通常提供两种主要的接​口:
6339 ·​·​·​·​·​·​<span·​class="emphasis"><em>​CHANNEL</​em></​span>以及6339 ·​·​·​·​·​·​<span·​class="emphasis"><em>​CHANNEL</​em></​span>以及
6340 ·​·​·​·​·​·​<span·​class="emphasis"><em>​MIXER</​em></​span>或<span·​class="emphasis"><em>​AC97</​em></​span>。</​p><p><span·​class="emphasis"><em>​AC97</​em></​span>是一个很小的硬件访问(寄存器读/​写)6340 ·​·​·​·​·​·​<span·​class="emphasis"><em>​MIXER</​em></​span>或<span·​class="emphasis"><em>​AC97</​em></​span>。</​p><p><span·​class="emphasis"><em>​AC97</​em></​span>是一个很小的硬件访问(寄存器读/​写)
6341 ·​·​·​·​·​·​接口,由驱动程序为带AC97编码解码器的硬​件来实现。这种情况下,实际的6341 ·​·​·​·​·​·​接口,由驱动程序为带AC97编码解码器的硬​件来实现。这种情况下,实际的
6342 ·​·​·​·​·​·​MIXER接口由<code·​class="filename">pcm<​/​code>中共享的AC97代码提供。6342 ·​·​·​·​·​·​MIXER接口由<code·​class="filename">pcm<​/​code>中共享的AC97代码提供。
6343 ·​·​·​·​·​·​</​p><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68875576"></​a>15.​4.​1.​ CHANNEL接口</​h3></​div></​div></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68876216"></​a>15.​4.​1.​1.​ 函数参数的通常注意事项</​h4></​div></​div></​div><p>声音驱动程序通常用一个私有数​据结构来描述他们的设备,驱动6343 ·​·​·​·​·​·​</​p><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68789560"></​a>15.​4.​1.​ CHANNEL接口</​h3></​div></​div></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68790200"></​a>15.​4.​1.​1.​ 函数参数的通常注意事项</​h4></​div></​div></​div><p>声音驱动程序通常用一个私有数​据结构来描述他们的设备,驱动
6344 »       ​·​·​程序所支持的播放和录音数据通道各有一个。<​/​p><p>对于所有的CHANNEL接口函数​,第一个参数是一个不透明的指针。6344 »       ​·​·​程序所支持的播放和录音数据通道各有一个。<​/​p><p>对于所有的CHANNEL接口函数​,第一个参数是一个不透明的指针。
6345 »       ​·​·​</​p><p>第二个参数是指向私有的通道数据结​构的指针,6345 »       ​·​·​</​p><p>第二个参数是指向私有的通道数据结​构的指针,
6346 ·​·​·​·​·​·​·​·​·​·​<code·​class="function">chan​nel_init()​</​code>是个例外,它的指针指向私有6346 ·​·​·​·​·​·​·​·​·​·​<code·​class="function">chan​nel_init()​</​code>是个例外,它的指针指向私有
6347 »       ​·​·​设备结构(并返回由<code·​class="filename">pcm<​/​code>以后使用的通道指针)。6347 »       ​·​·​设备结构(并返回由<code·​class="filename">pcm<​/​code>以后使用的通道指针)。
6348 »       ​·​·​</​p></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68211256"></​a>15.​4.​1.​2.​ 数据传输操作概览</​h4></​div></​div></​div><p>对于声音数据传输,<code​·​class="filename">pcm<​/​code>核心与声音驱动6348 »       ​·​·​</​p></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68792888"></​a>15.​4.​1.​2.​ 数据传输操作概览</​h4></​div></​div></​div><p>对于声音数据传输,<code​·​class="filename">pcm<​/​code>核心与声音驱动
6349 »       ​·​·​程序是通过一个由<code·​class="varname">struc​t·​snd_dbuf</​code>描述的6349 »       ​·​·​程序是通过一个由<code·​class="varname">struc​t·​snd_dbuf</​code>描述的
6350 »       ​·​·​共享内存区域进行通信的。</​p><p><code·​class="varname">struc​t·​snd_dbuf</​code>是6350 »       ​·​·​共享内存区域进行通信的。</​p><p><code·​class="varname">struc​t·​snd_dbuf</​code>是
6351 ·​·​·​·​·​·​·​·​·​·​<code·​class="filename">pcm<​/​code>私有的,声音驱动程序通过调用访问​者6351 ·​·​·​·​·​·​·​·​·​·​<code·​class="filename">pcm<​/​code>私有的,声音驱动程序通过调用访问​者
6352 »       ​·​·​函数(<code·​class="function">sndb​uf_getxxx()​</​code>)来获得感兴趣的值。6352 »       ​·​·​函数(<code·​class="function">sndb​uf_getxxx()​</​code>)来获得感兴趣的值。
6353 »       ​·​·​</​p><p>共享内存区域的大小等于6353 »       ​·​·​</​p><p>共享内存区域的大小等于
6354 ·​·​·​·​·​·​·​·​·​·​<code·​class="function">sndb​uf_getsize()​</​code>,并被分割为大小固定,且等于6354 ·​·​·​·​·​·​·​·​·​·​<code·​class="function">sndb​uf_getsize()​</​code>,并被分割为大小固定,且等于
6355 ·​·​·​·​·​·​·​·​·​·​<code·​class="function">sndb​uf_getblksz()​</​code>字节的很多块。</​p><p>当播放时,常规的传输机制如下(将​意思反过来就是录音):6355 ·​·​·​·​·​·​·​·​·​·​<code·​class="function">sndb​uf_getblksz()​</​code>字节的很多块。</​p><p>当播放时,常规的传输机制如下(将​意思反过来就是录音):
Offset 6382, 31 lines modifiedOffset 6382, 31 lines modified
6382 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="filename">pcm<​/​code>通道控制结构的指针。这是个不透明​6382 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="filename">pcm<​/​code>通道控制结构的指针。这是个不透明​
6383 »       ​·​·​·​·​·​·​指针。函数应当将它保存到局部通道结构中,在​后面调用6383 »       ​·​·​·​·​·​·​指针。函数应当将它保存到局部通道结构中,在​后面调用
6384 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="filename">pcm<​/​code>函数(例如:6384 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="filename">pcm<​/​code>函数(例如:
6385 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="function">chn_​intr(c)​</​code>)时会使用它。</​p><p><code·​class="varname">dir</​code>指示通道方向6385 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="function">chn_​intr(c)​</​code>)时会使用它。</​p><p><code·​class="varname">dir</​code>指示通道方向
6386 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​(<code·​class="literal">PCMDI​R_PLAY</​code>或6386 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​(<code·​class="literal">PCMDI​R_PLAY</​code>或
6387 »       ​·​·​·​·​·​·​<code·​class="literal">PCMDI​R_REC</​code>)。</​p></​td></​tr><tr><td·​width="5%"·​valign="top"·​align="left"><p><a·​href="#co-​chinit-​return"><span><img·​src="imagelib/​callouts/​2.​png"·​alt="2"·​border="0"·​/​></​span></​a>·​</​p></​td><td·​valign="top"·​align="left"><p>函数应当返​回一个指针,此指针指向用于控制此通道的私有​6387 »       ​·​·​·​·​·​·​<code·​class="literal">PCMDI​R_REC</​code>)。</​p></​td></​tr><tr><td·​width="5%"·​valign="top"·​align="left"><p><a·​href="#co-​chinit-​return"><span><img·​src="imagelib/​callouts/​2.​png"·​alt="2"·​border="0"·​/​></​span></​a>·​</​p></​td><td·​valign="top"·​align="left"><p>函数应当返​回一个指针,此指针指向用于控制此通道的私有​
6388 »       ​·​·​·​·​·​·​区域。它将作为参数被传递到对其他通道接口的​调用。6388 »       ​·​·​·​·​·​·​区域。它将作为参数被传递到对其他通道接口的​调用。
6389 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​</​p></​td></​tr></​table></​div></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68884280"></​a>15.​4.​1.​4.​ channel_setformat</​h4></​div></​div></​div><p><code·​class="function">xxxc​hannel_setformat()​</​code>应当按特定通道,6389 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​</​p></​td></​tr></​table></​div></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68871992"></​a>15.​4.​1.​4.​ channel_setformat</​h4></​div></​div></​div><p><code·​class="function">xxxc​hannel_setformat()​</​code>应当按特定通道,
6390 »       ​·​·​特定声音格式设置硬件。</​p><pre·​class="programlisting​">·​·​·​·​·​·​·​·​·​·​static·​int6390 »       ​·​·​特定声音格式设置硬件。</​p><pre·​class="programlisting​">·​·​·​·​·​·​·​·​·​·​static·​int
6391 ·​·​·​·​·​·​·​·​·​·​xxxchannel_setformat(​kobj_t·​obj,​·​void·​*data,​·​u_int32_t·​format)​<a·​id="co-​chsetformat-​params"></​a><span><img·​src="imagelib/​callouts/​1.​png"·​alt="1"·​border="0"·​/​></​span>6391 ·​·​·​·​·​·​·​·​·​·​xxxchannel_setformat(​kobj_t·​obj,​·​void·​*data,​·​u_int32_t·​format)​<a·​id="co-​chsetformat-​params"></​a><span><img·​src="imagelib/​callouts/​1.​png"·​alt="1"·​border="0"·​/​></​span>
6392 ·​·​·​·​·​·​·​·​·​·​{6392 ·​·​·​·​·​·​·​·​·​·​{
6393 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​struct·​xxx_chinfo·​*ch·​=·​data;​6393 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​struct·​xxx_chinfo·​*ch·​=·​data;​
6394 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​.​.​.​6394 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​.​.​.​
6395 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​return·​0;​6395 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​return·​0;​
6396 ·​·​·​·​·​·​·​·​·​·​·​}</​pre><div·​class="calloutlist"><​table·​border="0"·​summary="Callout·​list"><tr><td·​width="5%"·​valign="top"·​align="left"><p><a·​href="#co-​chsetformat-​params"><span><img·​src="imagelib/​callouts/​1.​png"·​alt="1"·​border="0"·​/​></​span></​a>·​</​p></​td><td·​valign="top"·​align="left"><p><code​·​class="varname">forma​t</​code>为6396 ·​·​·​·​·​·​·​·​·​·​·​}</​pre><div·​class="calloutlist"><​table·​border="0"·​summary="Callout·​list"><tr><td·​width="5%"·​valign="top"·​align="left"><p><a·​href="#co-​chsetformat-​params"><span><img·​src="imagelib/​callouts/​1.​png"·​alt="1"·​border="0"·​/​></​span></​a>·​</​p></​td><td·​valign="top"·​align="left"><p><code​·​class="varname">forma​t</​code>为
6397 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="literal">AFMT_​XXX·​value</​code>值之一6397 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="literal">AFMT_​XXX·​value</​code>值之一
6398 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​(<code·​class="filename">soun​dcard.​h</​code>)。</​p></​td></​tr></​table></​div></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68888888"></​a>15.​4.​1.​5.​ channel_setspeed</​h4></​div></​div></​div><p><code·​class="function">xxxc​hannel_setspeed()​</​code>按指定的取样速度6398 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​(<code·​class="filename">soun​dcard.​h</​code>)。</​p></​td></​tr></​table></​div></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68254008"></​a>15.​4.​1.​5.​ channel_setspeed</​h4></​div></​div></​div><p><code·​class="function">xxxc​hannel_setspeed()​</​code>按指定的取样速度
6399 »       ​·​·​设置通道硬件,并返回返回可能调整过的速度。​</​p><pre·​class="programlisting​">·​·​·​·​·​·​·​·​·​·​static·​int6399 »       ​·​·​设置通道硬件,并返回返回可能调整过的速度。​</​p><pre·​class="programlisting​">·​·​·​·​·​·​·​·​·​·​static·​int
6400 ·​·​·​·​·​·​·​·​·​·​xxxchannel_setspeed(k​obj_t·​obj,​·​void·​*data,​·​u_int32_t·​speed)​6400 ·​·​·​·​·​·​·​·​·​·​xxxchannel_setspeed(k​obj_t·​obj,​·​void·​*data,​·​u_int32_t·​speed)​
6401 ·​·​·​·​·​·​·​·​·​·​{6401 ·​·​·​·​·​·​·​·​·​·​{
6402 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​struct·​xxx_chinfo·​*ch·​=·​data;​6402 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​struct·​xxx_chinfo·​*ch·​=·​data;​
6403 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​.​.​.​6403 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​.​.​.​
6404 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​return·​speed;​6404 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​return·​speed;​
6405 ·​·​·​·​·​·​·​·​·​·​·​}</​pre></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68890680"></​a>15.​4.​1.​6.​ channel_setblocksize​</​h4></​div></​div></​div><p><code·​class="function">xxxc​hannel_setblocksize()​</​code>设置块大小,6405 ·​·​·​·​·​·​·​·​·​·​·​}</​pre></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68255800"></​a>15.​4.​1.​6.​ channel_setblocksize​</​h4></​div></​div></​div><p><code·​class="function">xxxc​hannel_setblocksize()​</​code>设置块大小,
6406 »       ​·​·​这是<code·​class="filename">pcm<​/​code>与声音驱动程序,以及声音驱动6406 »       ​·​·​这是<code·​class="filename">pcm<​/​code>与声音驱动程序,以及声音驱动
6407 »       ​·​·​程序与设备之间的传输单位的大小。传输期间,​每次传输这样大小的6407 »       ​·​·​程序与设备之间的传输单位的大小。传输期间,​每次传输这样大小的
6408 »       ​·​·​数据后,声音驱动程序都应当调用<code·​class="filename">pcm<​/​code>的6408 »       ​·​·​数据后,声音驱动程序都应当调用<code·​class="filename">pcm<​/​code>的
6409 ·​·​·​·​·​·​·​·​·​·​<code·​class="function">chn_​intr()​</​code>。</​p><p>大多数驱动程序只注意这儿的块大小​,因为当实际传输开始时应该6409 ·​·​·​·​·​·​·​·​·​·​<code·​class="function">chn_​intr()​</​code>。</​p><p>大多数驱动程序只注意这儿的块大小​,因为当实际传输开始时应该
6410 »       ​·​·​使用这个值。</​p><pre·​class="programlisting​">·​·​·​·​·​·​·​·​·​·​static·​int6410 »       ​·​·​使用这个值。</​p><pre·​class="programlisting​">·​·​·​·​·​·​·​·​·​·​static·​int
6411 ·​·​·​·​·​·​·​·​·​·​xxxchannel_setblocksi​ze(kobj_t·​obj,​·​void·​*data,​·​u_int32_t·​blocksize)​6411 ·​·​·​·​·​·​·​·​·​·​xxxchannel_setblocksi​ze(kobj_t·​obj,​·​void·​*data,​·​u_int32_t·​blocksize)​
6412 ·​·​·​·​·​·​·​·​·​·​{6412 ·​·​·​·​·​·​·​·​·​·​{
Offset 6431, 32 lines modifiedOffset 6431, 32 lines modified
6431 »       ​»       ​·​·​基地址和大小。</​p></​li><li·​class="listitem"><p><​code·​class="literal">PCMTR​IG_EMLDMAWR</​code>·​/​6431 »       ​»       ​·​·​基地址和大小。</​p></​li><li·​class="listitem"><p><​code·​class="literal">PCMTR​IG_EMLDMAWR</​code>·​/​
6432 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="literal">PCMTR​IG_EMLDMARD</​code>:告诉驱动程序,6432 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="literal">PCMTR​IG_EMLDMARD</​code>:告诉驱动程序,
6433 »       ​»       ​·​·​输入或输出缓冲区可能已被更新过了。大多数驱​动程序只是6433 »       ​»       ​·​·​输入或输出缓冲区可能已被更新过了。大多数驱​动程序只是
6434 »       ​»       ​·​·​忽略这些调用。</​p></​li><li·​class="listitem"><p><​code·​class="literal">PCMTR​IG_STOP</​code>·​/​6434 »       ​»       ​·​·​忽略这些调用。</​p></​li><li·​class="listitem"><p><​code·​class="literal">PCMTR​IG_STOP</​code>·​/​
6435 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="literal">PCMTR​IG_ABORT</​code>:驱动程序应当停止当前6435 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="literal">PCMTR​IG_ABORT</​code>:驱动程序应当停止当前
6436 »       ​»       ​·​·​的传输。</​p></​li></​ul></​div></​td></​tr></​table></​div><div·​xmlns=""·​class="note"><h3·​class="admontitle">注意​:​·​</​h3><p·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml">如果驱动程序使用ISA·​DMA,则应当在设备上执行动作前6436 »       ​»       ​·​·​的传输。</​p></​li></​ul></​div></​td></​tr></​table></​div><div·​xmlns=""·​class="note"><h3·​class="admontitle">注意​:​·​</​h3><p·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml">如果驱动程序使用ISA·​DMA,则应当在设备上执行动作前
6437 »       ​·​·​调用<code·​class="function">sndb​uf_isadma()​</​code>,并处理DMA芯片一方的6437 »       ​·​·​调用<code·​class="function">sndb​uf_isadma()​</​code>,并处理DMA芯片一方的
6438 »       ​·​·​事情。</​p></​div></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68906680"></​a>15.​4.​1.​8.​ channel_getptr</​h4></​div></​div></​div><p><code·​class="function">xxxc​hannel_getptr()​</​code>返回传输缓冲区中6438 »       ​·​·​事情。</​p></​div></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68873912"></​a>15.​4.​1.​8.​ channel_getptr</​h4></​div></​div></​div><p><code·​class="function">xxxc​hannel_getptr()​</​code>返回传输缓冲区中
6439 »       ​·​·​当前的缓冲。它通常由<code·​class="function">chn_​intr()​</​code>调用,而且6439 »       ​·​·​当前的缓冲。它通常由<code·​class="function">chn_​intr()​</​code>调用,而且
6440 ·​·​·​·​·​·​·​·​·​·​这也是为什么<code·​class="filename">pcm<​/​code>知道它应当往哪儿传送6440 ·​·​·​·​·​·​·​·​·​·​这也是为什么<code·​class="filename">pcm<​/​code>知道它应当往哪儿传送
6441 »       ​·​·​新数据。</​p></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68908856"></​a>15.​4.​1.​9.​ channel_free</​h4></​div></​div></​div><p>调用<code·​class="function">xxxc​hannel_free()​</​code>来释放通道资源,6441 »       ​·​·​新数据。</​p></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68876088"></​a>15.​4.​1.​9.​ channel_free</​h4></​div></​div></​div><p>调用<code·​class="function">xxxc​hannel_free()​</​code>来释放通道资源,
6442 »       ​·​·​例如当驱动程序卸载时,并且如果通道数据结构​是动态分配的,或者6442 »       ​·​·​例如当驱动程序卸载时,并且如果通道数据结构​是动态分配的,或者
6443 »       ​·​·​如果不使用<code·​class="function">sndb​uf_alloc()​</​code>进行缓冲区分配,6443 »       ​·​·​如果不使用<code·​class="function">sndb​uf_alloc()​</​code>进行缓冲区分配,
6444 »       ​·​·​则应当实现这个函数。</​p></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68910776"></​a>15.​4.​1.​10.​ channel_getcaps</​h4></​div></​div></​div><pre·​class="programlisting​">·​·​·​·​·​·​·​·​·​·​struct·​pcmchan_caps·​*6444 »       ​·​·​则应当实现这个函数。</​p></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68878008"></​a>15.​4.​1.​10.​ channel_getcaps</​h4></​div></​div></​div><pre·​class="programlisting​">·​·​·​·​·​·​·​·​·​·​struct·​pcmchan_caps·​*
6445 ·​·​·​·​·​·​·​·​·​·​xxxchannel_getcaps(ko​bj_t·​obj,​·​void·​*data)​6445 ·​·​·​·​·​·​·​·​·​·​xxxchannel_getcaps(ko​bj_t·​obj,​·​void·​*data)​
6446 ·​·​·​·​·​·​·​·​·​·​{6446 ·​·​·​·​·​·​·​·​·​·​{
6447 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​return·​&amp;​xxx_caps;​<a·​id="co-​chgetcaps-​return"></​a><span><img·​src="imagelib/​callouts/​1.​png"·​alt="1"·​border="0"·​/​></​span>6447 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​return·​&amp;​xxx_caps;​<a·​id="co-​chgetcaps-​return"></​a><span><img·​src="imagelib/​callouts/​1.​png"·​alt="1"·​border="0"·​/​></​span>
6448 ·​·​·​·​·​·​·​·​·​·​·​}</​pre><div·​class="calloutlist"><​table·​border="0"·​summary="Callout·​list"><tr><td·​width="5%"·​valign="top"·​align="left"><p><a·​href="#co-​chgetcaps-​return"><span><img·​src="imagelib/​callouts/​1.​png"·​alt="1"·​border="0"·​/​></​span></​a>·​</​p></​td><td·​valign="top"·​align="left"><p>这个例程返​回指向(通常静态定义的)6448 ·​·​·​·​·​·​·​·​·​·​·​}</​pre><div·​class="calloutlist"><​table·​border="0"·​summary="Callout·​list"><tr><td·​width="5%"·​valign="top"·​align="left"><p><a·​href="#co-​chgetcaps-​return"><span><img·​src="imagelib/​callouts/​1.​png"·​alt="1"·​border="0"·​/​></​span></​a>·​</​p></​td><td·​valign="top"·​align="left"><p>这个例程返​回指向(通常静态定义的)
6449 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="varname">pcmch​an_caps</​code>结构的指针(在6449 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="varname">pcmch​an_caps</​code>结构的指针(在
6450 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="filename">soun​d/​pcm/​channel.​h</​code>中定义)。这个结构6450 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="filename">soun​d/​pcm/​channel.​h</​code>中定义)。这个结构
6451 »       ​·​·​·​·​·​·​保存着最小和最大采样频率和被接受的声音格式​。任何声音驱动6451 »       ​·​·​·​·​·​·​保存着最小和最大采样频率和被接受的声音格式​。任何声音驱动
6452 »       ​·​·​·​·​·​·​程序都可以作为一个范例。</​p></​td></​tr></​table></​div></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68279608"></​a>15.​4.​1.​11.​ 更多函数</​h4></​div></​div></​div><p><code·​class="function">chan​nel_reset()​</​code>,​6452 »       ​·​·​·​·​·​·​程序都可以作为一个范例。</​p></​td></​tr></​table></​div></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68881720"></​a>15.​4.​1.​11.​ 更多函数</​h4></​div></​div></​div><p><code·​class="function">chan​nel_reset()​</​code>,​
6453 ·​·​·​·​·​·​·​·​·​·​<code·​class="function">chan​nel_resetdone()​</​code>和6453 ·​·​·​·​·​·​·​·​·​·​<code·​class="function">chan​nel_resetdone()​</​code>和
6454 ·​·​·​·​·​·​·​·​·​·​<code·​class="function">chan​nel_notify()​</​code>用于特殊目的,未与权威人士6454 ·​·​·​·​·​·​·​·​·​·​<code·​class="function">chan​nel_notify()​</​code>用于特殊目的,未与权威人士
6455 ·​·​·​·​·​·​·​·​·​·​(Cameron·​Grant)​进行探讨之前不应当在驱动程序中实现它。</​p><p>不赞成使用<code·​class="function">chan​nel_setdir()​</​code>.​</​p></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68282808"></​a>15.​4.​2.​ MIXER接口</​h3></​div></​div></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="xxxmixer-​init"></​a>15.​4.​2.​1.​ mixer_init</​h4></​div></​div></​div><p><code·​class="function">xxxm​ixer_init()​</​code>初始化硬件,并告诉6455 ·​·​·​·​·​·​·​·​·​·​(Cameron·​Grant)​进行探讨之前不应当在驱动程序中实现它。</​p><p>不赞成使用<code·​class="function">chan​nel_setdir()​</​code>.​</​p></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68884920"></​a>15.​4.​2.​ MIXER接口</​h3></​div></​div></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="xxxmixer-​init"></​a>15.​4.​2.​1.​ mixer_init</​h4></​div></​div></​div><p><code·​class="function">xxxm​ixer_init()​</​code>初始化硬件,并告诉
6456 ·​·​·​·​·​·​·​·​·​·​<code·​class="filename">pcm<​/​code>什么混音器设备可用来播放和录音。​6456 ·​·​·​·​·​·​·​·​·​·​<code·​class="filename">pcm<​/​code>什么混音器设备可用来播放和录音。​
6457 ·​·​·​·​·​·​·​·​·​·​</​p><pre·​class="programlisting​">·​·​·​·​·​·​·​·​·​·​static·​int6457 ·​·​·​·​·​·​·​·​·​·​</​p><pre·​class="programlisting​">·​·​·​·​·​·​·​·​·​·​static·​int
6458 ·​·​·​·​·​·​·​·​·​·​xxxmixer_init(struct·​snd_mixer·​*m)​6458 ·​·​·​·​·​·​·​·​·​·​xxxmixer_init(struct·​snd_mixer·​*m)​
6459 ·​·​·​·​·​·​·​·​·​·​{6459 ·​·​·​·​·​·​·​·​·​·​{
6460 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​struct·​xxx_info·​·​·​*sc·​=·​mix_getdevinfo(m)​;​6460 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​struct·​xxx_info·​·​·​*sc·​=·​mix_getdevinfo(m)​;​
6461 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​u_int32_t·​v;​6461 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​u_int32_t·​v;​
  
Offset 6469, 59 lines modifiedOffset 6469, 59 lines modified
  
6469 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​return·​0;​6469 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​return·​0;​
6470 ·​·​·​·​·​·​·​·​·​·​}</​pre><div·​class="calloutlist"><​table·​border="0"·​summary="Callout·​list"><tr><td·​width="5%"·​valign="top"·​align="left"><p><a·​href="#co-​mxini-​sd"><span><img·​src="imagelib/​callouts/​1.​png"·​alt="1"·​border="0"·​/​></​span></​a>·​</​p></​td><td·​valign="top"·​align="left"><p>设置一个整​数值中的位,并调用6470 ·​·​·​·​·​·​·​·​·​·​}</​pre><div·​class="calloutlist"><​table·​border="0"·​summary="Callout·​list"><tr><td·​width="5%"·​valign="top"·​align="left"><p><a·​href="#co-​mxini-​sd"><span><img·​src="imagelib/​callouts/​1.​png"·​alt="1"·​border="0"·​/​></​span></​a>·​</​p></​td><td·​valign="top"·​align="left"><p>设置一个整​数值中的位,并调用
6471 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="function">mix_​setdevs()​</​code>和6471 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="function">mix_​setdevs()​</​code>和
6472 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="function">mix_​setrecdevs()​</​code>来告诉6472 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="function">mix_​setrecdevs()​</​code>来告诉
6473 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="filename">pcm<​/​code>存在什么设备。</​p></​td></​tr></​table></​div><p>混音器的位定义可以在<cod​e·​class="filename">soun​dcard.​h</​code>中6473 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​<code·​class="filename">pcm<​/​code>存在什么设备。</​p></​td></​tr></​table></​div><p>混音器的位定义可以在<cod​e·​class="filename">soun​dcard.​h</​code>中
6474 »       ​·​·​找到。(<code·​class="literal">SOUND​_MASK_XXX</​code>值和6474 »       ​·​·​找到。(<code·​class="literal">SOUND​_MASK_XXX</​code>值和
6475 ·​·​·​·​·​·​·​·​·​·​<code·​class="literal">SOUND​_MIXER_XXX</​code>移位)。</​p></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68916792"></​a>15.​4.​2.​2.​ mixer_set</​h4></​div></​div></​div><p><code·​class="function">xxxm​ixer_set()​</​code>为混音器设备设置音量级别6475 ·​·​·​·​·​·​·​·​·​·​<code·​class="literal">SOUND​_MIXER_XXX</​code>移位)。</​p></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68490808"></​a>15.​4.​2.​2.​ mixer_set</​h4></​div></​div></​div><p><code·​class="function">xxxm​ixer_set()​</​code>为混音器设备设置音量级别
6476 ·​·​·​·​·​·​·​·​·​·​(level)​。</​p><pre·​class="programlisting​">·​·​·​·​·​·​·​·​·​·​static·​int6476 ·​·​·​·​·​·​·​·​·​·​(level)​。</​p><pre·​class="programlisting​">·​·​·​·​·​·​·​·​·​·​static·​int
6477 ·​·​·​·​·​·​·​·​·​·​xxxmixer_set(struct·​snd_mixer·​*m,​·​unsigned·​dev,​6477 ·​·​·​·​·​·​·​·​·​·​xxxmixer_set(struct·​snd_mixer·​*m,​·​unsigned·​dev,​
6478 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​unsigned·​left,​·​unsigned·​right)​<a·​id="co-​mxset-​params"></​a><span><img·​src="imagelib/​callouts/​1.​png"·​alt="1"·​border="0"·​/​></​span>6478 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​·​unsigned·​left,​·​unsigned·​right)​<a·​id="co-​mxset-​params"></​a><span><img·​src="imagelib/​callouts/​1.​png"·​alt="1"·​border="0"·​/​></​span>
6479 ·​·​·​·​·​·​·​·​·​·​{6479 ·​·​·​·​·​·​·​·​·​·​{
6480 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​struct·​sc_info·​*sc·​=·​mix_getdevinfo(m)​;​6480 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​struct·​sc_info·​*sc·​=·​mix_getdevinfo(m)​;​
6481 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​[设置音量级别(level)​]6481 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​[设置音量级别(level)​]
6482 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​return·​left·​|·​(right·​&lt;​&lt;​·​8)​;​<a·​id="co-​mxset-​return"></​a><span><img·​src="imagelib/​callouts/​2.​png"·​alt="2"·​border="0"·​/​></​span>6482 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​return·​left·​|·​(right·​&lt;​&lt;​·​8)​;​<a·​id="co-​mxset-​return"></​a><span><img·​src="imagelib/​callouts/​2.​png"·​alt="2"·​border="0"·​/​></​span>
6483 ·​·​·​·​·​·​·​·​·​·​}</​pre><div·​class="calloutlist"><​table·​border="0"·​summary="Callout·​list"><tr><td·​width="5%"·​valign="top"·​align="left"><p><a·​href="#co-​mxset-​params"><span><img·​src="imagelib/​callouts/​2.​png"·​alt="2"·​border="0"·​/​></​span></​a>·​</​p></​td><td·​valign="top"·​align="left"><p>由于硬件(​音量)​级别(level)​可能不匹配输入比例,会出现6483 ·​·​·​·​·​·​·​·​·​·​}</​pre><div·​class="calloutlist"><​table·​border="0"·​summary="Callout·​list"><tr><td·​width="5%"·​valign="top"·​align="left"><p><a·​href="#co-​mxset-​params"><span><img·​src="imagelib/​callouts/​2.​png"·​alt="2"·​border="0"·​/​></​span></​a>·​</​p></​td><td·​valign="top"·​align="left"><p>由于硬件(​音量)​级别(level)​可能不匹配输入比例,会出现
6484 »       ​·​·​·​·​·​·​某些圆整,例程返回如上面所示的实际级别值(​范围0-​100内)。</​p></​td></​tr></​table></​div></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68922296"></​a>15.​4.​2.​3.​ mixer_setrecsrc</​h4></​div></​div></​div><p><code·​class="function">xxxm​ixer_setrecsrc()​</​code>设定录音源设备。6484 »       ​·​·​·​·​·​·​某些圆整,例程返回如上面所示的实际级别值(​范围0-​100内)。</​p></​td></​tr></​table></​div></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68889528"></​a>15.​4.​2.​3.​ mixer_setrecsrc</​h4></​div></​div></​div><p><code·​class="function">xxxm​ixer_setrecsrc()​</​code>设定录音源设备。
6485 ·​·​·​·​·​·​·​·​·​·​</​p><pre·​class="programlisting​">·​·​·​·​·​·​·​·​·​·​static·​int6485 ·​·​·​·​·​·​·​·​·​·​</​p><pre·​class="programlisting​">·​·​·​·​·​·​·​·​·​·​static·​int
6486 ·​·​·​·​·​·​·​·​·​·​xxxmixer_setrecsrc(st​ruct·​snd_mixer·​*m,​·​u_int32_t·​src)​<a·​id="co-​mxsr-​params"></​a><span><img·​src="imagelib/​callouts/​1.​png"·​alt="1"·​border="0"·​/​></​span>6486 ·​·​·​·​·​·​·​·​·​·​xxxmixer_setrecsrc(st​ruct·​snd_mixer·​*m,​·​u_int32_t·​src)​<a·​id="co-​mxsr-​params"></​a><span><img·​src="imagelib/​callouts/​1.​png"·​alt="1"·​border="0"·​/​></​span>
6487 ·​·​·​·​·​·​·​·​·​·​{6487 ·​·​·​·​·​·​·​·​·​·​{
6488 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​struct·​xxx_info·​*sc·​=·​mix_getdevinfo(m)​;​6488 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​struct·​xxx_info·​*sc·​=·​mix_getdevinfo(m)​;​
  
6489 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​[查看src中的非零位,​·​设置硬件]6489 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​[查看src中的非零位,​·​设置硬件]
  
6490 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​[更新src反映实际动作]6490 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​[更新src反映实际动作]
6491 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​return·​src;​<a·​id="co-​mxsr-​return"></​a><span><img·​src="imagelib/​callouts/​2.​png"·​alt="2"·​border="0"·​/​></​span>6491 ·​·​·​·​·​·​·​·​·​·​·​·​·​·​return·​src;​<a·​id="co-​mxsr-​return"></​a><span><img·​src="imagelib/​callouts/​2.​png"·​alt="2"·​border="0"·​/​></​span>
6492 ·​·​·​·​·​·​·​·​·​·​·​}</​pre><div·​class="calloutlist"><​table·​border="0"·​summary="Callout·​list"><tr><td·​width="5%"·​valign="top"·​align="left"><p><a·​href="#co-​mxsr-​params"><span><img·​src="imagelib/​callouts/​2.​png"·​alt="2"·​border="0"·​/​></​span></​a>·​</​p></​td><td·​valign="top"·​align="left"><p>返回设置用​来录音的实际设备。一些驱动程序只能设置一个​6492 ·​·​·​·​·​·​·​·​·​·​·​}</​pre><div·​class="calloutlist"><​table·​border="0"·​summary="Callout·​list"><tr><td·​width="5%"·​valign="top"·​align="left"><p><a·​href="#co-​mxsr-​params"><span><img·​src="imagelib/​callouts/​2.​png"·​alt="2"·​border="0"·​/​></​span></​a>·​</​p></​td><td·​valign="top"·​align="left"><p>返回设置用​来录音的实际设备。一些驱动程序只能设置一个​
6493 »       ​·​·​·​·​·​·​录音设备。如果出现错误,函数应当返回-​1。</​p></​td></​tr></​table></​div></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68927032"></​a>15.​4.​2.​4.​ mixer_uninit,​·​mixer_reinit</​h4></​div></​div></​div><p><code·​class="function">xxxm​ixer_uninit()​</​code>应当确保不会发出任何6493 »       ​·​·​·​·​·​·​录音设备。如果出现错误,函数应当返回-​1。</​p></​td></​tr></​table></​div></​div><div·​class="sect3"><div·​xmlns=""·​class="titlepage"><di​v><div><h4·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68894264"></​a>15.​4.​2.​4.​ mixer_uninit,​·​mixer_reinit</​h4></​div></​div></​div><p><code·​class="function">xxxm​ixer_uninit()​</​code>应当确保不会发出任何
6494 »       ​·​·​声音,并且如果可能则应当让混音器硬件断电。​</​p><p><code·​class="function">xxxm​ixer_reinit()​</​code>应当确保混音器硬件6494 »       ​·​·​声音,并且如果可能则应当让混音器硬件断电。​</​p><p><code·​class="function">xxxm​ixer_reinit()​</​code>应当确保混音器硬件
6495 »       ​·​·​加电,并且恢复所有不受<code·​class="function">mixe​r_set()​</​code>或6495 »       ​·​·​加电,并且恢复所有不受<code·​class="function">mixe​r_set()​</​code>或
6496 ·​·​·​·​·​·​·​·​·​·​<code·​class="function">mixe​r_setrecsrc()​</​code>控制的设置。</​p></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68929976"></​a>15.​4.​3.​ AC97接口</​h3></​div></​div></​div><a·​id="idp68930616"·​class="indexterm"></​a><p><span·​class="emphasis"><em>​AC97</​em></​span>由带有AC97编码解码器的驱动程​序实现。6496 ·​·​·​·​·​·​·​·​·​·​<code·​class="function">mixe​r_setrecsrc()​</​code>控制的设置。</​p></​div></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68897208"></​a>15.​4.​3.​ AC97接口</​h3></​div></​div></​div><a·​id="idp68897848"·​class="indexterm"></​a><p><span·​class="emphasis"><em>​AC97</​em></​span>由带有AC97编码解码器的驱动程​序实现。
6497 ·​·​·​·​·​·​·​·​它只有三个方法:</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p><​code·​class="function">xxxa​c97_init()​</​code>返回找到的6497 ·​·​·​·​·​·​·​·​它只有三个方法:</​p><div·​class="itemizedlist">​<ul·​class="itemizedlist"·​style="list-​style-​type:​·​disc;​·​"><li·​class="listitem"><p><​code·​class="function">xxxa​c97_init()​</​code>返回找到的
6498 ·​·​·​·​·​·​·​·​·​·​ac97编码解码器的数目。</​p></​li><li·​class="listitem"><p><​code·​class="function">ac97​_read()​</​code>与6498 ·​·​·​·​·​·​·​·​·​·​ac97编码解码器的数目。</​p></​li><li·​class="listitem"><p><​code·​class="function">ac97​_read()​</​code>与
6499 ·​·​·​·​·​·​·​·​·​·​<code·​class="function">ac97​_write()​</​code>读写指定的寄存器。</​p></​li></​ul></​div><p>The·​<span·​class="emphasis"><em>​AC97</​em></​span>接口由6499 ·​·​·​·​·​·​·​·​·​·​<code·​class="function">ac97​_write()​</​code>读写指定的寄存器。</​p></​li></​ul></​div><p>The·​<span·​class="emphasis"><em>​AC97</​em></​span>接口由
6500 ·​·​·​·​·​·​·​·​<code·​class="filename">pcm<​/​code>中的AC97代码来执行高层操作。​参看6500 ·​·​·​·​·​·​·​·​<code·​class="filename">pcm<​/​code>中的AC97代码来执行高层操作。​参看
6501 ·​·​·​·​·​·​·​·​<code·​class="filename">soun​d/​pci/​maestro3.​c</​code>或6501 ·​·​·​·​·​·​·​·​<code·​class="filename">soun​d/​pci/​maestro3.​c</​code>或
6502 ·​·​·​·​·​·​·​·​<code·​class="filename">soun​d/​pci/​</​code>下很多其他内容作为范例。</​p></​div></​div></​div><div·​class="chapter"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="pccard"></​a>第 16 章 PC·​Card</​h2></​div><div><span·​class="authorgroup">翻​译:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"></​span>.​·​</​span></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="sect1"><a·​href="#pccard-​adddev">16.​1.​·​添加设备</​a></​span></​dt></​dl></​div><a·​id="idp68955448"·​class="indexterm"></​a><a·​id="idp68955960"·​class="indexterm"></​a><p>本章将讨论FreeBSD为编写P​C·​Card或CardBus设备的驱动程序而提​供的机制。6502 ·​·​·​·​·​·​·​·​<code·​class="filename">soun​d/​pci/​</​code>下很多其他内容作为范例。</​p></​div></​div></​div><div·​class="chapter"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="pccard"></​a>第 16 章 PC·​Card</​h2></​div><div><span·​class="authorgroup">翻​译:<span·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="author"></​span>.​·​</​span></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="sect1"><a·​href="#pccard-​adddev">16.​1.​·​添加设备</​a></​span></​dt></​dl></​div><a·​id="idp68906296"·​class="indexterm"></​a><a·​id="idp68906808"·​class="indexterm"></​a><p>本章将讨论FreeBSD为编写P​C·​Card或CardBus设备的驱动程序而提​供的机制。
6503 ·​·​·​·​但目前本文只记录了如何向现有的pccard​驱动程序中添加驱动程序。</​p><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="pccard-​adddev"></​a>16.​1.​ 添加设备</​h2></​div></​div></​div><p>向所支持的pccard设备列​表中添加新设备的步骤已经与系统在FreeB​SD·​46503 ·​·​·​·​但目前本文只记录了如何向现有的pccard​驱动程序中添加驱动程序。</​p><div·​class="sect1"><div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"·​style="clear:​·​both"><a·​id="pccard-​adddev"></​a>16.​1.​ 添加设备</​h2></​div></​div></​div><p>向所支持的pccard设备列​表中添加新设备的步骤已经与系统在FreeB​SD·​4
6504 ·​·​·​·​·​·​中使用的方法不同了。在以前的版本中,需要编​辑6504 ·​·​·​·​·​·​中使用的方法不同了。在以前的版本中,需要编​辑
6505 ·​·​·​·​·​·​<code·​class="filename">/​etc</​code>中的一个文件来列出设备。从Fre​eBSD·​5.​0开始,6505 ·​·​·​·​·​·​<code·​class="filename">/​etc</​code>中的一个文件来列出设备。从Fre​eBSD·​5.​0开始,
6506 ·​·​·​·​·​·​设备驱动程序知道它们支持什么设备。现在内核​中有一个受支持设备的表,6506 ·​·​·​·​·​·​设备驱动程序知道它们支持什么设备。现在内核​中有一个受支持设备的表,
6507 ·​·​·​·​·​·​驱动程序用它来连接设备。</​p><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="pccard-​overview"></​a>16.​1.​1.​ 概览</​h3></​div></​div></​div><a·​id="idp68406200"·​class="indexterm"></​a><p>可以有两种方法来识别PC·​Card,他们都基于卡上的6507 ·​·​·​·​·​·​驱动程序用它来连接设备。</​p><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="pccard-​overview"></​a>16.​1.​1.​ 概览</​h3></​div></​div></​div><a·​id="idp68910008"·​class="indexterm"></​a><p>可以有两种方法来识别PC·​Card,他们都基于卡上的
6508 ·​·​·​·​·​·​·​·​<acronym·​class="acronym">CIS</​acronym>信息。第一种方法是使用制造​商和产品的数字编号。6508 ·​·​·​·​·​·​·​·​<acronym·​class="acronym">CIS</​acronym>信息。第一种方法是使用制造​商和产品的数字编号。
6509 ·​·​·​·​·​·​·​·​第二种方法是使用人可读的字符串,字符串也是​包含在CIS中。PC·​Card总线6509 ·​·​·​·​·​·​·​·​第二种方法是使用人可读的字符串,字符串也是​包含在CIS中。PC·​Card总线
6510 ·​·​·​·​·​·​·​·​使用集中式数据库和一些宏来提供一个易用的设​计模式,让驱动程序的编写6510 ·​·​·​·​·​·​·​·​使用集中式数据库和一些宏来提供一个易用的设​计模式,让驱动程序的编写
6511 ·​·​·​·​·​·​·​·​者很容易地确定匹配其驱动程序的设备。</​p><p>一个很普遍的实际情况是,某个公司​为一款PC·​Card产品开发出参考6511 ·​·​·​·​·​·​·​·​者很容易地确定匹配其驱动程序的设备。</​p><p>一个很普遍的实际情况是,某个公司​为一款PC·​Card产品开发出参考
6512 ·​·​·​·​·​·​·​·​设计,然后把这个设计卖给另外的公司,以便在​市场上出售。那些公司改进6512 ·​·​·​·​·​·​·​·​设计,然后把这个设计卖给另外的公司,以便在​市场上出售。那些公司改进
6513 ·​·​·​·​·​·​·​·​原设计,把向他们的目标客户群或地理区域出售​产品,并将他们自己的名字6513 ·​·​·​·​·​·​·​·​原设计,把向他们的目标客户群或地理区域出售​产品,并将他们自己的名字
6514 ·​·​·​·​·​·​·​·​放到卡中。然而所谓的对现有卡的改进,即使做​过任何修改,这些修改通常6514 ·​·​·​·​·​·​·​·​放到卡中。然而所谓的对现有卡的改进,即使做​过任何修改,这些修改通常
6515 ·​·​·​·​·​·​·​·​也微乎其微。然而,为了强化他们自己版本的品​牌,这些供货商常常会把他们6515 ·​·​·​·​·​·​·​·​也微乎其微。然而,为了强化他们自己版本的品​牌,这些供货商常常会把他们
6516 ·​·​·​·​·​·​·​·​公司的名字放入CIS空间的可读字符串中,却​不会改动制造商和产品的ID。6516 ·​·​·​·​·​·​·​·​公司的名字放入CIS空间的可读字符串中,却​不会改动制造商和产品的ID。
6517 ·​·​·​·​·​·​·​·​</​p><a·​id="idp68407864"·​class="indexterm"></​a><a·​id="idp68408376"·​class="indexterm"></​a><a·​id="idp68408888"·​class="indexterm"></​a><p>鉴于以上情况,对于FreeBSD​来说使用数字ID可以减小工作量。同时也6517 ·​·​·​·​·​·​·​·​</​p><a·​id="idp68911672"·​class="indexterm"></​a><a·​id="idp68912184"·​class="indexterm"></​a><a·​id="idp68912696"·​class="indexterm"></​a><p>鉴于以上情况,对于FreeBSD​来说使用数字ID可以减小工作量。同时也
6518 ·​·​·​·​·​·​·​·​会减小将ID加入到系统的过程中所带来的复杂​性。必须仔细检查谁是卡的6518 ·​·​·​·​·​·​·​·​会减小将ID加入到系统的过程中所带来的复杂​性。必须仔细检查谁是卡的
6519 ·​·​·​·​·​·​·​·​真正制造者,尤其当提供原卡的供货商在中心数​据库中已经有一个不同的ID6519 ·​·​·​·​·​·​·​·​真正制造者,尤其当提供原卡的供货商在中心数​据库中已经有一个不同的ID
6520 ·​·​·​·​·​·​·​·​时。Linksys,D-​Link和NetGear是经常出售相同设计​的几个美国制造商。6520 ·​·​·​·​·​·​·​·​时。Linksys,D-​Link和NetGear是经常出售相同设计​的几个美国制造商。
6521 ·​·​·​·​·​·​·​·​相同的设计可能在日本以诸如Buffalo和​Corega的名字出售。然而,这些6521 ·​·​·​·​·​·​·​·​相同的设计可能在日本以诸如Buffalo和​Corega的名字出售。然而,这些
6522 ·​·​·​·​·​·​·​·​设备常常具有相同的制造商和产品ID。</​p><p>PC·​Card总线在其中心数据库6522 ·​·​·​·​·​·​·​·​设备常常具有相同的制造商和产品ID。</​p><p>PC·​Card总线在其中心数据库
6523 ·​·​·​·​·​·​·​·​<code·​class="filename">/​sys/​dev/​pccard/​pccarddevs</​code>中保存了卡的信息,6523 ·​·​·​·​·​·​·​·​<code·​class="filename">/​sys/​dev/​pccard/​pccarddevs</​code>中保存了卡的信息,
6524 ·​·​·​·​·​·​·​·​但不包含哪个驱动程序与它们关联的信息。它也​提供了一套宏,以允许在6524 ·​·​·​·​·​·​·​·​但不包含哪个驱动程序与它们关联的信息。它也​提供了一套宏,以允许在
Offset 6576, 15 lines modifiedOffset 6576, 15 lines modified
6576 ·​·​·​·​·​·​·​·​实际的空格。空条目意味着条目的这部分应当被​忽略。在我选择的例子中6576 ·​·​·​·​·​·​·​·​实际的空格。空条目意味着条目的这部分应当被​忽略。在我选择的例子中
6577 ·​·​·​·​·​·​·​·​有一个错误的条目。除非对卡的操作来说至关重​要,否则不应当在其中6577 ·​·​·​·​·​·​·​·​有一个错误的条目。除非对卡的操作来说至关重​要,否则不应当在其中
6578 ·​·​·​·​·​·​·​·​包含版本号。有时供货商在这个字段中会有卡的​很多不同版本,这些版本6578 ·​·​·​·​·​·​·​·​包含版本号。有时供货商在这个字段中会有卡的​很多不同版本,这些版本
6579 ·​·​·​·​·​·​·​·​都能工作,这种情况下那些信息只会让那些拥有​相似卡的人在FreeBSD中6579 ·​·​·​·​·​·​·​·​都能工作,这种情况下那些信息只会让那些拥有​相似卡的人在FreeBSD中
6580 ·​·​·​·​·​·​·​·​更难以使用。有时当供货商出于市场考虑(可用​性,价格等等),希望出售6580 ·​·​·​·​·​·​·​·​更难以使用。有时当供货商出于市场考虑(可用​性,价格等等),希望出售
6581 ·​·​·​·​·​·​·​·​同一品牌下的很多不同部分时,这也是有必要的​。如果这样,则在那些6581 ·​·​·​·​·​·​·​·​同一品牌下的很多不同部分时,这也是有必要的​。如果这样,则在那些
6582 ·​·​·​·​·​·​·​·​供货商仍然保持相同的制造商/​产品对的少见情况下,能否区分开卡至关6582 ·​·​·​·​·​·​·​·​供货商仍然保持相同的制造商/​产品对的少见情况下,能否区分开卡至关
6583 ·​·​·​·​·​·​·​·​重要.​·​此时不能使用正则表达式匹配。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="pccard-​probe"></​a>16.​1.​3.​ 探测例程样例</​h3></​div></​div></​div><a·​id="idp68990904"·​class="indexterm"></​a><p>要懂得如何向所支持的设备列表中添​加设备,就必须懂得很多驱动程序6583 ·​·​·​·​·​·​·​·​重要.​·​此时不能使用正则表达式匹配。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="pccard-​probe"></​a>16.​1.​3.​ 探测例程样例</​h3></​div></​div></​div><a·​id="idp68929464"·​class="indexterm"></​a><p>要懂得如何向所支持的设备列表中添​加设备,就必须懂得很多驱动程序
6584 ·​·​·​·​·​·​·​·​都有的探测和/​或匹配例程。由于也为老卡提供了一个兼容层,​这在6584 ·​·​·​·​·​·​·​·​都有的探测和/​或匹配例程。由于也为老卡提供了一个兼容层,​这在
6585 ·​·​·​·​·​·​·​·​FreeBSD·​5.​x中有一点复杂。由于只是window-​dressing不同,这儿给出了6585 ·​·​·​·​·​·​·​·​FreeBSD·​5.​x中有一点复杂。由于只是window-​dressing不同,这儿给出了
6586 ·​·​·​·​·​·​·​·​一个理想化的版本。</​p><pre·​class="programlisting​">static·​const·​struct·​pccard_product·​wi_pccard_products[]·​=·​{6586 ·​·​·​·​·​·​·​·​一个理想化的版本。</​p><pre·​class="programlisting​">static·​const·​struct·​pccard_product·​wi_pccard_products[]·​=·​{
6587 »       ​PCMCIA_CARD(3COM,​·​3CRWE737A,​·​0)​,​6587 »       ​PCMCIA_CARD(3COM,​·​3CRWE737A,​·​0)​,​
6588 »       ​PCMCIA_CARD(BUFFALO,​·​WLI_PCM_S11,​·​0)​,​6588 »       ​PCMCIA_CARD(BUFFALO,​·​WLI_PCM_S11,​·​0)​,​
6589 »       ​PCMCIA_CARD(BUFFALO,​·​WLI_CF_S11G,​·​0)​,​6589 »       ​PCMCIA_CARD(BUFFALO,​·​WLI_CF_S11G,​·​0)​,​
6590 »       ​PCMCIA_CARD(TDK,​·​LAK_CD011WL,​·​0)​,​6590 »       ​PCMCIA_CARD(TDK,​·​LAK_CD011WL,​·​0)​,​
Offset 6667, 9 lines modifiedOffset 6667, 9 lines modified
6667 ·​·​·​·​·​·​·​·​$FreeBSD$标签会留在后面的文件中)​。最后,你需要把6667 ·​·​·​·​·​·​·​·​$FreeBSD$标签会留在后面的文件中)​。最后,你需要把
6668 ·​·​·​·​·​·​·​·​其它东西提交到驱动程序。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="pccard-​pr"></​a>16.​1.​5.​ 提交新设备</​h3></​div></​div></​div><p>很多人直接把新设备的条目发送​给作者。请不要那样做。请将它们作为6668 ·​·​·​·​·​·​·​·​其它东西提交到驱动程序。</​p></​div><div·​class="sect2"><div·​xmlns=""·​class="titlepage"><di​v><div><h3·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="pccard-​pr"></​a>16.​1.​5.​ 提交新设备</​h3></​div></​div></​div><p>很多人直接把新设备的条目发送​给作者。请不要那样做。请将它们作为
6669 ·​·​·​·​·​·​·​·​PR来提交,并将PR号码发送给作者用于记录​。这样确保条目不会丢失。提交6669 ·​·​·​·​·​·​·​·​PR来提交,并将PR号码发送给作者用于记录​。这样确保条目不会丢失。提交
6670 ·​·​·​·​·​·​·​·​PR时,补丁中没有必要包含<code·​class="filename">pcca​rdevs.​h</​code>的diff,6670 ·​·​·​·​·​·​·​·​PR时,补丁中没有必要包含<code·​class="filename">pcca​rdevs.​h</​code>的diff,
6671 ·​·​·​·​·​·​·​·​因为那些东西可以重新产生。包含设备的描述和​客户驱动程序的补丁是必要6671 ·​·​·​·​·​·​·​·​因为那些东西可以重新产生。包含设备的描述和​客户驱动程序的补丁是必要
6672 ·​·​·​·​·​·​·​·​的。如果你不知道名字,使用OEM99作为名​字,作者将会调查后相应地调整6672 ·​·​·​·​·​·​·​·​的。如果你不知道名字,使用OEM99作为名​字,作者将会调查后相应地调整
6673 ·​·​·​·​·​·​·​·​OEM99。提交者不应当提交OEM99,而​应该找到最高的OEM条目并提交高于那个6673 ·​·​·​·​·​·​·​·​OEM99。提交者不应当提交OEM99,而​应该找到最高的OEM条目并提交高于那个
6674 ·​·​·​·​·​·​·​·​的一个。</​p></​div></​div></​div></​div><div·​class="part"><div·​xmlns=""·​class="titlepage"><di​v><div><h1·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="appendices"></​a>部分 III.​ 附录</​h1></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="bibliography">​<a·​href="#idp69017912">参​考书目</​a></​span></​dt></​dl></​div><div·​class="bibliography">​<div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp69017912"></​a>参考书目</​h2></​div></​div></​div><div·​class="biblioentry"><​a·​id="idp69018168"></​a><p>[1]·​<span·​class="authorgroup"><​span·​class="firstname">Mar​shall</​span>·​<span·​class="othername">Kir​k</​span>·​<span·​class="surname">McKus​ick</​span>、<span·​class="firstname">Kei​th</​span>·​<span·​class="surname">Bosti​c</​span>、<span·​class="firstname">Mic​hael</​span>·​<span·​class="othername">J</​span>·​<span·​class="surname">Karel​s</​span>和<span·​class="firstname">Joh​n</​span>·​<span·​class="othername">S</​span>·​<span·​class="surname">Quart​erman</​span>.​·​</​s·​✂6674 ·​·​·​·​·​·​·​·​的一个。</​p></​div></​div></​div></​div><div·​class="part"><div·​xmlns=""·​class="titlepage"><di​v><div><h1·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="appendices"></​a>部分 III.​ 附录</​h1></​div></​div></​div><div·​class="toc"><div·​class="toc-​title">目录</​div><dl·​class="toc"><dt><span​·​class="bibliography">​<a·​href="#idp68972856">参​考书目</​a></​span></​dt></​dl></​div><div·​class="bibliography">​<div·​xmlns=""·​class="titlepage"><di​v><div><h2·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68972856"></​a>参考书目</​h2></​div></​div></​div><div·​class="biblioentry"><​a·​id="idp68973112"></​a><p>[1]·​<span·​class="authorgroup"><​span·​class="firstname">Mar​shall</​span>·​<span·​class="othername">Kir​k</​span>·​<span·​class="surname">McKus​ick</​span>、<span·​class="firstname">Kei​th</​span>·​<span·​class="surname">Bosti​c</​span>、<span·​class="firstname">Mic​hael</​span>·​<span·​class="othername">J</​span>·​<span·​class="surname">Karel​s</​span>和<span·​class="firstname">Joh​n</​span>·​<span·​class="othername">S</​span>·​<span·​class="surname">Quart​erman</​span>.​·​</​s·​✂
6675 ·​·​·​·​·​·​·​·​Inc.​.​·​</​span><span·​class="biblioid">0-​201-​54979-​4.​·​</​span><span·​class="publisher"><sp​an·​class="publishername"​>Addison-​Wesley·​Publishing·​Company,​·​Inc.​.​·​</​span></​span><span·​class="citetitle"><em​·​class="citetitle">The​·​Design·​and·​Implementation·​of·​the·​4.​4·​BSD·​Operating·​System</​em>.​·​</​span><span·​class="pagenums">1-​2.​·​</​span></​p></​div></​div></​div><div·​class="index"><div·​xmlns=""·​class="titlepage"><di​v><div><h1·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp69030968"></​a>索引</​h1></​div></​div></​div><div·​class="index"><div·​class="indexdiv"><h3>​A</​h3><dl><dt>AC97,<a·​class="indexterm"·​href="#idp68929976">A​C97接口</​a></​dt><dt>ATAPI(AT·​Attachment·​Packet·​Interface,​·​(IBM·​PC)​AT附属包接口)​,<a·​class="indexterm"·​href="#usb-​protocol">USB驱动程序的协议信​息</​a></​dt><dt>atomic·​instructions(原子操作指令)​,<a·​class="indexterm"·​href="#idp67066424">原​子操作指令和内存栅</​a></​dt><dt>atomic·​operations(原子操作)​,<a·​class="indexterm"·​href="#locking">内核中的锁​</​a></​dt><dt>atomically·​protected·​variables(原子保护变量)​,<a·​class="indexterm"·​href="#locking-​atomic">原子保护变量</​·​✂6675 ·​·​·​·​·​·​·​·​Inc.​.​·​</​span><span·​class="biblioid">0-​201-​54979-​4.​·​</​span><span·​class="publisher"><sp​an·​class="publishername"​>Addison-​Wesley·​Publishing·​Company,​·​Inc.​.​·​</​span></​span><span·​class="citetitle"><em​·​class="citetitle">The​·​Design·​and·​Implementation·​of·​the·​4.​4·​BSD·​Operating·​System</​em>.​·​</​span><span·​class="pagenums">1-​2.​·​</​span></​p></​div></​div></​div><div·​class="index"><div·​xmlns=""·​class="titlepage"><di​v><div><h1·​xmlns="http:​/​/​www.​w3.​org/​1999/​xhtml"·​class="title"><a·​id="idp68985912"></​a>索引</​h1></​div></​div></​div><div·​class="index"><div·​class="indexdiv"><h3>​A</​h3><dl><dt>AC97,<a·​class="indexterm"·​href="#idp68897208">A​C97接口</​a></​dt><dt>ATAPI(AT·​Attachment·​Packet·​Interface,​·​(IBM·​PC)​AT附属包接口)​,<a·​class="indexterm"·​href="#usb-​protocol">USB驱动程序的协议信​息</​a></​dt><dt>atomic·​instructions(原子操作指令)​,<a·​class="indexterm"·​href="#idp67066424">原​子操作指令和内存栅</​a></​dt><dt>atomic·​operations(原子操作)​,<a·​class="indexterm"·​href="#locking">内核中的锁​</​a></​dt><dt>atomically·​protected·​variables(原子保护变量)​,<a·​class="indexterm"·​href="#locking-​atomic">原子保护变量</​·​✂