Для получения значений 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.