如何在Mac OS X中检测SSD?

10
有没有一种可靠、快速、确定的方法(即不是基准测试),可以检查Mac OS X系统驱动器是否为固态硬盘?还有其他指标可以衡量磁盘处理并行访问的能力吗?我试图调整我的程序用于磁盘密集型操作的线程数。我只关心哪种类型的访问(串行或并行)对驱动器来说更快,而不是原始速度或寻道时间。我不希望我的程序用户使用iSCSI或RAID。SSD是我的重点,其他任何东西都是好的。IOAHCIBlockStorageDevice设备特性包含了这些信息。如何以编程方式读取它呢?
到目前为止,我已经弄清楚了它是这样工作的:(以下是伪代码)
match = IOBSDNameMatching(kIOMasterPortDefault,0,"disk0s2");
IOServiceGetMatchingServices(kIOMasterPortDefault, match, &iterator);
while(entry = IOIteratorNext(iterator)) {
   do {
     entry = IORegistryEntryGetParentEntry(nextMedia, kIOServicePlane, &entry);
     dict = IORegistryEntryCreateCFProperty(nextMedia, 
            CFSTR(kIOPropertyDeviceCharacteristicsKey), kCFAllocatorDefault, 0);
     [dict objectForKey:CFSTR(kIOPropertyMediumTypeKey)];
   } 
   while(!dict && entry); 
}

编辑:这里有完整的源代码。我已经验证它可以与英特尔 SSD 和 OCZ Vertex 一起使用。


请参阅https://dev59.com/onNA5IYBdhLWcg3wku3O。 - Raedwald
4个回答

7
实际上,我认为您应该采用基准测试路线,因为它更准确地回答了您的问题 - 您并不 真正 关心磁盘恰好是 SSD,您只关心磁盘非常快。如果用户正在使用快速 RAID 设置、光纤通道阵列或使用 iSCSI 呢?只需从底层 /dev/diskX 中读取一堆随机扇区,如果满足您的要求,您就可以将其视为“快速”驱动器。

2
我没有“快速”的任何基准测量。我需要运行两个基准测试并进行比较。可靠基准测试所需的时间可能比选择更好的策略节省的时间更长。短期基准测试将不可靠,并且很难考虑缓存、延迟等因素。我正在寻找快速、确定性的解决方案。它只需要支持SSD,不需要支持其他任何东西。 - Kornel
3
可以,但是我告诉你,这就是Windows操作系统采用的方法——如果你看到一些统计数据,很容易就能明白;运行两个测试,一个是向后顺序读取(即扇区5、4、3、2等),然后再进行一个随机扇区读取的测试。如果方差基本相同,那么你就有一个固态硬盘;在机械硬盘上,它们会大不相同。 - Ana Betts

5

如果您正在尝试获取这种类型的信息,最好的选择是使用IOKit。

您可以使用ioreg命令行工具或IORegistryExplorer尝试其某些功能。


这里是一些可能对您有帮助的代码。它提取所有不是RAID和分区的硬盘。虽然这不是您想要的,但这可能会让您开始。

#import "TWDevice.h"

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <paths.h>
#include <sys/param.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOBSD.h>
#include <IOKit/storage/IOMedia.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/Kext/KextManager.h>


@implementation TWDevice

@synthesize name, devicePath, size, blockSize, writable, icon;

+ (NSArray *)allDevices {
    // create matching dictionary
    CFMutableDictionaryRef classesToMatch;
    classesToMatch = IOServiceMatching(kIOMediaClass);
    if (classesToMatch == NULL) {
        [NSException raise:@"TWError" format:@"Classes to match could not be created"];
    }

    // get iterator of matching services
    io_iterator_t mediaIterator;
    kern_return_t kernResult;
    kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault,
                                                                      classesToMatch,
                                                                      &mediaIterator);

    if (kernResult != KERN_SUCCESS) {
        [NSException raise:@"TWError" format:@"Matching services did not succed."];
    }

    // iterate over all found medias
    io_object_t nextMedia;
    NSMutableArray *detectedDevices = [NSMutableArray array];
    while (nextMedia = IOIteratorNext(mediaIterator)) {
        NSMutableDictionary *properties;
        kernResult = IORegistryEntryCreateCFProperties(nextMedia,
                                                                                  (CFMutableDictionaryRef *)&properties,
                                                                                  kCFAllocatorDefault, 0);

        if (kernResult != KERN_SUCCESS) {
            [NSException raise:@"TWError" format:@"Getting properties threw error."];
        }

        // is it a whole device or just a partition?
        if ([[properties valueForKey:@"Whole"] boolValue] &&
            ![[properties valueForKey:@"RAID"] boolValue]) {
            TWDevice *device = [[[TWDevice alloc] init] autorelease];

            device.devicePath = [NSString stringWithFormat:@"%sr%@", _PATH_DEV, [properties valueForKey:@"BSD Name"]];
            device.blockSize = [[properties valueForKey:@"Preferred Block Size"] unsignedLongLongValue];
            device.writable = [[properties valueForKey:@"Writable"] boolValue];
            device.size = [[properties valueForKey:@"Size"] unsignedLongLongValue];

            io_name_t name;
            IORegistryEntryGetName(nextMedia, name);
            device.name = [NSString stringWithCString:name encoding:NSASCIIStringEncoding];

            …

            [detectedDevices addObject:device];
        }

        // tidy up
        IOObjectRelease(nextMedia);
        CFRelease(properties);
    }
    IOObjectRelease(mediaIterator);

    return detectedDevices;
}

@end

谢谢。这非常有帮助。如果没有它,我将毫无意义地探索IOKit C++ API。 - Kornel
@porneL:看起来你已经自己解决了。我想你不需要在这个问题上再得到更多的帮助,是吗? - Georg Schölly
是的,我现在已经搞明白了。我已经添加到问题中的代码似乎可以工作(它需要一些额外的工作来支持文件保险库,但我可以从那里得到 - IOKit注册表对我来说不再是一个谜了)。 - Kornel

3

这似乎仅适用于内部驱动器。 我无法找到一种使用IOKit查询三星T5外部USB 3固态硬盘(或东芝Canvio USB硬盘)的方法,以获取其SSD特性。虽然我成功在我的MBP中管理了内部驱动器。

磁盘实用程序也认为三星不是固态硬盘,因此这使我认为可能没有其他方法,除了测量实际性能。


0

线程数量?1个线程会压垮任何磁盘、固态硬盘或RAID。磁盘速度慢,处理器速度快。操作系统会重新排序您的磁盘请求(或者至少应该这样做),以获得最小的磁头移动。


至少在Windows和Linux上,操作系统不会为SSD重新排序磁盘请求。 - Ana Betts
1
根据我的经验,OS X 在管理磁盘访问方面做得不太好(并行访问会导致垃圾回收而不是更有效的访问)。多个 I/O 限制线程访问 SSD 更快 - 可能只是因为涉及一些 CPU 开销,但仍然更快。 - Kornel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接