464 lines
36 KiB
Markdown
464 lines
36 KiB
Markdown
# Отчет по дисциплине «Операционные системы»
|
|
|
|
## РГР «Разработка простой файловой системы»
|
|
|
|
**Выполнил:** ____________________
|
|
**Группа:** ____________________
|
|
**Проверил:** ____________________
|
|
**Новосибирск, 2026**
|
|
|
|
## Оглавление
|
|
|
|
1. [Введение](#введение)
|
|
2. [Постановка задачи](#постановка-задачи)
|
|
3. [Выбранная архитектура файловой системы](#выбранная-архитектура-файловой-системы)
|
|
4. [Структура носителя и основные данные](#структура-носителя-и-основные-данные)
|
|
5. [Реализованные операции](#реализованные-операции)
|
|
6. [Алгоритмы работы](#алгоритмы-работы)
|
|
7. [Тестирование](#тестирование)
|
|
8. [Ограничения реализации](#ограничения-реализации)
|
|
9. [Вывод](#вывод)
|
|
|
|
## Введение
|
|
|
|
Цель работы — разработать учебную файловую систему на языке C, использующую обычный бинарный файл как виртуальный носитель произвольного доступа. Программа должна не только хранить файлы и каталоги, но и показывать основные принципы настоящих файловых систем: разметку носителя, учет свободного места, хранение метаданных, работу с путями и прикладные операции обслуживания.
|
|
|
|
В качестве ориентира использовался предоставленный пример реализации, однако приоритет был отдан требованиям задания. Поэтому итоговая программа сохраняет простую учебную архитектуру примера, но дополнительно реализует операции `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)
|
|
```
|
|
|
|
## Постановка задачи
|
|
|
|
Согласно заданию необходимо разработать систему управления файлами, где разделом является бинарный файл произвольного доступа. Реализация должна включать:
|
|
|
|
- физический уровень хранения данных;
|
|
- структуру учета свободных блоков;
|
|
- базовый уровень с индексными метаданными;
|
|
- размещение содержимого файлов;
|
|
- систему каталогов и путей;
|
|
- символьные операции над файлами и каталогами;
|
|
- прикладную утилиту для форматирования, просмотра, импорта, экспорта, дефрагментации и сжатия.
|
|
|
|
> **Теоретическая основа.** При проектировании файловой системы важно разделять данные пользователя и служебные данные самой системы. Пользователь работает с именами файлов и каталогов, однако внутри реализации требуется хранить состояние носителя, сведения о свободном пространстве, метаданные объектов и текущий контекст работы.
|
|
|
|
В программе эта постановка отражается в центральной структуре состояния файловой системы. Она объединяет физический носитель, карту свободных блоков, таблицу дескрипторов и текущий каталог пользователя.
|
|
|
|
```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` |
|
|
| Учет свободного места | Битовая карта | Показывает, какие кластеры свободны, а какие уже принадлежат служебным областям или файлам |
|
|
| Базовый уровень | Индексный файл с дескрипторами объектов | Хранит метаданные файлов и каталогов отдельно от пользовательских данных |
|
|
| Размещение файлов | Список номеров кластеров в дескрипторе | Позволяет файлу занимать несколько несмежных кластеров и читать их в правильном порядке |
|
|
| Логический уровень | Неупорядоченный список имен | Представляет каталоги через связь «объект — родительский каталог» и выполняет линейный поиск по именам |
|
|
| Символьный уровень | Пути вида `/docs/file.txt`, буферизированные операции `open/close/seek/read/write` | Преобразует удобные человеку имена и позиции в обращения к дескрипторам и кластерам |
|
|
| Прикладной уровень | Интерактивная консольная утилита | Дает команды для форматирования, просмотра, копирования, импорта, экспорта, сжатия и дефрагментации |
|
|
|
|
### Физический уровень
|
|
|
|
Носителем является обычный бинарный файл. Он рассматривается как последовательность кластеров одинакового размера. Фиксированный размер выбран ради простоты: адрес любого кластера находится прямым умножением, а алгоритм выделения не обязан искать переменные по длине участки памяти. Недостатком является внутреннее фрагментирование: даже файл в несколько байт занимает целый кластер.
|
|
|
|
> **Теория.** Кластер — минимальная единица выделения дискового пространства. Чем больше размер кластера, тем меньше служебных записей требуется для описания больших файлов, но тем выше потери на внутреннее фрагментирование при хранении маленьких файлов.
|
|
|
|
Адрес кластера вычисляется напрямую по его номеру. Это видно при записи данных:
|
|
|
|
```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`. Один дескриптор описывает один файл или каталог: имя, тип, родителя, размеры и список кластеров. Корневой каталог хранится в нулевой записи. Благодаря индексному файлу метаданные можно прочитать сразу после монтирования носителя, не просматривая всю область данных.
|
|
|
|
> **Теория.** Метаданные отделяются от содержимого файлов, чтобы система могла быстро находить объект, определять его тип, размер и расположение без чтения пользовательских данных. В реальных файловых системах аналогичную роль играют, например, 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`; внутри программы они работают через буфер и скрывают детали физического размещения.
|
|
|
|
> **Теория.** Логический уровень преобразует человекоориентированные имена в внутренние идентификаторы объектов. Символьный уровень дополняет это абстракцией «открытого файла», где пользователь работает с текущей позицией чтения и записи, а не с физическими блоками.
|
|
|
|
```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
|
|
flowchart LR
|
|
A[Команда пользователя] --> B[Путь /docs/a.txt]
|
|
B --> C[Поиск дескриптора]
|
|
C --> D[Список кластеров]
|
|
D --> E[Битовая карта]
|
|
D --> F[Байты в области данных]
|
|
```
|
|
|
|
## Структура носителя и основные данные
|
|
|
|
В начале виртуального диска находится суперблок. Он хранит сигнатуру файловой системы, размер диска, размер кластера, смещения служебных областей и индекс корневого каталога.
|
|
|
|
> **Теоретическая основа.** Суперблок — это точка входа в структуру файловой системы. После подключения носителя именно по нему программа понимает, корректный ли перед ней формат и где искать остальные области. Потеря суперблока делает интерпретацию содержимого носителя затруднительной даже при сохранности пользовательских данных.
|
|
|
|
```c
|
|
typedef struct {
|
|
uint32_t magic;
|
|
uint32_t disk_size;
|
|
uint32_t cluster_size;
|
|
uint32_t total_clusters;
|
|
uint32_t bitmap_offset;
|
|
uint32_t bitmap_size;
|
|
uint32_t index_offset;
|
|
uint32_t index_clusters;
|
|
uint32_t data_start_cluster;
|
|
uint32_t root_index;
|
|
} SuperBlock;
|
|
```
|
|
|
|
После суперблока располагается битовая карта. Каждый ее бит соответствует одному кластеру: `0` означает свободный кластер, `1` — занятый. Далее находится индексный файл, представленный массивом дескрипторов `Entry`.
|
|
|
|
```c
|
|
typedef struct {
|
|
char name[32];
|
|
uint8_t type;
|
|
uint8_t compressed;
|
|
uint32_t parent;
|
|
uint32_t size;
|
|
uint32_t stored_size;
|
|
uint32_t cluster_count;
|
|
uint32_t clusters[128];
|
|
} Entry;
|
|
```
|
|
|
|
Поле `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
|
|
flowchart TB
|
|
S[Суперблок] --> B[Битовая карта]
|
|
B --> I[Индексный файл]
|
|
I --> D[Область данных]
|
|
I --> R[Корневой каталог /]
|
|
R --> F[Дескрипторы файлов и каталогов]
|
|
F --> D
|
|
```
|
|
|
|
## Реализованные операции
|
|
|
|
Программа собирается из файла `simplefs.c` и запускается с именем файла-носителя:
|
|
|
|
```bash
|
|
gcc -std=c11 -Wall -Wextra -pedantic simplefs.c -o simplefs
|
|
./simplefs disk.img
|
|
```
|
|
|
|
Интерактивная оболочка поддерживает историю команд: в обычном терминале стрелки **Up** и **Down** перемещаются по ранее введенным строкам. Основные команды названы в стиле Unix, чтобы работа с учебной файловой системой напоминала привычную командную строку.
|
|
|
|
> **Теоретическая основа.** Набор операций файловой системы обычно разделяется на операции над объектами (`create`, `remove`, `rename`), операции навигации (`cd`, `ls`, `pwd`), операции передачи данных (`read`, `write`, `import`, `export`) и сервисные операции обслуживания носителя (`format`, `defrag`, `compress`).
|
|
|
|
### Справочник команд
|
|
|
|
| Команда | Синтаксис | Назначение |
|
|
| --- | --- | --- |
|
|
| Форматирование | `format <KiB>` | создает новый файл-носитель указанного размера |
|
|
| Информация | `info` | показывает размер диска, размер кластера и число свободных кластеров |
|
|
| Список | `ls [path]` | выводит содержимое текущего или указанного каталога |
|
|
| Текущий каталог | `pwd` | печатает текущий абсолютный путь |
|
|
| Переход | `cd <path>` | меняет текущий каталог |
|
|
| Создание каталога | `mkdir <path>` | создает новый каталог |
|
|
| Удаление каталога | `rmdir <path>` | удаляет только пустой каталог |
|
|
| Создание файла | `touch <path>` | создает пустой файл |
|
|
| Удаление файла | `rm <path>` | удаляет файл и освобождает его кластеры |
|
|
| Копирование внутри ФС | `cp <from> <to>` | копирует файл в новый путь или в существующий каталог |
|
|
| Перемещение/переименование | `mv <from> <to>` | переносит файл или каталог; если меняется только имя, работает как переименование |
|
|
| Запись текста | `write <path> <text>` | перезаписывает файл текстовой строкой |
|
|
| Просмотр файла | `cat <path>` | выводит логическое содержимое файла |
|
|
| Просмотр хранения | `rawcat <path>` | выводит физически сохраненные байты и объясняет пары RLE |
|
|
| Импорт файла | `import <host_file> <fs_path>` | копирует файл из основной ОС в учебную ФС |
|
|
| Экспорт файла | `export <fs_path> <host_file>` | копирует файл из учебной ФС в основную ОС |
|
|
| Импорт папки | `importdir <host_dir> <fs_path>` | рекурсивно импортирует каталог |
|
|
| Экспорт папки | `exportdir <fs_path> <host_dir>` | рекурсивно экспортирует каталог |
|
|
| Экспорт носителя | `savecarrier <host_file>` | сохраняет копию всего бинарного носителя |
|
|
| Импорт носителя | `loadcarrier <host_file>` | заменяет текущий носитель ранее сохраненной копией |
|
|
| Сжатие | `compress <path>` | применяет RLE-сжатие, если оно уменьшает файл |
|
|
| Распаковка | `decomp <path>` | восстанавливает файл в несжатом виде |
|
|
| Дефрагментация | `defrag` | заново записывает файлы подряд в область данных |
|
|
| Справка и выход | `help`, `exit`, `quit` | показывает команды или завершает программу |
|
|
|
|
На уровне программного интерфейса реализованы функции `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` создается бинарный файл нужного размера, вычисляются размеры служебных областей, резервируются занятые кластеры и создается корневой каталог `/`.
|
|
|
|
```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`, содержащее индекс родительского каталога. Для поиска элемента программа просматривает индексный файл и сравнивает имя и родителя.
|
|
|
|
```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` выполняет обратное преобразование и снова сохраняет файл без сжатия.
|
|
|
|
```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` временно считывает содержимое файлов, очищает карту размещения области данных и записывает файлы заново подряд. После операции используемые кластеры становятся компактно расположенными.
|
|
|
|
```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
|
|
format 512
|
|
mkdir /docs
|
|
touch /docs/a.txt
|
|
write /docs/a.txt aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
cat /docs/a.txt
|
|
compress /docs/a.txt
|
|
ls /docs
|
|
rename /docs/a.txt b.txt
|
|
export /docs/b.txt exported.txt
|
|
defrag
|
|
info
|
|
```
|
|
|
|
Ожидаемые результаты:
|
|
|
|
| Проверка | Результат |
|
|
| --- | --- |
|
|
| Форматирование носителя | создается корректный виртуальный диск |
|
|
| Создание каталога и файла | объекты появляются в `ls` |
|
|
| Запись и чтение | `cat` выводит ранее записанный текст |
|
|
| Сжатие повторяющихся данных | `stored_size` становится меньше `size` |
|
|
| Переименование | файл доступен под новым именем |
|
|
| Экспорт | создается внешний файл с тем же содержимым |
|
|
| Дефрагментация | содержимое файлов сохраняется, кластеры уплотняются |
|
|
|
|
Для проверки сценария полезно опираться на сами команды интерактивной оболочки. Разбор пользовательского ввода связывает каждую строку с соответствующей функцией файловой системы:
|
|
|
|
```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");
|
|
```
|
|
|
|
## Ограничения реализации
|
|
|
|
| Ограничение | Причина |
|
|
| --- | --- |
|
|
| Не более 128 объектов | фиксированный размер индексного файла |
|
|
| Не более 128 кластеров на файл | фиксированный массив номеров кластеров |
|
|
| Длина имени до 31 символа | размер поля `name[32]` |
|
|
| Линейный поиск по каталогу | выбрана простая неупорядоченная структура |
|
|
| RLE эффективно не для всех данных | алгоритм выбран ради понятности реализации |
|
|
| Нет одновременного доступа нескольких процессов | учебная однопользовательская модель |
|
|
|
|
> **Теоретическая основа.** Ограничения учебной реализации обычно связаны с заранее фиксированными структурами данных. Это упрощает код и делает поведение системы предсказуемым, но уменьшает масштабируемость по сравнению с промышленными файловыми системами, где размеры таблиц, число блоков и механизмы синхронизации более гибкие.
|
|
|
|
Большинство ограничений заданы непосредственно константами программы. Это делает реализацию прозрачной для учебных целей, но одновременно фиксирует максимальные размеры структур:
|
|
|
|
```c
|
|
#define MAX_NAME 32
|
|
#define MAX_PATH 256
|
|
#define MAX_ENTRIES 128
|
|
#define MAX_FILE_CLUSTERS 128
|
|
```
|
|
|
|
## Вывод
|
|
|
|
В ходе работы была реализована простая файловая система, работающая поверх бинарного файла как виртуального носителя. Реализация охватывает все основные уровни, указанные в задании: физическое размещение, учет свободных блоков, индексные метаданные, каталоги, операции над файлами и сервисные команды управления носителем.
|
|
|
|
Полученная программа остается достаточно простой для изучения начинающим разработчиком на 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;
|
|
}
|
|
```
|