HDD/SSDの情報はS.M.A.R.Tを取得することで読み込むことができます。
この記事を読めば、SMARTの説明とプログラミング言語C#を用いてSMART情報を取得する方法がわかります。
- HDDの稼働時間や書き込み回数などの情報を読み込みたい
- 独自ツールを作成して読み込みたい
HDD情報を読み込めるS.M.A.R.Tとは?
SMARTとは「Self-Monitoring Analysis and Reporting Technology」の略でSSDやHDDの自己診断機能になります。
SMARTを使うことでハードディスクのさまざまな情報を取得することができます。
SMARTを使うメリット
ハードディスクの故障予知をする上でSMARTの情報は超重要です。
SMART情報には、「稼働時間」や「温度」、「読み書きエラー」、「シークアウトエラー」、「起動回数」などの値が含まれており、これらの情報を活用することでハードディスク利用者に故障予想を提示することができます。
つまり、利用者の貴重な情報資源を守ることができるのです。
SMARTで読み込めるパラメータ
読み込めるパラメータはこちら。(IDは16進数表示)
ハードディスクのメーカーによって読み込める項目が異なるので注意が必要です。
ID | Attribute Name | Description |
---|---|---|
01 | Raw Read Error Rate | 読み取りエラー発生率 |
02 | Throughput Performance | ハードディスクの全般的な処理能力 |
03 | Spin Up Time | HDDが設計上定められた回転数に達するまでにかかった平均時間 |
04 | Start/Stop Count | モーターが回転/停止した回数 |
05 | Reallocated Sectors Count | 代替処理されたセクター数 |
07 | Seek Error Rate | ヘッドのシークエラー率 |
08 | Seek Time Performance | ヘッドのシーク平均時間 |
09 | Power-On Hours | HDDが通電されている時間の合計 |
0A | Spin Retry Count | ヘッドのスピンアップ再試行回数 |
0B | Recalibration Retries | 歪み補正の再試行回数 |
0C | Device Power Cycle Count | 電源ON/OFF回数 |
0D | Soft Read Error Rate | プログラムリードエラー回数 |
BF | G-sense Error Late | 外的な衝撃によって引き起こされたプログラムエラーの頻度 |
C0 | Power-off Retract Count | 電源オフ時にヘッドを退避(アンロード)した回数 |
C1 | Load/Unload Cycle Count | ヘッドを復帰/退避(ロード/アンロード)した回数 |
C2 | Temperature | 温度 |
C3 | Hardware ECC recovered | ECC(誤り訂正符号)により検知されたエラーの頻度 |
C4 | Reallocation Event Count | 不良セクタの代替処理した回数 |
C5 | Current Pending Sector Count | バッドセクタ(利用に適さないセクタ)と見なされ、代替処理待ちとなっているセクタ数 |
C6 | Off-Line Scan Uncorrectable Sector Count | オフラインスキャン時に訂正できなかったセクタ数 |
C7 | UltraDMA CRC Error Count | 通信エラーの回数 |
C8 | Write Error Rate (Multi Zone Error Rate) | 書き込みエラー発生率 |
C9 | Soft Read Error Rate | ヘッドの位置がトラックの中心からずれてデータを正しく読み出すことができなかったエラー(オフトラック)の数 |
CA | Data Address Mark Error | データアドレスマーク(DMA)に関係するエラーの発生頻度 |
CB | Run Out Cancel | ECC(誤り訂正符号)によって検知されたエラーの数 |
CC | Soft ECC Correction | ソフトウェアECC(誤り訂正符号)によって修正されたエラーの数 |
CD | Thermal Asperity Rate | ヘッドとプラッターの衝突や摩擦による熱に起因するエラーの数 |
CE | Flying Height | ヘッドの高さ。低すぎるとプラッタとぶつかりやすい。高すぎるとread/writeエラーが起こりやすい |
CF | Spin High Current | ドライブを回転させるために使用した最大電流の値 |
D0 | Spin Buzz | ヘッドがドライブに衝突するのを避けるためにヘッドを垂直方向に跳ね上げた数 |
D1 | Offline Seek Performance | オフラインスキャンによって測定されたシーク性能の値 |
D3 | Vibration During Write | 書き込み中に発生した振動の数 |
D4 | Shock During Write | 書き込み中に発生した衝撃の数 |
DC | Disk Shift | 外部からの衝撃などによって、固定されているプラッターが本来固定されていた位置からずれてしまった距離 |
DD | G-Sense Error Rate | 外的な衝撃によって引き起こされたプログラムエラーの頻度 |
DE | Loaded Hours | データロード作業におけるアクチュエーターの動作時間 |
DF | Load/Unload Retry Count | ヘッドがランディングゾーンに退避し、再度プラッター上に戻ろうとする動作(ロード/アンロード)の再試行回数再試行回数 |
E0 | Load Friction | 機械部品の摩擦によって生じたアクチュエーターの負荷 |
E1 | Load Cycle Count | ハードディスクの電源を完全にON/OFFした回数 |
E2 | Load-in Time | ヘッドが動作可能状態にある時間の合計 |
E3 | Torque Amplification Count | ディスク回転時に必要な瞬間的トルク増幅回数 |
E4 | Power-Off Retract Count | 電源OFF等によりread/write要求が撤回され、ヘッドが緊急退避した回数 |
E6 | GMR Head Amplitude | 駆動時における、GMRヘッドの振動の振り幅 |
E7 | SSD Life Left | SSDのおよその寿命 |
E8 | Available Reserved Space | 利用可能な予備領域 (寿命) |
E9 | Media Wearout Indicator | メディア消耗指標 |
EA | Average erase count AND Maximum Erase Count | byte 0-1-2;平均消去数。byte 3-4-5:最大消去数(どちらもbig endian). |
EB | Good Block Count AND System(Free) Block Count | byte 0-1-2:使用可能なブロックの数(big endian)。byte 3-4:システム領域ブロック数 |
F0 | Head Flying Hours | ヘッドの位置調整に費やした時間 |
F1 | Total LBAs Written | 総書き込み量 (ホスト) |
F2 | Total LBAs Read | 総読み込み量 (ホスト) |
F3 | Total LBAs Written Expanded | 上位5byte:デバイスが書き込んだ総LBA数。下位7byte:属性 F1(Total LBAs Written)の値 |
F4 | Total LBAs Read Expanded | 上位5byte:total デバイスが読み込んだ総LBA数. 下位7byte:F2(Total LBAs Read)の値 |
F9 | NAND_Writes_1GiB | 総書き込み量 (NAND) |
FA | Read Error Retry Rate | 読み込みエラー発生率 |
FB | Minimum Spares Remaining | 利用可能な予備領域の割合 |
FC | Newly Added Bad Flash Block | 工場出荷時の初期化で発見された不良ブロックの総数 |
FE | Free Fall Protection | 落下検出回数 |
SMARTで重要なパラメータ
HDDの保守において重要なのは不良セクタがいくつあるかです。
- Reallocated Sectors Count
- Reallocation Event Count
- Current Pending Sector Count
以上の3点はすべて不良セクタに関するもので、いずれか1つが「しきい値」を超えてしまうといつ故障が起きてもおかしくない状態です。
つまり、「ハードディスクを今すぐに新品交換しましょう」という合図なのです。
SMARTが読み込めるツール
SMART情報はこちらのツールから読み込むことができます。
一番有名どころは「CristalDiskInfo」ですね。ディスクの異常を検知してメールや音声で通知してくれるディスクユーティリティです。
C#でSMARTを取得してみた
SMART情報をXMLファイルに保存するC#コードを作成してみました。
SMARTを取得するためのサンプルコード
using System;
using System.Collections.Generic;
using System.Management;
using System.Runtime.InteropServices;
namespace GetSmart
{
public enum SmartAttributeType : byte
{
ReadErrorRate = 0x01,
ThroughputPerformance = 0x02,
SpinUpTime = 0x03,
StartStopCount = 0x04,
ReallocatedSectorsCount = 0x05,
ReadChannelMargin = 0x06,
SeekErrorRate = 0x07,
SeekTimePerformance = 0x08,
PowerOnHoursPOH = 0x09,
SpinRetryCount = 0x0A,
CalibrationRetryCount = 0x0B,
PowerCycleCount = 0x0C,
SoftReadErrorRate = 0x0D,
SATADownshiftErrorCount = 0xB7,
EndtoEnderror = 0xB8,
HeadStability = 0xB9,
InducedOpVibrationDetection = 0xBA,
ReportedUncorrectableErrors = 0xBB,
CommandTimeout = 0xBC,
HighFlyWrites = 0xBD,
AirflowTemperatureWDC = 0xBE,
TemperatureDifferencefrom100 = 0xBE,
GSenseErrorRate = 0xBF,
PoweroffRetractCount = 0xC0,
LoadCycleCount = 0xC1,
Temperature = 0xC2,
HardwareECCRecovered = 0xC3,
ReallocationEventCount = 0xC4,
CurrentPendingSectorCount = 0xC5,
UncorrectableSectorCount = 0xC6,
UltraDMACRCErrorCount = 0xC7,
MultiZoneErrorRate = 0xC8,
WriteErrorRateFujitsu = 0xC8,
OffTrackSoftReadErrorRate = 0xC9,
DataAddressMarkerrors = 0xCA,
RunOutCancel = 0xCB,
SoftECCCorrection = 0xCC,
ThermalAsperityRateTAR = 0xCD,
FlyingHeight = 0xCE,
SpinHighCurrent = 0xCF,
SpinBuzz = 0xD0,
OfflineSeekPerformance = 0xD1,
VibrationDuringWrite = 0xD3,
ShockDuringWrite = 0xD4,
DiskShift = 0xDC,
GSenseErrorRateAlt = 0xDD,
LoadedHours = 0xDE,
LoadUnloadRetryCount = 0xDF,
LoadFriction = 0xE0,
LoadUnloadCycleCount = 0xE1,
LoadInTime = 0xE2,
TorqueAmplificationCount = 0xE3,
PowerOffRetractCycle = 0xE4,
GMRHeadAmplitude = 0xE6,
DriveTemperature = 0xE7,
HeadFlyingHours = 0xF0,
TransferErrorRateFujitsu = 0xF0,
TotalLBAsWritten = 0xF1,
TotalLBAsRead = 0xF2,
ReadErrorRetryRate = 0xFA,
FreeFallProtection = 0xFE,
}
public class SmartData
{
readonly Dictionary<SmartAttributeType, SmartAttribute> attributes;
readonly ushort structureVersion;
public SmartData(byte[] arrVendorSpecific)
{
attributes = new Dictionary<SmartAttributeType, SmartAttribute>();
for (int offset = 2; offset < arrVendorSpecific.Length;)
{
var a = FromBytes<SmartAttribute>(arrVendorSpecific, ref offset, 12);
// Attribute values 0x00, 0xfe, 0xff are invalid
if (a.AttributeType != 0x00 && (byte)a.AttributeType != 0xfe && (byte)a.AttributeType != 0xff)
{
attributes[a.AttributeType] = a;
}
}
structureVersion = (ushort)(arrVendorSpecific[0] * 256 + arrVendorSpecific
);
}
public ushort StructureVersion
{
get
{
return this.structureVersion;
}
}
public SmartAttribute this[SmartAttributeType v]
{
get
{
return this.attributes[v];
}
}
public IEnumerable<SmartAttribute> Attributes
{
get
{
return this.attributes.Values;
}
}
static T FromBytes<T>(byte[] bytearray, ref int offset, int count)
{
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocHGlobal(count);
Marshal.Copy(bytearray, offset, ptr, count);
offset += count;
return (T)Marshal.PtrToStructure(ptr, typeof(T));
}
finally
{
if (ptr != IntPtr.Zero)
{
Marshal.FreeHGlobal(ptr);
}
}
}
}
[StructLayout(LayoutKind.Sequential)]
public struct SmartAttribute
{
public SmartAttributeType AttributeType;
public ushort Flags;
public byte Value;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] VendorData;
public bool Advisory
{
get
{
return (Flags & 0x1) == 0x0; // Bit 0 unset?
}
}
public bool FailureImminent
{
get
{
return (Flags & 0x1) == 0x1; // Bit 0 set?
}
}
public bool OnlineDataCollection
{
get
{
return (Flags & 0x2) == 0x2; // Bit 0 set?
}
}
}
class Program
{
public static void Main()
{
HddModel hddModel = new HddModel();
hddModel.Info = new List<Model>();
try
{
var searcher = new ManagementObjectSearcher("root\\WMI", "SELECT * FROM MSStorageDriver_ATAPISmartData");
foreach (ManagementObject queryObj in searcher.Get())
{
Console.WriteLine("-----------------------------------");
Console.WriteLine("MSStorageDriver_ATAPISmartData instance");
Console.WriteLine("-----------------------------------");
var arrVendorSpecific = (byte[])queryObj.GetPropertyValue("VendorSpecific");
// Create SMART data from 'vendor specific' array
var d = new SmartData(arrVendorSpecific);
foreach (var b in d.Attributes)
{
Console.WriteLine("AttributeType:{0}, Value:{1}, Flag:{2}", b.AttributeType, b.Value, b.Flags);
b.VendorData[6] = 0x00;
b.VendorData[7] = 0x00;
foreach (byte vendorByte in b.VendorData)
{
Console.Write("{0:x} ", vendorByte);
}
Console.WriteLine($"Data:{BitConverter.ToInt32(b.VendorData)}");
// XMLファイルに保存するためのモデルを作成
Model model = new Model();
model.Id = (int)b.AttributeType;
model.Item = b.AttributeType.ToString();
model.CurrentValue = b.Value;
model.ThresholdValue = b.Flags;
string data = "";
for (int index = (b.VendorData.Length - 1); 0 <= index; index--)
{
data += string.Format("{0:X2}", b.VendorData[index]);
}
model.RowValue = data;
hddModel.Info.Add(model);
}
}
//出力先XMLのストリーム
System.IO.FileStream stream = new System.IO.FileStream(@"sample.xml", System.IO.FileMode.Create);
System.IO.StreamWriter writer = new System.IO.StreamWriter(stream, System.Text.Encoding.UTF8);
//名前空間出力抑制
System.Xml.Serialization.XmlSerializerNamespaces ns = new System.Xml.Serialization.XmlSerializerNamespaces();
ns.Add(String.Empty, String.Empty);
//シリアライズ
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(HddModel));
serializer.Serialize(writer, hddModel, ns);
writer.Flush();
writer.Close();
}
catch (ManagementException e)
{
Console.WriteLine("An error occurred while querying for WMI data: " + e.Message);
}
}
}
}
namespace GetSmart
{
[System.Xml.Serialization.XmlRoot("hdd")]
public class HddModel
{
/// <summary>
/// 情報
/// </summary>
[System.Xml.Serialization.XmlElement("info")]
public System.Collections.Generic.List<GetSmart.Model> Info { get; set; }
}
public class Model
{
/// <summary>
/// ID
/// </summary>
[System.Xml.Serialization.XmlAttribute("id")]
public int Id { get; set; }
/// <summary>
/// 項目
/// </summary>
[System.Xml.Serialization.XmlElement("item")]
public string Item { get; set; }
/// <summary>
/// 現在値
/// </summary>
[System.Xml.Serialization.XmlElement("current")]
public int CurrentValue { get; set; }
/// <summary>
/// 最悪値
/// </summary>
[System.Xml.Serialization.XmlElement("worst")]
public int WorstValue { get; set; }
/// <summary>
/// しきい値
/// </summary>
[System.Xml.Serialization.XmlElement("treshold")]
public int ThresholdValue { get; set; }
/// <summary>
/// 生データ
/// </summary>
[System.Xml.Serialization.XmlElement("row")]
public int RowValue { get; set; }
}
}
SMART情報を読み込んだ結果
無事にXMLファイルのタグにAttributeの値が入ってきました。
<?xml version="1.0" encoding="utf-8"?>
<hdd>
<info id="1">
<item>ReadErrorRate</item>
<current>166</current>
<worst>0</worst>
<treshold>42496</treshold>
<row>0000000000000000</row>
</info>
<info id="5">
<item>ReallocatedSectorsCount</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>0000000000000000</row>
</info>
<info id="9">
<item>PowerOnHoursPOH</item>
<current>95</current>
<worst>0</worst>
<treshold>24320</treshold>
<row>0000000000001204</row>
</info>
<info id="12">
<item>PowerCycleCount</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>0000000000000DD4</row>
</info>
<info id="100">
<item>100</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>000000000008069B</row>
</info>
<info id="168">
<item>168</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>0000000000000001</row>
</info>
<info id="169">
<item>169</item>
<current>99</current>
<worst>0</worst>
<treshold>25344</treshold>
<row>0000000000000034</row>
</info>
<info id="174">
<item>174</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>000000000000000A</row>
</info>
<info id="175">
<item>175</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>0000000000000000</row>
</info>
<info id="176">
<item>176</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>0000000000000000</row>
</info>
<info id="177">
<item>177</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>000000000000000E</row>
</info>
<info id="178">
<item>178</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>0000000000000003</row>
</info>
<info id="179">
<item>179</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>0000000000000004</row>
</info>
<info id="180">
<item>180</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>0000000000000368</row>
</info>
<info id="181">
<item>181</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>0000000000000000</row>
</info>
<info id="182">
<item>182</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>0000000000000000</row>
</info>
<info id="184">
<item>EndtoEnderror</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>0000000000000004</row>
</info>
<info id="187">
<item>ReportedUncorrectableErrors</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>0000000000000004</row>
</info>
<info id="188">
<item>CommandTimeout</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>000000000000002B</row>
</info>
<info id="194">
<item>Temperature</item>
<current>55</current>
<worst>0</worst>
<treshold>11264</treshold>
<row>000000150037002C</row>
</info>
<info id="195">
<item>HardwareECCRecovered</item>
<current>45</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>0000000000000000</row>
</info>
<info id="196">
<item>ReallocationEventCount</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>0000000000000000</row>
</info>
<info id="198">
<item>UncorrectableSectorCount</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>0000000000000020</row>
</info>
<info id="199">
<item>UltraDMACRCErrorCount</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>0000000000000000</row>
</info>
<info id="204">
<item>SoftECCCorrection</item>
<current>1</current>
<worst>0</worst>
<treshold>256</treshold>
<row>000000000162FB5C</row>
</info>
<info id="212">
<item>ShockDuringWrite</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>000000000000E791</row>
</info>
<info id="234">
<item>234</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>00000003624CEB90</row>
</info>
<info id="238">
<item>238</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>0000000000000064</row>
</info>
<info id="241">
<item>TotalLBAsWritten</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>000000047EB8646E</row>
</info>
<info id="242">
<item>TotalLBAsRead</item>
<current>100</current>
<worst>0</worst>
<treshold>25600</treshold>
<row>00000003AA1A81CB</row>
</info>
</hdd>
気になる不良セクタのスコア
不良セクタの値はすべて「しきい値以下」ということが確認できました。対象はSSD(SK hynix SC401 SATA 512GB)で使ってまだ1年経っていないので納得の結果です。
なぜか、Current Pending Sector Count
は取得できない結果となりました。
不良セクタ | しきい値 | 現在値 |
---|---|---|
Reallocated Sectors Count | 25600 | 100 |
Reallocation Event Count | 25600 | 100 |
Current Pending Sector Count | ー | ー |
まとめ
HDD/SSD情報をSMARTを用いて取得するには、CristalDiskInfoなどのツールを使えば簡単に読み込めます。
ただし、取得したデータを自動的に2次利用したい、といった場合は自作ツールを作ってしまうのも一つの手です!
今回提示したサンプルコードが参考になれば幸いです。
コメント