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
相关产品推荐
相关产品推荐

