C++獲取SMART信息

1. SMART信息介紹

S.M.A.R.T. 全稱是“Self-Monitoring,Analysis and Reporting Technology”,中文意思是“自我監測分析與報告技術”,它可以對硬盤的溫度、內部電路、盤片表面介質材料等進行監測,力求及時分析出硬盤可能發出的問題,併發出警告,從而保護數據不受損失。SMART在96年已經成爲硬盤存儲行業的一個技術標準,主流硬盤企業均支持此技術。
SMART信息是一段512字節長度的信息,存放在硬盤控制中的內存中。
其中前2字節爲SMART的版本信息。後面的內容每12字節爲一個SMART屬性。

1.1. SMART屬性解釋:

  1. 1,ID# : 屬性ID, 從1到255.

  2. 2,ATTRIBUTE_NAME : 屬性名.

  3. 3,FLAG : 表示這個屬性攜帶的標記. 使用-f brief可以打印.

  4. 4,VALUE: Normalized value正常值, 取值範圍1到254. 越低表示越差. 越高表示越好.
    當前值是各ID項在硬盤運行時根據實測數據(RAW_VALUE)通過公式計算的結果,計算公式由硬盤廠家自定。 硬盤出廠時各ID項目都有一個預設的最大正常值,也即出廠值,這個預設的依據及計算方法爲硬盤廠家保密,不同型號的硬盤都不同,最大正常值通常爲100或200或253,
    新硬盤剛開始使用時顯示的當前值可以認爲是預設的最大正常值(有些ID項如溫度等除外)。
    隨着使用損耗或出現錯誤,當前值會根據實測數據而不斷刷新並逐漸減小。
    因此,當前值接近臨界值就意味着硬盤壽命的減少,發生故障的可能性增大,所以當前值也是判定硬盤健康狀態或推測壽命的依據之一。

  5. 5,WORST: 最差值,表示SMART開啓以來的, 所有Normalized values的最低值。
    最差值是硬盤運行時各ID項曾出現過的最大的非正常值。
    最差值是對硬盤運行中某項數據變劣的峯值統計,該數值也會不斷刷新。
    通常,最差值與當前值是相等的,如果最差值出現較大的波動(小於當前值),表明硬盤曾出現錯誤或曾經歷過惡劣的工作環境(如溫度)。

  6. 6,THRESH:閾值。當Normalized value小於等於THRESH值時, 表示這項指標已經failed了。
    注意, 如果這個屬性是pre-failure的, 那麼這項如果出現Normalized value<=THRESH, 那麼磁盤將馬上failed掉.

  7. 7,TYPE:這裏存在兩種TYPE類型, Pre-failed和Old_age.
    Pre-failed 類型的Normalized value可以用來預先知道磁盤是否要壞了. 例如Normalized value接近THRESH時, 就趕緊換硬盤吧.
    Old_age 類型的Normalized value是指正常的使用損耗值, 當Normalized value 接近THRESH時, 也需要注意, 但是比Pre-failed要好一點.

  8. 8,UPDATED:這個字段表示這個屬性的值在什麼情況下會被更新.
    一種是通常的操作和離線測試都更新(Always),
    另一種是隻在離線測試的情況下更新(Offline).

  9. 9,WHEN_FAILED:這個字段表示當前這個屬性的狀態。取值有以下三種:
    failing_now(normalized_value <= THRESH),
    或者in_the_past(WORST <= THRESH),
    或者 - , 正常(normalized_value以及wrost >= THRESH).

  10. 10,RAW_VALUE:表示這個屬性的未轉換前的RAW值, 可能是計數, 也可能是溫度, 也可能是其他的.
    注意RAW_VALUE轉換成Normalized value是由廠商的firmware提供的, smartmontools不提供轉換.

1.2. 屬性ID介紹

  • 01(001)底層數據讀取錯誤率 Raw Read Error Rate
      數據爲0或任意值,當前值應遠大於與臨界值。
      底層數據讀取錯誤率是磁頭從磁盤表面讀取數據時出現的錯誤,對某些硬盤來說,大於0的數據表明磁盤表面或者讀寫磁頭髮生問題,如介質損傷、磁頭污染、磁頭共振等等。不過對希捷硬盤來說,許多硬盤的這一項會有很大的數據量,這不代表有任何問題,主要是看當前值下降的程度。
      在固態硬盤中,此項的數據值包含了可校正的錯誤與不可校正的RAISE錯誤(UECC+URAISE)。
      注:RAISE(Redundant Array of Independent Silicon Elements)意爲獨立硅元素冗餘陣列,是固態硬盤特有的一種冗餘恢復技術,保證內部有類似RAID陣列的數據安全性。
  • 02(002)磁盤讀寫通量性能 Throughput Performance
      此參數表示硬盤的讀寫通量性能,數據值越大越好。當前值如果偏低或趨近臨界值,表示硬盤存在嚴重的問題,但現在的硬盤通常顯示數據值爲0或根本不顯示此項,一般在進行了人工脫機SMART測試後纔會有數據量。
  • 03(003)主軸起旋時間 Spin Up Time
      主軸起旋時間就是主軸電機從啓動至達到額定轉速所用的時間,數據值直接顯示時間,單位爲毫秒或者秒,因此數據值越小越好。不過對於正常硬盤來說,這一項僅僅是一個參考值,硬盤每次的啓動時間都不相同,某次啓動的稍慢些也不表示就有問題。硬盤的主軸電機從啓動至達到額定轉速大致需要4秒~15秒左右,過長的啓動時間說明電機驅動電路或者軸承機構有問題。旦這一參數的數據值在某些型號的硬盤上總是爲0,這就要看當前值和最差值來判斷了。 對於固態硬盤來說,所有的數據都是保存在半導體集成電路中,沒有主軸電機,所以這項沒有意義,數據固定爲0,當前值固定爲100。
  • 04(004)啓停計數 Start/Stop Count
      這一參數的數據是累計值,表示硬盤主軸電機啓動/停止的次數,新硬盤通常只有幾次,以後會逐漸增加。系統的某些功能如空閒時關閉硬盤等會使硬盤啓動/停止的次數大爲增加,在排除定時功能的影響下,過高的啓動/停止次數(遠大於通電次數0C)暗示硬盤電機及其驅動電路可能有問題。 這個參數的當前值是依據某種公式計算的結果,例如對希捷某硬盤來說臨界值爲20,當前值是通過公式“100-(啓停計數/1024)”計算得出的。若新硬盤的啓停計數爲0,當前值爲100-(0/1024)=100,隨着啓停次數的增加,該值不斷下降,當啓停次數達到81920次時,當前值爲100-(81920/1024)=20,已達到臨界值,表示從啓停次數來看,該硬盤已達設計壽命,當然這只是個壽命參考值,並不具有確定的指標性。 這一項對於固態硬盤同樣沒有意義,數據固定爲0,當前值固定爲100。
  • 05(005)重映射扇區計數 Reallocated Sectors Count/ 退役塊計數 Retired Block Count
      數據應爲0,當前值應遠大於臨界值。當硬盤的某扇區持續出現讀/寫/校驗錯誤時,硬盤固件程序會將這個扇區的物理地址加入缺陷表(G-list),將該地址重新定向到預先保留的備用扇區並將其中的數據一併轉移,這就稱爲重映射。執行重映射操作後的硬盤在Windows常規檢測中是無法發現不良扇區的,因其地址已被指向備用扇區,這等於屏蔽了不良扇區。 這項參數的數據值直接表示已經被重映射扇區的數量,當前值則隨着數據值的增加而持續下降。當發現此項的數據值不爲零時,要密切注意其發展趨勢,若能長期保持穩定,則硬盤還可以正常運行;若數據值不斷上升,說明不良扇區不斷增加,硬盤已處於不穩定狀態,應當考慮更換了。如果當前值接近或已到達臨界值(此時的數據值並不一定很大,因爲不同硬盤保留的備用扇區數並不相同),表示缺陷表已滿或備用扇區已用盡,已經失去了重映射功能,再出現不良扇區就會顯現出來並直接導致數據丟失。
      這一項不僅是硬盤的壽命關鍵參數,而且重映射扇區的數量也直接影響硬盤的性能,例如某些硬盤會出現數據量很大,但當前值下降不明顯的情況,這種硬盤儘管還可正常運行,但也不宜繼續使用。因爲備用扇區都是位於磁盤尾部(靠近盤片軸心處),大量的使用備用扇區會使尋道時間增加,硬盤性能明顯下降。
      這個參數在機械硬盤上是非常敏感的,而對於固態硬盤來說同樣具有重要意義。閃存的壽命是正態分佈的,例如說MLC能寫入一萬次以上,實際上說的是寫入一萬次之前不會發生“批量損壞”,但某些單元可能寫入幾十次就損壞了。換言之,機械硬盤的盤片不會因讀寫而損壞,出現不良扇區大多與工藝質量相關,而閃存的讀寫次數則是有限的,因而損壞是正常的。所以固態硬盤在製造時也保留了一定的空間,當某個存儲單元出現問題後即把損壞的部分隔離,用好的部分來頂替。這一替換方法和機械硬盤的扇區重映射是一個道理,只不過機械硬盤正常時極少有重映射操作,而對於固態硬盤是經常性的。 在固態硬盤中這一項的數據會隨着使用而不斷增長,只要增長的速度保持穩定就可以。通常情況下,數據值=100-(100×被替換塊/必需塊總數),因此也可以估算出硬盤的剩餘壽命。 Intel固態硬盤型號的第十二個字母表示了兩種規格,該字母爲1表示第一代的50納米技術的SSD,爲2表示第二代的34納米技術的SSD,如SSDSA2M160G2GN就表示是34nm的SSD。所以參數的查看也有兩種情況:
      50nm的SSD(一代)要看當前值。這個值初始是100,當出現替換塊的時候這個值並不會立即變化,一直到已替換四個塊時這個值變爲1,之後每增加四個塊當前值就+1。也就是100對應0~3個塊,1對應4~7個塊,2對應8~11個塊……
      34nm的SSD(二代)直接查看數據值,數據值直接表示有多少個被替換的塊。
  • 06(006)讀取通道餘量 Read Channel Margin
      這一項功能不明,現在的硬盤也不顯示這一項。
  • 07(007)尋道錯誤率 Seek Error Rate
      數據應爲0,當前值應遠大於與臨界值。
      這一項表示磁頭尋道時的錯誤率,有衆多因素可導致尋道錯誤率上升,如磁頭組件的機械系統、伺服電路有局部問題,盤片表面介質不良,硬盤溫度過高等等。
      通常此項的數據應爲0,但對希捷硬盤來說,即使是新硬盤,這一項也可能有很大的數據量,這不代表有任何問題,還是要看當前值是否下降。
  • 08(008)尋道性能 Seek Time Performance
      此項表示硬盤尋道操作的平均性能(尋道速度),通常與前一項(尋道錯誤率)相關聯。當前值持續下降標誌着磁頭組件、尋道電機或伺服電路出現問題,但現在許多硬盤並不顯示這一項。
  • 09(009)通電時間累計 Power-On Time Count (POH)
      這個參數的含義一目瞭然,表示硬盤通電的時間,數據值直接累計了設備通電的時長,新硬盤當然應該接近0,但不同硬盤的計數單位有所不同,有以小時計數的,也有以分、秒甚至30秒爲單位的,這由磁盤製造商來定義。
      這一參數的臨界值通常爲0,當前值隨着硬盤通電時間增加會逐漸下降,接近臨界值表明硬盤已接近預計的設計壽命,當然這並不表明硬盤將出現故障或立即報廢。參考磁盤製造商給出的該型號硬盤的MTBF(平均無故障時間)值,可以大致估計剩餘壽命或故障概率。
      對於固態硬盤,要注意“設備優先電源管理功能(device initiated power management,DIPM)”會影響這個統計:如果啓用了DIPM,持續通電計數裏就不包括睡眠時間;如果關閉了DIPM功能,那麼活動、空閒和睡眠三種狀態的時間都會被統計在內。
  • 0A(010)主軸起旋重試次數 Spin up Retry Count
      數據應爲0,當前值應大於臨界值。
      主軸起旋重試次數的數據值就是主軸電機嘗試重新啓動的計數,即主軸電機啓動後在規定的時間裏未能成功達到額定轉速而嘗試再次啓動的次數。數據量的增加表示電機驅動電路或是機械子系統出現問題,整機供電不足也會導致這一問題。
  • 0B(011)磁頭校準重試計數 Calibration Retry Count
      數據應爲0,當前值應遠大於與臨界值。
      硬盤在溫度發生變化時,機械部件(特別是盤片)會因熱脹冷縮出現形變,因此需要執行磁頭校準操作消除誤差,有的硬盤還內置了磁頭定時校準功能。這一項記錄了需要再次校準(通常因上次校準失敗)的次數。
      這一項的數據量增加,表示電機驅動電路或是機械子系統出現問題,但有些型號的新硬盤也有一定的數據量,並不表示有問題,還要看當前值和最差值。
  • 0C(012)通電週期計數 Power Cycle Count
      通電週期計數的數據值表示了硬盤通電/斷電的次數,即電源開關次數的累計,新硬盤通常只有幾次。
      這一項與啓停計數(04)是有區別的,一般來說,硬盤通電/斷電意味着計算機的開機與關機,所以經歷一次開關機數據纔會加1;而啓停計數(04)表示硬盤主軸電機的啓動/停止(硬盤在運行時可能多次啓停,如系統進入休眠或被設置爲空閒多少時間而關閉)。所以大多情況下這個通電/斷電的次數會小於啓停計數(04)的次數。
      通常,硬盤設計的通電次數都很高,如至少5000次,因此這一計數只是壽命參考值,本身不具指標性。
  • 0D(013)軟件讀取錯誤率 Soft Read Error Rate
      軟件讀取錯誤率也稱爲可校正的讀取誤碼率,就是報告給操作系統的未經校正的讀取錯誤。數據值越低越好,過高則可能暗示盤片磁介質有問題。
  • AA(170)壞塊增長計數 Grown Failing Block Count(Micron 鎂光)
      讀寫失敗的塊增長的總數。
  • AB(171)編程失敗塊計數 Program Fail Block Count
      Flash編程失敗塊的數量。
  • AC(172)擦寫失敗塊計數 Erase Fail Block Count
      擦寫失敗塊的數量。
  • AD(173)磨損平衡操作次數(平均擦寫次數) / Wear Leveling Count(Micron 鎂光)
      所有好塊的平均擦寫次數。
      Flash芯片有寫入次數限制,當使用FAT文件系統時,需要頻繁地更新文件分配表。如果閃存的某些區域讀寫過於頻繁,就會比其它區域磨損的更快,這將明顯縮短整個硬盤的壽命(即便其它區域的擦寫次數還遠小於最大限制)。所以,如果讓整個區域具有均勻的寫入量,就可明顯延長芯片壽命,這稱爲磨損均衡措施。
  • AE(174)意外失電計數 Unexpected Power Loss Count
      硬盤自啓用後發生意外斷電事件的次數。
  • B1(177)磨損範圍對比值 Wear Range Delta
      磨損最重的塊與磨損最輕的塊的磨損百分比之差。
  • B4(180)未用的備用塊計數 Unused Reserved Block Count Total(惠普)
      固態硬盤會保留一些容量來準備替換損壞的存儲單元,所以可用的預留空間數非常重要。這個參數的當前值表示的是尚未使用的預留的存儲單元數量。
  • B5(181)編程失敗計數 Program Fail Count
      用4個字節顯示已編程失敗的次數,與(AB)參數相似。
      B5(181)非4KB對齊訪問數 Non-4k Aligned Access(Micron 鎂光)
  • B6(182)擦寫失敗計數 Erase Fail Count
      用4個字節顯示硬盤自啓用後塊擦寫失敗的次數,與(AC)參數相似。

B7(183)串口降速錯誤計數 SATA Downshift Error Count
  這一項表示了SATA接口速率錯誤下降的次數。通常硬盤與主板之間的兼容問題會導致SATA傳輸級別降級運行。

  • B8(184)I/O錯誤檢測與校正 I/O Error Detection and Correction(IOEDC)
      “I/O錯誤檢測與校正”是惠普公司專有的SMART IV技術的一部分,與其他製造商的I/O錯誤檢測和校正架構一樣,它記錄了數據通過驅動器內部高速緩存RAM傳輸到主機時的奇偶校驗錯誤數量。
      B8(184)點到點錯誤檢測計數 End to End Error Detection Count
      Intel第二代的34nm固態硬盤有點到點錯誤檢測計數這一項。固態硬盤裏有一個LBA(logical block addressing,邏輯塊地址)記錄,這一項顯示了SSD內部邏輯塊地址與真實物理地址間映射的出錯次數。
      B8(184)原始壞塊數 Init Bad Block Count(Indilinx芯片)
      硬盤出廠時已有的壞塊數量。
  • B9(185)磁頭穩定性 Head Stability
      意義不明。
  • BB(187)無法校正的錯誤 Reported Uncorrectable Errors
      報告給操作系統的無法通過硬件ECC校正的錯誤。如果數據值不爲零,就應該備份硬盤上的數據了。
      報告給操作系統的在所有存取命令中出現的無法校正的RAISE(URAISE)錯誤。
  • BC(188)命令超時 Command Timeout
      由於硬盤超時導致操作終止的次數。通常數據值應爲0,如果遠大於零,最有可能出現的是電源供電問題或者數據線氧化致使接觸不良,也可能是硬盤出現嚴重問題。
  • BD(189)高飛寫入 High Fly Writes
      磁頭飛行高度監視裝置可以提高讀寫的可靠性,這一裝置時刻監測磁頭的飛行高度是否在正常範圍來保證可靠的寫入數據。如果磁頭的飛行高度出現偏差,寫入操作就會停止,然後嘗試重新寫入或者換一個位置寫入。這種持續的監測過程提高了寫入數據的可靠性,同時也降低了讀取錯誤率。這一項的數據值就統計了寫入時磁頭飛行高度出現偏差的次數。
  • BE(190)氣流溫度 Airflow Temperature
      這一項表示的是硬盤內部盤片表面的氣流溫度。在希捷公司的某些硬盤中,當前值=(100-當前溫度),因此氣流溫度越高,當前值就越低,最差值則是當前值曾經到達過的最低點,臨界值由製造商定義的最高允許溫度來確定,而數據值不具實際意義。許多硬盤也沒有這一項參數。
  • BF(191)衝擊錯誤率 G-sense error rate
      這一項的數據值記錄了硬盤受到機械衝擊導致出錯的頻度。
  • C0(192)斷電返回計數 Power-Off Retract Count
      當計算機關機或意外斷電時,硬盤的磁頭都要返回停靠區,不能停留在盤片的數據區裏。正常關機時電源會給硬盤一個通知,即Standby Immediate,就是說主機要求將緩存數據寫入硬盤,然後就準備關機斷電了(休眠、待機也是如此);意外斷電則表示硬盤在未收到關機通知時就失電,此時磁頭會自動復位,迅速離開盤片。
      這個參數的數據值累計了磁頭返回的次數。但要注意這個參數對某些硬盤來說僅記錄意外斷電時磁頭的返回動作;而某些硬盤記錄了所有(包括休眠、待機,但不包括關機時)的磁頭返回動作;還有些硬盤這一項沒有記錄。因此這一參數的數據值在某些硬盤上持續爲0或稍大於0,但在另外的硬盤上則會大於通電週期計數(0C)或啓停計數(04)的數據。在一些新型節能硬盤中,這一參數的數據量還與硬盤的節能設計相關,可能會遠大於通電週期計數(0C)或啓停計數(04)的數據,但又遠小於磁頭加載/卸載計數(C1)的數據量。
      對於固態硬盤來說,雖然沒有磁頭的加載/卸載操作,但這一項的數據量仍然代表了不安全關機,即發生意外斷電的次數。
  • C1(193)磁頭加載/卸載計數 Load/Unload Cycle Count
      對於過去的硬盤來說,盤片停止旋轉時磁頭臂停靠於盤片中心軸處的停泊區,磁頭與盤片接觸,只有當盤片旋轉到一定轉速時,磁頭纔開始漂浮於盤片之上並開始向外側移動至數據區。這使得磁頭在硬盤啓停時都與盤片發生摩擦,雖然盤片的停泊區不存儲數據,但無疑啓停一個循環,就使磁頭經歷兩次磨損。所以對以前的硬盤來說,磁頭起降(加載/卸載)次數是一項重要的壽命關鍵參數。
      而在現代硬盤中,平時磁頭臂是停靠於盤片之外的一個專門設計的停靠架上,遠離盤片。只有當盤片旋轉達到額定轉速後,磁頭臂纔開始向內(盤片軸心)轉動使磁頭移至盤片區域(加載),磁頭臂向外轉動返回至停靠架即卸載。這樣就徹底杜絕了硬盤啓停時磁頭與盤片接觸的現象,西部數據公司將其稱爲“斜坡加載技術”。由於磁頭在加載/卸載過程中始終不與盤片接觸,不存在磁頭的磨損,使得這一參數的重要性已經大大下降。
      這個參數的數據值就是磁頭執行加載/卸載操作的累計次數。從原理上講,這個加載/卸載次數應當與硬盤的啓停次數相當,但對於筆記本內置硬盤以及臺式機新型節能硬盤來說,這一項的數據量會很大。這是因爲磁頭臂組件設計有一個固定的返回力矩,保證在意外斷電時磁頭能靠彈簧力自動離開盤片半徑範圍,迅速返回停靠架。所以要讓硬盤運行時磁頭保持在盤片的半徑之內,就要使磁頭臂驅動電機(尋道電機)持續通以電流。而讓磁頭臂在硬盤空閒幾分鐘後就立即執行卸載動作,返回到停靠架上,既有利於節能,又降低了硬盤受外力衝擊導致磁頭與盤片接觸的概率。雖然再次加載會增加一點尋道時間,但畢竟弊大於利,所以在這類硬盤中磁頭的加載/卸載次數會遠遠大於通電週期計數(0C)或啓停計數(04)的數據量。不過這種加載/卸載方式已經沒有了磁頭與盤片的接觸,所以設計值也已大大增加,通常筆記本內置硬盤的磁頭加載/卸載額定值在30~60萬次,而臺式機新型節能硬盤的磁頭加載/卸載設計值可達一百萬次。
  • C2(194)溫度 Temperature
      溫度的數據值直接表示了硬盤內部的當前溫度。硬盤運行時最好不要超過45℃,溫度過高雖不會導致數據丟失,但引起的機械變形會導致尋道與讀寫錯誤率上升,降低硬盤性能。硬盤的最高允許運行溫度可查看硬盤廠商給出的數據,一般不會超過60℃。 不同廠家對溫度參數的當前值、最差值和臨界值有不同的表示方法:希捷公司某些硬盤的當前值就是實際溫度(攝氏)值,最差值則是曾經達到過的最高溫度,臨界值不具意義;而西部數據公司一些硬盤的最差值是溫度上升到某值後的時間函數,每次升溫後的持續時間都將導致最差值逐漸下降,當前值則與當前溫度成反比,即當前溫度越高,當前值越低,隨實際溫度波動。
  • C3(195)硬件ECC校正 Hardware ECC Recovered
      ECC(Error Correcting Code)的意思是“錯誤檢查和糾正”,這個技術能夠容許錯誤,並可以將錯誤更正,使讀寫操作得以持續進行,不致因錯誤而中斷。這一項的數據值記錄了磁頭在盤片上讀寫時通過ECC技術校正錯誤的次數,不過許多硬盤有其製造商特定的數據結構,因此數據量的大小並不能直接說明問題。
  • C4(196)重映射事件計數 Reallocetion Events Count
      數據應爲0,當前值應遠大於臨界值。
      這個參數的數據值記錄了將重映射扇區的數據轉移到備用扇區的嘗試次數,是重映射操作的累計值,成功的轉移和不成功的轉移都會被計數。因此這一參數與重映射扇區計數(05)相似,都是反映硬盤已經存在不良扇區。
      C4(196)擦除錯誤塊計數 Erase Failure block Count(Indilinx芯片)
      在固態硬盤中,這一參數記錄了被重映射的塊編程失敗的數量。
  • C5(197)當前待映射扇區計數 Current Pending Sector Count
      數據應爲0,當前值應遠大於臨界值。
      這個參數的數據表示了“不穩定的”扇區數,即等待被映射的扇區(也稱“被掛起的扇區”)數量。如果不穩定的扇區隨後被讀寫成功,該扇區就不再列入等待範圍,數據值就會下降。
      僅僅讀取時出錯的扇區並不會導致重映射,只是被列入“等待”,也許以後讀取就沒有問題,所以只有在寫入失敗時纔會發生重映射。下次對該扇區寫入時如果繼續出錯,就會產生一次重映射操作,此時重映射扇區計數(05)與重映射事件計數(C4)的數據值增加,此參數的數據值下降。
  • C6(198)脫機無法校正的扇區計數 Offline Uncorrectable Sector Count
      數據應爲0,當前值應遠大於臨界值。
      這個參數的數據累計了讀寫扇區時發生的無法校正的錯誤總數。數據值上升表明盤片表面介質或機械子系統出現問題,有些扇區肯定已經不能讀取,如果有文件正在使用這些扇區,操作系統會返回讀盤錯誤的信息。下一次寫操作時會對該扇區執行重映射。 C6(198)總讀取頁數 Total Count of Read Sectors(Indilinx芯片)
  • C7(199)Ultra ATA訪問校驗錯誤率 Ultra ATA CRC Error Rate
      這個參數的數據值累計了通過接口循環冗餘校驗(Interface Cyclic Redundancy Check,ICRC)發現的數據線傳輸錯誤的次數。如果數據值不爲0且持續增長,表示硬盤控制器→數據線→硬盤接口出現錯誤,劣質的數據線、接口接觸不良都可能導致此現象。由於這一項的數據值不會復零,所以某些新硬盤也會出現一定的數據量,只要更換數據線後數據值不再繼續增長,即表示問題已得到解決。
      C7(199)總寫入頁數 Total Count of Write Sectors(Indilinx芯片)
  • C8(200)寫入錯誤率 Write Error Rate / 多區域錯誤率 Multi-Zone Error Rate(西部數據)
      數據應爲0,當前值應遠大於臨界值。
      這個參數的數據累計了向扇區寫入數據時出現錯誤的總數。有的新硬盤也會有一定的數據量,若數據值持續快速升高(當前值偏低),表示盤片、磁頭組件可能有問題。
      C8(200)總讀取指令數 Total Count of Read Command(Indilinx芯片)
  • C9(201)脫道錯誤率 Off Track Error Rate / 邏輯讀取錯誤率 Soft Read Error Rate
      數據值累積了讀取時脫軌的錯誤數量,如果數據值不爲0,最好備份硬盤上的資料。
      C9(201)TA Counter Detected(意義不明)
      C9(201)寫入指令總數 Total Count of Write Command(Indilinx芯片)
  • CA(202)數據地址標記錯誤 Data Address Mark errors
      此項的數據值越低越好(或者由製造商定義)。
      CA(202)TA Counter Increased(意義不明)
      CA(202)剩餘壽命 Percentage Of The Rated Lifetime Used(Micron 鎂光芯片)
      當前值從100開始下降至0,表示所有塊的擦寫餘量統計。計算方法是以MLC擦寫次數除以50,SLC擦寫次數除以1000,結果取整數,將其與100的差值作爲當前值(MLC預計擦寫次數爲5000,SLC預計擦寫次數爲100000)。
      CA(202)閃存總錯誤bit數 Total Count of error bits from flash(Indilinx芯片)
  • CB(203)軟件ECC錯誤數 Run Out Cancel
      錯誤檢查和糾正(ECC)出錯的頻度。
      CB(203)校正bit錯誤的總讀取頁數 Total Count of Read Sectors with correct bits error(Indilinx芯片)
  • CC(204)軟件ECC校正 Soft ECC Correction
      通過軟件ECC糾正錯誤的計數。
      CC(204)壞塊滿標誌 Bad Block Full Flag(Indilinx芯片)
  • CD(205)熱騷動錯誤率 Thermal Asperity Rate (TAR)
      由超溫導致的錯誤。數據值應爲0。
      CD(205)最大可編程/擦除次數 Max P/E Count(Indilinx芯片)
  • CE(206)磁頭飛行高度 Flying Height
      磁頭距離盤片表面的垂直距離。高度過低則增加了磁頭與盤片接觸導致損壞的可能性;高度偏高則增大了讀寫錯誤率。不過準確地說,硬盤中並沒有任何裝置可以直接測出磁頭的飛行高度,製造商也只是根據磁頭讀取的信號強度來推算磁頭飛行高度。
      CE(206)底層數據寫入出錯率 Write Error Rate
      CE(206)最小擦寫次數 Erase Count Min(Indilinx芯片)
  • CF(207)主軸過電流 Spin High Current
      數據值記錄了主軸電機運行時出現浪涌電流的次數,數據量的增加意味着軸承或電機可能有問題。
      CF(207)最大擦寫次數 Erase Count Max(Indilinx芯片)
  • D0(208)主軸電機重啓次數 Spin Buzz
      數據值記錄了主軸電機反覆嘗試啓動的次數,這通常是由於電源供電不足引起的。
      D0(208)平均擦寫次數Erase Count Average(Indilinx芯片)
  • D1(209)脫機尋道性能 Offline Seek Performance
      這一項表示驅動器在脫機狀態下的尋道性能,通常用於工廠內部測試。
      D1(209)剩餘壽命百分比 Remaining Life %(Indilinx芯片)
  • D2(210)斜坡加載值 Ramp Load Value
      這一項僅見於幾年前邁拓製造的部分硬盤。通常數據值爲0,意義不明。
      D2(210)壞塊管理錯誤日誌 BBM Error Log(Indilinx芯片)
  • D3(211)寫入時振動 Vibration During Write
      寫入數據時受到受到外部振動的記錄。
      D3(211)SATA主機接口CRC寫入錯誤計數 SATA Error Count CRC (Write)(Indilinx芯片)
  • D4(212)寫入時衝擊 Shock During Write
      寫入數據時受到受到外部機械衝擊的記錄。
      D4(212)SATA主機接口讀取錯誤計數 SATA Error Count Count CRC (Read)(Indilinx芯片)
  • DC(220)盤片偏移量 Disk Shift
      硬盤中的盤片相對主軸的偏移量(通常是受外力衝擊或溫度變化所致),單位未知,數據值越小越好。
  • DD(221)衝擊錯誤率 G-sense error rate
      與(BF)相同,數據值記錄了硬盤受到外部機械衝擊或振動導致出錯的頻度。
  • DE(222)磁頭尋道時間累計 Loaded Hours
      磁頭臂組件運行的小時數,即尋道電機運行時間累計。
  • DF(223)磁頭加載/卸載重試計數 Load/Unload Retry Count
      這一項與(C1)項類似,數據值累積了磁頭嘗試重新加載/卸載的次數。
  • E0(224)磁頭阻力 Load Friction
      磁頭工作時受到的機械部件的阻力。
  • E1(225)主機寫入數據量 Host Writes
      由於閃存的擦寫次數是有限的,所以這項是固態硬盤特有的統計。Intel的SSD是每當向硬盤寫入了65536個扇區,這一項的數據就+1。如果用HDTune等軟件查看SMART時可以自己計算,Intel SSD Toolbox已經爲你算好了,直接就顯示了曾向SSD中寫入過的數據量。
  • E2(226)磁頭加載時間累計 Load ‘In’-time
      磁頭組件運行時間的累積數,即磁頭臂不在停靠區的時間,與(DE)項相似。
  • E3(227)扭矩放大計數 Torque Amplification Count
      主軸電機試圖提高扭矩來補償盤片轉速變化的次數。當主軸軸承存在問題時,主軸電機會嘗試增加驅動力使盤片穩定旋轉。這個參數的當前值下降,說明硬盤的機械子系統出現了嚴重的問題。
  • E4(228)斷電返回計數 Power-Off Retract Cycle
      數據值累計了磁頭因設備意外斷電而自動返回的次數,與(C0)項相似。
  • E6(230)GMR磁頭振幅 GMR Head Amplitude
      磁頭“抖動”,即正向/反向往復運動的距離。
  • E7(231)溫度 Temperature
      溫度的數據值直接表示了硬盤內部的當前溫度,與(C2)項相同。
      E7(231)剩餘壽命 SSD Life Left
      剩餘壽命是基於P/E週期與可用的備用塊作出的預測。新硬盤爲100;10表示PE週期已到設計值,但尚有足夠的保留塊;0表示保留塊不足,硬盤將處於只讀方式以便備份數據。
  • E8(232)壽命餘量 Endurance Remaining
      壽命餘量是指硬盤已擦寫次數與設計最大可擦寫次數的百分比,與(CA)項相似。
      E8(232)預留空間剩餘量 Available Reserved Space(Intel芯片)
      對於Intel的SSD來說,前邊05項提到會保留一些容量來準備替換損壞的存儲單元,所以可用的預留空間數非常重要。當保留的空間用盡,再出現損壞的單元就將出現數據丟失,這個SSD的壽命就結束了。所以僅看05項意義並不大,這一項才最重要。這項參數可以看當前值,新的SSD裏所有的預留空間都在,所以是100。隨着預留空間的消耗,當前值將不斷下降,減小到接近臨界值(一般是10)時,就說明只剩下10%的預留空間了,SSD的壽命將要結束。這個與(B4)項相似。
  • E9(233)通電時間累計 Power-On Hours
      對於普通硬盤來說,這一項與(09)相同。
      E9(233)介質磨耗指數 Media Wareout Indicator(Intel芯片)
      由於固態硬盤的擦寫次數是有限的,當到達一定次數的時候,就會出現大量的單元同時損壞,這時候預留空間也頂不住了,所以這項參數實際上表示的是硬盤設計壽命。Intel的SSD要看當前值,隨着NAND的平均擦寫次數從0增長到最大的設計值,這一參數的當前值從開始的100逐漸下降至1爲止。這表示SSD的設計壽命已經終結。當然到達設計壽命也不一定意味着SSD就立即報廢,這與閃存芯片的品質有着很大的關係。
      注:Total Erase Count全擦寫計數是指固態硬盤中所有塊的擦寫次數的總和,不同規格的NAND芯片以及不同容量的SSD,其最大全擦寫次數均有所不同。
  • F0(240)磁頭飛行時間 Head Flying Hours / 傳輸錯誤率 Transfer Error Rate(富士通)
      磁頭位於工作位置的時間。
      富士通硬盤表示在數據傳輸時連接被重置的次數。
  • F1(241)LBA寫入總數 Total LBAs Written
      LBA寫入數的累計。
      F1(241)寫入剩餘壽命 Lifetime Writes from Host
      自硬盤啓用後主機向硬盤寫入的數據總量,以4個字節表示,每寫入64GB字節作爲一個單位。
  • F2(242)LBA讀取總數 Total LBAs Read
      LBA讀取數的累計。某些SMART讀取工具會顯示負的數據值,是因爲採用了48位LBA,而不是32位LBA。
      F2(242)讀取剩餘壽命 Lifetime Reads from Host
      自硬盤啓用後主機從硬盤讀取的數據總量,以4個字節表示,每讀取64GB字節作爲一個單位。
  • FA(250)讀取錯誤重試率 Read Error Retry Rate
      從磁盤上讀取時出錯的次數。
  • FE(254)自由墜落保護 Free Fall Protection
      現在有些筆記本硬盤具有自由墜落保護功能,當硬盤內置的加速度探測裝置檢測到硬盤位移時,會立即停止讀寫操作,將磁頭臂復位。這個措施防止了磁頭與盤片之間發生摩擦撞擊,提高了硬盤的抗震性能。這個參數的數據裏記錄了這一保護裝置動作的次數。

2. C++獲取SMART信息

2.1. 通過DeviceIoControl

  • 通過DeviceIoControl發送SMART_GET_VERSION命令碼獲取磁盤設備是否支持SMART技術
  • 通過DeviceIoControl發送SMART_RCV_DRIVE_DATA命令碼獲取磁盤設備的SMART信息Buff。
  • 按規則解析SMART信息。
// SmartReader.h: interface for the CSmartReader class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_SMARTREADER_H__494F15B9_0FFA_4BB4_BDD0_2D4C5129E530__INCLUDED_)
#define AFX_SMARTREADER_H__494F15B9_0FFA_4BB4_BDD0_2D4C5129E530__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#define _WIN32_WINNT 0x500
#include <devioctl.h>
#include <ntdddisk.h>
#pragma warning(disable:4786)
#include <map>
using namespace std;
typedef map<DWORD,LPVOID> SMARTINFOMAP;


#define SMART_ATTRIB_RAW_READ_ERROR_RATE					1
#define SMART_ATTRIB_THROUGHPUT_PERFORMANCE					2
#define SMART_ATTRIB_SPIN_UP_TIME							3
#define SMART_ATTRIB_START_STOP_COUNT						4
#define SMART_ATTRIB_START_REALLOCATION_SECTOR_COUNT		5
#define SMART_ATTRIB_SEEK_ERROR_RATE						7
#define SMART_ATTRIB_POWER_ON_HOURS_COUNT					9
#define SMART_ATTRIB_SPIN_RETRY_COUNT						10
#define SMART_ATTRIB_RECALIBRATION_RETRIES					11
#define SMART_ATTRIB_DEVICE_POWER_CYCLE_COUNT				12
#define SMART_ATTRIB_SOFT_READ_ERROR_RATE					13
#define SMART_ATTRIB_LOAD_UNLOAD_CYCLE_COUNT				193
#define SMART_ATTRIB_TEMPERATURE							194
#define SMART_ATTRIB_ECC_ON_THE_FLY_COUNT					195
#define SMART_ATTRIB_REALLOCATION_EVENT_COUNT				196
#define SMART_ATTRIB_CURRENT_PENDING_SECTOR_COUNT			197
#define SMART_ATTRIB_UNCORRECTABLE_SECTOR_COUNT				198
#define SMART_ATTRIB_ULTRA_DMA_CRC_ERROR_COUNT				199
#define SMART_ATTRIB_WRITE_ERROR_RATE						200
#define SMART_ATTRIB_TA_COUNTER_INCREASED					202
#define SMART_ATTRIB_GSENSE_ERROR_RATE						221
#define SMART_ATTRIB_POWER_OFF_RETRACT_COUNT				228
#define MAX_ATTRIBUTES	256

#define INDEX_ATTRIB_INDEX									0
#define INDEX_ATTRIB_UNKNOWN1								1
#define INDEX_ATTRIB_UNKNOWN2								2
#define INDEX_ATTRIB_VALUE									3
#define INDEX_ATTRIB_WORST									4
#define INDEX_ATTRIB_RAW									5
typedef struct 
{
	WORD wGenConfig;
	WORD wNumCyls;
	WORD wReserved;
	WORD wNumHeads;
	WORD wBytesPerTrack;
	WORD wBytesPerSector;
	WORD wSectorsPerTrack;
	WORD wVendorUnique[3];
	BYTE sSerialNumber[20];
	WORD wBufferType;
	WORD wBufferSize;
	WORD wECCSize;
	BYTE sFirmwareRev[8];
	BYTE sModelNumber[39];
	WORD wMoreVendorUnique;
	WORD wDoubleWordIO;
	WORD wCapabilities;
	WORD wReserved1;
	WORD wPIOTiming;
	WORD wDMATiming;
	WORD wBS;
	WORD wNumCurrentCyls;
	WORD wNumCurrentHeads;
	WORD wNumCurrentSectorsPerTrack;
	WORD ulCurrentSectorCapacity;
	WORD wMultSectorStuff;
	DWORD ulTotalAddressableSectors;
	WORD wSingleWordDMA;
	WORD wMultiWordDMA;
	BYTE bReserved[127];
}ST_IDSECTOR;

typedef struct
{
	BYTE  bDriverError;
	BYTE  bIDEStatus;
	BYTE  bReserved[2];
	DWORD dwReserved[2];
} ST_DRIVERSTAT;

typedef struct
{
	DWORD      cBufferSize;
	ST_DRIVERSTAT DriverStatus;
	BYTE       bBuffer[1];
} ST_ATAOUTPARAM;

typedef struct
{
	BYTE m_ucAttribIndex;
	DWORD m_dwAttribValue;
	BYTE m_ucValue;
	BYTE m_ucWorst;
	DWORD m_dwThreshold;
}ST_SMART_INFO;

typedef struct
{
	GETVERSIONINPARAMS m_stGVIP;
	ST_IDSECTOR m_stInfo;
	ST_SMART_INFO m_stSmartInfo[256];
	BYTE m_ucSmartValues;
	BYTE m_ucDriveIndex;
	CString m_csErrorString;
}ST_DRIVE_INFO;

typedef struct
{
	BOOL m_bCritical;
	BYTE m_ucAttribId;
	CString m_csAttribName;
	CString m_csAttribDetails;
}ST_SMART_DETAILS;

typedef map<BYTE,ST_SMART_DETAILS> SMARTDETAILSMAP;

class CSmartReader  
{
public:
	CSmartReader();
	~CSmartReader();

	BOOL ReadSMARTValuesForAllDrives();
	ST_SMART_DETAILS *GetSMARTDetails(BYTE ucAttribIndex);
	ST_SMART_INFO *GetSMARTValue(BYTE ucDriveIndex,BYTE ucAttribIndex);
	ST_DRIVE_INFO *GetDriveInfo(BYTE ucDriveIndex);

	BYTE m_ucDrivesWithInfo; // Number of drives with information read
	BYTE m_ucDrives;// Fixed HDD's
	ST_DRIVE_INFO m_stDrivesInfo[32];


private:
	VOID InitAll();
	VOID CloseAll();
	VOID FillAttribGenericDetails();
	VOID ConvertString(PBYTE pString,DWORD cbData);

	BOOL ReadSMARTInfo(BYTE ucDriveIndex);
	BOOL IsSmartEnabled(HANDLE hDevice,UCHAR ucDriveIndex);
	BOOL CollectDriveInfo(HANDLE hDevice,UCHAR ucDriveIndex);
	BOOL ReadSMARTAttributes(HANDLE hDevice,UCHAR ucDriveIndex);

	ST_SMART_DETAILS m_stSmartDetails;
	SMARTINFOMAP m_oSmartInfo;
	SMARTDETAILSMAP m_oSMARTDetails;
};

#endif // !defined(AFX_SMARTREADER_H__494F15B9_0FFA_4BB4_BDD0_2D4C5129E530__INCLUDED_)

// SmartReader.cpp: implementation of the CSmartReader class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "SMART.h"
#include "SmartReader.h"


#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
#define DRIVE_HEAD_REG	0xA0

CSmartReader::CSmartReader()
{
	InitAll();
	FillAttribGenericDetails();
}

CSmartReader::~CSmartReader()
{
	CloseAll();
}

VOID CSmartReader::InitAll()
{
	m_ucDrivesWithInfo=m_ucDrives=0;
	m_oSmartInfo.clear();
}

VOID CSmartReader::CloseAll()
{
	InitAll();
}

BOOL CSmartReader::ReadSMARTValuesForAllDrives()
{
	DWORD dwBits,dwBitVal;
	BOOL bFlag=0;
	char szDrv[MAX_PATH]={0};
	BYTE ucDriveIndex=0,ucT2=0;

	CloseAll();
	dwBits=GetLogicalDrives();
	dwBitVal=1;ucT2=0;
	bFlag=(dwBits & dwBitVal);
	while(ucT2<32)
	{
		if(bFlag)
		{
			wsprintf(szDrv,"%c:\\",'A'+ucT2);
			switch(GetDriveType(szDrv))
			{
				case DRIVE_FIXED:
						ucDriveIndex=ucT2-2;
						if(ReadSMARTInfo(ucDriveIndex))
							m_ucDrivesWithInfo++;
						m_ucDrives++;
					break;

				default:
					
					break;
			}
		}
		dwBitVal=dwBitVal*2;
		bFlag=(dwBits & dwBitVal);
		++ucT2;
	}
	if(m_ucDrives==m_ucDrivesWithInfo)
		return TRUE;
	else
		return FALSE;
}

BOOL CSmartReader::ReadSMARTInfo(BYTE ucDriveIndex)
{
	HANDLE hDevice=NULL;
	char szT1[MAX_PATH]={0};
	BOOL bRet=FALSE;
	DWORD dwRet=0;
	
	wsprintf(szT1,"\\\\.\\PHYSICALDRIVE%d",ucDriveIndex);
	hDevice=CreateFile(szT1,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_SYSTEM,NULL);
	if(hDevice!=INVALID_HANDLE_VALUE)
	{
		bRet=DeviceIoControl(hDevice,SMART_GET_VERSION,NULL,0,&m_stDrivesInfo[ucDriveIndex].m_stGVIP,sizeof(GETVERSIONINPARAMS),&dwRet,NULL);
		if(bRet)
		{			
			if((m_stDrivesInfo[ucDriveIndex].m_stGVIP.fCapabilities & CAP_SMART_CMD)==CAP_SMART_CMD)
			{
				if(IsSmartEnabled(hDevice,ucDriveIndex))
				{
					bRet=CollectDriveInfo(hDevice,ucDriveIndex);
					bRet=ReadSMARTAttributes(hDevice,ucDriveIndex);
				}
			}
		}
		CloseHandle(hDevice);
	}
	return bRet;
}

BOOL CSmartReader::IsSmartEnabled(HANDLE hDevice,UCHAR ucDriveIndex)
{
	SENDCMDINPARAMS stCIP={0};
	SENDCMDOUTPARAMS stCOP={0};
	DWORD dwRet=0;
	BOOL bRet=FALSE;

	stCIP.cBufferSize=0;
	stCIP.bDriveNumber =ucDriveIndex;
	stCIP.irDriveRegs.bFeaturesReg=ENABLE_SMART;
	stCIP.irDriveRegs.bSectorCountReg = 1;
	stCIP.irDriveRegs.bSectorNumberReg = 1;
	stCIP.irDriveRegs.bCylLowReg = SMART_CYL_LOW;
	stCIP.irDriveRegs.bCylHighReg = SMART_CYL_HI;
	stCIP.irDriveRegs.bDriveHeadReg = DRIVE_HEAD_REG;
	stCIP.irDriveRegs.bCommandReg = SMART_CMD;
	
	bRet=DeviceIoControl(hDevice,SMART_SEND_DRIVE_COMMAND,&stCIP,sizeof(stCIP),&stCOP,sizeof(stCOP),&dwRet,NULL);
	if(bRet)
	{

	}
	else
	{
		dwRet=GetLastError();
		m_stDrivesInfo[ucDriveIndex].m_csErrorString.Format("Error %d in reading SMART Enabled flag",dwRet);
	}
	return bRet;
}

BOOL CSmartReader::CollectDriveInfo(HANDLE hDevice,UCHAR ucDriveIndex)
{
	BOOL bRet=FALSE;
	SENDCMDINPARAMS stCIP={0};
	DWORD dwRet=0;
	#define OUT_BUFFER_SIZE IDENTIFY_BUFFER_SIZE+16
	char szOutput[OUT_BUFFER_SIZE]={0};

	stCIP.cBufferSize=IDENTIFY_BUFFER_SIZE;
	stCIP.bDriveNumber =ucDriveIndex;
	stCIP.irDriveRegs.bFeaturesReg=0;
	stCIP.irDriveRegs.bSectorCountReg = 1;
	stCIP.irDriveRegs.bSectorNumberReg = 1;
	stCIP.irDriveRegs.bCylLowReg = 0;
	stCIP.irDriveRegs.bCylHighReg = 0;
	stCIP.irDriveRegs.bDriveHeadReg = DRIVE_HEAD_REG;
	stCIP.irDriveRegs.bCommandReg = ID_CMD;

	bRet=DeviceIoControl(hDevice,SMART_RCV_DRIVE_DATA,&stCIP,sizeof(stCIP),szOutput,OUT_BUFFER_SIZE,&dwRet,NULL);
	if(bRet)
	{
		CopyMemory(&m_stDrivesInfo[ucDriveIndex].m_stInfo,szOutput+16,sizeof(ST_IDSECTOR));
		ConvertString(m_stDrivesInfo[ucDriveIndex].m_stInfo.sModelNumber,39);
		ConvertString(m_stDrivesInfo[ucDriveIndex].m_stInfo.sSerialNumber,20);
		ConvertString(m_stDrivesInfo[ucDriveIndex].m_stInfo.sFirmwareRev,8);
	}
	else
		dwRet=GetLastError();
	return bRet;
}

VOID CSmartReader::ConvertString(PBYTE pString,DWORD cbData)
{
	CString csT1;
	char szT1[MAX_PATH]={0};
	for(int nC1=0;nC1<cbData;nC1+=2)
	{
		szT1[nC1]=pString[nC1+1];
		szT1[nC1+1]=pString[nC1];
	}
	csT1=szT1;csT1.TrimLeft();csT1.TrimRight();
	lstrcpy(szT1,(PCHAR)(LPCTSTR)csT1);
	memcpy(pString,szT1,cbData);
}

VOID CSmartReader::FillAttribGenericDetails()
{
	char szINIFileName[MAX_PATH]={0},szKeyName[MAX_PATH]={0},szValue[1024]={0};
	int nC1,nSmartAttribs;
	ST_SMART_DETAILS stSD;

	m_oSMARTDetails.clear();
//	if(IsDebuggerPresent()==FALSE)
	{
		GetModuleFileName(NULL,szINIFileName,MAX_PATH);
		szINIFileName[lstrlen(szINIFileName)-3]=0;
		lstrcat(szINIFileName,"ini");
	}
//	else
//		wsprintf(szINIFileName,"D:\\Saneesh\\Projects\\Helpers\\SMART\\Smart.ini");
	nSmartAttribs=GetPrivateProfileInt("General","Max Attributes",0,szINIFileName);
	for(nC1=0;nC1<nSmartAttribs;++nC1)
	{
		wsprintf(szKeyName,"Attrib%d",nC1);
		stSD.m_ucAttribId=GetPrivateProfileInt(szKeyName,"Id",0,szINIFileName);
		stSD.m_bCritical=GetPrivateProfileInt(szKeyName,"Critical",0,szINIFileName);
		GetPrivateProfileString(szKeyName,"Name","",szValue,1024,szINIFileName);
		stSD.m_csAttribName=szValue;
		GetPrivateProfileString(szKeyName,"Details","",szValue,1024,szINIFileName);
		stSD.m_csAttribDetails=szValue;
		m_oSMARTDetails.insert(SMARTDETAILSMAP::value_type(stSD.m_ucAttribId,stSD));
	}
}

ST_SMART_DETAILS *CSmartReader::GetSMARTDetails(BYTE ucAttribIndex)
{
	SMARTDETAILSMAP::iterator pIt;
	ST_SMART_DETAILS *pRet=NULL;

	pIt=m_oSMARTDetails.find(ucAttribIndex);
	if(pIt!=m_oSMARTDetails.end())
		pRet=&pIt->second;

	return pRet;
}

ST_SMART_INFO *CSmartReader::GetSMARTValue(BYTE ucDriveIndex,BYTE ucAttribIndex)
{
	SMARTINFOMAP::iterator pIt;
	ST_SMART_INFO *pRet=NULL;

	pIt=m_oSmartInfo.find(MAKELPARAM(ucAttribIndex,ucDriveIndex));
	if(pIt!=m_oSmartInfo.end())
		pRet=(ST_SMART_INFO *)pIt->second;
	return pRet;
}

BOOL CSmartReader::ReadSMARTAttributes(HANDLE hDevice,UCHAR ucDriveIndex)
{
	SENDCMDINPARAMS stCIP={0};
	DWORD dwRet=0;
	BOOL bRet=FALSE;
	BYTE	szAttributes[sizeof(ST_ATAOUTPARAM) + READ_ATTRIBUTE_BUFFER_SIZE - 1];
	UCHAR ucT1;
	PBYTE pT1,pT3;PDWORD pT2;
	ST_SMART_INFO *pSmartValues;

	stCIP.cBufferSize=READ_ATTRIBUTE_BUFFER_SIZE;
	stCIP.bDriveNumber =ucDriveIndex;
	stCIP.irDriveRegs.bFeaturesReg=READ_ATTRIBUTES;
	stCIP.irDriveRegs.bSectorCountReg = 1;
	stCIP.irDriveRegs.bSectorNumberReg = 1;
	stCIP.irDriveRegs.bCylLowReg = SMART_CYL_LOW;
	stCIP.irDriveRegs.bCylHighReg = SMART_CYL_HI;
	stCIP.irDriveRegs.bDriveHeadReg = DRIVE_HEAD_REG;
	stCIP.irDriveRegs.bCommandReg = SMART_CMD;
	bRet=DeviceIoControl(hDevice,SMART_RCV_DRIVE_DATA,&stCIP,sizeof(stCIP),szAttributes,sizeof(ST_ATAOUTPARAM) + READ_ATTRIBUTE_BUFFER_SIZE - 1,&dwRet,NULL);
	if(bRet)
	{
		m_stDrivesInfo[ucDriveIndex].m_ucSmartValues=0;
		m_stDrivesInfo[ucDriveIndex].m_ucDriveIndex=ucDriveIndex;
		pT1=(PBYTE)(((ST_ATAOUTPARAM*)szAttributes)->bBuffer);
		for(ucT1=0;ucT1<30;++ucT1)
		{
			pT3=&pT1[2+ucT1*12];
			pT2=(PDWORD)&pT3[INDEX_ATTRIB_RAW];
			pT3[INDEX_ATTRIB_RAW+2]=pT3[INDEX_ATTRIB_RAW+3]=pT3[INDEX_ATTRIB_RAW+4]=pT3[INDEX_ATTRIB_RAW+5]=pT3[INDEX_ATTRIB_RAW+6]=0;
			if(pT3[INDEX_ATTRIB_INDEX]!=0)
			{
				pSmartValues=&m_stDrivesInfo[ucDriveIndex].m_stSmartInfo[m_stDrivesInfo[ucDriveIndex].m_ucSmartValues];
				pSmartValues->m_ucAttribIndex=pT3[INDEX_ATTRIB_INDEX];
				pSmartValues->m_ucValue=pT3[INDEX_ATTRIB_VALUE];
				pSmartValues->m_ucWorst=pT3[INDEX_ATTRIB_WORST];
				pSmartValues->m_dwAttribValue=pT2[0];
				pSmartValues->m_dwThreshold=MAXDWORD;
				m_oSmartInfo[MAKELPARAM(pSmartValues->m_ucAttribIndex,ucDriveIndex)]=pSmartValues;
				m_stDrivesInfo[ucDriveIndex].m_ucSmartValues++;
			}
		}
	}
	else
		dwRet=GetLastError();

	stCIP.irDriveRegs.bFeaturesReg=READ_THRESHOLDS;
	stCIP.cBufferSize=READ_THRESHOLD_BUFFER_SIZE; // Is same as attrib size
	bRet=DeviceIoControl(hDevice,SMART_RCV_DRIVE_DATA,&stCIP,sizeof(stCIP),szAttributes,sizeof(ST_ATAOUTPARAM) + READ_ATTRIBUTE_BUFFER_SIZE - 1,&dwRet,NULL);
	if(bRet)
	{
		pT1=(PBYTE)(((ST_ATAOUTPARAM*)szAttributes)->bBuffer);
		for(ucT1=0;ucT1<30;++ucT1)
		{
			pT2=(PDWORD)&pT1[2+ucT1*12+5];
			pT3=&pT1[2+ucT1*12];
			pT3[INDEX_ATTRIB_RAW+2]=pT3[INDEX_ATTRIB_RAW+3]=pT3[INDEX_ATTRIB_RAW+4]=pT3[INDEX_ATTRIB_RAW+5]=pT3[INDEX_ATTRIB_RAW+6]=0;
			if(pT3[0]!=0)
			{
				pSmartValues=GetSMARTValue(ucDriveIndex,pT3[0]);
				if(pSmartValues)
					pSmartValues->m_dwThreshold=pT2[0];
			}
		}
	}
	return bRet;
}

ST_DRIVE_INFO *CSmartReader::GetDriveInfo(BYTE ucDriveIndex)
{
	return &m_stDrivesInfo[ucDriveIndex];
}

2.2. 通過WMI

微軟自Windows 2000之後提供了WMI組件,WMI組件主要用來查詢電腦設備的屬性,顯卡溫度、CPU溫度、風扇轉速…,還有硬盤的相關信息(包括SMART信息).

  • 初始化COM組件
  • 創建CLSID_WbemLocator對象
  • 連接命名“ROOT\WMI”服務
  • 查詢“MSStorageDriver_ATAPISmartData”即獲得所有硬盤設備
  • 對每個設備查詢子項“VendorSpecific”,即爲SMART Buff
  • 解析SMART Buff
#ifndef _WMIINFO_H_
#define _WMIINFO_H_
 
#include <WbemIdl.h>  
#pragma comment(lib,"WbemUuid.lib")

class CWmiInfo
{
public:
    CWmiInfo();
    ~CWmiInfo();

public:  
    HRESULT InitWmi();    //初始化WMI  
    HRESULT ReleaseWmi(); //釋放 

	 BOOL GetSMARTItemInfo(CString ClassName,CString ClassMember,CString &chRetValue);

private:  
    void VariantToString(const LPVARIANT,CString &) const;//將Variant類型的變量轉換爲CString

private:  
    IEnumWbemClassObject* m_pEnumClsObj;  
    IWbemClassObject* m_pWbemClsObj;  
    IWbemServices* m_pWbemSvc;  
    IWbemLocator* m_pWbemLoc;  
};
#endif
#include "stdafx.h"
#include "WmiInfo.h"  

CWmiInfo::CWmiInfo(void)  
{  
	m_pWbemSvc=NULL;  
	m_pWbemLoc=NULL;  
	m_pEnumClsObj = NULL;  
}

CWmiInfo::~CWmiInfo(void)  
{  
	m_pWbemSvc=NULL;  
	m_pWbemLoc=NULL;  
	m_pEnumClsObj = NULL;  
}

HRESULT CWmiInfo::InitWmi()  
{  
	HRESULT hr;  

	//一、初始化COM組件  
	//初始化COM  
	hr=::CoInitializeEx(0,COINIT_MULTITHREADED);  
	if (SUCCEEDED(hr) || RPC_E_CHANGED_MODE == hr)  
	{  
		//設置進程的安全級別,(調用COM組件時在初始化COM之後要調用CoInitializeSecurity設置進程安全級別,否則會被系統識別爲病毒)  
		hr=CoInitializeSecurity(NULL,  
			-1,  
			NULL,                     
			NULL,  
			RPC_C_AUTHN_LEVEL_PKT,  
			RPC_C_IMP_LEVEL_IMPERSONATE,  
			NULL,  
			EOAC_NONE,  
			NULL);  
		//VERIFY(SUCCEEDED(hr));  

		//二、創建一個WMI命名空間連接  
		//創建一個CLSID_WbemLocator對象  
		hr=CoCreateInstance(CLSID_WbemLocator,  
			0,  
			CLSCTX_INPROC_SERVER,  
			IID_IWbemLocator,  
			(LPVOID*)&m_pWbemLoc);  
		//        VERIFY(SUCCEEDED(hr));  

		//使用m_pWbemLoc連接到"root\cimv2"並設置m_pWbemSvc的指針  
		//hr=m_pWbemLoc->ConnectServer(CComBSTR(L"ROOT\\CIMV2"),  
		hr=m_pWbemLoc->ConnectServer(CComBSTR(L"ROOT\\WMI"),  
			NULL,  
			NULL,  
			0,  
			NULL,  
			0,  
			0,  
			&m_pWbemSvc);  
		//        VERIFY(SUCCEEDED(hr));  

		//三、設置WMI連接的安全性  
		hr=CoSetProxyBlanket(m_pWbemSvc,  
			RPC_C_AUTHN_WINNT,  
			RPC_C_AUTHZ_NONE,  
			NULL,  
			RPC_C_AUTHN_LEVEL_CALL,  
			RPC_C_IMP_LEVEL_IMPERSONATE,  
			NULL,  
			EOAC_NONE);  
		//        VERIFY(SUCCEEDED(hr));  

	}  
	return(hr);  
}  

HRESULT CWmiInfo::ReleaseWmi()  
{  
	HRESULT hr;  

	if (NULL != m_pWbemSvc)  
	{  
		hr=m_pWbemSvc->Release();  
	}  
	if (NULL != m_pWbemLoc)  
	{  
		hr=m_pWbemLoc->Release();  
	}  
	if (NULL != m_pEnumClsObj)  
	{  
		hr=m_pEnumClsObj->Release();  
	}  

	::CoUninitialize();  

	return(hr);  
}  


BOOL CWmiInfo::GetSMARTItemInfo(CString ClassName,CString ClassMember,CString &chRetValue)  
{  
	USES_CONVERSION;  

	CComBSTR query("SELECT * FROM ");  
	VARIANT vtProp;  
	ULONG uReturn;  
	HRESULT hr;  
	BOOL bRet = FALSE;  

	if (NULL != m_pWbemSvc)  
	{  
		//查詢類ClassName中的所有字段,保存到m_pEnumClsObj中  
		query+=CComBSTR(ClassName);  
		hr=m_pWbemSvc->ExecQuery(CComBSTR("WQL"),query,WBEM_FLAG_FORWARD_ONLY|WBEM_FLAG_RETURN_IMMEDIATELY,  
			0,&m_pEnumClsObj);  
		if (SUCCEEDED(hr))  
		{  
			//初始化vtProp值  
			VariantInit(&vtProp);  
			uReturn=0;  

			//返回從當前位置起的第一個對象到m_pWbemClsObj中  
			hr=m_pEnumClsObj->Next(WBEM_INFINITE,1,&m_pWbemClsObj,&uReturn);  
			if(SUCCEEDED(hr)&&uReturn>0)   
			{  
				//從m_pWbemClsObj中找出ClassMember標識的成員屬性值,並保存到vtProp變量中  
				hr=m_pWbemClsObj->Get(CComBSTR(ClassMember),0,&vtProp,0,0);  
				if (SUCCEEDED(hr))  
				{  
					VariantToString(&vtProp,chRetValue);  
					VariantClear(&vtProp);//清空vtProp  
					bRet = TRUE;  
				}  
			}  
		}  
	}  
	if(NULL != m_pEnumClsObj)   
	{  
		hr=m_pEnumClsObj->Release();  
		m_pEnumClsObj = NULL;  
	}  
	if(NULL != m_pWbemClsObj)   
	{  
		hr=m_pWbemClsObj->Release();  
		m_pWbemClsObj = NULL;  
	}  
	return bRet;  
}  

#include "stdafx.h"
#include <iostream>
#include "WMIInfo.h"
using namespace std;


int main()
{
	CWmiInfo WMI;
	WMI.InitWmi();

	CString strSmart;
	WMI.GetSMARTItemInfo(L"MSStorageDriver_ATAPISmartData", L"VendorSpecific", strSmart);
	return 1;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章