Rambler's
Top100

Логическая структура жесткого диска

Автор: uncle Bob
Дата: 05.01.2004
Раздел: Низкоуровневое программирование в Linux


В статье расcматривается логическая структура жесткого диска, соответствующая
стандарту Microsoft - "основной раздел - расширенный раздел - разделы не-DOS".


Пространство на жестком диске может быть организовано в виде одного или
нескольких разделов, а разделы могут содержать один или несколько логических
дисков.

На жестком диске по физическому адресу 0-0-1 располагается главная загрузочная 
запись (master boot record, MBR). В структуре MBR находятся следующие элементы:
- внесистемный загрузчик (non-system bootstrap - NSB);
- таблица описания разделов диска (таблица разделов, partition table, PT).
Располагается в MBR по смещению 0x1BE и занимает 64 байта;
- сигнатура MBR. Последние два байта MBR должны содержать число 0xAA55.
Таблица разделов описывает размещение и характеристики имеющихся на винчестере 
разделов. Разделы диска могут быть двух типов - primary (первичный, основной) и 
extended (расширенный). Максимальное число primary-разделов равно четырем. 
Наличие на диске хотя бы одного primary-раздела является обязательным. 
Extended-раздел может быть разделен на большое количество подразделов - 
логических дисков.Упрощенно структура MBR представлена в таблице 1. Таблица 
разделов располагается в конце MBR, для описания раздела в таблице отводится 16 
байт.

Таблица 1. 
Структура MBR
Смещение (offset)	Размер (Size)		Содержимое (contents)
-------------------------------------------------------------------------
	0		446		Программа анализа таблицы разделов
					и загрузки System Bootstrap
					с активного раздела
-------------------------------------------------------------------------
	0x1BE		16		Partition 1 entry ( элемент таблицы разделов)
-------------------------------------------------------------------------
	0x1CE		16		Partition 2 entry
-------------------------------------------------------------------------
	0x1DE		16		Partition 3 entry
-------------------------------------------------------------------------
	0x1EE		16		Partition 4 entry
-------------------------------------------------------------------------
	0x1FE		2		Сигнатура 0xAA55


Структура записи элемента таблицы разделов показана в таблице 2.

Таблица 2.
Структура записи элемента таблицы разделов

Смещение	Размер поля,		Содержание
		байт
-------------------------------------------------------------------------
0x00		1		Признак активности (0 - раздел не активный,
				0x80 - раздел активный)
--------------------------------------------------------------------------
0x01		1		Номер головки диска, с которой
				начинается раздел
---------------------------------------------------------------------------
0x02		2		Номер цилиндра и номер сектора, с которых
				начинается раздел
----------------------------------------------------------------------------
0x04		1		Код типа раздела System ID
----------------------------------------------------------------------------
0x05		1		Номер головки диска, на которой
				заканчивается раздел
----------------------------------------------------------------------------
0x06		2		Номер цилиндра и номер сектора, которыми
				заканчивается раздел
----------------------------------------------------------------------------
0x08		4		Абсолютный (логический) номер начального
				сектора раздела
----------------------------------------------------------------------------
0x0C		4		Размер раздела (число секторов)


Первым байтом в элементе раздела идет флаг активности раздела (0 - неактивен, 
0x80 - активен). Он служит для определения, является ли раздел системным 
загрузочным и есть ли необходимость производить загрузку операционной системы с 
него при старте компьютера. Активным может быть только один раздел. За флагом 
активности раздела следуют координаты начала раздела - три байта, означающие 
номер головки, номер сектора и номер цилиндра. Номера цилиндра и сектора задаются
в формате прерывания Int 0x13, т.е. биты 0-5 содержат номер сектора, биты 6-7 -
старшие два бита 10-разрядного номера цилиндра, биты 8-15 - младшие восемь битов
номера цилиндра.
Затем следует кодовый идентификатор System ID, указывающий на принадлежность
данного раздела к той или иной операционной системе. Идентификатор занимает один
байт. За системным идентификатором расположены координаты конца раздела - три
байта, содержащие номера головки, сектора и цилиндра, соответственно. Следующие
четыре байта - это число секторов перед разделом, и последние четыре байта -
размер раздела в секторах.

Таким образом, элемент таблицы раздела можно описать при помощи следующей
структуры:	

struct pt_struct {
	    u8 bootable;	// флаг активности раздела
	    u8 start_part[3];	// координаты начала раздела
	    u8 type_part;	// системный идентификатор
	    u8 end_part[3];	// координаты конца раздела	    
	    u32 sect_before;	// число секторов перед разделом    
	    u32 sect_total;	// размер раздела в секторах (число секторов в разделе)
};

Элемент первичного раздела указывает сразу на загрузочный сектор логического диска
(в первичном разделе всегда имеется только один логический диск), а элемент
расширенного раздела - на список логических дисков, составленный из структур,
которые именуются вторичными MBR (Secondary MBR, SMBR).

Свой блок SMBR имеется у каждого диска расширенного раздела. SMBR имеет структуру,
аналогичную MBR, но загрузочная запись у него отсутствует (заполнена нулями),
а из четырех полей описателей разделов используются только два. Первый элемент
раздела при этом указывает на логический диск, второй элемент указывает на
следующую структуру SMBR в списке. Последний SMBR списка содержит во втором 
элементе нулевой код раздела.


Рассмотрим программный модуль, отображающий информацию обо всех разделах
(основных и логических), созданных на жестком диске. Модуль функционирует под
управлением ОС Linux.

Заголовочные файлы:
#include 
#include 
#include 
#include 
#include 

Сигнатура MBR:
#define SIGNATURE 0xAA55

Файл устройства, с которого будет считываться информация о разделах:
#define DEVICE "/dev/hda"

Размер элемента таблицы разделов (0x10):
#define PT_SIZE 0x10

Следующий массив структур устанавоивает соответствие между кодом типа раздела
и его символьным отображением:

struct systypes {
    __u8 part_type;
    __u8 *part_name;
};

struct systypes i386_sys_types[] = {
	{0x00, "Empty"},
	{0x01, "FAT12"},
	{0x04, "FAT16 <32M"},
	{0x05, "Extended"},		/* DOS 3.3+ extended partition */
	{0x06, "FAT16"},		/* DOS 16-bit >=32M */
	{0x0b, "Win95 FAT32"},
	{0x0c, "Win95 FAT32 (LBA)"},/* LBA really is `Extended Int 13h' */
	{0x0e, "Win95 FAT16 (LBA)"},
	{0x0f, "Win95 Ext'd (LBA)"},
	{0x82, "Linux swap"},	/* also Solaris */
	{0x83, "Linux"},
	{0x85, "Linux extended"},
	{0x07, "HPFS/NTFS"}
};

Определим число элементов в массиве i386_sys_types при помощи макроса:
#define PART_NUM (sizeof(i386_sys_types) / sizeof(i386_sys_types[0]))

int hard;	// дескриптор файла устройства
__u8 mbr[512];	// сюда считаем MBR

Следующий массив структура будет содержать информацию о логических дисках
на устройстве (жестком диске):
struct pt_struct {
    __u8 bootable;
    __u8 start_part[3];
    __u8 type_part;
    __u8 end_part[3];
    __u32 sect_before;
    __u32 sect_total;
} pt_t[MAX_PART];

Установим ограничение на количество логических дисков:
#define MAX_PART 20


Рассмотрим главную функцию.
int main()
{
    int i = 0;
    __u64 seek;

Открываем файл устройства, считываем таблицу разделов и проверяем сигнатуру:

    hard = open(DEVICE, O_RDONLY);
    if(hard < 0) {
	perror("open");
	exit(-1);
    }

    read_main_ptable();

    if(check_sign() < 0) {
	printf("Not valid signature!\n");
	exit(-1);
    }

Ищем идентификатор расширенного раздела. Если таковой имеется - вычисляем 
смещение к расширенному разделу и считываем информацию о логических дисках:

    for(; i < 4; i++) {
	if((pt_t[i].type_part == 0xF) || \
	    (pt_t[i].type_part == 0x5) || \
	    (pt_t[i].type_part == 0x0C)) {
		seek = (__u64)pt_t[i].sect_before * 512;
		read_ext_ptable(seek);
		break;
	}
    }

Отображаем информацию о логических дисках:

    show_pt_info();

    return 0;
}



Функция проверки сигнатуры 0xAA55 выглядит следующим образом:

int check_sign()
{
    __u16 sign = 0;

    memcpy((void *)&sign, (void *)(mbr + 0x1FE), 2);

#ifdef DEBUG
    printf("Signature - 0x%X\n", sign);
#endif

    if(sign != SIGNATURE) return -1;

    return 0;
}


Функция чтения таблицы разделов:

void read_main_ptable()
{
    if(read(hard, mbr, 512) < 0) {
	perror("read");
	close(hard);
	exit(-1);
    }

    memset((void *)pt_t, 0, (PT_SIZE * 4));
    memcpy((void *)pt_t, mbr + 0x1BE, (PT_SIZE * 4));

    return;
}


Функция чтения расширенной таблицы разделов:

void read_ext_ptable(__u64 seek)
{
    int num = 4; // начиная с этой позиции, массив структур pt_t будет
		 // заполняться информацией о логических дисках
    __u8 smbr[512];

Функция принимает один параметр seek - смещение (в байтах) к расширеному разделу
от начала диска.

Для получения информации о логических дисках организуем цикл:

    for(;;num++) {

Считываем SMBR, находящуюся по смещению seek от начала диска:

	memset((void *)smbr, 0, 512);
	pread64(hard, smbr, 512, seek);

Заполняем два элемента таблицы pt_t, начиная с num. Первый элемент будет
указывать на логический диск, а второй - на следующую структуру SMBR:

	memset((void *)&pt_t[num], 0, PT_SIZE * 2);
	memcpy((void *)&pt_t[num], smbr + 0x1BE, PT_SIZE * 2);

Вносим поправку в поле "Номер начального сектора" - отсчет ведется от начала
диска:

	pt_t[num].sect_before += (seek / 512);

Если код типа раздела равен нулю, то больше логических дисков нет:

        if(!(pt_t[num + 1].type_part)) break;

Вычисляем смещение к следующей SMBR:
	seek = ((__u64)(pt_t[num].sect_before + pt_t[num].sect_total)) * 512;

    }

    return;
}


Функция show_pt_info() отображает информацию о найденых логических дисках
на устройстве:

void show_pt_info()
{
    int i = 0, n;

    printf("PART NUM - %d\n", PART_NUM);

    for(; i < MAX_PART; i++) {

	if(!pt_t[i].type_part) break;

	printf("\nТип раздела %d - ", i);

        for(n = 0; n < PART_NUM; n++) {

	    if(pt_t[i].type_part == i386_sys_types[n].part_type) {
		printf("%s\n", i386_sys_types[n].part_name);
		break;
	    }

	}
	if(n == PART_NUM) printf("unknown type\n");

	printf("Признак загрузки - 0x%X\n", pt_t[i].bootable);
	printf("Секторов в разделе %d - %d\n", i, pt_t[i].sect_total);
	printf("Секторов перед разделом %d - %d\n\n", i, pt_t[i].sect_before);
    }

    return;
}



Рассмотрим пример функционирования этого модуля.

Имеется жесткий диск, подключеный как Primary Master. На диске
созданы три основных раздела (FAT16, Linux, Linux swap) и расширенный раздел в
составе трех логических дисков (два FAT32-диска и Linux-диск).

Результат работы модуля:

root@darkstar:/home/HDD/PART# ./part_view

Signature - 0xAA55
PART NUM - 13

Тип раздела 0 - Win95 FAT16 (LBA)
Признак загрузки - 0x80
Секторов в разделе 0 - 4096512
Секторов перед разделом 0 - 63


Тип раздела 1 - Win95 Ext'd (LBA)
Признак загрузки - 0x0
Секторов в разделе 1 - 36065925
Секторов перед разделом 1 - 4096575


Тип раздела 2 - Linux
Признак загрузки - 0x0
Секторов в разделе 2 - 38556000
Секторов перед разделом 2 - 40162500


Тип раздела 3 - Linux swap
Признак загрузки - 0x0
Секторов в разделе 3 - 1686825
Секторов перед разделом 3 - 78718500


Тип раздела 4 - Win95 FAT32
Признак загрузки - 0x0
Секторов в разделе 4 - 30443112
Секторов перед разделом 4 - 4096638


Тип раздела 5 - Win95 FAT32
Признак загрузки - 0x0
Секторов в разделе 5 - 1863477
Секторов перед разделом 5 - 34539813


Тип раздела 6 - Win95 FAT32
Признак загрузки - 0x0
Секторов в разделе 6 - 867447
Секторов перед разделом 6 - 36403353


Тип раздела 7 - Linux
Признак загрузки - 0x0
Секторов в разделе 7 - 2891637
Секторов перед разделом 7 - 37270863


Для контроля получим информацию о логических дисках при помощи fdisk:

root@darkstar:/# fdisk -l -u

Disk /dev/hda: 41.1 GB, 41174138880 bytes
255 heads, 63 sectors/track, 5005 cylinders, total 80418240 sectors
Units = sectors of 1 * 512 = 512 bytes

   Device Boot    Start       End    Blocks   Id  System
/dev/hda1   *        63   4096574   2048256    e  Win95 FAT16 (LBA)
/dev/hda2       4096575  40162499  18032962+   f  Win95 Ext'd (LBA)
/dev/hda3      40162500  78718499  19278000   83  Linux
/dev/hda4      78718500  80405324    843412+  82  Linux swap
/dev/hda5       4096638  34539749  15221556    b  Win95 FAT32
/dev/hda6      34539813  36403289    931738+   b  Win95 FAT32
/dev/hda7      36403353  37270799    433723+   b  Win95 FAT32
/dev/hda8      37270863  40162499   1445818+  83  Linux


Литература:
1. Программирование на аппаратном уровне: специальный справочник.
2-е изд. / В.Кулаков. - СПб.: Питер, 2003. - 848 с.


Исходники к статье


[Главная] [Другие статьи] [Обсудить в форуме]
©2003-2004 Lowlevel.RU

Rambler's Top100