You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows平台下ATA/SAS/SATA磁盘S.M.A.R.T信息获取实现求助

获取ATA/SAS/SATA磁盘SMART信息的实现方案

以下是基于Windows平台Visual Studio环境的完整实现代码,通过DeviceIoControl结合对应总线的命令协议来读取并打印SMART数据:

核心依赖与注意事项

  • 必须以管理员权限运行程序,磁盘设备操作需要高权限。
  • 需包含头文件:Windows.h、winioctl.h,编译时链接kernel32.lib(VS默认配置已包含)。
  • 结构需严格字节对齐,避免内存解析错误。

完整代码实现

#include <Windows.h>
#include <winioctl.h>
#include <iostream>
#include <vector>
#include <iomanip>
#include <string>

#pragma pack(push, 1)
// ATA SMART读取数据结构
typedef struct _ATA_SMART_READ_DATA {
    USHORT      Command;
    USHORT      Feature;
    USHORT      SectorCount;
    USHORT      LbaLow;
    USHORT      LbaMid;
    USHORT      LbaHigh;
    USHORT      Device;
    USHORT      Reserved;
    USHORT      Status;
    USHORT      Control;
    USHORT      Length;
    UCHAR       SmartData[512];
} ATA_SMART_READ_DATA, *PATA_SMART_READ_DATA;

// ATA透传命令结构
typedef struct _ATA_PASS_THROUGH_EX {
    USHORT Length;
    UCHAR AtaFlags;
    UCHAR PathId;
    UCHAR TargetId;
    UCHAR Lun;
    UCHAR ReservedAsUchar;
    USHORT DataTransferLength;
    USHORT TimeOutValue;
    USHORT ReservedAsUshort;
    PVOID DataBuffer;
    ULONG_PTR Reserved[3];
    UCHAR AtaCommand[16];
} ATA_PASS_THROUGH_EX, *PATA_PASS_THROUGH_EX;

// SCSI透传命令结构(用于SAS磁盘)
typedef struct _SCSI_PASS_THROUGH_DIRECT {
    USHORT Length;
    UCHAR ScsiStatus;
    UCHAR PathId;
    UCHAR TargetId;
    UCHAR Lun;
    UCHAR CdbLength;
    UCHAR SenseInfoLength;
    UCHAR DataIn;
    ULONG DataTransferLength;
    ULONG TimeOutValue;
    PVOID DataBuffer;
    ULONG SenseInfoOffset;
    UCHAR Cdb[16];
} SCSI_PASS_THROUGH_DIRECT, *PSCSI_PASS_THROUGH_DIRECT;
#pragma pack(pop)

// 打开物理磁盘设备
HANDLE OpenDiskDevice(int diskNumber) {
    wchar_t diskPath[64];
    swprintf_s(diskPath, L"\\\\.\\PhysicalDrive%d", diskNumber);
    
    HANDLE hDisk = CreateFileW(
        diskPath,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        0,
        NULL
    );
    
    if (hDisk == INVALID_HANDLE_VALUE) {
        std::wcerr << L"打开磁盘失败,错误码: " << GetLastError() << std::endl;
    }
    return hDisk;
}

// 获取ATA/SATA磁盘SMART数据
bool GetAtaSmartData(HANDLE hDisk, ATA_SMART_READ_DATA& smartData) {
    ZeroMemory(&smartData, sizeof(smartData));
    
    ATA_PASS_THROUGH_EX ptCmd = {0};
    ptCmd.Length = sizeof(ATA_PASS_THROUGH_EX);
    ptCmd.AtaFlags = ATA_FLAGS_DATA_IN; // 数据从设备读入主机
    ptCmd.DataTransferLength = sizeof(smartData.SmartData);
    ptCmd.TimeOutValue = 3; // 超时3秒
    ptCmd.DataBuffer = smartData.SmartData;
    
    // 填充ATA SMART READ DATA命令(0xD0)
    ptCmd.AtaCommand[0] = 0xD0;
    ptCmd.AtaCommand[1] = 0x40; // 启用SMART
    ptCmd.AtaCommand[6] = 0xA0; // 设备位设置SMART标志
    
    DWORD bytesReturned = 0;
    BOOL success = DeviceIoControl(
        hDisk,
        IOCTL_ATA_PASS_THROUGH_EX,
        &ptCmd,
        sizeof(ptCmd),
        &smartData,
        sizeof(smartData),
        &bytesReturned,
        NULL
    );
    
    if (!success) {
        std::wcerr << L"读取ATA SMART失败,错误码: " << GetLastError() << std::endl;
        return false;
    }
    return true;
}

// 打印ATA/SATA SMART属性
void PrintAtaSmartData(const ATA_SMART_READ_DATA& smartData) {
    std::cout << "\nATA/SATA SMART 属性列表:\n";
    std::cout << std::left 
              << std::setw(12) << "属性ID" 
              << std::setw(25) << "描述" 
              << std::setw(8) << "当前值" 
              << std::setw(8) << "最差值" 
              << std::setw(10) << "阈值" 
              << std::setw(15) << "原始值" << "\n";
    std::cout << "-------------------------------------------------------------------------\n";
    
    const UCHAR* attrPtr = &smartData.SmartData[2]; // 跳过前2字节头部
    for (int i = 0; i < 30; ++i) {
        UCHAR attrId = attrPtr[0];
        if (attrId == 0) break; // 空属性,终止遍历
        
        UCHAR current = attrPtr[3];
        UCHAR worst = attrPtr[4];
        UCHAR threshold = attrPtr[5];
        ULONGLONG rawValue = *(ULONGLONG*)&attrPtr[6];
        
        // 常见属性映射,可根据厂商文档扩展
        std::string desc;
        switch(attrId) {
            case 0x01: desc = "原始读取错误率"; break;
            case 0x05: desc = "重分配扇区计数"; break;
            case 0x09: desc = "通电时间"; break;
            case 0x0C: desc = "通电循环次数"; break;
            case 0x12: desc = "断电缩回计数"; break;
            case 0x19: desc = "加载循环计数"; break;
            case 0x32: desc = "温度"; break;
            case 0x3C: desc = "UltraDMA CRC错误计数"; break;
            default: desc = "未知属性"; break;
        }
        
        std::cout << std::left 
                  << std::hex << std::setw(12) << (int)attrId 
                  << std::dec << std::setw(25) << desc 
                  << std::setw(8) << (int)current 
                  << std::setw(8) << (int)worst 
                  << std::setw(10) << (int)threshold 
                  << std::setw(15) << rawValue << "\n";
        
        attrPtr += 12; // 每个属性占12字节
    }
}

// 获取SAS磁盘SMART数据
bool GetSasSmartData(HANDLE hDisk, std::vector<UCHAR>& smartData) {
    SCSI_PASS_THROUGH_DIRECT scsiCmd = {0};
    scsiCmd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
    scsiCmd.CdbLength = 12;
    scsiCmd.DataIn = SCSI_IOCTL_DATA_IN;
    scsiCmd.DataTransferLength = 512;
    scsiCmd.TimeOutValue = 3;
    smartData.resize(512);
    scsiCmd.DataBuffer = smartData.data();
    
    // 填充SCSI READ SMART DATA命令(0xB0)
    scsiCmd.Cdb[0] = 0xB0;
    scsiCmd.Cdb[1] = 0x00; // SMART READ DATA服务动作
    scsiCmd.Cdb[8] = 0x02; // 传输长度512字节
    
    DWORD bytesReturned = 0;
    BOOL success = DeviceIoControl(
        hDisk,
        IOCTL_SCSI_PASS_THROUGH_DIRECT,
        &scsiCmd,
        sizeof(scsiCmd),
        &scsiCmd,
        sizeof(scsiCmd),
        &bytesReturned,
        NULL
    );
    
    if (!success) {
        std::wcerr << L"读取SAS SMART失败,错误码: " << GetLastError() << std::endl;
        return false;
    }
    return true;
}

// 打印SAS SMART原始数据(简化版)
void PrintSasSmartData(const std::vector<UCHAR>& smartData) {
    std::cout << "\nSAS SMART 原始数据(前64字节):\n";
    for (size_t i = 0; i < smartData.size() && i < 64; ++i) {
        std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)smartData[i] << " ";
        if ((i+1) % 16 == 0) std::cout << "\n";
    }
    std::cout << std::dec << "\n";
}

int main() {
    int diskNum = 0; // 替换为目标磁盘编号,0为第一个物理磁盘
    HANDLE hDisk = OpenDiskDevice(diskNum);
    if (hDisk == INVALID_HANDLE_VALUE) {
        return 1;
    }
    
    // 查询磁盘总线类型(复用你已实现的逻辑)
    STORAGE_PROPERTY_QUERY propQuery = {0};
    propQuery.PropertyId = StorageAdapterProperty;
    propQuery.QueryType = PropertyStandardQuery;
    
    STORAGE_ADAPTER_DESCRIPTOR adapterDesc = {0};
    DWORD bytesReturned = 0;
    if (!DeviceIoControl(
        hDisk,
        IOCTL_STORAGE_QUERY_PROPERTY,
        &propQuery,
        sizeof(propQuery),
        &adapterDesc,
        sizeof(adapterDesc),
        &bytesReturned,
        NULL
    )) {
        std::wcerr << L"查询总线类型失败" << std::endl;
        CloseHandle(hDisk);
        return 1;
    }
    
    std::wcout << L"磁盘总线类型: ";
    switch(adapterDesc.BusType) {
        case BusTypeAta: std::wcout << L"ATA"; break;
        case BusTypeSata: std::wcout << L"SATA"; break;
        case BusTypeSas: std::wcout << L"SAS"; break;
        default: std::wcout << L"未知"; break;
    }
    std::wcout << "\n";
    
    // 根据总线类型读取SMART
    if (adapterDesc.BusType == BusTypeAta || adapterDesc.BusType == BusTypeSata) {
        ATA_SMART_READ_DATA ataSmart;
        if (GetAtaSmartData(hDisk, ataSmart)) {
            PrintAtaSmartData(ataSmart);
        }
    } else if (adapterDesc.BusType == BusTypeSas) {
        std::vector<UCHAR> sasSmart;
        if (GetSasSmartData(hDisk, sasSmart)) {
            PrintSasSmartData(sasSmart);
        }
    } else {
        std::cout << "不支持该总线类型的SMART读取" << std::endl;
    }
    
    CloseHandle(hDisk);
    return 0;
}

额外说明

  • SAS磁盘的SMART数据格式与ATA类似,但部分厂商可能有自定义字段,完整解析需参考对应磁盘的SCSI协议文档。
  • 若SAS磁盘是SAS转ATA的桥接设备,可尝试用ATA方式读取SMART,若失败再切换到SCSI方式。
  • 程序运行前需确认目标磁盘编号,可通过磁盘管理工具查看物理磁盘序号。

内容的提问来源于stack exchange,提问作者csypt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 06:07:09