存储系统:分区方案驱动与媒体过滤器驱动详解
分区方案驱动概述
分区方案驱动主要负责读取磁盘上已有的分区表,而
IOPartitionScheme
类不包含写入分区表的方法。若要创建分区表,可通过用户空间实用程序进程直接写入磁盘设备。
示例分区方案的实现
以下是一个示例分区方案驱动的属性列表中的匹配字典:
<key>IOKitPersonalities</key>
<dict>
<key>SamplePartitionScheme</key>
<dict>
<key>CFBundleIdentifier</key>
<string>com.osxkernel.SamplePartitionScheme</string>
<key>IOClass</key>
<string>com_osxkernel_driver_SamplePartitionScheme</string>
<key>IOMatchCategory</key>
<string>IOStorage</string>
<key>IOProviderClass</key>
<string>IOMedia</string>
<key>IOPropertyMatch</key>
<dict>
<key>Whole</key>
<true/>
</dict>
</dict>
</dict>
该匹配字典有三个重要方面:
-
指定提供者类
:指定提供者类为
IOMedia
,当插入新磁盘并创建
IOMedia
对象时,分区驱动有机会检查磁盘内容,查看是否存在支持的分区表。
-
限定匹配对象
:分区驱动仅对代表整个磁盘的
IOMedia
对象感兴趣。通过
IOPropertyMatch
键,要求 I/O 套件仅在
IOMedia
对象包含名为 “Whole” 且值为
true
的属性时加载驱动,此属性用于区分对象是代表整个磁盘还是磁盘分区。
-
指定匹配类别
:属性列表指定
IOMatchCategory
为
IOStorage
,此属性对存储驱动栈的正确构建很重要,某些驱动会用它判断自身是否处于驱动栈顶部,或顶部驱动是否也是
IOStorage
栈的一部分。
分区方案驱动的方法
分区方案驱动作为从标准 I/O 套件类
IOService
派生的驱动,通过
init()
、
probe()
和
start()
方法处理磁盘插入,通过
stop()
和
free()
方法处理卸载。其中,
probe()
方法对
IOPartitionScheme
驱动尤为重要。
以下是
probe()
和
start()
方法的示例实现:
#include <IOKit/storage/IOPartitionScheme.h>
// Define the superclass
#define super IOPartitionScheme
OSDefineMetaClassAndStructors(com_osxkernel_driver_PartitionScheme, IOPartitionScheme)
IOService* com_osxkernel_driver_PartitionScheme::probe(IOService* provider, SInt32* score)
{
if (super::probe(provider, score) == NULL)
return NULL;
// Scan the IOMedia for a supported partition table.
m_partitions = scan(score);
// If no partition table was found, return NULL.
return m_partitions ? this : NULL;
}
bool com_osxkernel_driver_PartitionScheme::start (IOService *provider)
{
IOMedia* partition;
OSIterator* partitionIterator;
if (super::start(provider) == false)
return false;
// Create an iterator for the IOMedia objects that were
// found and instantiated during probe.
partitionIterator = OSCollectionIterator::withCollection(m_partitions);
if (partitionIterator == NULL)
return false;
// Attach and register each IOMedia object (representing found partitions).
while ((partition = (IOMedia*)partitionIterator->getNextObject()))
{
if (partition->attach(this))
{
attachMediaObjectToDeviceTree(partition);
partition->registerService();
}
}
partitionIterator->release();
return true;
}
probe()
方法的作用是读取足够的磁盘内容,判断磁盘上的分区表是否受驱动支持,若支持则继续读取分区表条目。苹果提供的分区方案驱动在
probe()
方法中会为找到的每个分区实例化一个
IOMedia
对象。
scan() 方法
scan()
方法用于检测磁盘上是否存在支持的分区表,并为分区表实例化
IOMedia
对象。以下是其示例实现:
OSSet* com_osxkernel_driver_PartitionScheme::scan(SInt32* score)
{
IOBufferMemoryDescriptor* buffer = NULL;
SamplePartitionTable* sampleTable;
IOMedia* media = getProvider();
UInt64 mediaBlockSize = media->getPreferredBlockSize();
bool mediaIsOpen = false;
OSSet* partitions = NULL;
IOReturn status;
// Determine whether this media is formatted.
if (media->isFormatted() == false)
goto bail;
// Allocate a sector-sized buffer to hold data read from disk.
buffer = IOBufferMemoryDescriptor::withCapacity(mediaBlockSize, kIODirectionIn);
if (buffer == NULL)
goto bail;
// Allocate a set to hold the media objects representing disk partitions.
partitions = OSSet::withCapacity(8);
if (partitions == NULL)
goto bail;
// Open the storage driver stack that (of which this partition driver is part)
// for read access.
mediaIsOpen = open(this, 0, kIOStorageAccessReader);
if (mediaIsOpen == false)
goto bail;
// Read the first sector of the disk.
status = media->read(this, 0, buffer);
if (status != kIOReturnSuccess)
goto bail;
sampleTable = (SamplePartitionTable*)buffer->getBytesNoCopy();
// Determine whether the first sector contains our recognized partition signature.
if (strcmp(sampleTable->partitionIdentifier, kSamplePartitionIdentifier) != 0)
goto bail;
// Scan for valid partition entries in the partition map.
for (int index = 0; index < sampleTable->partitionCount; index++)
{
if (isPartitionInvalid(&sampleTable->partitionEntries[index]))
continue;
IOMedia* newMedia;
newMedia = instantiateMediaObject(&sampleTable->partitionEntries[index],
1+index);
if ( newMedia )
{
partitions->setObject(newMedia);
newMedia->release();
}
}
// Release temporary resources.
close(this);
buffer->release();
return partitions;
bail:
// Non-successful return; release all allocated objects.
if ( mediaIsOpen ) close(this);
if ( partitions ) partitions->release();
if ( buffer ) buffer->release();
return NULL;
}
scan()
方法的步骤如下:
1. 获取代表整个磁盘的
IOMedia
对象指针,通过该对象进行所有磁盘读取操作。
2. 检查磁盘介质属性,若未格式化则中止扫描。
3. 分配一个
IOBufferMemoryDescriptor
来存储从磁盘读取的数据,方向设为
kIODirectionIn
。
4. 分配一个
OSSet
容器来存储代表磁盘分区的
IOMedia
对象。
5. 以只读方式打开存储驱动栈。
6. 读取磁盘的第一个扇区。
7. 根据读取的数据判断磁盘是否包含支持的分区方案。
8. 遍历分区表中的每个条目。
9. 验证分区条目是否有效。
10. 实例化一个新的
IOMedia
对象来代表分区条目。
11. 若分区表扫描成功,关闭存储驱动栈并返回
IOMedia
对象集合。
12. 若出错,释放部分分配的资源。
instantiateMediaObject() 方法
该方法用于实例化代表单个磁盘分区的
IOMedia
对象,示例实现如下:
IOMedia* com_osxkernel_driver_PartitionScheme::instantiateMediaObject
(SamplePartitionEntry* sampleEntry, int index)
{
IOMedia* media = getProvider();
UInt64 mediaBlockSize = media->getPreferredBlockSize();
IOMedia* newMedia;
newMedia = new IOMedia;
if ( newMedia )
{
UInt64 partitionBase, partitionSize;
partitionBase = OSSwapLittleToHostInt64(sampleEntry->blockStart) *
mediaBlockSize;
partitionSize = OSSwapLittleToHostInt64(sampleEntry->blockCount) *
mediaBlockSize;
if ( newMedia->init(partitionBase, partitionSize, mediaBlockSize,
media->getAttributes(), false, media->isWritable()))
{
// Set a name for this partition.
newMedia->setName(sampleEntry->name);
// Set a location value (the partition number) for this partition.
char location[12];
snprintf(location, sizeof(location), "%d", index);
newMedia->setLocation(location);
// Set the "Partition ID" key for this partition.
newMedia->setProperty(kIOMediaPartitionIDKey, index, 32);
}
else
{
newMedia->release();
newMedia = NULL;
}
}
return newMedia;
}
其步骤如下:
1. 使用 C++ 的 “new” 运算符分配一个
IOMedia
对象。
2. 从分区表条目中读取分区的初始磁盘块号和大小,使用字节序宏确保数据读取正确。
3. 初始化分配的
IOMedia
对象,设置分区在磁盘上的位置、大小等参数。
4. 设置分区的各种属性,如分区名称、位置和分区 ID。
5. 若
IOMedia
对象初始化失败,释放该对象。
6. 返回初始化的
IOMedia
对象,若初始化失败则返回
NULL
。
分区方案驱动的卸载
当分区方案驱动卸载时,需从驱动栈中移除并释放其
IOMedia
对象。以下是
stop()
和
free()
方法的示例实现:
void com_osxkernel_driver_PartitionScheme::stop(IOService* provider)
{
IOMedia* partition;
OSIterator* partitionIterator;
// Detach the media objects we previously attached to the device tree.
partitionIterator = OSCollectionIterator::withCollection(m_partitions);
if (partitionIterator)
{
while ((partition = (IOMedia*)partitionIterator->getNextObject()))
{
detachMediaObjectFromDeviceTree(partition);
}
partitionIterator->release();
}
super::stop(provider);
}
void com_osxkernel_driver_PartitionScheme::free (void)
{
if (m_partitions != NULL)
m_partitions->release();
super::free();
}
stop()
方法从 I/O 注册表的设备平面中移除每个
IOMedia
对象,
free()
方法释放保存
IOMedia
对象集合的
OSSet
。
媒体内容提示属性
IOMedia
类的初始化方法有一个
contentHint
参数,虽
IOMedia
对象不解释该参数,但它对驱动存储栈的构建很重要。
contentHint
是一个字符串值,用于描述磁盘上
IOMedia
对象包含的内容。对于代表整个磁盘的
IOMedia
对象,内容提示可标识磁盘的分区方案;对于代表单个分区的
IOMedia
对象,内容提示可标识卷使用的文件系统类型,也可用于自定义目的,如磁盘加密驱动用它描述磁盘使用的加密方案。
内容提示的作用如下:
- 为系统上的其他驱动提供信息,而非向用户描述内容。
- 作为 I/O 注册表属性设置在
IOMedia
对象上,使存储栈中的其他驱动可访问,还可用于驱动匹配。
- 用于识别要为
IOMedia
卷加载的正确文件系统驱动,Mac OS X 仅在
IOMedia
对象的内容提示值标识支持的文件系统时加载文件系统驱动。
不同分区表提供内容提示的方式不同,如 Apple 分区映射直接将分区条目的字符串值用作内容提示,GUID 分区表将每个分区的 128 位 GUID 转换为字符串表示作为内容提示。因此,文件系统驱动需匹配
IOMedia
内容提示的每个可能值。
媒体过滤器驱动
驱动存储栈顶部可能包含一个或多个媒体过滤器驱动,也称为过滤器方案驱动。它匹配存储栈中现有的
IOMedia
对象,创建代表过滤后媒体对象的新
IOMedia
对象。所有对磁盘的读写请求都通过过滤器方案驱动,允许其操作读取的块或在原始
IOMedia
对象和存储栈中上方的过滤后
IOMedia
对象之间操作数据。
过滤器方案驱动的用途如下:
- 实现块级磁盘加密,匹配代表加密分区的
IOMedia
对象,发布代表文件系统使用的未加密分区的
IOMedia
对象。
- 实现 RAID 驱动,匹配多个代表 RAID 集中单个磁盘的
IOMedia
对象,创建代表逻辑卷的单个
IOMedia
对象。
分区方案驱动可视为一种特殊的过滤器驱动,但与一般过滤器驱动有区别:
- 一般过滤器驱动可以有多个提供者类,如 RAID 驱动。
- 分区方案驱动通常不参与处理通过其创建的
IOMedia
对象发出的每个读写请求。
I/O 套件提供
IOFilterScheme
类作为实现媒体过滤方案的驱动的超类。过滤器方案驱动通常使用匹配的
IOMedia
对象的 “Content Hint” 属性值,限制仅在支持的
IOMedia
对象上加载过滤方案。例如,Apple 软件 RAID 驱动用 GUID 分区表格式化 RAID 集中的每个磁盘,每个磁盘的
IOMedia
对象包含一个 GUID 作为 “Content Hint” 属性,Apple RAID 驱动会匹配该 GUID。创建代表逻辑卷的子
IOMedia
对象时,会给该对象一个代表写入整个 RAID 集的文件系统的内容提示。
需要注意的是,文件系统驱动仅会加载到驱动存储栈的顶级(叶)
IOMedia
对象上,即即使过滤器方案驱动匹配包含可读文件系统的
IOMedia
对象并创建另一个包含可读文件系统的
IOMedia
对象,只有栈中过滤器方案驱动上方的对象会挂载到用户桌面。
综上所述,分区方案驱动和媒体过滤器驱动在存储系统中起着重要作用,它们通过不同的方法和属性实现对磁盘分区和数据的管理与处理,为系统的正常运行和数据的有效存储提供了支持。
存储系统:分区方案驱动与媒体过滤器驱动详解(续)
分区方案驱动与媒体过滤器驱动的关联与区别
在存储系统中,分区方案驱动和媒体过滤器驱动虽然都与
IOMedia
对象相关,但它们有着不同的特点和作用。下面通过表格来详细对比两者的区别:
| 对比项 | 分区方案驱动 | 媒体过滤器驱动 |
| — | — | — |
| 功能目的 | 读取磁盘上的分区表,为每个分区创建
IOMedia
对象 | 对现有
IOMedia
对象进行过滤,创建新的
IOMedia
对象 |
| 提供者类 | 通常只有一个代表整个磁盘的
IOMedia
对象作为提供者 | 可以有多个提供者类,如 RAID 驱动可匹配多个代表单个磁盘的
IOMedia
对象 |
| 读写请求处理 | 一般不参与处理通过其创建的
IOMedia
对象的每个读写请求 | 参与处理通过其创建的
IOMedia
对象的读写请求,可操作数据 |
| 驱动超类 | 基于
IOPartitionScheme
类 | 基于
IOFilterScheme
类 |
从关联角度来看,分区方案驱动可看作是一种特殊的过滤器驱动。它们都依赖于
IOMedia
对象,并且都在存储驱动栈的构建中发挥作用。例如,分区方案驱动和媒体过滤器驱动都使用
IOMedia
对象的属性(如内容提示属性)来进行匹配和操作。
内容提示属性在驱动匹配中的应用示例
内容提示属性在驱动匹配中有着重要的应用,下面通过一个具体的流程图来展示其在磁盘加密驱动中的应用:
graph TD;
A[磁盘插入] --> B[IOMedia对象创建];
B --> C{判断是否加密};
C -- 是 --> D[内容提示含加密信息];
C -- 否 --> E[内容提示不含加密信息];
D --> F[磁盘加密驱动匹配加载];
E --> G[磁盘加密驱动不加载];
在这个流程中,当磁盘插入系统后,会创建相应的
IOMedia
对象。然后根据
IOMedia
对象的内容提示属性判断磁盘是否加密。如果内容提示包含加密信息,磁盘加密驱动会匹配并加载;如果不包含,则磁盘加密驱动不会加载。这就体现了内容提示属性在驱动匹配中的重要作用,能够避免不必要的驱动加载,提高系统效率。
过滤器驱动的实现流程
以 RAID 驱动为例,其实现流程如下:
1.
匹配提供者类
:RAID 驱动会匹配多个代表 RAID 集中单个磁盘的
IOMedia
对象。在这个过程中,会检查这些
IOMedia
对象的 “Content Hint” 属性,确保其包含特定的 GUID 以表明该磁盘分区是 RAID 集的一部分。
2.
创建逻辑卷的
IOMedia
对象
:当匹配到合适的提供者类后,RAID 驱动会创建一个代表逻辑卷的单个
IOMedia
对象。此时,会给这个新的
IOMedia
对象设置一个内容提示,该内容提示代表写入整个 RAID 集的文件系统。
3.
处理读写请求
:所有对逻辑卷的读写请求都会通过 RAID 驱动。RAID 驱动会根据 RAID 算法对请求进行处理,例如将数据分散存储到多个磁盘上(RAID 0)或进行数据冗余存储(RAID 1 等)。
分区方案驱动和过滤器驱动的错误处理
在分区方案驱动和过滤器驱动的实现中,错误处理是非常重要的。例如,在
scan()
方法中,如果出现错误,会跳转到
bail
标签处释放所有已分配的对象。同样,在其他方法中也有相应的错误处理机制。下面是一个简单的错误处理流程图:
graph TD;
A[开始操作] --> B{操作是否成功};
B -- 是 --> C[继续后续操作];
B -- 否 --> D[释放已分配资源];
D --> E[返回错误结果];
在这个流程中,当操作失败时,会立即释放已分配的资源,避免资源泄漏,并返回错误结果,以便上层程序进行相应的处理。
存储系统驱动的未来发展趋势
随着存储技术的不断发展,分区方案驱动和媒体过滤器驱动也会有新的发展趋势。
1.
更高的性能要求
:随着数据量的不断增加和对数据读写速度要求的提高,驱动需要更高效地处理数据。例如,优化读写请求的处理流程,减少数据传输延迟。
2.
更强的安全性
:数据安全越来越受到重视,驱动需要提供更强大的安全功能。如加强磁盘加密算法,防止数据泄露。
3.
更好的兼容性
:随着新的存储设备和文件系统的不断出现,驱动需要具备更好的兼容性,能够支持更多类型的设备和文件系统。
4.
智能化管理
:未来的驱动可能会具备智能化管理功能,能够自动检测磁盘状态,根据磁盘性能和使用情况进行优化配置。
总之,分区方案驱动和媒体过滤器驱动在存储系统中扮演着至关重要的角色,它们的不断发展和优化将推动整个存储系统的进步。我们需要不断关注这些驱动的技术发展,以适应不断变化的存储需求。
超级会员免费看


被折叠的 条评论
为什么被折叠?



