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">原子保护变量</​·​✂