<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>知识库 &#187; 默认分类</title>
	<atom:link href="http://www.wezu.net/blog/archives/category/%e9%bb%98%e8%ae%a4/feed" rel="self" type="application/rss+xml" />
	<link>http://www.wezu.net/blog</link>
	<description>知识就是力量，知识就是财富！</description>
	<lastBuildDate>Fri, 06 Feb 2009 03:15:06 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>基于产品结构树的图文档管理系统的设计与实现</title>
		<link>http://www.wezu.net/blog/archives/53</link>
		<comments>http://www.wezu.net/blog/archives/53#comments</comments>
		<pubDate>Mon, 12 Jan 2009 02:16:26 +0000</pubDate>
		<dc:creator>snox</dc:creator>
				<category><![CDATA[默认分类]]></category>
		<category><![CDATA[图文档管理系统]]></category>

		<guid isPermaLink="false">http://www.wezu.net/blog/?p=53</guid>
		<description><![CDATA[    O 引言         近年来在机械制造加工行业，随着&#8221;甩图板&#8221;工程的迅猛发展， CAD 应用及企业信息化建设方面已取得了巨大成效，企业逐渐积累了大量的图纸、文档等技术资料。这些电子图档是企业产品设计的主要信息资源，为企业今后进一步实现信息化集成打下了良好的基础。但目前国内绝大部分企业还只停留在单纯使用CAD 进行产品设计的初期阶段，也没考虑到如何对大量的设计技术图档进行合理、有效、安全的管理，设计资料的重用率低;设计人员仍各自为政，难以避免图档的重复设计;设计数据的传递只能通过最传统的文件传递方式来完成，因此数据安全性无法保证，实对交流不便，对图文档的分类管理也无从谈起，图档版本混乱，更无法实时监控图档的整个设计流程;当然还有很多其它弊端。内地的中小型企业此类现象尤为突出。         1 系统设计目标         1.1 提高图文档的管理水平         对图文档及用户进行分级管理，有效控制未授权人员的非法使用;建立与图文档相对应的产品结构树，使产品零件与图文裆进行关联，有利于对各类产品及相关数据信息的集中管理。         1.2 提高工作效率         对各类图文档进行综合管理，便于设计图挡的适时更新和共享，加快技术文挡的查询速度;对同一产品零件图挡的不同版本进行管理，从前提高设计部门的运作效率;通过权限设置实现图文挡的安全共享。         1.3 确保技术文档安全性         建立两种数据库的备份机制，一种是利用DBMS 系统提供的维护计划自动定时备份数据库，另一种通过客户端操作实现备份或恢复。另外，如果在硬件条件允许的情况下可增加一台数据库备份服务器。在客户端，连接数据库服务器设置参数经过加密后以二进制方式存入配置文件，并将此配置文件属性设置为系统、隐藏，以防数据库账号外泄。要对用户进行身份验证，并对用户密码进行双重加密后保存到数据库内，避免用户通过读取数据库获取用户密码。另外，用户要经过授权后方可访问其图文档数据信息。所有用户操作内容都记入日志，以备后查。         1 .4提供数据集中、管理分散的操作模式         本软件采用C/S 构架;通过企业局域网，客户端可以分布在网络的任何节点上;最终的操作结果均存储在数据库服务器上，通过网络实现多个用户之间的协同工作。         2 系统解决方案         系统设计采用基于.Net 技术的Visual C# 为前台开发语言和ADO.NET 数据库访问接口技术，并以SQL Server2000 [...]]]></description>
			<content:encoded><![CDATA[<p><strong>    O 引言<br />
</strong>   <br />
    近年来在机械制造加工行业，随着&#8221;甩图板&#8221;工程的迅猛发展， CAD 应用及企业信息化建设方面已取得了巨大成效，企业逐渐积累了大量的图纸、文档等技术资料。这些电子图档是企业产品设计的主要信息资源，为企业今后进一步实现信息化集成打下了良好的基础。但目前国内绝大部分企业还只停留在单纯使用CAD 进行产品设计的初期阶段，也没考虑到如何对大量的设计技术图档进行合理、有效、安全的管理，设计资料的重用率低;设计人员仍各自为政，难以避免图档的重复设计;设计数据的传递只能通过最传统的文件传递方式来完成，因此数据安全性无法保证，实对交流不便，对图文档的分类管理也无从谈起，图档版本混乱，更无法实时监控图档的整个设计流程;当然还有很多其它弊端。内地的中小型企业此类现象尤为突出。<br />
   <br />
<strong>    1 系统设计目标<br />
   <br />
    1.1 提高图文档的管理水平<br />
</strong>   <br />
    对图文档及用户进行分级管理，有效控制未授权人员的非法使用;建立与图文档相对应的产品结构树，使产品零件与图文裆进行关联，有利于对各类产品及相关数据信息的集中管理。<br />
   <br />
<strong>    1.2 提高工作效率<br />
</strong>   <br />
    对各类图文档进行综合管理，便于设计图挡的适时更新和共享，加快技术文挡的查询速度;对同一产品零件图挡的不同版本进行管理，从前提高设计部门的运作效率;通过权限设置实现图文挡的安全共享。<br />
   <br />
<strong>    1.3 确保技术文档安全性</strong><br />
   <br />
    建立两种数据库的备份机制，一种是利用DBMS 系统提供的维护计划自动定时备份数据库，另一种通过客户端操作实现备份或恢复。另外，如果在硬件条件允许的情况下可增加一台数据库备份服务器。在客户端，连接数据库服务器设置参数经过加密后以二进制方式存入配置文件，并将此配置文件属性设置为系统、隐藏，以防数据库账号外泄。要对用户进行身份验证，并对用户密码进行双重加密后保存到数据库内，避免用户通过读取数据库获取用户密码。另外，用户要经过授权后方可访问其图文档数据信息。所有用户操作内容都记入日志，以备后查。<br />
   <br />
  <strong>  1 .4提供数据集中、管理分散的操作模式</strong><br />
   <br />
    本软件采用C/S 构架;通过企业局域网，客户端可以分布在网络的任何节点上;最终的操作结果均存储在数据库服务器上，通过网络实现多个用户之间的协同工作。<br />
<strong>   <br />
    2 系统解决方案</strong><br />
   <br />
    系统设计采用基于.Net 技术的Visual C# 为前台开发语言和ADO.NET 数据库访问接口技术，并以SQL Server2000 作为网络数据库系统平台，充分发挥C# 真正面向对象(OOP) 的编程优点，系统采用N 层结构模式，在系统的实现过程中，分别将相对独立的功能模块及代码封装在不同的类中，通过创建类的实例可方便调用各功能函数。<br />
    系统采用一级身份验证和二级权限分配机制。用户登录后首先从用户权限表获取该用户的权限。在产品结构树中只显示该用户有预览权的项目目录。<br />
    用户(或部门)对产品结构树中某一项(或组)的权限级别有:入库、预览、修改、删除、出库及批量出库六种。系统管理员具有所有权限，并能对各部门负责人进行委托授权，有权查看及管理所有日志，并对系统参数进行设置，如设置图档的保留版本数，图档归档流程及再修改授权流程等。系统操作流程及功能模块如图l 所示。<br />
   </p>
<p align="center"><img src="http://www.e-works.net.cn/fileupload/images/128370752991406250_new.bmp" alt="" width="414" height="279" align="center" /></p>
<p><strong>    3 系统的实现</strong><br />
   <br />
    本系统主要完成企业设计技术文挡的分类管理，实现对各类技术文档的预览、版本管理、入库、出库、批量出库、图纸与产品结构树的动态关联等。具有用户权限的动态设置及自顶向下的委托授权机制，同时强调系统的安全性和稳定性;数据库结构的设计符合2NF 范式;在代码编写过程中，将可能产生错误的程序代码加入异常处理机制，捕捉错误并加以适当解决，尽量避免异常现象的发生。以下对主要的几个核心功能的实现方法加以阐述。<br />
<strong>   <br />
    3.1 产品结构树设计</strong><br />
   </p>
<p align="center"><img src="http://www.e-works.net.cn/fileupload/images/128370753314843750_new.bmp" alt="" width="272" height="268" align="center" /></p>
<p>    具有的基本功能:通过右键菜单可动态设计产品结构树项目。操作界面如图2 所示。<br />
    此功能的实现主要是基于数据库的树状结构的实时显示，经分析采用递归算法来实现，并充分利用C# 语言的OOP 程序开发优点，添加基于数据库的树状目录结构动态构建函数及公用类。代码如下:<br />
   <br />
    public class GenTree<br />
    { public void genTreeNode(TreeNodeCollection nodes,<br />
    DataRowColleclion data, string parentld)<br />
    { foreach (DataRow dr in data)<br />
    {if(dr["parentld"[.ToString() == parentld)<br />
    { TreeNode newNode = new TreeNode(dn"name"].<br />
    ToString()};<br />
    newNode.Name =dr["id"].ToString();<br />
    newNode.Tag =drrisChild1;<br />
    if(bool)dr["isChild"]<br />
    { newNode.lmagelndex =3; nodes. Add(newNode);}<br />
    else<br />
    { newNode.lmagelndex =0; nodes.Add(newNode);<br />
    genTreeNode(newNode.Nodes ， data,<br />
    dr["id"].ToString()}<br />
    }<br />
               }<br />
            }<br />
         }<br />
    }</p>
<p>    在需要显示产品结构树窗体的Load 事件内，置入下面的代码:<br />
   <br />
    private void inputDocFrm_Load(object sender, EventArgs e)<br />
    { SqlDataAdapter cmd =new SqlDataAdapter (&#8220;select* from<br />
    M_ProductorTree &#8220;sqlConnection1 );<br />
    cmd.Fill(ds，&#8221;treeInfo&#8221;); GenTree myTree =new GenTree();<br />
    myTree.genTreeNode (ProTree.Nodes [O].Nodes,ds.Tables<br />
    ["treelnfo"].Rows，<br />
    ProTree.Nodes[O].Name};<br />
    }// ProTree 是TreeView 控件实例<br />
   <br />
    以上公用类也可在人员部门结构树及其它结构树的实现时直接调用。<br />
   <br />
 <strong>   3.2 图文档文件的版本管理</strong><br />
   </p>
<p align="center"><img src="http://www.e-works.net.cn/fileupload/images/128370753776718750_new.bmp" alt="" width="404" height="288" align="center" /></p>
<p>    假设系统参数设置图文档需保留五个版本，最新版本保留在M_Doc 数据表内，其它版本保存到M_DocBak.数据表内。图文档存储麦、产品结构树表和图档关联表结构关系如图3 所示。<br />
    M_DocBak 表和M_Doc 表内的Doc 字段为Image 类型，用于存放文件。设计更新版本的存储过程UpdateDoc ，最后在业务逻辑层直接调用此存储过程并指定参数完成版本的更新，此模块采用分层设计模式来实现。创建对数据表M_DocFile 的更新图文档存储过程，代码如下:<br />
   <br />
    CREATE PROCEDURE UpdateDoc(@DclD int,@Doc image,<br />
    @Modifier nvarchar(10),@Demo nvarchar(30),@Vercount int,<br />
    @Newver int output)<br />
    AS declare @baksum int;<br />
    begin transaction<br />
    select @baksum=count(*) from M_DocBak;<br />
    // 存储现有版本数<br />
    if(@baksum&lt;@Vercount)<br />
    begin<br />
    Insert into dbo.M_DocBak(DocID, Ver, Doc, DocType,<br />
    Modifier， Demo)<br />
    <a href="mailto:select@D00ID"><span style="color: #1b60ad;">select@D00ID</span></a> ， @baksum+1 ，Doc ， docType ， inputName ，<br />
    Demo from dbo.M Doc<br />
    where <a href="mailto:docld=@DoclD"><span style="color: #1b60ad;">docld=@DoclD</span></a>;<br />
    update dbo.M_Doc set <a href="mailto:Doc=@Doc,inputName=@Modifier"><span style="color: #1b60ad;">Doc=@Doc,inputName=@Modifier</span></a>,<br />
    inputDate=getdate() ，authorized =false<br />
    where <a href="mailto:docId=@DocID"><span style="color: #1b60ad;">docId=@DocID</span></a>;<br />
    select @Newver=@baksum+1;<br />
    return;<br />
    end //设置返回版本号<br />
    else<br />
    begin // 如果版本已满<br />
    update dbo.M_DocBak set Ver=Ver-1<br />
    where <a href="mailto:DocID=@DoclD"><span style="color: #1b60ad;">DocID=@DoclD</span></a>; // 版本号均减1<br />
    declare @Tinyver nehar(6):<br />
    select @Tinyver=min(Ver) from dbo.M_DocBak<br />
    where <a href="mailto:DocID=@DoclD"><span style="color: #1b60ad;">DocID=@DoclD</span></a>;<br />
    delete from dbo.M_DocBak where <a href="mailto:Ver-@Tinyver"><span style="color: #1b60ad;">Ver-@Tinyver</span></a>;<br />
    Insert into dbo.M_DocBak(DocID, Ver, Doc, DocType,<br />
    Modifier， Demo)<br />
    select @DocID ， @baksum ， Doc ，也cType.inputName ，@Demo<br />
    from dbo.M_Doc where <a href="mailto:docld=@DocID"><span style="color: #1b60ad;">docld=@DocID</span></a>;<br />
    update dbo.M_Doc set <a href="mailto:Doc=@Doc.inputName=@Modifier"><span style="color: #1b60ad;">Doc=@Doc.inputName=@Modifier</span></a>,<br />
    inputDate=getdate(). authorized=false<br />
    where <a href="mailto:docld=@DoclD"><span style="color: #1b60ad;">docld=@DoclD</span></a>;<br />
    select @Newver=@baksum;<br />
    return:<br />
    end commit GO// 提交事务<br />
   <br />
    在其它模块中可调用以上存储过程实现图档版本的更新。<br />
<strong>   <br />
    3.3 图文挡的实时预览</strong><br />
   <br />
    传统的文档预览及修改功能一般是通过OLE 技术来实现的。OLE 即对象连接与嵌入，是应用程序共享对象的一种工业标准，为程序协同工作提供了一种方式。但利用OLE 技术首先需要客户方计算机已安装有此应用程序，且占用内存多，预览速度慢，不利于软件的后期部署。另外也可调用AutoCAD 自带的Vola View Express 来预览dwg 、dwf 及dxf 等常用格式的图档文件，但也需要客户端先安装有此类应用程序。<br />
    综合以上考虑，本系统采用由AutoDesk 公司提供的一个ActiveX 控件来实现对dwg 文件的预览功能。此ActiveX 控件以ocx 格式提供.对应AutoCAD 安装盘内MIGRATE\AMA\Ocx 目录下的DwgThumbnail. ocx 文件。操作步骤如下:<br />
    首先将DwgThumbnail. ocx 文件从安装光盘考入Winodws系统目录下的System32 目录内，然后在Windows 的开始→运行中输入regsvr32 DwgThumbnail.00x 并确定，注册此控件。<br />
    打开C# 开发环境，通过菜单P叫eet→Add Reference&#8230; 调出刚拷入系统目录下的DwgThumbnaiL.ocx 文件，此时在C# 的解决方案管理器的References 目录下自动添加了DWGTHUMBNAILL 、stdole 和StdType 三个ActiveX 引用。<br />
<strong>   <br />
    4 系统的安装部署<br />
</strong>   <br />
    本系统是基于N盯架构和SQL Server2，创始后台DBMS的一个应用软件，必须首先安装.NET FrameWork 框架，然后再安装SQL Server2则客户端连接(数据库连接驱动程序) ，系统方能正常运行。<br />
<strong>   <br />
    5 结束语</strong><br />
   <br />
    本系统实现了企业对大量图纸、文件、技术资料的规范管理，可以将企业的所有设计文档全部按产品结构树进行分类管理，并且提供了完善的版本及权限管理机制，确保技术文档管理的安全性和有效性;另外，系统提供的图档关联功能可以将同一图档关联到其它多个产品结构树分支上，实现→图多用，保证了图档的一致性。<br />
    本系统能改善企业现有的图文档管理现状，提高管理规范性、安全保密性、操作可控性和版本一致性，从而提高产品数据信息的利用率，改善电子设计资料管理混乱的局面，为提高企业的技术创新能力和产品设计及研发效率起到非常重要的作用，同时也为后期的PDM 系统集成奠定良好的基础。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.wezu.net/blog/archives/53/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>在MySQL中管理分级数据</title>
		<link>http://www.wezu.net/blog/archives/44</link>
		<comments>http://www.wezu.net/blog/archives/44#comments</comments>
		<pubDate>Mon, 15 Dec 2008 08:52:20 +0000</pubDate>
		<dc:creator>snox</dc:creator>
				<category><![CDATA[默认分类]]></category>

		<guid isPermaLink="false">http://www.wezu.net/blog/?p=44</guid>
		<description><![CDATA[翻译至：http://dev.mysql.com/tech-resources/articles/hierarchical-data.html 作者网址： 介绍 Most users at one time or another have dealt with hierarchical data in a SQL database and no doubt learned that the management of hierarchical data is not what a relational database is intended for. The tables of a relational database are not hierarchical (like XML), but are simply a flat list. [...]]]></description>
			<content:encoded><![CDATA[<p>翻译至：<a href="http://dev.mysql.com/tech-resources/articles/hierarchical-data.html">http://dev.mysql.com/tech-resources/articles/hierarchical-data.html</a><br />
作者网址：</p>
<h3>介绍</h3>
<p>Most users at one time or another have dealt with hierarchical data in a SQL database and no doubt learned that the management of hierarchical data is not what a relational database is intended for. The tables of a relational database are not hierarchical (like XML), but are simply a flat list. Hierarchical data has a parent-child relationship that is not naturally represented in a relational database table.</p>
<p>我们认为，分层的数据结合是这样的，每个项有一个父节点同时可以有0个或多个子项（根项父节点）。分层数据在大量的数据库应用程序中使用，包括论坛、邮件列表主题、商业组织结构、内容管理分类和产品分类。我们采用产品商店的产品分类作为示例：</p>
<p><a href="http://dev.mysql.com/tech-resources/articles/hierarchical-data-1.png"></a></p>
<p><img class="alignnone size-full wp-image-47" title="hierarchical-data-1" src="http://www.wezu.net/blog/wp-content/uploads/2008/12/hierarchical-data-1.png" alt="hierarchical-data-1" width="625" height="340" /></p>
<p> </p>
<p>大多数分层数据的组织都和上述类似，本文我们将介绍两种模式处理MySQL中的分层数据。我们从毗邻目录模式谈起吧。</p>
<h3>毗邻目录模式</h3>
<p>通常我们对于上述的示例分配采用下面的方式的存储与数据表之中：</p>
<pre>CREATE TABLE category(
category_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20) NOT NULL,
parent INT DEFAULT NULL);

INSERT INTO category
VALUES(1,'ELECTRONICS',NULL),(2,'TELEVISIONS',1),(3,'TUBE',2),
(4,'LCD',2),(5,'PLASMA',2),(6,'PORTABLE ELECTRONICS',1),
(7,'MP3 PLAYERS',6),(8,'FLASH',7),
(9,'CD PLAYERS',6),(10,'2 WAY RADIOS',6);

SELECT * FROM category ORDER BY category_id;

+-------------+----------------------+--------+
| category_id | name                 | parent |
+-------------+----------------------+--------+
|           1 | ELECTRONICS          |   NULL |
|           2 | TELEVISIONS          |      1 |
|           3 | TUBE                 |      2 |
|           4 | LCD                  |      2 |
|           5 | PLASMA               |      2 |
|           6 | PORTABLE ELECTRONICS |      1 |
|           7 | MP3 PLAYERS          |      6 |
|           8 | FLASH                |      7 |
|           9 | CD PLAYERS           |      6 |
|          10 | 2 WAY RADIOS         |      6 |
+-------------+----------------------+--------+
10 rows in set (0.00 sec)</pre>
<p>在毗邻目录模式中，表中的每一个记录都包含一个父节点的指向。顶端的对象（本例中的电子产品）的父指向为NULL。毗邻目录模式的优点是简单、可以简单的看到mp3播放器是便携式电子产品的子项，同时也是电子产品的子项。While the adjacency list model can be dealt with fairly easily in client-side code, working with the model can be more problematic in pure SQL.</p>
<h3>恢复完整的树结构</h3>
<p>第一常见的工作便是用这些分层数据来显示一个完成树，</p>
<p>The first common task when dealing with hierarchical data is the display of the entire tree, usually with some form of indentation. The most common way of doing this is in pure SQL is through the use of a self-join:</p>
<pre>SELECT t1.name AS lev1, t2.name as lev2, t3.name as lev3, t4.name as lev4
FROM category AS t1
LEFT JOIN category AS t2 ON t2.parent = t1.category_id
LEFT JOIN category AS t3 ON t3.parent = t2.category_id
LEFT JOIN category AS t4 ON t4.parent = t3.category_id
WHERE t1.name = 'ELECTRONICS';

+-------------+----------------------+--------------+-------+
| lev1        | lev2                 | lev3         | lev4  |
+-------------+----------------------+--------------+-------+
| ELECTRONICS | TELEVISIONS          | TUBE         | NULL  |
| ELECTRONICS | TELEVISIONS          | LCD          | NULL  |
| ELECTRONICS | TELEVISIONS          | PLASMA       | NULL  |
| ELECTRONICS | PORTABLE ELECTRONICS | MP3 PLAYERS  | FLASH |
| ELECTRONICS | PORTABLE ELECTRONICS | CD PLAYERS   | NULL  |
| ELECTRONICS | PORTABLE ELECTRONICS | 2 WAY RADIOS | NULL  |
+-------------+----------------------+--------------+-------+
6 rows in set (0.00 sec)</pre>
<h3>找出所有的叶节点</h3>
<p>We can find all the leaf nodes in our tree (没有子项) by using a LEFT JOIN query:</p>
<pre>SELECT t1.name FROM
category AS t1 LEFT JOIN category as t2
ON t1.category_id = t2.parent
WHERE t2.category_id IS NULL;

+--------------+
| name         |
+--------------+
| TUBE         |
| LCD          |
| PLASMA       |
| FLASH        |
| CD PLAYERS   |
| 2 WAY RADIOS |
+--------------+</pre>
<h3>Retrieving a Single Path恢复单一路径</h3>
<p>The self-join also allows us to see the full path through our hierarchies:</p>
<pre>SELECT t1.name AS lev1, t2.name as lev2, t3.name as lev3, t4.name as lev4
FROM category AS t1
LEFT JOIN category AS t2 ON t2.parent = t1.category_id
LEFT JOIN category AS t3 ON t3.parent = t2.category_id
LEFT JOIN category AS t4 ON t4.parent = t3.category_id
WHERE t1.name = 'ELECTRONICS' AND t4.name = 'FLASH';

+-------------+----------------------+-------------+-------+
| lev1        | lev2                 | lev3        | lev4  |
+-------------+----------------------+-------------+-------+
| ELECTRONICS | PORTABLE ELECTRONICS | MP3 PLAYERS | FLASH |
+-------------+----------------------+-------------+-------+
1 row in set (0.01 sec)</pre>
<p>The main limitation of such an approach is that you need one self-join for every level in the hierarchy, and performance will naturally degrade with each level added as the joining grows in complexity.</p>
<h3>毗邻目录模式的不足</h3>
<p>Working with the adjacency list model in pure SQL can be difficult at best. Before being able to see the full path of a category we have to know the level at which it resides. In addition, special care must be taken when deleting nodes because of the potential for orphaning an entire sub-tree in the process (delete the portable electronics category and all of its children are orphaned). Some of these limitations can be addressed through the use of client-side code or stored procedures. With a procedural language we can start at the bottom of the tree and iterate upwards to return the full tree or a single path. We can also use procedural programming to delete nodes without orphaning entire sub-trees by promoting one child element and re-ordering the remaining children to point to the new parent.</p>
<h3>嵌套集合模式</h3>
<p>What I would like to focus on in this article is a different approach, commonly referred to as the <strong>Nested Set Model</strong>. In the Nested Set Model, we can look at our hierarchy in a new way, not as nodes and lines, but as nested containers. Try picturing our electronics categories this way:</p>
<p><img src="http://dev.mysql.com/tech-resources/articles/hierarchical-data-2.png" alt="" /></p>
<p>Notice how our hierarchy is still maintained, as parent categories envelop their children.We represent this form of hierarchy in a table through the use of left and right values to represent the nesting of our nodes:</p>
<pre>CREATE TABLE nested_category (
 category_id INT AUTO_INCREMENT PRIMARY KEY,
 name VARCHAR(20) NOT NULL,
 lft INT NOT NULL,
 rgt INT NOT NULL
);

INSERT INTO nested_category
VALUES(1,'ELECTRONICS',1,20),(2,'TELEVISIONS',2,9),(3,'TUBE',3,4),
(4,'LCD',5,6),(5,'PLASMA',7,8),(6,'PORTABLE ELECTRONICS',10,19),
(7,'MP3 PLAYERS',11,14),(8,'FLASH',12,13),
(9,'CD PLAYERS',15,16),(10,'2 WAY RADIOS',17,18);

SELECT * FROM nested_category ORDER BY category_id;

+-------------+----------------------+-----+-----+
| category_id | name                 | lft | rgt |
+-------------+----------------------+-----+-----+
|           1 | ELECTRONICS          |   1 |  20 |
|           2 | TELEVISIONS          |   2 |   9 |
|           3 | TUBE                 |   3 |   4 |
|           4 | LCD                  |   5 |   6 |
|           5 | PLASMA               |   7 |   8 |
|           6 | PORTABLE ELECTRONICS |  10 |  19 |
|           7 | MP3 PLAYERS          |  11 |  14 |
|           8 | FLASH                |  12 |  13 |
|           9 | CD PLAYERS           |  15 |  16 |
|          10 | 2 WAY RADIOS         |  17 |  18 |
+-------------+----------------------+-----+-----+</pre>
<p>之所以我们使用 <strong>lft</strong> 和 <strong>rgt</strong> ，因为 left 和 right 是MySQL的保留关键字, 参见保留关键字列表 <a href="http://dev.mysql.com/doc/mysql/en/reserved-words.html">http://dev.mysql.com/doc/mysql/en/reserved-words.html</a> 。</p>
<p>那么我们想知道怎么去设置左右的值呢？我们从最左边的边框开始编号，一直到最右边：</p>
<p><img src="http://dev.mysql.com/tech-resources/articles/hierarchical-data-3.png" alt="" /></p>
<p>这个设计转化成树结构就是这个样子：</p>
<p><img src="http://dev.mysql.com/tech-resources/articles/hierarchical-data-4.png" alt="" /></p>
<p>When working with a tree, we work from left to right, one layer at a time, descending to each node&#8217;s children before assigning a right-hand number and moving on to the right. This approach is called the modified <strong>preorder tree traversal algorithm.</strong></p>
<h3>恢复整个树</h3>
<p>We can retrieve the full tree through the use of a self-join that links parents with nodes on the basis that a node&#8217;s <strong>lft</strong> value will always appear between its parent&#8217;s <strong>lft</strong> and <strong>rgt</strong> values:</p>
<pre>SELECT node.name
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND parent.name = 'ELECTRONICS'
ORDER BY node.lft;

+----------------------+
| name                 |
+----------------------+
| ELECTRONICS          |
| TELEVISIONS          |
| TUBE                 |
| LCD                  |
| PLASMA               |
| PORTABLE ELECTRONICS |
| MP3 PLAYERS          |
| FLASH                |
| CD PLAYERS           |
| 2 WAY RADIOS         |
+----------------------+</pre>
<p>Unlike our previous examples with the adjacency list model, this query will work regardless of the depth of the tree. We do not concern ourselves with the rgt value of the node in our BETWEEN clause because the rgt value will always fall within the same parent as the lft values.</p>
<h3>找出所有的叶结点</h3>
<p>Finding all leaf nodes in the nested set model even simpler than the LEFT JOIN method used in the adjacency list model. If you look at the nested_category table, you may notice that the lft and rgt values for leaf nodes are consecutive numbers. To find the leaf nodes, we look for nodes where rgt = lft + 1:</p>
<pre>SELECT name
FROM nested_category
WHERE rgt = lft + 1;

+--------------+
| name         |
+--------------+
| TUBE         |
| LCD          |
| PLASMA       |
| FLASH        |
| CD PLAYERS   |
| 2 WAY RADIOS |
+--------------+</pre>
<h3>恢复单一路径</h3>
<p>With the nested set model, we can retrieve a single path without having multiple self-joins:</p>
<pre>SELECT parent.name
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.name = 'FLASH'
ORDER BY parent.lft;

+----------------------+
| name                 |
+----------------------+
| ELECTRONICS          |
| PORTABLE ELECTRONICS |
| MP3 PLAYERS          |
| FLASH                |
+----------------------+</pre>
<h3>查找节点的深度</h3>
<p>We have already looked at how to show the entire tree, but what if we want to also show the depth of each node in the tree, to better identify how each node fits in the hierarchy? This can be done by adding a COUNT function and a GROUP BY clause to our existing query for showing the entire tree:</p>
<pre>SELECT node.name, (COUNT(parent.name) - 1) AS depth
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+----------------------+-------+
| name                 | depth |
+----------------------+-------+
| ELECTRONICS          |     0 |
| TELEVISIONS          |     1 |
| TUBE                 |     2 |
| LCD                  |     2 |
| PLASMA               |     2 |
| PORTABLE ELECTRONICS |     1 |
| MP3 PLAYERS          |     2 |
| FLASH                |     3 |
| CD PLAYERS           |     2 |
| 2 WAY RADIOS         |     2 |
+----------------------+-------+</pre>
<p>We can use the depth value to indent our category names with the CONCAT and REPEAT string functions:</p>
<pre>SELECT CONCAT( REPEAT(' ', COUNT(parent.name) - 1), node.name) AS name
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+-----------------------+
| name                  |
+-----------------------+
| ELECTRONICS           |
|  TELEVISIONS          |
|   TUBE                |
|   LCD                 |
|   PLASMA              |
|  PORTABLE ELECTRONICS |
|   MP3 PLAYERS         |
|    FLASH              |
|   CD PLAYERS          |
|   2 WAY RADIOS        |
+-----------------------+</pre>
<p>Of course, in a client-side application you will be more likely to use the depth value directly to display your hierarchy. Web developers could loop through the tree, adding &lt;li&gt;&lt;/li&gt; and &lt;ul&gt;&lt;/ul&gt; tags as the depth number increases and decreases.</p>
<h3>子树的深度</h3>
<p>When we need depth information for a sub-tree, we cannot limit either the node or parent tables in our self-join because it will corrupt our results. Instead, we add a third self-join, along with a sub-query to determine the depth that will be the new starting point for our sub-tree:</p>
<pre>SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth
FROM nested_category AS node,
	nested_category AS parent,
	nested_category AS sub_parent,
	(
		SELECT node.name, (COUNT(parent.name) - 1) AS depth
		FROM nested_category AS node,
		nested_category AS parent
		WHERE node.lft BETWEEN parent.lft AND parent.rgt
		AND node.name = 'PORTABLE ELECTRONICS'
		GROUP BY node.name
		ORDER BY node.lft
	)AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
	AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
	AND sub_parent.name = sub_tree.name
GROUP BY node.name
ORDER BY node.lft;

+----------------------+-------+
| name                 | depth |
+----------------------+-------+
| PORTABLE ELECTRONICS |     0 |
| MP3 PLAYERS          |     1 |
| FLASH                |     2 |
| CD PLAYERS           |     1 |
| 2 WAY RADIOS         |     1 |
+----------------------+-------+</pre>
<p>This function can be used with any node name, including the root node. The depth values are always relative to the named node.</p>
<h3>Find the Immediate Subordinates of a Node</h3>
<p>Imagine you are showing a category of electronics products on a retailer web site. When a user clicks on a category, you would want to show the products of that category, as well as list its immediate sub-categories, but not the entire tree of categories beneath it. For this, we need to show the node and its immediate sub-nodes, but no further down the tree. For example, when showing the PORTABLE ELECTRONICS category, we will want to show MP3 PLAYERS, CD PLAYERS, and 2 WAY RADIOS, but not FLASH.</p>
<p>This can be easily accomplished by adding a HAVING clause to our previous query:</p>
<pre>SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth
FROM nested_category AS node,
	nested_category AS parent,
	nested_category AS sub_parent,
	(
		SELECT node.name, (COUNT(parent.name) - 1) AS depth
		FROM nested_category AS node,
		nested_category AS parent
		WHERE node.lft BETWEEN parent.lft AND parent.rgt
		AND node.name = 'PORTABLE ELECTRONICS'
		GROUP BY node.name
		ORDER BY node.lft
	)AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
	AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
	AND sub_parent.name = sub_tree.name
GROUP BY node.name
HAVING depth &lt;= 1
ORDER BY node.lft;

+----------------------+-------+
| name                 | depth |
+----------------------+-------+
| PORTABLE ELECTRONICS |     0 |
| MP3 PLAYERS          |     1 |
| CD PLAYERS           |     1 |
| 2 WAY RADIOS         |     1 |
+----------------------+-------+</pre>
<p>If you do not wish to show the parent node, change the <strong>HAVING depth &lt;= 1 line to HAVING depth = 1</strong>.</p>
<h3>Aggregate Functions in a Nested Set</h3>
<p>Let&#8217;s add a table of products that we can use to demonstrate aggregate functions with:</p>
<pre>CREATE TABLE product(
product_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(40),
category_id INT NOT NULL
);

INSERT INTO product(name, category_id) VALUES('20" TV',3),('36" TV',3),
('Super-LCD 42"',4),('Ultra-Plasma 62"',5),('Value Plasma 38"',5),
('Power-MP3 5gb',7),('Super-Player 1gb',8),('Porta CD',9),('CD To go!',9),
('Family Talk 360',10);

SELECT * FROM product;

+------------+-------------------+-------------+
| product_id | name              | category_id |
+------------+-------------------+-------------+
|          1 | 20" TV            |           3 |
|          2 | 36" TV            |           3 |
|          3 | Super-LCD 42"     |           4 |
|          4 | Ultra-Plasma 62"  |           5 |
|          5 | Value Plasma 38"  |           5 |
|          6 | Power-MP3 128mb   |           7 |
|          7 | Super-Shuffle 1gb |           8 |
|          8 | Porta CD          |           9 |
|          9 | CD To go!         |           9 |
|         10 | Family Talk 360   |          10 |
+------------+-------------------+-------------+</pre>
<p>Now let&#8217;s produce a query that can retrieve our category tree, along with a product count for each category:</p>
<pre>SELECT parent.name, COUNT(product.name)
FROM nested_category AS node ,
nested_category AS parent,
product
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.category_id = product.category_id
GROUP BY parent.name
ORDER BY node.lft;

+----------------------+---------------------+
| name                 | COUNT(product.name) |
+----------------------+---------------------+
| ELECTRONICS          |                  10 |
| TELEVISIONS          |                   5 |
| TUBE                 |                   2 |
| LCD                  |                   1 |
| PLASMA               |                   2 |
| PORTABLE ELECTRONICS |                   5 |
| MP3 PLAYERS          |                   2 |
| FLASH                |                   1 |
| CD PLAYERS           |                   2 |
| 2 WAY RADIOS         |                   1 |
+----------------------+---------------------+</pre>
<p>This is our typical whole tree query with a COUNT and GROUP BY added, along with a reference to the product table and a join between the node and product table in the WHERE clause. As you can see, there is a count for each category and the count of subcategories is reflected in the parent categories.</p>
<h3>添加一个新的节点</h3>
<p>Now that we have learned how to query our tree, we should take a look at how to update our tree by adding a new node. Let&#8217;s look at our nested set diagram again:</p>
<p><img src="http://dev.mysql.com/tech-resources/articles/hierarchical-data-5.png" alt="" /></p>
<p>If we wanted to add a new node between the TELEVISIONS and PORTABLE ELECTRONICS nodes, the new node would have lft and rgt values of 10 and 11, and all nodes to its right would have their lft and rgt values increased by two. We would then add the new node with the appropriate lft and rgt values. While this can be done with a stored procedure in MySQL 5, I will assume for the moment that most readers are using 4.1, as it is the latest stable version, and I will isolate my queries with a LOCK TABLES statement instead:</p>
<pre>LOCK TABLE nested_category WRITE;

SELECT @myRight := rgt FROM nested_category
WHERE name = 'TELEVISIONS';

UPDATE nested_category SET rgt = rgt + 2 WHERE rgt &gt; @myRight;
UPDATE nested_category SET lft = lft + 2 WHERE lft &gt; @myRight;

INSERT INTO nested_category(name, lft, rgt) VALUES('GAME CONSOLES', @myRight + 1, @myRight + 2);

UNLOCK TABLES;

We can then check our nesting with our indented tree query:

SELECT CONCAT( REPEAT( ' ', (COUNT(parent.name) - 1) ), node.name) AS name
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+-----------------------+
| name                  |
+-----------------------+
| ELECTRONICS           |
|  TELEVISIONS          |
|   TUBE                |
|   LCD                 |
|   PLASMA              |
|  GAME CONSOLES        |
|  PORTABLE ELECTRONICS |
|   MP3 PLAYERS         |
|    FLASH              |
|   CD PLAYERS          |
|   2 WAY RADIOS        |
+-----------------------+</pre>
<p>If we instead want to add a node as a child of a node that has no existing children, we need to modify our procedure slightly. Let&#8217;s add a new FRS node below the 2 WAY RADIOS node:</p>
<pre>LOCK TABLE nested_category WRITE;

SELECT @myLeft := lft FROM nested_category

WHERE name = '2 WAY RADIOS';

UPDATE nested_category SET rgt = rgt + 2 WHERE rgt &gt; @myLeft;
UPDATE nested_category SET lft = lft + 2 WHERE lft &gt; @myLeft;

INSERT INTO nested_category(name, lft, rgt) VALUES('FRS', @myLeft + 1, @myLeft + 2);

UNLOCK TABLES;</pre>
<p>In this example we expand everything to the right of the left-hand number of our proud new parent node, then place the node to the right of the left-hand value. As you can see, our new node is now properly nested:</p>
<pre>SELECT CONCAT( REPEAT( ' ', (COUNT(parent.name) - 1) ), node.name) AS name
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+-----------------------+
| name                  |
+-----------------------+
| ELECTRONICS           |
|  TELEVISIONS          |
|   TUBE                |
|   LCD                 |
|   PLASMA              |
|  GAME CONSOLES        |
|  PORTABLE ELECTRONICS |
|   MP3 PLAYERS         |
|    FLASH              |
|   CD PLAYERS          |
|   2 WAY RADIOS        |
|    FRS                |
+-----------------------+</pre>
<h3>删除节点</h3>
<p>The last basic task involved in working with nested sets is the removal of nodes. The course of action you take when deleting a node depends on the node&#8217;s position in the hierarchy; deleting leaf nodes is easier than deleting nodes with children because we have to handle the orphaned nodes.</p>
<p>When deleting a leaf node, the process if just the opposite of adding a new node, we delete the node and its width from every node to its right:</p>
<pre>LOCK TABLE nested_category WRITE;

SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1
FROM nested_category
WHERE name = 'GAME CONSOLES';

DELETE FROM nested_category WHERE lft BETWEEN @myLeft AND @myRight;

UPDATE nested_category SET rgt = rgt - @myWidth WHERE rgt &gt; @myRight;
UPDATE nested_category SET lft = lft - @myWidth WHERE lft &gt; @myRight;

UNLOCK TABLES;</pre>
<p>And once again, we execute our indented tree query to confirm that our node has been deleted without corrupting the hierarchy:</p>
<pre>SELECT CONCAT( REPEAT( ' ', (COUNT(parent.name) - 1) ), node.name) AS name
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+-----------------------+
| name                  |
+-----------------------+
| ELECTRONICS           |
|  TELEVISIONS          |
|   TUBE                |
|   LCD                 |
|   PLASMA              |
|  PORTABLE ELECTRONICS |
|   MP3 PLAYERS         |
|    FLASH              |
|   CD PLAYERS          |
|   2 WAY RADIOS        |
|    FRS                |
+-----------------------+</pre>
<p>This approach works equally well to delete a node and all its children:</p>
<pre>LOCK TABLE nested_category WRITE;

SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1
FROM nested_category
WHERE name = 'MP3 PLAYERS';

DELETE FROM nested_category WHERE lft BETWEEN @myLeft AND @myRight;

UPDATE nested_category SET rgt = rgt - @myWidth WHERE rgt &gt; @myRight;
UPDATE nested_category SET lft = lft - @myWidth WHERE lft &gt; @myRight;

UNLOCK TABLES;</pre>
<p>And once again, we query to see that we have successfully deleted an entire sub-tree:</p>
<pre>SELECT CONCAT( REPEAT( ' ', (COUNT(parent.name) - 1) ), node.name) AS name
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+-----------------------+
| name                  |
+-----------------------+
| ELECTRONICS           |
|  TELEVISIONS          |
|   TUBE                |
|   LCD                 |
|   PLASMA              |
|  PORTABLE ELECTRONICS |
|   CD PLAYERS          |
|   2 WAY RADIOS        |
|    FRS                |
+-----------------------+</pre>
<p>The other scenario we have to deal with is the deletion of a parent node but not the children. In some cases you may wish to just change the name to a placeholder until a replacement is presented, such as when a supervisor is fired. In other cases, the child nodes should all be moved up to the level of the deleted parent:</p>
<pre>LOCK TABLE nested_category WRITE;

SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1
FROM nested_category
WHERE name = 'PORTABLE ELECTRONICS';

DELETE FROM nested_category WHERE lft = @myLeft;

UPDATE nested_category SET rgt = rgt - 1, lft = lft - 1 WHERE lft BETWEEN @myLeft AND @myRight;
UPDATE nested_category SET rgt = rgt - 2 WHERE rgt &gt; @myRight;
UPDATE nested_category SET lft = lft - 2 WHERE lft &gt; @myRight;

UNLOCK TABLES;</pre>
<p>In this case we subtract two from all elements to the right of the node (since without children it would have a width of two), and one from the nodes that are its children (to close the gap created by the loss of the parent&#8217;s left value). Once again, we can confirm our elements have been promoted:</p>
<pre>SELECT CONCAT( REPEAT( ' ', (COUNT(parent.name) - 1) ), node.name) AS name
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+---------------+
| name          |
+---------------+
| ELECTRONICS   |
|  TELEVISIONS  |
|   TUBE        |
|   LCD         |
|   PLASMA      |
|  CD PLAYERS   |
|  2 WAY RADIOS |
|   FRS         |
+---------------+</pre>
<p>Other scenarios when deleting nodes would include promoting one of the children to the parent position and moving the child nodes under a sibling of the parent node, but for the sake of space these scenarios will not be covered in this article.</p>
<h3>Final Thoughts</h3>
<p>While I hope the information within this article will be of use to you, the concept of nested sets in SQL has been around for over a decade, and there is a lot of additional information available in books and on the Internet. In my opinion the most comprehensive source of information on managing hierarchical information is a book called <a href="http://www.openwin.org/mike/books/index.php/trees-and-hierarchies-in-sql">Joe Celko&#8217;s Trees and Hierarchies in SQL for Smarties</a>, written by a very respected author in the field of advanced SQL, Joe Celko. Joe Celko is often credited with the nested sets model and is by far the most prolific author on the subject. I have found Celko&#8217;s book to be an invaluable resource in my own studies and highly recommend it. The book covers advanced topics which I have not covered in this article, and provides additional methods for managing hierarchical data in addition to the Adjacency List and Nested Set models.</p>
<p>In the References / Resources section that follows I have listed some web resources that may be of use in your research of managing hierarchal data, including a pair of PHP related resources that include pre-built PHP libraries for handling nested sets in MySQL. Those of you who currently use the adjacency list model and would like to experiment with the nested set model will find sample code for converting between the two in the <a href="http://www.sitepoint.com/article/hierarchical-data-database">Storing Hierarchical Data in a Database</a> resource listed below.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.wezu.net/blog/archives/44/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>vtiger CRM 安装问题总结</title>
		<link>http://www.wezu.net/blog/archives/41</link>
		<comments>http://www.wezu.net/blog/archives/41#comments</comments>
		<pubDate>Thu, 04 Sep 2008 04:24:55 +0000</pubDate>
		<dc:creator>snox</dc:creator>
				<category><![CDATA[默认分类]]></category>

		<guid isPermaLink="false">http://www.wezu.net/blog/?p=41</guid>
		<description><![CDATA[IMAP webMail的设置 webMail的中文正确显示 1. IMAP webMail的设置 以GMAIL为例 Mail Server Name or IP: imap. gmail. com Mail Protocol: IMAP2 SSL Options: SSL Certificate Validations: Do not validate 2. webMail的中文正确显示 修改 vtigercrm/modules/webmails/functions.php文件 在第一行新增 $charset = 'utf-8' 注释下面一行 global $charset;]]></description>
			<content:encoded><![CDATA[<ol>
<li>IMAP webMail的设置</li>
<li><span style="font-size: x-small;">webMail的中文正确显示</span></li>
</ol>
<p><span id="more-41"></span></p>
<p>1. IMAP webMail的设置</p>
<p>以GMAIL为例</p>
<p><span style="font-size: x-small;">Mail Server Name or IP: imap. gmail. com<br />
Mail Protocol: IMAP2<br />
SSL Options: SSL<br />
Certificate Validations: Do not validate </span></p>
<p>2. webMail的中文正确显示</p>
<p>修改 vtigercrm/modules/webmails/functions.php文件</p>
<p>在第一行新增<br />
<code>$charset = 'utf-8'</code><br />
注释下面一行<br />
<code>global $charset;</code></p>
]]></content:encoded>
			<wfw:commentRss>http://www.wezu.net/blog/archives/41/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>分布式文件系统</title>
		<link>http://www.wezu.net/blog/archives/37</link>
		<comments>http://www.wezu.net/blog/archives/37#comments</comments>
		<pubDate>Tue, 29 Jul 2008 09:38:27 +0000</pubDate>
		<dc:creator>snox</dc:creator>
				<category><![CDATA[默认分类]]></category>

		<guid isPermaLink="false">http://www.wezu.net/blog/?p=37</guid>
		<description><![CDATA[分布式文件系统（Distributed File System）是指文件系统管理的物理存储资源不一定直接连接在本地节点上，而是通过计算机网络与节点相连。 下面简单介绍分布式文件系统的历史与现状： 对象存储突破了高性能计算环境中存储系统的瓶颈，由此也引发了人们对分布式文件系统的关注。最初的分布式文件系统应用发生在20世纪70年代，之后逐渐扩展到各个领域。从早期的NFS到现在的StorageTank，分布式文件系统在体系结构、系统规模、性能、可扩展性、可用性等方面经历了较大的变化。 文件系统是操作系统的一个重要组成部分，通过对操作系统所管理的存储空间的抽象，向用户提供统一的、对象化的访问接口，屏蔽对物理设备的直接操作和资源管理。 根据计算环境和所提供功能的不同，文件系统可划分为四个层次，从低到高依次是：单处理器单用户的本地文件系统，如DOS的文件系统；多处理器单用户的本地文件系统，如OS/2的文件系统；多处理器多用户的文件系统，如Unix的本地文件系统；多处理器多用户的分布式文件系统。 本地文件系统（Local File System）是指文件系统管理的物理存储资源直接连接在本地节点上，处理器通过系统总线可以直接访问。分布式文件系统（Distributed File System）是指文件系统管理的物理存储资源不一定直接连接在本地节点上，而是通过计算机网络与节点相连。上述按照层次的分类中，高层次的文件系统都是以低层次的文件系统为基础，实现了更高级的功能。比如多处理器单用户的本地文件系统需要比单处理器单用户的本地文件系统多考虑并发控制（Concurrency Control），因为可能存在多个处理器同时访问文件系统的情况；多处理器多用户的文件系统需要比多处理器单用户的本地文件系统多考虑数据安全访问方面的设计，因为多个用户存在于同一个系统中，保证数据的授权访问是一个关键；多处理器多用户的分布式文件系统需要比多处理器多用户的文件系统多考虑分布式体系结构带来的诸多问题，比如同步访问、缓冲一致性等。 随着层次的提高，文件系统在设计和实现方面的难度也会成倍提高。但是，现在的分布式文件系统一般还是保持与最基本的本地文件系统几乎相同的访问接口和对象模型，这主要是为了向用户提供向后的兼容性，同时保持原来的简单对象模型和访问接口。但这并不说明文件系统设计和实现的难度没有增加。正是由于对用户透明地改变了结构，满足用户的需求，以掩盖分布式文件操作的复杂性，才大大增加了分布式文件系统的实现难度。 在计算机性能不断提升的同时，计算机部件的平均价格却在不断下降。用户可以用更低的成本，购买更好、更快、更稳定的设备。存储系统、文件系统面临的新挑战也随之而来：如何管理更多的设备，提供更好的性能，更加有效地降低管理成本等。各种新的存储技术和分布式文件技术层出不穷，以满足用户日益增长的需求。因此，有必要简要回顾分布式文件系统发展的历史，分析对比当前主流的分布式文件系统在体系结构、缓存一致性、安全等方面的长处和不足。 文件系统最初是用来管理本地磁盘，提供用户访问接口的。某些数据的集合叫做一个文件（File），并赋予每个文件一定的属性，以标识该数据集合的某些属性。文件按照树（Tree）结构层次进行管理和检索。最初的文件系统只能管理本地磁盘空间。主机之间的文件共享与传输则通过文件传输协议（FTP，File Transfer Protocol）实现。但FTP没有提供与本地文件系统一致的访问接口和对象模型。 随着计算机应用范围的扩展，通过文件访问接口在不同主机之间共享文件的需求日益增强。下面分为几个阶段介绍分布式文件系统的发展过程。 1 1980～1990年 早期的分布式文件系统一般以提供标准接口的远程文件访问为目的，更多地关注访问的性能和数据的可靠性。 早期的文件系统以NFS和AFS(Andrew File System)最具代表性，它们对以后的文件系统设计也具有十分重要的影响。 NFS从1985年出现至今，已经经历了四个版本的更新，被移植到了几乎所有主流的操作系统中，成为分布式文件系统事实上的标准。NFS利用Unix系统中的虚拟文件系统（Virtual File System，VFS）机制，将客户机对文件系统的请求，通过规范的文件访问协议和远程过程调用，转发到服务器端进行处理；服务器端在VFS之上，通过本地文件系统完成文件的处理，实现了全局的分布式文件系统。Sun公司公开了NFS的实施规范，互联网工程任务组（The Internet Engineering Task Force，IETF）将其列为征求意见稿（RFC-Request for Comments），这很大程度上促使NFS的很多设计实现方法成为标准，也促进了NFS的流行。NFS不断发展，在第四版中提供了基于租赁（Lease）的同步锁和基于会话（Session）语义的一致性等。 Carnegie Mellon大学在1983年设计开发的AFS将分布式文件系统的可扩展性放在了设计和实现的首要位置，并且着重考虑了在不安全的网络中实现安全访问的需求。因此，它在位置透明、用户迁移、与已有系统的兼容性等方面进行了特别设计。AFS具有很好的扩展性，能够很容易地支持数百个节点，甚至数千个节点的分布式环境。同时，在大规模的分布式文件系统中，AFS利用本地存储作为分布式文件的缓存，在远程文件无法访问时，依然可以部分工作，提高了系统可用性。后来的Coda File System、Inter-mezzo File System都受到AFS的影响，更加注重文件系统的高可用性（High Availability）和安全性，特别是Coda，在支持移动计算方面做了很多的研究工作。 Sprite File System也是早期比较有特色的分布式文件系统。它是Sprite Network Operation System的组成部分，为分布式计算环境提供全局文件访问。与NFS相比，Sprite File System在服务器端和客户端都设置缓存，大大提高了系统性能。它通过简单的读写锁保证整个系统的缓存一致性，并且通过和虚拟存储部分交互，尽量多地缓存数据。 早期的分布式文件系统一般以提供标准接口的远程文件访问为目的，在受网络环境、本地磁盘、处理器速度等方面限制的情况下，更多地关注访问的性能和数据的可靠性。AFS在系统结构方面进行了有意义的探索。它们所采用的协议和相关技术，为后来的分布式文件系统设计提供了很多借鉴。 PanFS的系统结构图 2 1990～1995年 20世纪90年代初，面对广域网和大容量存储需求，加利福尼亚大学设计开发的xFS借鉴了当时先进的高性能对称多处理器的设计思想。 20世纪90年代初，面对广域网和大容量存储应用的需求，借鉴当时先进的高性能对称多处理器的设计思想，加利福尼亚大学设计开发的xFS，克服了以前的分布式文件系统一般都运行在局域网（LAN）上的弱点，很好地解决了在广域网上进行缓存，以减少网络流量的难题。它所采用的多层次结构很好地利用了文件系统的局部访问的特性，无效写回（Invalidation-based [...]]]></description>
			<content:encoded><![CDATA[<p><strong>分布式文件系统</strong>（Distributed File System）是指文件系统管理的<span class="new">物理存储资源</span>不一定直接连接在本地<span class="new">节点上</span>，而是通过计算机网络与节点相连。</p>
<p>下面简单介绍分布式文件系统的历史与现状：</p>
<p>对象存储突破了高性能计算环境中存储系统的瓶颈，由此也引发了人们对分布式文件系统的关注。最初的分布式文件系统应用发生在20世纪70年代，之后逐渐扩展到各个领域。从早期的NFS到现在的<span class="new">StorageTank</span>，分布式文件系统在体系结构、系统规模、性能、可扩展性、可用性等方面经历了较大的变化。</p>
<p>文件系统是操作系统的一个重要组成部分，通过对操作系统所管理的存储空间的抽象，向用户提供统一的、对象化的访问接口，屏蔽对物理设备的直接操作和资源管理。</p>
<p>根据计算环境和所提供功能的不同，文件系统可划分为四个层次，从低到高依次是：<span class="new">单处理器</span>单用户的本地文件系统，如DOS的文件系统；多处理器单用户的本地文件系统，如OS/2的文件系统；多处理器多用户的文件系统，如Unix的本地文件系统；多处理器多用户的分布式文件系统。</p>
<p>本地文件系统（Local File System）是指文件系统管理的物理存储资源直接连接在本地节点上，处理器通过系统总线可以直接访问。分布式文件系统（Distributed File  System）是指文件系统管理的物理存储资源不一定直接连接在本地节点上，而是通过计算机网络与节点相连。上述按照层次的分类中，高层次的文件系统都是以低层次的文件系统为基础，实现了更高级的功能。比如多处理器单用户的本地文件系统需要比单处理器单用户的本地文件系统多考虑并发控制（Concurrency  Control），因为可能存在多个处理器同时访问文件系统的情况；多处理器多用户的文件系统需要比多处理器单用户的本地文件系统多考虑数据安全访问方面的设计，因为多个用户存在于同一个系统中，保证数据的授权访问是一个关键；多处理器多用户的分布式文件系统需要比多处理器多用户的文件系统多考虑分布式体系结构带来的诸多问题，比如同步访问、缓冲一致性等。</p>
<p>随着层次的提高，文件系统在设计和实现方面的难度也会成倍提高。但是，现在的分布式文件系统一般还是保持与最基本的本地文件系统几乎相同的访问接口和对象模型，这主要是为了向用户提供向后的兼容性，同时保持原来的简单对象模型和访问接口。但这并不说明文件系统设计和实现的难度没有增加。正是由于对用户透明地改变了结构，满足用户的需求，以掩盖分布式文件操作的复杂性，才大大增加了分布式文件系统的实现难度。</p>
<p>在计算机性能不断提升的同时，计算机部件的平均价格却在不断下降。用户可以用更低的成本，购买更好、更快、更稳定的设备。存储系统、文件系统面临的新挑战也随之而来：如何管理更多的设备，提供更好的性能，更加有效地降低管理成本等。各种新的存储技术和分布式文件技术层出不穷，以满足用户日益增长的需求。因此，有必要简要回顾分布式文件系统发展的历史，分析对比当前主流的分布式文件系统在体系结构、缓存一致性、安全等方面的长处和不足。</p>
<p>文件系统最初是用来管理本地磁盘，提供用户访问接口的。某些数据的集合叫做一个文件（File），并赋予每个文件一定的属性，以标识该数据集合的某些属性。文件按照树（Tree）结构层次进行管理和检索。最初的文件系统只能管理本地磁盘空间。主机之间的文件共享与传输则通过文件传输协议（FTP，File  Transfer Protocol）实现。但FTP没有提供与本地文件系统一致的访问接口和对象模型。</p>
<p>随着计算机应用范围的扩展，通过文件访问接口在不同主机之间共享文件的需求日益增强。<strong>下面分为几个阶段介绍分布式文件系统的发展过程。</strong></p>
<p><strong>1</strong></p>
<p>1980～1990年</p>
<p>早期的分布式文件系统一般以提供标准接口的远程文件访问为目的，更多地关注访问的性能和数据的可靠性。</p>
<p>早期的文件系统以NFS和AFS(Andrew File System)最具代表性，它们对以后的文件系统设计也具有十分重要的影响。</p>
<p>NFS从1985年出现至今，已经经历了四个版本的更新，被移植到了几乎所有主流的操作系统中，成为分布式文件系统事实上的标准。NFS利用Unix系统中的虚拟文件系统（Virtual  File  System，VFS）机制，将客户机对文件系统的请求，通过规范的文件访问协议和远程过程调用，转发到服务器端进行处理；服务器端在VFS之上，通过本地文件系统完成文件的处理，实现了全局的分布式文件系统。Sun公司公开了NFS的实施规范，互联网工程任务组（The  Internet Engineering Task Force，IETF）将其列为征求意见稿（RFC-Request for  Comments），这很大程度上促使NFS的很多设计实现方法成为标准，也促进了NFS的流行。NFS不断发展，在第四版中提供了基于租赁（Lease）的同步锁和基于会话（Session）语义的一致性等。</p>
<p>Carnegie  Mellon大学在1983年设计开发的AFS将分布式文件系统的可扩展性放在了设计和实现的首要位置，并且着重考虑了在不安全的网络中实现安全访问的需求。因此，它在位置透明、用户迁移、与已有系统的兼容性等方面进行了特别设计。AFS具有很好的扩展性，能够很容易地支持数百个节点，甚至数千个节点的分布式环境。同时，在大规模的分布式文件系统中，AFS利用本地存储作为分布式文件的缓存，在远程文件无法访问时，依然可以部分工作，提高了系统可用性。后来的Coda  File System、Inter-mezzo File System都受到AFS的影响，更加注重文件系统的高可用性（High  Availability）和安全性，特别是Coda，在支持移动计算方面做了很多的研究工作。</p>
<p>Sprite File System也是早期比较有特色的分布式文件系统。它是Sprite Network Operation  System的组成部分，为分布式计算环境提供全局文件访问。与NFS相比，Sprite File  System在服务器端和客户端都设置缓存，大大提高了系统性能。它通过简单的读写锁保证整个系统的缓存一致性，并且通过和虚拟存储部分交互，尽量多地缓存数据。</p>
<p>早期的分布式文件系统一般以提供标准接口的远程文件访问为目的，在受网络环境、本地磁盘、处理器速度等方面限制的情况下，更多地关注访问的性能和数据的可靠性。AFS在系统结构方面进行了有意义的探索。它们所采用的协议和相关技术，为后来的分布式文件系统设计提供了很多借鉴。</p>
<p>PanFS的系统结构图</p>
<p><strong>2</strong><br />
1990～1995年</p>
<p>20世纪90年代初，面对广域网和大容量存储需求，加利福尼亚大学设计开发的xFS借鉴了当时先进的高性能对称多处理器的设计思想。</p>
<p>20世纪90年代初，面对广域网和大容量存储应用的需求，借鉴当时先进的高性能对称多处理器的设计思想，加利福尼亚大学设计开发的xFS，克服了以前的分布式文件系统一般都运行在局域网（LAN）上的弱点，很好地解决了在广域网上进行缓存，以减少网络流量的难题。它所采用的多层次结构很好地利用了文件系统的局部访问的特性，无效写回（Invalidation-based  Write Back）缓存一致性协议，减少了网络负载。对本地主机和本地存储空间的有效利用，使它具有较好的性能。</p>
<p>Tiger  Shark并行文件系统是针对大规模实时多媒体应用设计的。它采用了多种技术策略保证多媒体传输的实时性和稳定性：采用资源预留和优化的调度手段，保证数据实时访问性能；通过加大文件系统数据块的大小，最大限度地发挥磁盘的传输效率；通过将大文件分片存储在多个存储设备中，取得尽量大的并行吞吐率；通过复制文件系统元数据和文件数据，克服单点故障，提高系统可用性。</p>
<p>基于虚拟共享磁盘Petal的Frangipani分布式文件系统，采用了一种新颖的系统结构—分层次的存储系统。Petal提供一个可以全局统一访问的磁盘空间。Frangipani基于Petal的特性提供文件系统的服务。这种分层结构使两者的设计实现都得到了简化。在Frangipani中，每个客户端也是文件系统服务器，参与文件系统的管理，可以平等地访问Petal提供的虚拟磁盘系统，并通过分布式锁实现同步访问控制。分层结构使系统具有很好的扩展性，可以在线动态地添加存储设备，增加新用户、备份等，同时系统具有很好的机制来处理节点失效、网络失效等故障，提高了系统的可用性。</p>
<p>Slice File  System（SFS）考虑标准的NFS在容量、性能方面存在的限制，采用在客户机和服务器之间架设一个μproxy中间转发器，以提高性能和可扩展性。它将客户端的访问分为小文件、元数据服务、大文件数据三类请求。通过μproxy将前两种请求转发到不同的文件服务器上，将后者直接发送到存储服务器上。这样SFS系统就可以支持多个存储服务器，提高整个系统的容量和性能。μproxy根据请求内容的转发是静态的，对于整个系统中负载的变化难以做出及时反应。</p>
<p><strong>3</strong></p>
<p>1995～2000年</p>
<p>网络技术的发展和普及应用极大地推动了网络存储技术的发展，基于光纤通道的SAN、NAS得到了广泛应用。这也推动了分布式文件系统的研究。</p>
<p>Cluster FS的Lustre系统结构图</p>
<p>在这个阶段，计算机技术和网络技术有了突飞猛进的发展，单位存储的成本大幅降低。而数据总线带宽、磁盘速度的增长无法满足应用对数据带宽的需求，存储子系统成为计算机系统发展的瓶颈。</p>
<p>网络技术的发展和普及应用极大地推动了网络存储技术的发展，基于光纤通道的SAN、NAS得到了广泛应用。这也推动了分布式文件系统的研究。这个阶段，出现了多种体系结构，充分利用了网络技术。</p>
<p>Global File  System（GFS）吸取了对称多处理器（SMP）系统设计和实现的原理，将系统中的每一个客户机类比于SMP中的一个处理器。客户机间没有任何区别，可以平等地访问系统中的所有存储设备，就像处理器可以机会均等地访问主存一样。这样的设计可以更好地利用系统中的资源，消除单个服务器带来的性能瓶颈和单点失效问题。客户端之间无需通信，因此可以很好地消除客户机失效带来的威胁。GFS采用特殊设计的DLOCK锁机制，同步多个客户机对同一设备的访问，具有很高的效率。</p>
<p>General Parallel File System（GPFS）是从Tiger  Shark发展过来的，是目前应用范围较广的一个系统。GPFS在系统设计中采用了多项先进技术。它是一个共享磁盘（Shared-disk）的分布式并行文件系统，客户端采用基于光纤通道或者iSCSI与存储设备相连，也可以通过通用网络相连。GPFS的磁盘数据结构可以支持大容量的文件系统和大文件，通过采用分片存储、较大的文件系统块、数据预读等方法获得了较高的数据吞吐率；采用扩展哈希（Extensible  Hashing）技术支持含有大量文件和子目录的大目录，提高文件的查找和检索效率。GPFS采用分布式锁解决系统中的并发访问和数据同步问题：字节范围的锁用于用户数据的同步，动态选择元数据节点（Metanode）进行元数据的集中管理；分布式锁管理整个系统的空间分配等。GPFS采用日志技术对系统进行在线灾难恢复。每个节点都有各自独立的日志，且单个节点失效时，系统中的其他节点可以代替失效节点检查文件系统日志，进行元数据恢复操作。</p>
<p>惠普的DiFFS和SGI公司的CXFS都是基于SAN的分布式文件系统。DiFFS通过将存储系统划分成不同的区域，把对资源的共享访问冲突限制在各个区域内部，以解决机群文件系统的可扩展性问题。DiFFS采用了动态分配策略和文件级的负载平衡等多项技术。CXFS是在XFS的基础上开发的，实现了元数据服务器内置的失效接替和恢复功能；采用快速元数据算法，提高元数据的访问性能。</p>
<p>此外，还有多种体系结构，如EMC的HighRoad、Sun的qFS、XNFS等。数据容量、性能和共享的需求使得这一时期的分布式文件系统管理的系统规模更大、系统更复杂，对物理设备的直接访问、磁盘布局和检索效率的优化、元数据的集中管理等都反映了对性能和容量的追求。规模的扩展使得系统的动态性，如在线增减设备、缓存的一致性、系统可靠性的需求逐渐增强，更多的先进技术应用到系统实现中，如分布式锁、缓存管理技术、SoftUpdates技术、文件级的负载平衡等。<br />
<strong>4</strong></p>
<p>2000年以后</p>
<p>随着SAN和NAS两种结构逐渐成熟，研究人员开始考虑如何将两种结构结合起来。网格的研究成果等也推动了分布式文件系统体系结构的发展。</p>
<p>随着SAN和NAS两种体系结构逐渐成熟，研究人员开始考虑如何将两种体系结构结合起来，以充分利用两者的优势。另一方面，基于多种分布式文件系统的研究成果，人们对体系结构的认识不断深入，网格的研究成果等也推动了分布式文件系统体系结构的发展。这一时期，IBM的StorageTank、Cluster的Lustre、Panasas的PanFS、蓝鲸文件系统（BWFS）等是这种体系结构的代表。各种应用对存储系统提出了更多的需求：</p>
<p>大容量—现在的数据量比以前任何时期更多，生成的速度更快；</p>
<p>高性能—数据访问需要更高的带宽；</p>
<p>高可用性—不仅要保证数据的高可用性，还要保证服务的高可用性；</p>
<p>可扩展性—应用在不断变化，系统规模也在不断变化，这就要求系统提供很好的扩展性，并在容量、性能、管理等方面都能适应应用的变化；</p>
<p>可管理性—随着数据量的飞速增长，存储的规模越来越庞大，存储系统本身也越来越复杂，这给系统的管理、运行带来了很高的维护成本；</p>
<p>按需服务—能够按照应用需求的不同提供不同的服务，如不同的应用、不同的客户端环境、不同的性能等。</p>
<p>IBM公司在<span class="new">GPFS</span>的基础上发展进化来的Storage  Tank，以及基于Storage Tank的TotalStorage SAN File  System，又将分布式文件系统的设计理念和系统架构向前推进了一步。它们除了具有一般的分布式文件系统的特性之外，还采用SAN作为整个文件系统的数据存储和传输路径。它们采用带外（out-of-band）结构，将文件系统元数据在高速以太网上传输，由专门的元数据服务器来处理和存储。文件系统元数据和文件数据的分离管理和存储，可以更好地利用各自存储设备和传输网络的特性，提高系统的性能，有效降低系统的成本。在TotalStorage中，块虚拟层将整个SAN的存储进行统一的虚拟管理，为文件系统提供统一的存储空间。SAN  File System采用了基于策略的文件数据位置选择方法，能有效地利用系统的资源，提高性能，降低成本。</p>
<p>处于这个阶段的系统都在研究中，但从中也可以看出一些发展趋势：体系结构的研究逐渐成熟，表现在不同文件系统的体系结构趋于一致；系统设计的策略基本一致，如采用专用服务器方式等；每个系统在设计的细节上各自采用了很多特有的先进技术，也都取得了很好的性能和扩展性。另外，在协议方面的探索也是研究的热点之一，如Direct  Access File System利用了远程内存直接访问的特性，借鉴了NFS第四版本和Common Internet File  System等协议，设计了一套新的网络文件访问协议。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.wezu.net/blog/archives/37/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Int 13 中断功能</title>
		<link>http://www.wezu.net/blog/archives/15</link>
		<comments>http://www.wezu.net/blog/archives/15#comments</comments>
		<pubDate>Wed, 12 Mar 2008 12:16:08 +0000</pubDate>
		<dc:creator>snox</dc:creator>
				<category><![CDATA[默认分类]]></category>

		<guid isPermaLink="false">http://www.wezu.net/blog/?p=15</guid>
		<description><![CDATA[INT 13H，AH=00H 软、硬盘控制器复位 说明： 此功能复位磁盘（软盘和硬盘）控制器板和磁盘驱动器，它在磁盘控制器芯片上完成复位操场作并在磁盘进行所需的操作之前做一系列用于磁盘校准的磁盘操作。 当磁盘I/O功能调用出现错误时，需要调用此功能，此刻复位功能将使BIOS象该磁盘重新插入一样检查驱动器中磁盘状态，并将磁头校准使之在应该在的位置上。 此功能调用不影响软盘或硬盘上的数据。 入口参数： AH=00H 指明调用复位磁盘功能。 DL 需要复位的驱动器号。 返回参数： 若产生错误，进位标志CF=1，错误码在AH寄存器。详情请见磁盘错误状态返回码一文。 示例： C_SEG SEGMENT PUBLIC ASSUME CS:C_SEG,DS:C_SEG ORG 100H START: MOV AH, 00H MOV DL, 80H INT 13H ;复位硬盘 C JC ERROR …… ERROR: …… C_SEG ENDS END START INT 13H，AH=02H 读扇区说明： 调用此功能将从磁盘上把一个或更多的扇区内容读进存贮器。因为这是一个低级功能，在一个操作中读取的全部扇区必须在同一条磁道上（磁头号和磁道号相同）。BIOS不能自动地从一条磁道末尾切换到另一条磁道开始，因此用户必须 把跨多条磁道的读操作分为若干条单磁道读操作。 入口参数： AH=02H 指明调用读扇区功能。 AL 置要读的扇区数目，不允许使用读磁道末端以外的数值，也不允许使该寄存器为0。 DL 需要进行读操作的驱动器号。 DH [...]]]></description>
			<content:encoded><![CDATA[<p>INT 13H，AH=00H 软、硬盘控制器复位</p>
<p>说明：<br />
此功能复位磁盘（软盘和硬盘）控制器板和磁盘驱动器，它在磁盘控制器芯片上完成复位操场作并在磁盘进行所需的操作之前做一系列用于磁盘校准的磁盘操作。<br />
当磁盘I/O功能调用出现错误时，需要调用此功能，此刻复位功能将使BIOS象该磁盘重新插入一样检查驱动器中磁盘状态，并将磁头校准使之在应该在的位置上。<br />
此功能调用不影响软盘或硬盘上的数据。<br />
入口参数：<br />
AH=00H 指明调用复位磁盘功能。<br />
DL 需要复位的驱动器号。<br />
返回参数：<br />
若产生错误，进位标志CF=1，错误码在AH寄存器。详情请见磁盘错误状态返回码一文。<br />
示例：<br />
C_SEG SEGMENT PUBLIC<br />
ASSUME CS:C_SEG,DS:C_SEG<br />
ORG 100H<br />
START: MOV AH, 00H<br />
MOV DL, 80H<br />
INT 13H<br />
;复位硬盘 C<br />
JC ERROR<br />
……<br />
ERROR: ……<br />
C_SEG ENDS<br />
END START</p>
<p>INT 13H，AH=02H 读扇区说明：<br />
调用此功能将从磁盘上把一个或更多的扇区内容读进存贮器。因为这是一个低级功能，在一个操作中读取的全部扇区必须在同一条磁道上（磁头号和磁道号相同）。BIOS不能自动地从一条磁道末尾切换到另一条磁道开始，因此用户必须<br />
把跨多条磁道的读操作分为若干条单磁道读操作。<br />
入口参数：<br />
AH=02H 指明调用读扇区功能。<br />
AL 置要读的扇区数目，不允许使用读磁道末端以外的数值，也不允许使该寄存器为0。<br />
DL 需要进行读操作的驱动器号。<br />
DH 所读磁盘的磁头号。<br />
CH 磁道号的低8位数。<br />
CL 低5位放入所读起始扇区号，位7-6表示磁道号的高2位。<br />
ES:BX 读出数据的缓冲区地址。<br />
返回参数：<br />
如果CF=1，AX中存放出错状态。读出后的数据在ES:BX区域依次排列。详情请参见磁盘错误状态返回码一文。<br />
示例：<br />
C_SEG SEGMENT PUBLIC<br />
ASSUME CS:C_SEG,DS:C_SEG<br />
ORG 100H<br />
START: JMP READ<br />
BUFFER DB 512 DUP(0)<br />
READ: PUSH CS<br />
POP ES<br />
MOV BX, OFFSET BUFFER<br />
MOV AX, 0201H<br />
MOV CX, 0001H<br />
MOV DX, 0000H<br />
INT 13H<br />
;读软盘A, 0面0道1扇区<br />
;读出后数据在BUFFER中<br />
JC ERROR<br />
……<br />
ERROR: ……<br />
C_SEG ENDS<br />
END START</p>
<p>INT 13H，AH=03H 写扇区</p>
<p>说明：<br />
调用此功能将从磁盘上把一个或更多的扇区内容写入驱动器。因为这是一个低级功能，在一个写入操作中的全部扇区必须在同一条磁道上（磁头号和磁道号相同）。BIOS不能自动地从一条磁道末尾切换到另一条磁道开始，因此用户必须把跨多条磁道的写操作分为若干条单磁道写操作。<br />
入口参数：<br />
AH=03H 指明调用写扇区功能。<br />
AL 置要写的扇区数目，不允许使用超出磁道末端以外的数值，也不允许使该寄存器为0。<br />
DL 需要进行写操作的驱动器号。<br />
DH 所写磁盘的磁头号。<br />
CH 磁道号的低8位数。<br />
CL 低5位放入所读起始扇区号，位7-6表示磁道号的高2位。<br />
ES:BX 放置写入数据的存贮区地址。<br />
返回参数：<br />
如果CF=1，AX中存放出错状态。详情请参见磁盘错误状态返回码一文。<br />
示例：<br />
C_SEG SEGMENT PUBLIC<br />
ASSUME CS:C_SEG,DS:C_SEG<br />
ORG 100H<br />
START: JMP WRITE<br />
BUFFER DB 512 DUP(0FFH)<br />
WRITE: PUSH CS<br />
POP ES<br />
MOV BX, OFFSET BUFFER<br />
MOV AX, 0301H<br />
MOV CX, 0001H<br />
MOV DX, 0000H<br />
INT 13H<br />
;写入软盘A, 0面0道1扇区<br />
;把此扇区数据全部置为0FFH<br />
JC ERROR<br />
……<br />
ERROR: ……<br />
C_SEG ENDS<br />
END START<br />
INT 13H，AH=04H 检测扇区</p>
<p>说明：<br />
这个功能检测磁盘上1个或更多的扇区。这个验证测试不是把磁盘上的数据和内存中的数据进行比较，而只是简单地确定读出的数据有无CRC错误。<br />
这个功能可用来验证驱动器中的软盘版。如果盘片的格式正确，CF=0。<br />
入口参数：<br />
AH=03H 指明调用检测扇区功能。<br />
AL 置要检测的连续扇区数目，不允许使用超出磁道末端以外的数值，也不允许使该寄存器为0。<br />
DL 需要进行检测的驱动器号。<br />
DH 磁盘的磁头号。<br />
CH 磁道号的低8位数。<br />
CL 低5位放入起始扇区号，位7-6表示磁道号的高2位。<br />
返回参数：<br />
如果CF=1，AX中存放出错状态。CF=0，检测正确。详情请参见磁盘错误状态返回码一文。<br />
示例：<br />
C_SEG SEGMENT PUBLIC<br />
ASSUME CS:C_SEG,DS:C_SEG<br />
ORG 100H<br />
START: MOV AX, 0401H<br />
MOV CX, 0001H<br />
MOV DX, 0000H<br />
INT 13H<br />
;检测软盘A, 0面0道1扇区<br />
JC ERROR<br />
……<br />
ERROR: ……<br />
C_SEG ENDS<br />
END START</p>
<p>磁盘错误状态返回码:</p>
<p>磁盘错误状态</p>
<p>AH=<br />
00H 未出错<br />
01H 非法功能调用命令区。<br />
02H 地址标记损坏，扇区标识（ID）无效或未找到。<br />
03H 企图对有写保护的软盘执行写操作。<br />
04H 所寻找的扇区没找到。<br />
05H 复位操作失败。<br />
06H 无介质。<br />
07H 初始化错误，数据未存在DMA的64K缓冲区内。<br />
08H DMA故障<br />
09H DMA边界错误，数据未存在DMA的64K缓冲区内。<br />
0AH 检测出错误码率的扇区标志。<br />
0BH 所寻找的磁道没找到。<br />
0CH 介质类型没发现。<br />
0DH 扇区号有问题。<br />
0EH 发现控制数据地址标记。<br />
0FH 超出DMA边界<br />
10H 读磁盘时奇偶校验错，且纠错码（EDC）不能纠正。<br />
11H 读磁盘时奇偶校验错，但纠错码（EDC）已纠正错误。<br />
20H 控制器错。<br />
40H 查找操作无效。<br />
80H 超时错误，驱动器不响应。<br />
AAH 驱动器未准备好。<br />
BBH 不明错误。<br />
CCH 被选驱动器出现写故障。<br />
E0H 错误寄存器是零<br />
FFH 非法操作。</p>
<p>备注：<br />
控制器的最后状态将会在磁盘操作完成后写入相应的BIOS数据区（40：41）</p>
]]></content:encoded>
			<wfw:commentRss>http://www.wezu.net/blog/archives/15/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Using the Windows Headers</title>
		<link>http://www.wezu.net/blog/archives/13</link>
		<comments>http://www.wezu.net/blog/archives/13#comments</comments>
		<pubDate>Fri, 22 Feb 2008 05:46:32 +0000</pubDate>
		<dc:creator>snox</dc:creator>
				<category><![CDATA[默认分类]]></category>

		<guid isPermaLink="false">http://www.wezu.net/blog/?p=13</guid>
		<description><![CDATA[From: MSDN Windows API 头文件允许您创建32- 和64-位应用程序。它们包含了Unicode 和 ANSI 版本的 API声明。更多的信息可参见 Unicode in the Windows API。They use data types that allow you to build both 32- and 64-bit versions of your application from a single source code base. For more information, see Getting Ready for 64-bit Windows. Additional features include Header Annotations and STRICT Type Checking. Microsoft Visual [...]]]></description>
			<content:encoded><![CDATA[<p xmlns:msxsl="urn:schemas-microsoft-com:xslt" class="title">From: <a href="http://msdn2.microsoft.com/en-us/library/aa383745.aspx">MSDN</a></p>
<p><!--Content type: HTML. Transform: psdk2mtps.xslt.--></p>
<p class="clsServerSDKContent">Windows API 头文件允许您创建32- 和64-位应用程序。它们包含了Unicode 和 ANSI 版本的 API声明。更多的信息可参见 <a href="http://msdn2.microsoft.com/en-us/library/ms776442(VS.85).aspx" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl01',this);" id="ctl00_rs1_mainContentContainer_ctl01">Unicode in the Windows API</a>。They use data types that allow you to build both 32- and 64-bit versions of your application from a single source code base. For more information, see <a href="http://msdn2.microsoft.com/en-us/library/aa384198(VS.85).aspx" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl02',this);" id="ctl00_rs1_mainContentContainer_ctl02">Getting Ready for 64-bit Windows</a>. Additional features include <a href="http://msdn2.microsoft.com/en-us/library/aa383701(VS.85).aspx" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl03',this);" id="ctl00_rs1_mainContentContainer_ctl03">Header Annotations</a> and <a href="http://msdn2.microsoft.com/en-us/library/aa383732(VS.85).aspx" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl04',this);" id="ctl00_rs1_mainContentContainer_ctl04">STRICT Type Checking</a>.</p>
<p>Microsoft Visual C++ includes copies of the Windows header files that were current at the time Visual C++ was released. Therefore, if you install updated header files from an SDK, you may end up with multiple versions of the Windows header files on your computer. If you do not ensure that you are using the latest version of the SDK header files, you will receive the following error code when compiling code that uses features that were introduced after Visual C++ was released: error C2065: undeclared identifier.</p>
<h4>条件申明<a name="conditional_declarations" title="conditional_declarations"></a><!----></h4>
<p>某些函数在部分Windows版本才具备，使用条件代码来声明它们。您就可以使用编译器来检测这些函数是否被目标Windows版本支持。必须定义适当的宏来编译使用到这些函数的应用程序。否则，你会收到C2065的出错信息。</p>
<p>Windows头文件使用宏来决定</p>
<p>The Windows header files use macros to indicate which versions of Windows support many programming elements. Therefore, you must define these macros to use new functionality introduced in each major operating system release. (Individual header files may use different macros; therefore, if compilation problems occur, check the header file that contains the definition for conditional definitions.) For more information, see SdkDdkver.h.</p>
<p>下表描述了Windows 头文件中的首选宏。</p>
<table class="clsStd">
<tr>
<th>最小系统需求</th>
<th>NTDDI_VERSION</th>
</tr>
<tr>
<td>Windows Server 2008</td>
<td>NTDDI_WS08</td>
</tr>
<tr>
<td>Windows Vista SP1</td>
<td>NTDDI_VISTASP1</td>
</tr>
<tr>
<td>Windows Vista</td>
<td>NTDDI_VISTA</td>
</tr>
<tr>
<td>Windows Server 2003 SP1</td>
<td>NTDDI_WS03SP1</td>
</tr>
<tr>
<td>Windows Server 2003</td>
<td>NTDDI_WS03</td>
</tr>
<tr>
<td>Windows XP SP2</td>
<td>NTDDI_WINXPSP2</td>
</tr>
<tr>
<td>Windows XP SP1</td>
<td>NTDDI_WINXPSP1</td>
</tr>
<tr>
<td>Windows XP</td>
<td>NTDDI_WINXP</td>
</tr>
<tr>
<td>Windows 2000 SP4</td>
<td>NTDDI_WIN2KSP4</td>
</tr>
<tr>
<td>Windows 2000 SP3</td>
<td>NTDDI_WIN2KSP3</td>
</tr>
<tr>
<td>Windows 2000 SP2</td>
<td>NTDDI_WIN2KSP2</td>
</tr>
<tr>
<td>Windows 2000 SP1</td>
<td>NTDDI_WIN2KSP1</td>
</tr>
<tr>
<td>Windows 2000</td>
<td>NTDDI_WIN2K</td>
</tr>
</table>
<p>下表描述了在Windows头文件中使用的宏。</p>
<table class="clsStd">
<tr>
<th>最小系统需求</th>
<th>_WIN32_WINNT 和 WINVER的最小值</th>
</tr>
<tr>
<td>Windows Server 2008</td>
<td>0&#215;0600</td>
</tr>
<tr>
<td>Windows Vista</td>
<td>0&#215;0600</td>
</tr>
<tr>
<td>Windows Server 2003 SP1, Windows XP SP2</td>
<td>0&#215;0502</td>
</tr>
<tr>
<td>Windows Server 2003, Windows XP</td>
<td>0&#215;0501</td>
</tr>
<tr>
<td>Windows 2000</td>
<td>0&#215;0500</td>
</tr>
</table>
<table class="clsStd">
<tr>
<th>最小版本需求</th>
<th>_WIN32_IE的最小值</th>
</tr>
<tr>
<td>Internet Explorer 7.0</td>
<td>0&#215;0700</td>
</tr>
<tr>
<td>Internet Explorer 6.0 SP2</td>
<td>0&#215;0603</td>
</tr>
<tr>
<td>Internet Explorer 6.0 SP1</td>
<td>0&#215;0601</td>
</tr>
<tr>
<td>Internet Explorer 6.0</td>
<td>0&#215;0600</td>
</tr>
<tr>
<td>Internet Explorer 5.5</td>
<td>0&#215;0550</td>
</tr>
<tr>
<td>Internet Explorer 5.01</td>
<td>0&#215;0501</td>
</tr>
<tr>
<td>Internet Explorer 5.0, 5.0a, 5.0b</td>
<td>0&#215;0500</td>
</tr>
</table>
<p>Note that some features introduced in the latest version of Windows may be added to a service pack for a previous version of Windows. Therefore, to target a service pack, you may need to define _WIN32_WINNT with the value for the next major operating system release. For example, the <a href="http://msdn2.microsoft.com/en-us/library/ms683186(VS.85).aspx" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl05',this);" id="ctl00_rs1_mainContentContainer_ctl05"><strong>GetDllDirectory</strong></a> function was introduced in Windows Server 2003 and is conditionally defined if _WIN32_WINNT is 0&#215;0502 or greater. This function was also added to Windows XP SP1. Therefore, if you were to define _WIN32_WINNT 0&#215;0501 to target Windows XP, you would miss features that are defined in Windows XP SP1.</p>
<p>You can define these symbols by using the #define statement in each source file, or by specifying the /D compiler option supported by Visual C++. To specify compiler options, go to the <strong>Projects</strong> menu and click <strong>Properties</strong>. Go to <strong>Configuration Properties</strong>, then <strong>C++</strong>, then <strong>Command Line</strong>. Enter the option under <strong>Additional Options</strong>.</p>
<h4><a name="controlling_structure_packing" title="controlling_structure_packing"></a><!---->控制结构包</h4>
<p>Projects should be compiled to use the default structure packing, which is currently 8 bytes because the largest integral type is 8 bytes. Doing so ensures that all structure types within the header files are compiled into the application with the same alignment the Windows API expects. It also ensures that structures with 8-byte values are properly aligned and will not cause alignment faults on processors that enforce data alignment.</p>
<p>With Microsoft Visual C++ 2005, structure packing is controlled by the /Zp switch. When this switch is omitted from a project build, the compiler automatically uses the default structure packing size.</p>
<p>If you are using a packing setting other than the default, be sure to use a pragma pack statement as shown in the following example to ensure that the Windows header files are packed correctly.</p>
<pre lang="c"> </pre>
<h4><!---->用最少的头文件来快速编译</h4>
<p>You can reduce the size of the Windows header files by excluding some of the less common API declarations as follows:</p>
<ul>
<li>Define WIN32_LEAN_AND_MEAN to exclude APIs such as Cryptography, DDE, RPC, Shell, and Windows Sockets.</li>
<li>Define one or more of the NO<em>api</em> symbols to exclude the API. For example, NOCOMM excludes the serial communication API. For a list of support NO<em>api</em> symbols, see Windows.h.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.wezu.net/blog/archives/13/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>在中间层驱动中处理IRP</title>
		<link>http://www.wezu.net/blog/archives/11</link>
		<comments>http://www.wezu.net/blog/archives/11#comments</comments>
		<pubDate>Thu, 21 Feb 2008 07:06:15 +0000</pubDate>
		<dc:creator>snox</dc:creator>
				<category><![CDATA[默认分类]]></category>

		<guid isPermaLink="false">http://www.wezu.net/blog/?p=11</guid>
		<description><![CDATA[Higher-level drivers have a different set of standard routines than lowest-level device drivers, with an overlapping subset of standard routines common to both types of drivers. The set of routines for intermediate and highest-level drivers also varies according to the following criteria: The nature of the underlying physical device Whether an underlying device driver sets [...]]]></description>
			<content:encoded><![CDATA[<p>Higher-level drivers have a different set of standard routines than lowest-level device drivers, with an overlapping subset of standard routines common to both types of drivers.</p>
<p>The set of routines for intermediate and highest-level drivers also varies according to the following criteria:</p>
<ul type="disc">
<li>The nature of the underlying physical device</li>
<li>Whether an underlying device driver sets up device objects for direct or buffered I/O</li>
<li>The design of the individual higher-level driver</li>
</ul>
<p>The following figure illustrates the path an IRP might take through the standard routines of an intermediate <mshelp:link keywords="glossary_b55850e8-07b6-4a3e-a0f5-b6578937e3ae.xml">mirror driver</mshelp:link> layered somewhere over the lowest-level device driver described in the previous section.</p>
<p>下图展示的驱动有下面的特性：</p>
<ul type="disc">
<li>The driver is layered over more than one physical device and possibly over more than one device driver.</li>
<li>The driver sometimes allocates additional IRPs for lower-level drivers, depending on the requested operation in the input IRP.</li>
<li>The driver has at least one file system driver layered above it, and that file system driver might be layered over other intermediate drivers at a higher level than this one.</li>
</ul>
<p><a href="http://www.wezu.net/blog/wp-content/uploads/2008/02/4hiddirp.jpg" title="4hiddirp.jpg"><img src="http://www.wezu.net/blog/wp-content/uploads/2008/02/4hiddirp.jpg" alt="4hiddirp.jpg" /></a></p>
<p><strong><a name="127022c1-b767-438e-a0eb-c043c8169921" title="127022c1-b767-438e-a0eb-c043c8169921"></a>IRP Path through Intermediate Driver Routines </strong></p>
<p>As the figure shows, the I/O manager creates an IRP and sends it to the driver&#8217;s dispatch routine for the given major function code. Assuming the function code is IRP_MJ_WRITE, the dispatch routine is <strong>DDDispatchWrite</strong>. The intermediate driver&#8217;s I/O stack location is shown in the middle, with an indefinite number of I/O stack locations for higher- and lower-level drivers shown shaded.</p>
<h3><a name="4aaf5aa2-f542-4fad-99a0-433ab22004ee" title="4aaf5aa2-f542-4fad-99a0-433ab22004ee"></a>分派IRPs</h3>
<p>The mirror driver&#8217;s purpose is to send write requests to several physical devices, and to send read requests alternately to the drivers of these devices. For write requests, the driver creates duplicate IRPs for each device on which the data is to be written, assuming the parameters in the input IRP are valid.</p>
<p>The previous figure shows a call to <mshelp:link keywords="k104_326eb80d-9bc3-4a91-9f33-710f7975808a.xml"><strong>IoAllocateIrp</strong></mshelp:link> but higher-level drivers can call other support routines to allocate IRPs for lower-level drivers. See <a href="ms-help://MS.WDK.v10.6001.071220/Kernel_d/hh/Kernel_d/IRPs_aa21cfd8-9bd3-461f-b42d-44b8b850e466.xml.htm">Creating IRPs for Lower-Level Drivers</a>.</p>
<p>When the dispatch routine calls <strong>IoAllocateIrp</strong>, it specifies the number of I/O stack locations needed for the IRP. The driver must specify a stack location for each lower driver in the chain, getting the appropriate value from the device objects of each driver just below the mirror driver. Optionally, the driver can add one to this value when it calls <strong>IoAllocateIrp</strong> to get a stack location of its own for each IRP it allocates, as the driver in the previous figure does.</p>
<p>This intermediate driver&#8217;s dispatch routine calls <mshelp:link keywords="k104_f92e53c7-8a26-4cb0-859b-b0a7be4dce6a.xml"><strong>IoGetCurrentIrpStackLocation</strong></mshelp:link> (not shown) with the original IRP, to check parameters.</p>
<p>It calls <mshelp:link keywords="k104_34f06977-f176-4590-9d5a-adffebbdd9b9.xml"><strong>IoSetNextIrpStackLocation</strong></mshelp:link> because it allocated its own stack location in each newly created IRP and <mshelp:link keywords="k104_f92e53c7-8a26-4cb0-859b-b0a7be4dce6a.xml"><strong>IoGetCurrentIrpStackLocation</strong></mshelp:link> to create a context for itself that it uses later in the <em>IoCompletion</em> routine.</p>
<p>Next, it calls <mshelp:link keywords="k104_28da61f1-a732-4336-a14a-90a5bd2682c3.xml"><strong>IoGetNextIrpStackLocation</strong></mshelp:link> with each newly created IRP so that it can set up the next lower-level drivers&#8217; I/O stack locations in the IRPs it allocated. The mirror driver&#8217;s dispatch routine copies the IRP function codes and parameters (pointer to the transfer buffer, length in bytes to be transferred for IRP_MJ_WRITE) into the I/O stack locations for the next-lower drivers. These drivers, in turn, will set up the I/O stack locations for the drivers just below them, if any.</p>
<h3><a name="f31e0d27-af67-4541-b7c1-ee0e11f722fa" title="f31e0d27-af67-4541-b7c1-ee0e11f722fa"></a>调用IoSetCompletionRoutine 和 IoCallDriver</h3>
<p>The dispatch routine in the previous figure calls <mshelp:link keywords="k104_cbc51352-796e-4b64-9725-7d8a08c4aea9.xml"><strong>IoSetCompletionRoutine</strong></mshelp:link> for each IRP it allocated. Because the driver in the previous figure must dispose of the IRPs it allocated, this driver sets its <em>IoCompletion</em> routine to be called when lower drivers complete its IRPs, whether the I/O operation completed successfully, failed, or was canceled.</p>
<p>Because the driver in the previous figure mirrors in parallel, it passes both IRPs that it allocated on to the next-lower-level drivers by calling <mshelp:link keywords="k104_8579a946-2f96-455f-825c-c3f86caba99c.xml"><strong>IoCallDriver</strong></mshelp:link> twice, once for each target device object representing a mirrored partition.</p>
<h3><a name="1027a8d5-39f8-44bc-a52a-efa7bf793dbe" title="1027a8d5-39f8-44bc-a52a-efa7bf793dbe"></a>在驱动 <em>IoCompletion</em> 函数中处理 IRPs</h3>
<p>When either set of lower-level drivers completes the requested operation, the I/O manager calls the intermediate mirror driver&#8217;s <em>IoCompletion</em> routine. The mirror driver maintains a count in its own I/O stack location for the original IRP, to track when the lower drivers have completed all the duplicate IRPs.</p>
<p>Assuming that the I/O status block indicates that one set of lower drivers has completed the duplicate IRP shown in the <a href="ms-help://MS.WDK.v10.6001.071220/Kernel_d/hh/Kernel_d/IRPs_124b0051-9657-4667-9e3f-b5501ae598d2.xml.htm#127022c1-b767-438e-a0eb-c043c8169921">previous</a> figure successfully, the mirror driver&#8217;s <em>IoCompletion</em> routine decrements its count but cannot complete the original IRP until it decrements the count to zero. If the decremented count is not yet zero, the <em>IoCompletion</em> routine calls <mshelp:link keywords="k104_fc262cc4-a482-4a92-9f8e-1e5765c9b1d4.xml"><strong>IoFreeIrp</strong></mshelp:link> with the first-returned IRP (DupIRP1 in Figure 4.5) that the driver allocated and returns STATUS_MORE_PROCESSING_REQUIRED.</p>
<p>When the mirror driver&#8217;s <em>IoCompletion</em> routine is called again with the DupIRP2 shown in the <a href="http://www.wezu.net/blog/wp-admin/post.php#127022c1-b767-438e-a0eb-c043c8169921">previous</a> figure, the <em>IoCompletion</em> routine decrements the count in the original IRP and determines that both sets of lower-level drivers have carried out the requested operations.</p>
<p>Assuming the I/O status block in DupIRP2 also is set with STATUS_SUCCESS, the <em>IoCompletion</em> routine copies the I/O status block from DupIRP2 into the original IRP and frees DupIRP2. It calls <mshelp:link keywords="k104_79ea2b93-3ce8-46eb-990b-ca3e56d3e3a8.xml"><strong>IoCompleteRequest</strong></mshelp:link> with the original IRP and returns STATUS_MORE_PROCESSING_REQUIRED. Returning this status prevents the I/O manager from attempting any further completion processing on DupIRP2; because the IRP is not associated with a thread, its completion processing should end with the driver that created it.</p>
<p>If either set of lower-level drivers does not complete the mirror driver&#8217;s IRPs successfully, the mirror driver&#8217;s <em>IoCompletion</em> routine should log an error and attempt appropriate mirrored-data recovery.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.wezu.net/blog/archives/11/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
