今天是: Monday, 8th September 2008
登录

Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy.

欢迎来到知识库,这里是收集整理的地方。希望能够给你点帮助

vtiger CRM 安装问题总结

  1. IMAP webMail的设置
  2. webMail的中文正确显示

(more…)

分布式文件系统

分布式文件系统(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 Write Back)缓存一致性协议,减少了网络负载。对本地主机和本地存储空间的有效利用,使它具有较好的性能。

Tiger Shark并行文件系统是针对大规模实时多媒体应用设计的。它采用了多种技术策略保证多媒体传输的实时性和稳定性:采用资源预留和优化的调度手段,保证数据实时访问性能;通过加大文件系统数据块的大小,最大限度地发挥磁盘的传输效率;通过将大文件分片存储在多个存储设备中,取得尽量大的并行吞吐率;通过复制文件系统元数据和文件数据,克服单点故障,提高系统可用性。

基于虚拟共享磁盘Petal的Frangipani分布式文件系统,采用了一种新颖的系统结构—分层次的存储系统。Petal提供一个可以全局统一访问的磁盘空间。Frangipani基于Petal的特性提供文件系统的服务。这种分层结构使两者的设计实现都得到了简化。在Frangipani中,每个客户端也是文件系统服务器,参与文件系统的管理,可以平等地访问Petal提供的虚拟磁盘系统,并通过分布式锁实现同步访问控制。分层结构使系统具有很好的扩展性,可以在线动态地添加存储设备,增加新用户、备份等,同时系统具有很好的机制来处理节点失效、网络失效等故障,提高了系统的可用性。

Slice File System(SFS)考虑标准的NFS在容量、性能方面存在的限制,采用在客户机和服务器之间架设一个μproxy中间转发器,以提高性能和可扩展性。它将客户端的访问分为小文件、元数据服务、大文件数据三类请求。通过μproxy将前两种请求转发到不同的文件服务器上,将后者直接发送到存储服务器上。这样SFS系统就可以支持多个存储服务器,提高整个系统的容量和性能。μproxy根据请求内容的转发是静态的,对于整个系统中负载的变化难以做出及时反应。

3

1995~2000年

网络技术的发展和普及应用极大地推动了网络存储技术的发展,基于光纤通道的SAN、NAS得到了广泛应用。这也推动了分布式文件系统的研究。

Cluster FS的Lustre系统结构图

在这个阶段,计算机技术和网络技术有了突飞猛进的发展,单位存储的成本大幅降低。而数据总线带宽、磁盘速度的增长无法满足应用对数据带宽的需求,存储子系统成为计算机系统发展的瓶颈。

网络技术的发展和普及应用极大地推动了网络存储技术的发展,基于光纤通道的SAN、NAS得到了广泛应用。这也推动了分布式文件系统的研究。这个阶段,出现了多种体系结构,充分利用了网络技术。

Global File System(GFS)吸取了对称多处理器(SMP)系统设计和实现的原理,将系统中的每一个客户机类比于SMP中的一个处理器。客户机间没有任何区别,可以平等地访问系统中的所有存储设备,就像处理器可以机会均等地访问主存一样。这样的设计可以更好地利用系统中的资源,消除单个服务器带来的性能瓶颈和单点失效问题。客户端之间无需通信,因此可以很好地消除客户机失效带来的威胁。GFS采用特殊设计的DLOCK锁机制,同步多个客户机对同一设备的访问,具有很高的效率。

General Parallel File System(GPFS)是从Tiger Shark发展过来的,是目前应用范围较广的一个系统。GPFS在系统设计中采用了多项先进技术。它是一个共享磁盘(Shared-disk)的分布式并行文件系统,客户端采用基于光纤通道或者iSCSI与存储设备相连,也可以通过通用网络相连。GPFS的磁盘数据结构可以支持大容量的文件系统和大文件,通过采用分片存储、较大的文件系统块、数据预读等方法获得了较高的数据吞吐率;采用扩展哈希(Extensible Hashing)技术支持含有大量文件和子目录的大目录,提高文件的查找和检索效率。GPFS采用分布式锁解决系统中的并发访问和数据同步问题:字节范围的锁用于用户数据的同步,动态选择元数据节点(Metanode)进行元数据的集中管理;分布式锁管理整个系统的空间分配等。GPFS采用日志技术对系统进行在线灾难恢复。每个节点都有各自独立的日志,且单个节点失效时,系统中的其他节点可以代替失效节点检查文件系统日志,进行元数据恢复操作。

惠普的DiFFS和SGI公司的CXFS都是基于SAN的分布式文件系统。DiFFS通过将存储系统划分成不同的区域,把对资源的共享访问冲突限制在各个区域内部,以解决机群文件系统的可扩展性问题。DiFFS采用了动态分配策略和文件级的负载平衡等多项技术。CXFS是在XFS的基础上开发的,实现了元数据服务器内置的失效接替和恢复功能;采用快速元数据算法,提高元数据的访问性能。

此外,还有多种体系结构,如EMC的HighRoad、Sun的qFS、XNFS等。数据容量、性能和共享的需求使得这一时期的分布式文件系统管理的系统规模更大、系统更复杂,对物理设备的直接访问、磁盘布局和检索效率的优化、元数据的集中管理等都反映了对性能和容量的追求。规模的扩展使得系统的动态性,如在线增减设备、缓存的一致性、系统可靠性的需求逐渐增强,更多的先进技术应用到系统实现中,如分布式锁、缓存管理技术、SoftUpdates技术、文件级的负载平衡等。
4

2000年以后

随着SAN和NAS两种结构逐渐成熟,研究人员开始考虑如何将两种结构结合起来。网格的研究成果等也推动了分布式文件系统体系结构的发展。

随着SAN和NAS两种体系结构逐渐成熟,研究人员开始考虑如何将两种体系结构结合起来,以充分利用两者的优势。另一方面,基于多种分布式文件系统的研究成果,人们对体系结构的认识不断深入,网格的研究成果等也推动了分布式文件系统体系结构的发展。这一时期,IBM的StorageTank、Cluster的Lustre、Panasas的PanFS、蓝鲸文件系统(BWFS)等是这种体系结构的代表。各种应用对存储系统提出了更多的需求:

大容量—现在的数据量比以前任何时期更多,生成的速度更快;

高性能—数据访问需要更高的带宽;

高可用性—不仅要保证数据的高可用性,还要保证服务的高可用性;

可扩展性—应用在不断变化,系统规模也在不断变化,这就要求系统提供很好的扩展性,并在容量、性能、管理等方面都能适应应用的变化;

可管理性—随着数据量的飞速增长,存储的规模越来越庞大,存储系统本身也越来越复杂,这给系统的管理、运行带来了很高的维护成本;

按需服务—能够按照应用需求的不同提供不同的服务,如不同的应用、不同的客户端环境、不同的性能等。

IBM公司在GPFS的基础上发展进化来的Storage Tank,以及基于Storage Tank的TotalStorage SAN File System,又将分布式文件系统的设计理念和系统架构向前推进了一步。它们除了具有一般的分布式文件系统的特性之外,还采用SAN作为整个文件系统的数据存储和传输路径。它们采用带外(out-of-band)结构,将文件系统元数据在高速以太网上传输,由专门的元数据服务器来处理和存储。文件系统元数据和文件数据的分离管理和存储,可以更好地利用各自存储设备和传输网络的特性,提高系统的性能,有效降低系统的成本。在TotalStorage中,块虚拟层将整个SAN的存储进行统一的虚拟管理,为文件系统提供统一的存储空间。SAN File System采用了基于策略的文件数据位置选择方法,能有效地利用系统的资源,提高性能,降低成本。

处于这个阶段的系统都在研究中,但从中也可以看出一些发展趋势:体系结构的研究逐渐成熟,表现在不同文件系统的体系结构趋于一致;系统设计的策略基本一致,如采用专用服务器方式等;每个系统在设计的细节上各自采用了很多特有的先进技术,也都取得了很好的性能和扩展性。另外,在协议方面的探索也是研究的热点之一,如Direct Access File System利用了远程内存直接访问的特性,借鉴了NFS第四版本和Common Internet File System等协议,设计了一套新的网络文件访问协议。

Int 13 中断功能

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 所读磁盘的磁头号。
CH 磁道号的低8位数。
CL 低5位放入所读起始扇区号,位7-6表示磁道号的高2位。
ES:BX 读出数据的缓冲区地址。
返回参数:
如果CF=1,AX中存放出错状态。读出后的数据在ES:BX区域依次排列。详情请参见磁盘错误状态返回码一文。
示例:
C_SEG SEGMENT PUBLIC
ASSUME CS:C_SEG,DS:C_SEG
ORG 100H
START: JMP READ
BUFFER DB 512 DUP(0)
READ: PUSH CS
POP ES
MOV BX, OFFSET BUFFER
MOV AX, 0201H
MOV CX, 0001H
MOV DX, 0000H
INT 13H
;读软盘A, 0面0道1扇区
;读出后数据在BUFFER中
JC ERROR
……
ERROR: ……
C_SEG ENDS
END START

INT 13H,AH=03H 写扇区

说明:
调用此功能将从磁盘上把一个或更多的扇区内容写入驱动器。因为这是一个低级功能,在一个写入操作中的全部扇区必须在同一条磁道上(磁头号和磁道号相同)。BIOS不能自动地从一条磁道末尾切换到另一条磁道开始,因此用户必须把跨多条磁道的写操作分为若干条单磁道写操作。
入口参数:
AH=03H 指明调用写扇区功能。
AL 置要写的扇区数目,不允许使用超出磁道末端以外的数值,也不允许使该寄存器为0。
DL 需要进行写操作的驱动器号。
DH 所写磁盘的磁头号。
CH 磁道号的低8位数。
CL 低5位放入所读起始扇区号,位7-6表示磁道号的高2位。
ES:BX 放置写入数据的存贮区地址。
返回参数:
如果CF=1,AX中存放出错状态。详情请参见磁盘错误状态返回码一文。
示例:
C_SEG SEGMENT PUBLIC
ASSUME CS:C_SEG,DS:C_SEG
ORG 100H
START: JMP WRITE
BUFFER DB 512 DUP(0FFH)
WRITE: PUSH CS
POP ES
MOV BX, OFFSET BUFFER
MOV AX, 0301H
MOV CX, 0001H
MOV DX, 0000H
INT 13H
;写入软盘A, 0面0道1扇区
;把此扇区数据全部置为0FFH
JC ERROR
……
ERROR: ……
C_SEG ENDS
END START
INT 13H,AH=04H 检测扇区

说明:
这个功能检测磁盘上1个或更多的扇区。这个验证测试不是把磁盘上的数据和内存中的数据进行比较,而只是简单地确定读出的数据有无CRC错误。
这个功能可用来验证驱动器中的软盘版。如果盘片的格式正确,CF=0。
入口参数:
AH=03H 指明调用检测扇区功能。
AL 置要检测的连续扇区数目,不允许使用超出磁道末端以外的数值,也不允许使该寄存器为0。
DL 需要进行检测的驱动器号。
DH 磁盘的磁头号。
CH 磁道号的低8位数。
CL 低5位放入起始扇区号,位7-6表示磁道号的高2位。
返回参数:
如果CF=1,AX中存放出错状态。CF=0,检测正确。详情请参见磁盘错误状态返回码一文。
示例:
C_SEG SEGMENT PUBLIC
ASSUME CS:C_SEG,DS:C_SEG
ORG 100H
START: MOV AX, 0401H
MOV CX, 0001H
MOV DX, 0000H
INT 13H
;检测软盘A, 0面0道1扇区
JC ERROR
……
ERROR: ……
C_SEG ENDS
END START

磁盘错误状态返回码:

磁盘错误状态

AH=
00H 未出错
01H 非法功能调用命令区。
02H 地址标记损坏,扇区标识(ID)无效或未找到。
03H 企图对有写保护的软盘执行写操作。
04H 所寻找的扇区没找到。
05H 复位操作失败。
06H 无介质。
07H 初始化错误,数据未存在DMA的64K缓冲区内。
08H DMA故障
09H DMA边界错误,数据未存在DMA的64K缓冲区内。
0AH 检测出错误码率的扇区标志。
0BH 所寻找的磁道没找到。
0CH 介质类型没发现。
0DH 扇区号有问题。
0EH 发现控制数据地址标记。
0FH 超出DMA边界
10H 读磁盘时奇偶校验错,且纠错码(EDC)不能纠正。
11H 读磁盘时奇偶校验错,但纠错码(EDC)已纠正错误。
20H 控制器错。
40H 查找操作无效。
80H 超时错误,驱动器不响应。
AAH 驱动器未准备好。
BBH 不明错误。
CCH 被选驱动器出现写故障。
E0H 错误寄存器是零
FFH 非法操作。

备注:
控制器的最后状态将会在磁盘操作完成后写入相应的BIOS数据区(40:41)

Using the Windows Headers

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 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.

条件申明

某些函数在部分Windows版本才具备,使用条件代码来声明它们。您就可以使用编译器来检测这些函数是否被目标Windows版本支持。必须定义适当的宏来编译使用到这些函数的应用程序。否则,你会收到C2065的出错信息。

Windows头文件使用宏来决定

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.

下表描述了Windows 头文件中的首选宏。

最小系统需求 NTDDI_VERSION
Windows Server 2008 NTDDI_WS08
Windows Vista SP1 NTDDI_VISTASP1
Windows Vista NTDDI_VISTA
Windows Server 2003 SP1 NTDDI_WS03SP1
Windows Server 2003 NTDDI_WS03
Windows XP SP2 NTDDI_WINXPSP2
Windows XP SP1 NTDDI_WINXPSP1
Windows XP NTDDI_WINXP
Windows 2000 SP4 NTDDI_WIN2KSP4
Windows 2000 SP3 NTDDI_WIN2KSP3
Windows 2000 SP2 NTDDI_WIN2KSP2
Windows 2000 SP1 NTDDI_WIN2KSP1
Windows 2000 NTDDI_WIN2K

下表描述了在Windows头文件中使用的宏。

最小系统需求 _WIN32_WINNT 和 WINVER的最小值
Windows Server 2008 0×0600
Windows Vista 0×0600
Windows Server 2003 SP1, Windows XP SP2 0×0502
Windows Server 2003, Windows XP 0×0501
Windows 2000 0×0500
最小版本需求 _WIN32_IE的最小值
Internet Explorer 7.0 0×0700
Internet Explorer 6.0 SP2 0×0603
Internet Explorer 6.0 SP1 0×0601
Internet Explorer 6.0 0×0600
Internet Explorer 5.5 0×0550
Internet Explorer 5.01 0×0501
Internet Explorer 5.0, 5.0a, 5.0b 0×0500

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 GetDllDirectory function was introduced in Windows Server 2003 and is conditionally defined if _WIN32_WINNT is 0×0502 or greater. This function was also added to Windows XP SP1. Therefore, if you were to define _WIN32_WINNT 0×0501 to target Windows XP, you would miss features that are defined in Windows XP SP1.

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 Projects menu and click Properties. Go to Configuration Properties, then C++, then Command Line. Enter the option under Additional Options.

控制结构包

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.

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.

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.

 

用最少的头文件来快速编译

You can reduce the size of the Windows header files by excluding some of the less common API declarations as follows:

  • Define WIN32_LEAN_AND_MEAN to exclude APIs such as Cryptography, DDE, RPC, Shell, and Windows Sockets.
  • Define one or more of the NOapi symbols to exclude the API. For example, NOCOMM excludes the serial communication API. For a list of support NOapi symbols, see Windows.h.

在中间层驱动中处理IRP

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 up device objects for direct or buffered I/O
  • The design of the individual higher-level driver

The following figure illustrates the path an IRP might take through the standard routines of an intermediate mirror driver layered somewhere over the lowest-level device driver described in the previous section.

下图展示的驱动有下面的特性:

  • The driver is layered over more than one physical device and possibly over more than one device driver.
  • The driver sometimes allocates additional IRPs for lower-level drivers, depending on the requested operation in the input IRP.
  • 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.

4hiddirp.jpg

IRP Path through Intermediate Driver Routines

As the figure shows, the I/O manager creates an IRP and sends it to the driver’s dispatch routine for the given major function code. Assuming the function code is IRP_MJ_WRITE, the dispatch routine is DDDispatchWrite. The intermediate driver’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.

分派IRPs

The mirror driver’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.

The previous figure shows a call to IoAllocateIrp but higher-level drivers can call other support routines to allocate IRPs for lower-level drivers. See Creating IRPs for Lower-Level Drivers.

When the dispatch routine calls IoAllocateIrp, 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 IoAllocateIrp to get a stack location of its own for each IRP it allocates, as the driver in the previous figure does.

This intermediate driver’s dispatch routine calls IoGetCurrentIrpStackLocation (not shown) with the original IRP, to check parameters.

It calls IoSetNextIrpStackLocation because it allocated its own stack location in each newly created IRP and IoGetCurrentIrpStackLocation to create a context for itself that it uses later in the IoCompletion routine.

Next, it calls IoGetNextIrpStackLocation with each newly created IRP so that it can set up the next lower-level drivers’ I/O stack locations in the IRPs it allocated. The mirror driver’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.

调用IoSetCompletionRoutine 和 IoCallDriver

The dispatch routine in the previous figure calls IoSetCompletionRoutine for each IRP it allocated. Because the driver in the previous figure must dispose of the IRPs it allocated, this driver sets its IoCompletion routine to be called when lower drivers complete its IRPs, whether the I/O operation completed successfully, failed, or was canceled.

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 IoCallDriver twice, once for each target device object representing a mirrored partition.

在驱动 IoCompletion 函数中处理 IRPs

When either set of lower-level drivers completes the requested operation, the I/O manager calls the intermediate mirror driver’s IoCompletion 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.

Assuming that the I/O status block indicates that one set of lower drivers has completed the duplicate IRP shown in the previous figure successfully, the mirror driver’s IoCompletion 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 IoCompletion routine calls IoFreeIrp with the first-returned IRP (DupIRP1 in Figure 4.5) that the driver allocated and returns STATUS_MORE_PROCESSING_REQUIRED.

When the mirror driver’s IoCompletion routine is called again with the DupIRP2 shown in the previous figure, the IoCompletion routine decrements the count in the original IRP and determines that both sets of lower-level drivers have carried out the requested operations.

Assuming the I/O status block in DupIRP2 also is set with STATUS_SUCCESS, the IoCompletion routine copies the I/O status block from DupIRP2 into the original IRP and frees DupIRP2. It calls IoCompleteRequest 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.

If either set of lower-level drivers does not complete the mirror driver’s IRPs successfully, the mirror driver’s IoCompletion routine should log an error and attempt appropriate mirrored-data recovery.