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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章