Для получения значений S.M.A.R.T. атрибутов жёстких дисков на сегодняшний день существует множество платных и бесплатных программ, отличающихся дополнительными функциями при обработке данных. К наиболее известным программам данного типа относятся: HDDLife, Hard Drive Inspector, HD Tune, PCS, ActivSMART и др. Однако с помощью несложных команд возможно написание собственных программ для получения значений атрибутов и предсказания выхода из строя жёстких дисков. Для получения значения атрибутов необходимо обратиться или непосредственно к жёсткому диску или к драйверу мини-порта контроллера SCSI.
Пример алгоритма программы по получению значений атрибутов показан на рис. 2. Данная программа опрашивает первый накопитель (из четырёх) на интерфейсе IDE (PATA), а в случае отсутствия накопителей с таким интерфейсом выдает сообщение об ошибке. В приложении А приведён пример листинга программы для FreePascal, которая получает значения атрибута температуры и выводит их в консоли.
Чтение S.M.A.R.T. атрибутов можно выполнить с помощью функции DeviceIOControl, которая отправляет управляющий код непосредственно указанному драйверу устройства, заставляя соответствующее устройство выполнять указанную операцию.
Перед выполнением функции DeviceIOControl необходимо прочитать манипулятор (дескриптор или handle) диска как файл функцией CreateFile, которая для операционной системы Windows NT/2000/XP имеет следующий синтаксис:
NameHandle:= CreateFile ( lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttribute, hTemplateFile)
где NameHandle – имя манипулятора;
Рис. 2
lpFileName – указатель на символьную строку, устанавливающую имя открываемого объекта;
dwDesiredAccess – режим доступа к объекту;
dwShareMode – режим совместного использования;
lpSecurityAttributes – указатель на структуру SECURITY _ATTRIBUTES, которая устанавливает может ли возвращённый дескриптор быть унаследован дочерними процессами;
dwCreationDisposition – выполняемое действие;
dwFlagsAndAttribute –атрибуты и флаги файла;
hTemplateFile – дескриптор шаблона файла.
Для создания манипулятора диска с именем SMART_Handle команда будет иметь следующие параметры:
SMART_Handle:= CreateFile (PChar('\\.\PhysicalDrive'+IntToStr(drvNumber)), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
Данная функция работает, если программа запущена с административными правами доступа, в противном случае функция CreateFile завершается с ошибкой и возвращает значение – INVALID_HANDLE_VALUE.
Функция DeviceIOControl имеет следующий формат:
Var:= DeviceIOControl (hDevice, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, lpBytesReturned, lpOverlapped)
где hDevice – имя манипулятора устройства, над которым должна выполниться операция;
dwIoControlCode – управляющий код для операции;
lpInBuffer – указатель на буфер ввода данных, который содержит данные необходимые, чтобы выполнить операцию;
nInBufferSize – размер буфера ввода данных, в байтах;
lpOutBuffer – указатель на буфер вывода данных, который должен получить данные, возвращенные операцией;
nOutBufferSize – размер буфера вывода данных, в байтах;
lpBytesReturned – указатель на переменную, которая получает размер данных, сохраненных в буфере вывода данных, в байтах;
lpOverlapped – указатель на структуру OVERLAPPED.
Для передачи жесткому диску команды активации S.M.A.R.T. в качестве управляющего кода необходимо указать константу DFP_SEND_DRIVE_COMMAND (0007С084h), а для получения данных от диска константу DFP_RECEIVE_DRIVE_DATA (0007C088h). Сами команды S.M.A.R.T. указываются в буфере ввода данных, который удобно оформить в виде структуры данных (табл. 6.2). Для активации S.M.A.R.T. в поле bFeaturesReg необходимо занести число D8h, а для получения текущего и порогового значений атрибута D0h и D1h соответственно. Для вычисления номера диска/головки IDE в поле bDriveHeadReg необходимо записать формулу [A0h or ((drvNumber and 1) shl 4)].
Структура буфера вывода данных представлена в табл. 6.3. Так как буферы вывода данных для текущих и пороговых значений имеют одинаковую структуру, то желательно создать два буфера с разными именами. Значения всех атрибутов возвращается в массиве bBuffer со смещение в 2 байта. Его состав для текущих и пороговых значений показан в табл. 6.4.
| Табл. 6.2 – Структура буфера ввода данных для команды DeviceIOControl | Описание | размер буфера в байтах | структура регистров диска | физический номер диска | зарезервировано | зарезервировано | входной буфер | регистр подкоманды S.M.A.R.T. | регистр количества секторов диска | регистр номера сектора диска | младший разряд номера цилиндра диска | старший разряд номера цилиндра диска | регистр диска/головки | регистр фактической команды | зарезервировано |
| Значения элемента | 4Fh | С2h | В0h | ||||||||||||
| Размер элемента | 4 байта | TIDERegs | 1 байт | 3 байта | 16 байт | 1 байт | 1 байт | 1 байт | 1 байт | 1 байт | 1 байт | 1 байт | 1 байт | 1 байт | |
| Элемент структуры | cBufferSize | irDriveRegs | bDriveNumber | bReserved | dwReserved | bBuffer | bFeaturesReg | bSectorCountReg | bSectorNumberReg | bCylLowReg | bCylHighReg | bDriveHeadReg | bCommandReg | bReserved | |
| Имя структуры записи | TSendCmdInParams (SCIP) | TIDERegs |
Табл. 6.3 – Структура буфера вывода данных для команды DeviceIOControl
| Имя структуры записи | Элемент структуры | Размер элемента | Описание |
| TSendCmdOutParams (SCOP) | cBufferSize | 4 байта | размер буфера в байтах |
| DriverStatus | TDriverStatus | структура состояния диска | |
| bBuffer | массив | буфер произвольной длины для сохранения данных, прочитанных с диска | |
| TDriverStatus | bDriverError | 1 байт | код ошибки драйвера |
| bIDEStatus | 1 байт | содержание регистра ошибки | |
| bReserved | 1 байт | зарезервировано | |
| dwReserved | 2 байта | зарезервировано |
Табл. 6.4 – Структура возвращаемых данных в bBuffer
| Имя структуры записи | Элемент структуры | Размер элемента | Описание |
| TDriveAttribute | bAttrID | 1 байт | идентификатор атрибута |
| wStatusFlags | 2 байта | флаги состояния | |
| bAttrValue | 1 байт | текущее нормализованное значение | |
| bWorstValue | 1 байт | худшее значение | |
| bRawValue | массив [0..5] байт | текущее ненормализованное значение | |
| bReserved | 1 байт | зарезервировано | |
| TAttrThreshold | bAttrID | 1 байт | идентификатор атрибута |
| bWarrantyThreshold | 1 байт | пороговое значение | |
| bReserved | массив [0..9] байт | зарезервировано |
ПРИЛОЖЕНИЕ А
Листинг 1 – Программа SMARTView
program SMARTView;
{$MODE DELPHI}
Uses
Windows, strings, SysUtils;
Const
READ_ATTRIBUTE_BUFFER_SIZE = 512; // Размер буфера атрибутов
READ_THRESHOLD_BUFFER_SIZE = 512; // Размер буфера пороговых значений
DFP_SEND_DRIVE_COMMAND = $0007C084; // управляющие коды для
DFP_RECEIVE_DRIVE_DATA = $0007C088; // функции DeviceIOControl
Type
TIDERegs = packed record // структура записи регистров IDE
bFeaturesReg: BYTE; // регистр подкоманда SMART
bSectorCountReg: BYTE; // регистр количества секторов IDE
bSectorNumberReg: BYTE; // регистр номера сектора IDE
bCylLowReg: BYTE; // младший разряд номера цилиндра IDE
bCylHighReg: BYTE; // старший разряд номера цилиндра IDE
bDriveHeadReg: BYTE; // регистр номера диска/головки IDE
bCommandReg: BYTE; // фактическая команда IDE
bReserved: BYTE; // зарезервировано. Должен быть 0
end;
IDEREGS = TIDERegs;
PIDERegs = ^TIDERegs;
TsendCmdInParams = packed record // структуру записи входных параметров для функции DeviceIOControl
cBufferSize: DWORD; // размер буфера в байтах
irDriveRegs: TIDERegs; // структура со значениями регистров диска
bDriveNumber: BYTE; // физ. номер диска для посылки команд SMART (0...3)
bReserved: array [0..2] of BYTE; // зарезервировано
dwReserved: array [0..3] of DWORD; // зарезервировано
bBuffer: array [0..0] of BYTE; // входной буфер
end;
SENDCMDINPARAMS = TSendCmdInParams;
PSendCmdInParams = ^TSendCmdInParams;
TDriverStatus = packed record // структура состояния диска
bDriverError: BYTE; // код ошибки драйвера
bIDEStatus: BYTE; // содержание регистра ошибки только, когда bDriverError = SMART_IDE_ERROR
bReserved: array [0..1] of BYTE; // зарезервировано
dwReserved: array [0..1] of DWORD; // зарезервировано
end;
DRIVERSTATUS = TDriverStatus;
PDriverStatus = ^TDriverStatus;
TSendCmdOutParams = packed record // структуру записи выходных параметров, возвращаемых функцией DeviceIOControl
cBufferSize: DWORD; // размер буфера в байтах
DriverStatus: TDriverStatus; // структура состояния диска
bBuffer: array [0..0] of BYTE; // буфер произвольной длины для сохранения данных, полученных от диска
end;
SENDCMDOUTPARAMS = TsendCmdOutParams;
PSendCmdOutParams = ^TsendCmdOutParams;
TDriveAttribute = packed record // структуру записи значений атрибутов
bAttrID: BYTE; // идентификатор атрибута
wStatusFlags: WORD; // флаг состояния
bAttrValue: BYTE; // текущее нормализованное значение
bWorstValue: BYTE; // худшее значение
bRawValue: array [0..5] of BYTE; // текущее ненормализованное значение
bReserved: BYTE; // зарезервировано
end;
DRIVEATTRIBUTE = TDriveAttribute;
PDriveAttribute = ^TDriveAttribute;
TAttrThreshold = packed record // структуру записи порогового значения атрибута
bAttrID: BYTE; // идентификатор атрибута
bWarrantyThreshold: BYTE; // пороговое значение
bReserved: array [0..9] of BYTE; // зарезервировано
end;
ATTRTHRESHOLD = TAttrThreshold;
PAttrThreshold = ^TAttrThreshold;
TParam = record // общая структура со значениями атрибутов диска
DriveAttr: TDriveAttribute; // атрибуты диска
AttrThres: TAttrThreshold; // пороговое значение
end;
Var
driveHandle: THandle; // дескриптор жёсткого диска
drvNumber: integer; // номер жёсткого диска
i: integer;
j: integer;
p: TParam;
function startSMART(drvNumber: byte; var SMART_Handle: THandle): boolean;
Var
SCIP: TSendCmdInParams; // входные параметры
SCOP: TSendCmdOutParams; // выходные параметры
bytesReturned: Cardinal; // число байт, возвращённых функцией
Begin
result:=false;
SMART_Handle:= CreateFile(PChar('\\.\PhysicalDrive'+IntToStr(drvNumber)), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
if SMART_Handle = INVALID_HANDLE_VALUE then
begin
result:=false;
exit;
end;
ZeroMemory(@SCIP, sizeof(TSendCmdInParams));
SCIP.cBufferSize:= 0;
with SCIP.irDriveRegs do
begin
bFeaturesReg:= $D8;
bSectorCountReg:= 1;
bSectorNumberReg:= 1;
bCylLowReg:= $4F;
bCylHighReg:= $C2;
bDriveHeadReg:= $A0 or ((drvNumber and 1) shl 4);
bCommandReg:= $B0;
end;
SCIP.bDriveNumber:= DrvNumber;
result:=DeviceIOControl(SMART_Handle, DFP_SEND_DRIVE_COMMAND, @SCIP, sizeof(SCIP), @SCOP, sizeof(SCOP), bytesReturned, nil);
end;
function ReadParam(var param: TParam): boolean;
Const
ATTR_SIZE = sizeof(TSendCmdOutParams)+READ_ATTRIBUTE_BUFFER_SIZE; // размер буфера под значения атрибутов
THRES_SIZE =sizeof(TSendCmdOutParams)+READ_THRESHOLD_BUFFER_SIZE; // размер буфера под пороговые значения атрибутов
Var
SCIP: TSendCmdInParams; // структура входных параметров
pSCOP, pSCOP1: PSendCmdOutParams; // структуры выходных параметров
bytesReturned: Cardinal; // число байт, возвращённых функцией
pDA: PDriveAttribute; // структуру значений атрибутов
pAT: PAttrThreshold; // структура пороговых значений атрибутов
i: integer;
Begin
getmem(pSCOP, ATTR_SIZE);
getmem(pSCOP1, THRES_SIZE);
SCIP.cBufferSize:= READ_ATTRIBUTE_BUFFER_SIZE;
SCIP.bDriveNumber:= DrvNumber;
with SCIP.irDriveRegs do
begin
bFeaturesReg:= $D0;
bCylLowReg:= $4F;
bCylHighReg:= $C2;
bDriveHeadReg:= $A0 or ((drvNumber and 1) shl 4);
bCommandReg:= $B0;
end;
result:= DeviceIOControl(driveHandle, DFP_RECEIVE_DRIVE_DATA, @SCIP, sizeof(SCIP), pSCOP, ATTR_SIZE, bytesReturned, nil);
SCIP.cBufferSize:= READ_THRESHOLD_BUFFER_SIZE;
with SCIP.irDriveRegs do
begin
bFeaturesReg:= $D1;
bCommandReg:= $B0;
end;
result:= result and DeviceIOControl(driveHandle, DFP_RECEIVE_DRIVE_DATA, @SCIP, sizeof(SCIP), pSCOP1, THRES_SIZE, bytesReturned, nil);
//атрибуты и предельные значения имеют смещение 2 байта
pDA:= PDriveAttribute(@PSendCmdOutParams(@PChar(pSCOP)[2]).bBuffer);
pAT:= PAttrThreshold(@PSendCmdOutParams(@PChar(pSCOP1)[2]).bBuffer);
ZeroMemory(@param, sizeof(param));
for i:= 1 to 30 do
begin
if (pDA^.bAttrID = 194) or (pDA^.bAttrID = 231) then
begin
param.DriveAttr:= pDA^;
param.AttrThres:= pAT^;
break;
end;
inc(pDA);
inc(pAT);
end;
freemem(pSCOP, ATTR_SIZE);
freemem(pSCOP1, THRES_SIZE);
end;
BEGIN
drvNumber:= -1;
for i:=0 to 3 do
if StartSMART(i, driveHandle) then
begin
drvNumber:=i;
if ReadParam(p) then
begin
writeln('S.M.A.R.T. attribute HDD', drvNumber);
writeln(' Attribute= ', p.DriveAttr.bAttrID,' - Temperature');
writeln('Attribute Value= ', p.DriveAttr.bAttrValue);
writeln(' Worst Value= ', p.DriveAttr.bWorstValue);
writeln('Threshold Value= ', p.AttrThres.bWarrantyThreshold);
write(' RAW Value= ');
for j:= 0 to 5 do
write(p.DriveAttr.bRawValue[j]);
writeln;
end;
end;
if drvNumber < 0 then
begin
Writeln (' 'Нет устройств, поддерживающих технологию S.M.A.R.T. или у Вас нет прав администратора');
end;
END.






