На протяжении нескольких лет не вступаю в священные holywars. А вот последнее время чувствую, что если не вступать в споры, то тебя никто и не заметит. Получается, с одной стороны, споры отнимают много времени, а с другой стороны - без них никак не обойтись. Главное - знать с кем и о чём спорить.
Недавно нарочно ввязался в спор C vs C++, чтобы маленько попиарить Хамелеон. На примере сравнения виртуальных файловых систем. Эта ссылка ведёт на информацию об устройстве виртуальной файловой системы Linux
В Хамелеоне это реализовано несколько иначе:
class ISuperblock { public: virtual ~ISuperblock(); virtual int ConnectToDevice(class IBlockDevicePartition * Device) = 0; // Mount service virtual class IBlockDevicePartition * GetDevice(void) = 0; virtual class Inode * GetRootInode(void) = 0; virtual class ISuperblock * GetParentSuperblock(void) = 0; virtual const ino_t GetParenInode(void) = 0; virtual void SetParent(class Inode * inode) = 0; // Controls how many objects use Superblock virtual void AddReference() = 0; virtual void DelReference() = 0; virtual const int GetReferenceCount(void) = 0; // Inode service virtual class Inode * GetInode(int nInodeNumber, int nParenNumber ) = 0; virtual class Inode * MakeInode (unsigned short uid, unsigned short gid, unsigned short mode) = 0; virtual int RemoveInode(class Inode * inode) const = 0; virtual int ReleaseInode(class Inode * inode) = 0; // Directory service virtual typeDirentry * ReadDir(class DirectoryHandler * dir) = 0; virtual int InsertDirectoryEntry (class Inode * where, class Inode * which, const char *szName) = 0; virtual int DeleteDirectoryEntry (class Inode * where, const char *szName) = 0; virtual bool IsDirectoryEmpty ( class ISuperblock * sb, class Inode * inode_dir) = 0; // Symbolic link service virtual int MakeSymbolicLink(class Inode * inode, const char * szLinkTo) = 0; virtual class Inode * GetSymlink (class IProcess * process, class Inode * node) = 0; // File system name and status service virtual int StatFS( statfs_t * stat) = 0; virtual char * GetFileSystemName(void) = 0; // Prepare Inode to read/write operations virtual int OpenNode(class Inode * inode) = 0; virtual int CloseNode(class Inode * inode) = 0; // Disk blocks service virtual block_t AllocateFreeBlocks(int nCount) = 0; virtual int ReleaseBlock(block_t nBlock) = 0; virtual block_t GetBlockNumberByIndex(class Inode * node, unsigned int nIndex) = 0; virtual int SetBlockNumberByIndex(class Inode * node, unsigned int nIndex, block_t nBlockNumber) = 0; virtual int TruncateInode(class Inode * inode, unsigned int nSize = 0) = 0; // Flushing service virtual int FlushInode( class Inode * inode, FlushMode_t flush_mode ) = 0; virtual int FlushDiskCaches(void) = 0; // File path service virtual int GetCurrentDirectoryName (class IProcess * process, class Inode * inode, char * dirname) = 0; };
Т.е. "движок" любой файловой системы должен "всего-лишь" поддерживать этот интерфейс для предоставления POSIX совместимого сервиса. Естественно, напрашивается желание завернуть всё это в некое подобие COM технологии, наследовать от IUnknown или, тем паче, от ISubject... Но это в перспективе, а пока от этого класса наследованы MinixSuperblock, Ext2Superblock, IsoSuperblock, FatSuperblock и DevSuperblock. Последний - это суперблок для devfs, файловой системы, где регистрируются сервисы (в т.ч. драйвера устройств).
Как это ни странно, наиболее сложной оказалась поддержка FAT. Дело в том, что в файловая система FAT не использует Inode. Это было бы полбеды, поскольку в момент чтения FAT директории, для каждого файла в оперативной памяти автоматически создаются inode. Такой же подход используется и в реализации IsoSuperblock и этот метод работает. Проблемы начинаются когда в FAT директории находится файл нулевой длины. Соответственно, у этого файла не имеется записи в таблице FAT и поэтому у пустого файла невозможно однозначно индексировать inode. Как ты уже понял, для ненулевого файла в качестве индекса inode брался номер первого элемента в таблице FAT. О, какая тафтология получается, но я верю, ты понял о чём речь. Пока я не придумал ничего лучше, чем использовать счётчик отрицательных чисел, для индексации inode, принадлежащих пустым файлам на FAT. И при записи первого байта в такую "виртуальную" inode, она автоматически переиндексируется номером первого элемента в таблице FAT.
Ох, чувствую, я тебя утомил излишними подробностями. Но если ты был внимателен, то у тебя возник закономерный вопрос: "А где же методы чтения и записи файла?".
Методы Read и Write реализованы в классе Inode. Каким образом удалось не задействовать класс Superblock для чтения и записи файлов? Таки он задействован. Дело в том, что ISuperblock предоставляет целых три метода, которые используются при чтении/записи файлов. Вот они:
block_t AllocateFreeBlocks(int nCount);
block_t GetBlockNumberByIndex(class Inode * node, unsigned int nIndex);
int SetBlockNumberByIndex(class Inode * node, unsigned int nIndex, block_t nBlockNumber);
Например, при чтении файла с некоторой позиции, мы сначала находим индекс блока для чтения по формуле: index = position / block_size, а затем получаем логический номер блока посредством метода GetBlockNumberByIndex. И уже полученный номер мы передаём в качестве аргумента методу ReadBlock интерфейса IBlockDevicePartition. Запись происходит по аналогии, только номер блока для записи предварительно возвращается методом AllocateFreeBlocks. Разумеется, методы выделения свободных блоков и индексации уникальны для каждой файловой системы. Помимо этого, не каждая файловая система обязана поддерживать все методы интерфейса ISuperblock. К примеру, методы AllocateFreeBlocks и SetBlockNumberByIndex не имеют смысла для файловой системы ISO и должны возвращать код ошибки EROFS (файловая система только для чтения). Другой пример - методы MakeSymbolicLink и GetSymlink не имеют смысла для файловой системы FAT, поскольку она не поддерживает символические ссылки. Соответсвенно, заглушки этих методов должны возвращать код ошибки ENOSYS.
В заключение хотелось бы сказать, что вышеприведённый интерфейс окончательно не устоялся. За время написания этого поста я обнаружил несколько мест, которые возможно и необходимо оптимизировать. Другой вопрос, я пока не уверен, что в эту схему красиво ложатся файловые системы, которые умеют хранить несколько файлов в одном логическом блоке. Хотелось бы услышать мнение экспертов по этому поводу.
Алексей Мандрыкин
7 ноября 2007 года