update report
This commit is contained in:
+225
@@ -25,6 +25,18 @@
|
|||||||
|
|
||||||
В качестве ориентира использовался предоставленный пример реализации, однако приоритет был отдан требованиям задания. Поэтому итоговая программа сохраняет простую учебную архитектуру примера, но дополнительно реализует операции `open`, `close`, `seek`, `read`, `write`, `rename`, импорт и экспорт, дефрагментацию и сжатие.
|
В качестве ориентира использовался предоставленный пример реализации, однако приоритет был отдан требованиям задания. Поэтому итоговая программа сохраняет простую учебную архитектуру примера, но дополнительно реализует операции `open`, `close`, `seek`, `read`, `write`, `rename`, импорт и экспорт, дефрагментацию и сжатие.
|
||||||
|
|
||||||
|
> **Теоретическая основа.** Файловая система — это способ организации долговременного хранения данных, при котором содержимое носителя представляется не как сплошной массив байтов, а как совокупность именованных объектов, метаданных и правил размещения. Даже в упрощенной реализации должны присутствовать три ключевых слоя: физическое хранение, служебные структуры и пользовательский интерфейс доступа к файлам.
|
||||||
|
|
||||||
|
Уже в начале программы задаются основные параметры будущей файловой системы: сигнатура формата, размер кластера, число дескрипторов и ограничения на открытый файл.
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define FS_MAGIC 0x53465331u
|
||||||
|
#define CLUSTER_SIZE 1024u
|
||||||
|
#define MAX_ENTRIES 128
|
||||||
|
#define MAX_FILE_CLUSTERS 128
|
||||||
|
#define MAX_OPEN_DATA (MAX_FILE_CLUSTERS * CLUSTER_SIZE)
|
||||||
|
```
|
||||||
|
|
||||||
## Постановка задачи
|
## Постановка задачи
|
||||||
|
|
||||||
Согласно заданию необходимо разработать систему управления файлами, где разделом является бинарный файл произвольного доступа. Реализация должна включать:
|
Согласно заданию необходимо разработать систему управления файлами, где разделом является бинарный файл произвольного доступа. Реализация должна включать:
|
||||||
@@ -37,10 +49,28 @@
|
|||||||
- символьные операции над файлами и каталогами;
|
- символьные операции над файлами и каталогами;
|
||||||
- прикладную утилиту для форматирования, просмотра, импорта, экспорта, дефрагментации и сжатия.
|
- прикладную утилиту для форматирования, просмотра, импорта, экспорта, дефрагментации и сжатия.
|
||||||
|
|
||||||
|
> **Теоретическая основа.** При проектировании файловой системы важно разделять данные пользователя и служебные данные самой системы. Пользователь работает с именами файлов и каталогов, однако внутри реализации требуется хранить состояние носителя, сведения о свободном пространстве, метаданные объектов и текущий контекст работы.
|
||||||
|
|
||||||
|
В программе эта постановка отражается в центральной структуре состояния файловой системы. Она объединяет физический носитель, карту свободных блоков, таблицу дескрипторов и текущий каталог пользователя.
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct {
|
||||||
|
FILE *disk;
|
||||||
|
char image_path[MAX_PATH];
|
||||||
|
SuperBlock sb;
|
||||||
|
unsigned char *bitmap;
|
||||||
|
Entry entries[MAX_ENTRIES];
|
||||||
|
uint32_t current_dir;
|
||||||
|
bool mounted;
|
||||||
|
} FileSystem;
|
||||||
|
```
|
||||||
|
|
||||||
## Выбранная архитектура файловой системы
|
## Выбранная архитектура файловой системы
|
||||||
|
|
||||||
Файловая система разделена на уровни. Такое деление полезно не только для описания в отчете: каждый уровень решает свою задачу и скрывает детали от следующего. Пользователь работает с командами и путями, а не с номерами кластеров; функции чтения работают с файлами, а не с отдельными битами карты свободного пространства.
|
Файловая система разделена на уровни. Такое деление полезно не только для описания в отчете: каждый уровень решает свою задачу и скрывает детали от следующего. Пользователь работает с командами и путями, а не с номерами кластеров; функции чтения работают с файлами, а не с отдельными битами карты свободного пространства.
|
||||||
|
|
||||||
|
> **Теоретическая основа.** Многоуровневая архитектура уменьшает связность системы. Физический уровень отвечает за доступ к байтам носителя, базовый — за метаданные, логический — за каталоги и пути, а прикладной — за команды пользователя. Такое разделение облегчает развитие системы: например, способ учета свободных блоков можно изменить, почти не затрагивая синтаксис команд.
|
||||||
|
|
||||||
| Уровень | Реализация | Назначение |
|
| Уровень | Реализация | Назначение |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| Физический уровень | Кластеры фиксированного размера по 1024 байта | Делит носитель на одинаковые адресуемые блоки; смещение кластера вычисляется как `номер * 1024` |
|
| Физический уровень | Кластеры фиксированного размера по 1024 байта | Делит носитель на одинаковые адресуемые блоки; смещение кластера вычисляется как `номер * 1024` |
|
||||||
@@ -55,22 +85,90 @@
|
|||||||
|
|
||||||
Носителем является обычный бинарный файл. Он рассматривается как последовательность кластеров одинакового размера. Фиксированный размер выбран ради простоты: адрес любого кластера находится прямым умножением, а алгоритм выделения не обязан искать переменные по длине участки памяти. Недостатком является внутреннее фрагментирование: даже файл в несколько байт занимает целый кластер.
|
Носителем является обычный бинарный файл. Он рассматривается как последовательность кластеров одинакового размера. Фиксированный размер выбран ради простоты: адрес любого кластера находится прямым умножением, а алгоритм выделения не обязан искать переменные по длине участки памяти. Недостатком является внутреннее фрагментирование: даже файл в несколько байт занимает целый кластер.
|
||||||
|
|
||||||
|
> **Теория.** Кластер — минимальная единица выделения дискового пространства. Чем больше размер кластера, тем меньше служебных записей требуется для описания больших файлов, но тем выше потери на внутреннее фрагментирование при хранении маленьких файлов.
|
||||||
|
|
||||||
|
Адрес кластера вычисляется напрямую по его номеру. Это видно при записи данных:
|
||||||
|
|
||||||
|
```c
|
||||||
|
fseek(fs->disk, (long)(entry->clusters[i] * CLUSTER_SIZE), SEEK_SET);
|
||||||
|
fwrite(data + written, 1, chunk, fs->disk);
|
||||||
|
```
|
||||||
|
|
||||||
### Уровень свободного пространства
|
### Уровень свободного пространства
|
||||||
|
|
||||||
Для каждого кластера хранится один бит. При форматировании служебные кластеры суперблока, битовой карты и индексного файла сразу помечаются занятыми. При записи файла функция выделения просматривает карту от начала области данных и выбирает первые свободные кластеры; при удалении файла соответствующие биты сбрасываются обратно в ноль.
|
Для каждого кластера хранится один бит. При форматировании служебные кластеры суперблока, битовой карты и индексного файла сразу помечаются занятыми. При записи файла функция выделения просматривает карту от начала области данных и выбирает первые свободные кластеры; при удалении файла соответствующие биты сбрасываются обратно в ноль.
|
||||||
|
|
||||||
|
> **Теория.** Битовая карта — одна из классических структур учета свободного пространства. Она компактна и проста в реализации: состояние каждого блока кодируется одним битом. Недостатком является необходимость линейного просмотра при поиске свободных блоков, если не используются дополнительные индексы или кеши.
|
||||||
|
|
||||||
|
```c
|
||||||
|
static bool bit_is_set(FileSystem *fs, uint32_t cluster) {
|
||||||
|
return (fs->bitmap[cluster / 8] & (1u << (cluster % 8))) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int alloc_cluster(FileSystem *fs) {
|
||||||
|
for (uint32_t i = fs->sb.data_start_cluster; i < fs->sb.total_clusters; i++) {
|
||||||
|
if (!bit_is_set(fs, i)) {
|
||||||
|
bit_set(fs, i);
|
||||||
|
return (int)i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Базовый уровень
|
### Базовый уровень
|
||||||
|
|
||||||
Индексный файл — это таблица дескрипторов `Entry`. Один дескриптор описывает один файл или каталог: имя, тип, родителя, размеры и список кластеров. Корневой каталог хранится в нулевой записи. Благодаря индексному файлу метаданные можно прочитать сразу после монтирования носителя, не просматривая всю область данных.
|
Индексный файл — это таблица дескрипторов `Entry`. Один дескриптор описывает один файл или каталог: имя, тип, родителя, размеры и список кластеров. Корневой каталог хранится в нулевой записи. Благодаря индексному файлу метаданные можно прочитать сразу после монтирования носителя, не просматривая всю область данных.
|
||||||
|
|
||||||
|
> **Теория.** Метаданные отделяются от содержимого файлов, чтобы система могла быстро находить объект, определять его тип, размер и расположение без чтения пользовательских данных. В реальных файловых системах аналогичную роль играют, например, inode-структуры.
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct {
|
||||||
|
char name[MAX_NAME];
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t compressed;
|
||||||
|
uint16_t reserved;
|
||||||
|
uint32_t parent;
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t stored_size;
|
||||||
|
uint32_t cluster_count;
|
||||||
|
uint32_t clusters[MAX_FILE_CLUSTERS];
|
||||||
|
} Entry;
|
||||||
|
```
|
||||||
|
|
||||||
### Уровень размещения файлов
|
### Уровень размещения файлов
|
||||||
|
|
||||||
Каждый файл хранит массив номеров своих кластеров. Поэтому файл может лежать не непрерывно, а частями в разных местах виртуального диска. При чтении система идет по этому массиву и собирает содержимое в правильной последовательности. Дефрагментация перестраивает эти списки так, чтобы занятые кластеры снова располагались компактно.
|
Каждый файл хранит массив номеров своих кластеров. Поэтому файл может лежать не непрерывно, а частями в разных местах виртуального диска. При чтении система идет по этому массиву и собирает содержимое в правильной последовательности. Дефрагментация перестраивает эти списки так, чтобы занятые кластеры снова располагались компактно.
|
||||||
|
|
||||||
|
> **Теория.** Несмежное размещение повышает гибкость выделения места: файл можно расширять даже при отсутствии одного большого непрерывного участка. Однако чтение таких файлов требует хранения списка блоков и может приводить к фрагментации.
|
||||||
|
|
||||||
|
```c
|
||||||
|
for (i = 0; i < entry->cluster_count; i++) {
|
||||||
|
uint32_t chunk = entry->stored_size - read_count;
|
||||||
|
if (chunk > CLUSTER_SIZE) chunk = CLUSTER_SIZE;
|
||||||
|
fseek(fs->disk, (long)(entry->clusters[i] * CLUSTER_SIZE), SEEK_SET);
|
||||||
|
fread(data + read_count, 1, chunk, fs->disk);
|
||||||
|
read_count += chunk;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Логический и символьный уровни
|
### Логический и символьный уровни
|
||||||
|
|
||||||
Каталог реализован без отдельного файла содержимого: у каждого объекта есть поле `parent`, содержащее индекс родительского каталога. Путь разбивается по символу `/`, после чего система шаг за шагом ищет дочерний объект в текущем каталоге. Символьный уровень дает привычные операции над открытым файлом: `open`, `close`, `seek`, `read`, `write`; внутри программы они работают через буфер и скрывают детали физического размещения.
|
Каталог реализован без отдельного файла содержимого: у каждого объекта есть поле `parent`, содержащее индекс родительского каталога. Путь разбивается по символу `/`, после чего система шаг за шагом ищет дочерний объект в текущем каталоге. Символьный уровень дает привычные операции над открытым файлом: `open`, `close`, `seek`, `read`, `write`; внутри программы они работают через буфер и скрывают детали физического размещения.
|
||||||
|
|
||||||
|
> **Теория.** Логический уровень преобразует человекоориентированные имена в внутренние идентификаторы объектов. Символьный уровень дополняет это абстракцией «открытого файла», где пользователь работает с текущей позицией чтения и записи, а не с физическими блоками.
|
||||||
|
|
||||||
|
```c
|
||||||
|
static int find_child(FileSystem *fs, uint32_t parent, const char *name) {
|
||||||
|
for (int i = 0; i < MAX_ENTRIES; i++) {
|
||||||
|
if (fs->entries[i].type != TYPE_FREE &&
|
||||||
|
fs->entries[i].parent == parent &&
|
||||||
|
strcmp(fs->entries[i].name, name) == 0) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
A[Команда пользователя] --> B[Путь /docs/a.txt]
|
A[Команда пользователя] --> B[Путь /docs/a.txt]
|
||||||
@@ -84,6 +182,8 @@ flowchart LR
|
|||||||
|
|
||||||
В начале виртуального диска находится суперблок. Он хранит сигнатуру файловой системы, размер диска, размер кластера, смещения служебных областей и индекс корневого каталога.
|
В начале виртуального диска находится суперблок. Он хранит сигнатуру файловой системы, размер диска, размер кластера, смещения служебных областей и индекс корневого каталога.
|
||||||
|
|
||||||
|
> **Теоретическая основа.** Суперблок — это точка входа в структуру файловой системы. После подключения носителя именно по нему программа понимает, корректный ли перед ней формат и где искать остальные области. Потеря суперблока делает интерпретацию содержимого носителя затруднительной даже при сохранности пользовательских данных.
|
||||||
|
|
||||||
```c
|
```c
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
@@ -116,6 +216,17 @@ typedef struct {
|
|||||||
|
|
||||||
Поле `size` хранит логический размер файла, видимый пользователю, а `stored_size` — фактическое количество байт на носителе. Это различие необходимо для поддержки сжатых файлов.
|
Поле `size` хранит логический размер файла, видимый пользователю, а `stored_size` — фактическое количество байт на носителе. Это различие необходимо для поддержки сжатых файлов.
|
||||||
|
|
||||||
|
При форматировании эти области рассчитываются последовательно: после суперблока располагается битовая карта, затем индексный файл, после чего начинается пользовательская область данных.
|
||||||
|
|
||||||
|
```c
|
||||||
|
fs->sb.bitmap_offset = CLUSTER_SIZE;
|
||||||
|
fs->sb.bitmap_size = (fs->sb.total_clusters + 7) / 8;
|
||||||
|
bitmap_clusters = bytes_to_clusters(fs->sb.bitmap_size);
|
||||||
|
fs->sb.index_offset = (1 + bitmap_clusters) * CLUSTER_SIZE;
|
||||||
|
fs->sb.index_clusters = bytes_to_clusters((uint32_t)(sizeof(Entry) * MAX_ENTRIES));
|
||||||
|
fs->sb.data_start_cluster = 1 + bitmap_clusters + fs->sb.index_clusters;
|
||||||
|
```
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TB
|
flowchart TB
|
||||||
S[Суперблок] --> B[Битовая карта]
|
S[Суперблок] --> B[Битовая карта]
|
||||||
@@ -137,6 +248,8 @@ gcc -std=c11 -Wall -Wextra -pedantic simplefs.c -o simplefs
|
|||||||
|
|
||||||
Интерактивная оболочка поддерживает историю команд: в обычном терминале стрелки **Up** и **Down** перемещаются по ранее введенным строкам. Основные команды названы в стиле Unix, чтобы работа с учебной файловой системой напоминала привычную командную строку.
|
Интерактивная оболочка поддерживает историю команд: в обычном терминале стрелки **Up** и **Down** перемещаются по ранее введенным строкам. Основные команды названы в стиле Unix, чтобы работа с учебной файловой системой напоминала привычную командную строку.
|
||||||
|
|
||||||
|
> **Теоретическая основа.** Набор операций файловой системы обычно разделяется на операции над объектами (`create`, `remove`, `rename`), операции навигации (`cd`, `ls`, `pwd`), операции передачи данных (`read`, `write`, `import`, `export`) и сервисные операции обслуживания носителя (`format`, `defrag`, `compress`).
|
||||||
|
|
||||||
### Справочник команд
|
### Справочник команд
|
||||||
|
|
||||||
| Команда | Синтаксис | Назначение |
|
| Команда | Синтаксис | Назначение |
|
||||||
@@ -168,32 +281,107 @@ gcc -std=c11 -Wall -Wextra -pedantic simplefs.c -o simplefs
|
|||||||
|
|
||||||
На уровне программного интерфейса реализованы функции `fs_open`, `fs_close`, `fs_seek`, `fs_read` и `fs_write`. Открытый файл использует буфер в памяти, поэтому чтение и запись записей произвольной длины выполняются без прямой работы пользователя с кластерами.
|
На уровне программного интерфейса реализованы функции `fs_open`, `fs_close`, `fs_seek`, `fs_read` и `fs_write`. Открытый файл использует буфер в памяти, поэтому чтение и запись записей произвольной длины выполняются без прямой работы пользователя с кластерами.
|
||||||
|
|
||||||
|
```c
|
||||||
|
uint32_t fs_write(OpenFile *f, const unsigned char *buffer, uint32_t count) {
|
||||||
|
if (!f || !buffer || f->mode != MODE_WRITE ||
|
||||||
|
f->position + count > f->capacity) return 0;
|
||||||
|
memcpy(f->data + f->position, buffer, count);
|
||||||
|
f->position += count;
|
||||||
|
if (f->position > f->size) f->size = f->position;
|
||||||
|
f->dirty = true;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Алгоритмы работы
|
## Алгоритмы работы
|
||||||
|
|
||||||
|
В этом разделе приведены основные алгоритмы, которые обеспечивают корректное изменение состояния носителя. Они важны не только как детали реализации, но и как отражение базовых принципов файловых систем: инициализация структуры, выделение ресурсов, разрешение имен, преобразование данных и восстановление компактного размещения.
|
||||||
|
|
||||||
### Форматирование
|
### Форматирование
|
||||||
|
|
||||||
При выполнении `format` создается бинарный файл нужного размера, вычисляются размеры служебных областей, резервируются занятые кластеры и создается корневой каталог `/`.
|
При выполнении `format` создается бинарный файл нужного размера, вычисляются размеры служебных областей, резервируются занятые кластеры и создается корневой каталог `/`.
|
||||||
|
|
||||||
|
```c
|
||||||
|
for (i = 0; i < fs->sb.data_start_cluster; i++) bit_set(fs, i);
|
||||||
|
memset(fs->entries, 0, sizeof(fs->entries));
|
||||||
|
safe_copy(fs->entries[0].name, "/", MAX_NAME);
|
||||||
|
fs->entries[0].type = TYPE_DIR;
|
||||||
|
fs->entries[0].parent = 0;
|
||||||
|
```
|
||||||
|
|
||||||
### Выделение свободного кластера
|
### Выделение свободного кластера
|
||||||
|
|
||||||
Алгоритм линейно просматривает битовую карту, начиная с области данных. Первый свободный бит помечается как занятый, а номер соответствующего кластера возвращается вызывающей функции.
|
Алгоритм линейно просматривает битовую карту, начиная с области данных. Первый свободный бит помечается как занятый, а номер соответствующего кластера возвращается вызывающей функции.
|
||||||
|
|
||||||
|
```c
|
||||||
|
static int alloc_cluster(FileSystem *fs) {
|
||||||
|
for (uint32_t i = fs->sb.data_start_cluster; i < fs->sb.total_clusters; i++) {
|
||||||
|
if (!bit_is_set(fs, i)) {
|
||||||
|
bit_set(fs, i);
|
||||||
|
return (int)i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Работа с каталогами
|
### Работа с каталогами
|
||||||
|
|
||||||
Каталог не хранит отдельный массив дочерних элементов. Вместо этого у каждого дескриптора есть поле `parent`, содержащее индекс родительского каталога. Для поиска элемента программа просматривает индексный файл и сравнивает имя и родителя.
|
Каталог не хранит отдельный массив дочерних элементов. Вместо этого у каждого дескриптора есть поле `parent`, содержащее индекс родительского каталога. Для поиска элемента программа просматривает индексный файл и сравнивает имя и родителя.
|
||||||
|
|
||||||
|
```c
|
||||||
|
bool fs_create_dir(FileSystem *fs, const char *path) {
|
||||||
|
int parent, index;
|
||||||
|
char name[MAX_NAME];
|
||||||
|
if (!split_parent_name(fs, path, &parent, name) ||
|
||||||
|
find_child(fs, parent, name) >= 0) return false;
|
||||||
|
index = free_entry_index(fs);
|
||||||
|
if (index < 0) return false;
|
||||||
|
memset(&fs->entries[index], 0, sizeof(Entry));
|
||||||
|
safe_copy(fs->entries[index].name, name, MAX_NAME);
|
||||||
|
fs->entries[index].type = TYPE_DIR;
|
||||||
|
fs->entries[index].parent = (uint32_t)parent;
|
||||||
|
fs_sync(fs);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Сжатие
|
### Сжатие
|
||||||
|
|
||||||
Команда `compress` использует простой алгоритм RLE: одинаковые подряд идущие байты заменяются парой «количество + значение». Если сжатый вариант не меньше исходного, файл не изменяется. При чтении сжатый файл автоматически восстанавливается в обычный вид. Для наблюдения за внутренним представлением предусмотрена команда `rawcat`: она показывает сохраненные байты в шестнадцатеричном виде и расшифровывает каждую RLE-пару. Команда `decomp` выполняет обратное преобразование и снова сохраняет файл без сжатия.
|
Команда `compress` использует простой алгоритм RLE: одинаковые подряд идущие байты заменяются парой «количество + значение». Если сжатый вариант не меньше исходного, файл не изменяется. При чтении сжатый файл автоматически восстанавливается в обычный вид. Для наблюдения за внутренним представлением предусмотрена команда `rawcat`: она показывает сохраненные байты в шестнадцатеричном виде и расшифровывает каждую RLE-пару. Команда `decomp` выполняет обратное преобразование и снова сохраняет файл без сжатия.
|
||||||
|
|
||||||
|
```c
|
||||||
|
while (i < size) {
|
||||||
|
unsigned char value = src[i];
|
||||||
|
uint32_t count = 1;
|
||||||
|
while (i + count < size && src[i + count] == value && count < 255) count++;
|
||||||
|
out[j++] = (unsigned char)count;
|
||||||
|
out[j++] = value;
|
||||||
|
i += count;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Дефрагментация
|
### Дефрагментация
|
||||||
|
|
||||||
Команда `defrag` временно считывает содержимое файлов, очищает карту размещения области данных и записывает файлы заново подряд. После операции используемые кластеры становятся компактно расположенными.
|
Команда `defrag` временно считывает содержимое файлов, очищает карту размещения области данных и записывает файлы заново подряд. После операции используемые кластеры становятся компактно расположенными.
|
||||||
|
|
||||||
|
```c
|
||||||
|
memset(fs->bitmap, 0, fs->sb.bitmap_size);
|
||||||
|
for (c = 0; c < fs->sb.data_start_cluster; c++) bit_set(fs, c);
|
||||||
|
for (i = 0; i < MAX_ENTRIES; i++) {
|
||||||
|
if (fs->entries[i].type == TYPE_FILE) {
|
||||||
|
fs->entries[i].cluster_count = 0;
|
||||||
|
write_stored_bytes(fs, &fs->entries[i], copies[i], stored_sizes[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Тестирование
|
## Тестирование
|
||||||
|
|
||||||
Пример базового сценария:
|
Пример базового сценария:
|
||||||
|
|
||||||
|
> **Теоретическая основа.** Тестирование файловой системы должно проверять не только отдельные команды, но и сохранение инвариантов: после записи файл должен читаться тем же содержимым, после удаления пространство должно освобождаться, после сжатия логические данные не должны меняться, а после дефрагментации содержимое должно сохраняться при изменении физического размещения.
|
||||||
|
|
||||||
```text
|
```text
|
||||||
format 512
|
format 512
|
||||||
mkdir /docs
|
mkdir /docs
|
||||||
@@ -220,6 +408,15 @@ info
|
|||||||
| Экспорт | создается внешний файл с тем же содержимым |
|
| Экспорт | создается внешний файл с тем же содержимым |
|
||||||
| Дефрагментация | содержимое файлов сохраняется, кластеры уплотняются |
|
| Дефрагментация | содержимое файлов сохраняется, кластеры уплотняются |
|
||||||
|
|
||||||
|
Для проверки сценария полезно опираться на сами команды интерактивной оболочки. Разбор пользовательского ввода связывает каждую строку с соответствующей функцией файловой системы:
|
||||||
|
|
||||||
|
```c
|
||||||
|
else if (strcmp(cmd, "mkdir") == 0 && a) printf(fs_create_dir(&fs, a) ? "ok\n" : "mkdir failed\n");
|
||||||
|
else if (strcmp(cmd, "write") == 0 && a && b) printf(fs_write_text(&fs, a, b) ? "ok\n" : "write failed\n");
|
||||||
|
else if (strcmp(cmd, "compress") == 0 && a) printf(fs_compress_file(&fs, a) ? "ok\n" : "compression did not reduce file\n");
|
||||||
|
else if (strcmp(cmd, "defrag") == 0) printf(fs_defrag(&fs) ? "ok\n" : "defrag failed\n");
|
||||||
|
```
|
||||||
|
|
||||||
## Ограничения реализации
|
## Ограничения реализации
|
||||||
|
|
||||||
| Ограничение | Причина |
|
| Ограничение | Причина |
|
||||||
@@ -231,8 +428,36 @@ info
|
|||||||
| RLE эффективно не для всех данных | алгоритм выбран ради понятности реализации |
|
| RLE эффективно не для всех данных | алгоритм выбран ради понятности реализации |
|
||||||
| Нет одновременного доступа нескольких процессов | учебная однопользовательская модель |
|
| Нет одновременного доступа нескольких процессов | учебная однопользовательская модель |
|
||||||
|
|
||||||
|
> **Теоретическая основа.** Ограничения учебной реализации обычно связаны с заранее фиксированными структурами данных. Это упрощает код и делает поведение системы предсказуемым, но уменьшает масштабируемость по сравнению с промышленными файловыми системами, где размеры таблиц, число блоков и механизмы синхронизации более гибкие.
|
||||||
|
|
||||||
|
Большинство ограничений заданы непосредственно константами программы. Это делает реализацию прозрачной для учебных целей, но одновременно фиксирует максимальные размеры структур:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define MAX_NAME 32
|
||||||
|
#define MAX_PATH 256
|
||||||
|
#define MAX_ENTRIES 128
|
||||||
|
#define MAX_FILE_CLUSTERS 128
|
||||||
|
```
|
||||||
|
|
||||||
## Вывод
|
## Вывод
|
||||||
|
|
||||||
В ходе работы была реализована простая файловая система, работающая поверх бинарного файла как виртуального носителя. Реализация охватывает все основные уровни, указанные в задании: физическое размещение, учет свободных блоков, индексные метаданные, каталоги, операции над файлами и сервисные команды управления носителем.
|
В ходе работы была реализована простая файловая система, работающая поверх бинарного файла как виртуального носителя. Реализация охватывает все основные уровни, указанные в задании: физическое размещение, учет свободных блоков, индексные метаданные, каталоги, операции над файлами и сервисные команды управления носителем.
|
||||||
|
|
||||||
Полученная программа остается достаточно простой для изучения начинающим разработчиком на C, но при этом показывает полный путь данных: от пользовательской команды до изменения битовой карты, индексного дескриптора и байтов в кластерах виртуального диска.
|
Полученная программа остается достаточно простой для изучения начинающим разработчиком на C, но при этом показывает полный путь данных: от пользовательской команды до изменения битовой карты, индексного дескриптора и байтов в кластерах виртуального диска.
|
||||||
|
|
||||||
|
> **Итоговое обобщение.** Разработанная система демонстрирует фундаментальный принцип файловых систем: удобные для человека операции над именованными объектами опираются на строго организованный набор служебных структур и алгоритмов управления блоками носителя.
|
||||||
|
|
||||||
|
Ключевая идея реализации хорошо видна в операции синхронизации: состояние файловой системы собирается из нескольких уровней и затем последовательно сохраняется на виртуальный носитель.
|
||||||
|
|
||||||
|
```c
|
||||||
|
static bool fs_sync(FileSystem *fs) {
|
||||||
|
fseek(fs->disk, 0, SEEK_SET);
|
||||||
|
fwrite(&fs->sb, sizeof(SuperBlock), 1, fs->disk);
|
||||||
|
fseek(fs->disk, fs->sb.bitmap_offset, SEEK_SET);
|
||||||
|
fwrite(fs->bitmap, 1, fs->sb.bitmap_size, fs->disk);
|
||||||
|
fseek(fs->disk, fs->sb.index_offset, SEEK_SET);
|
||||||
|
fwrite(fs->entries, sizeof(Entry), MAX_ENTRIES, fs->disk);
|
||||||
|
fflush(fs->disk);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user