Глава 2. Проучвания на възможностите за реализиране на журнална файлова система за Hurd

Съдържание

2.1. Изисквания към файловите системи за UNIX
2.2. Исторически преглед на файловите системи за UNIX
2.2.1. Първо поколение файлови системи
2.2.2. Второ поколение файлови системи
2.2.3. Трето поколение файлови системи
2.2.4. Четвърто поколение файлови системи
2.3. Обзор на журнални файлови системи
2.3.1. Принципи и предимства на журналните файлови системи
2.3.2. Структури от данни, използвани във файловите системи от трето поколение
2.3.3. Журнални файлови системи с отворен код
2.3.3.1. UFS2
2.3.3.2. ext3
2.3.3.3. reiserfs3
2.3.3.4. reiserfs4
2.3.3.5. XFS
2.3.3.6. JFS
2.3.3.7. Обобщение
2.3.4. Реализация на файлови системи под Linux
2.3.5. Обобщение
2.4. Журнална файлова система ext3
2.4.1. Файлова система ext2
2.4.1.1. Конкретна структура
2.4.1.2. Заделяне на блокове и файлови описатели
2.4.2. Разлики между ext2 и ext3
2.4.3. Структура на журнала на ext3
2.4.3.1. Основи
2.4.3.2. Обща структура
2.4.3.3. Освободени блокове
2.4.3.4. Физическа структура
2.4.4. Обобщение
2.5. Архитектура на Hurd
2.5.1. Микроядрото Mach
2.5.1.1. История
2.5.1.2. Понятия
2.5.1.2.1. Нишки
2.5.1.2.2. Портове
2.5.1.2.3. Адресно пространство
2.5.1.3. Remote Procedure Calls
2.5.1.3.1. Интерфейси на GNU Mach
2.5.1.4. Обобщение
2.5.2. Файловите системи в Hurd
2.5.2.1. Модел
2.5.2.2. Реализиране
2.6. Ограничение от 2G за файлова система в Hurd
2.7. Обобщени изводи

Още в началото на развитието на UNIX се е установил определен модел на файловите системи за UNIX. Сега много от идеите на този модел се считат за естествени, но това е само потвърждение на сполучливостта на този модел.

Смисълът на файловите системи е да съхраняват данни. За основна единица се приема файлът, който представя данните като последователност от байтове. Интерпретацията на тази последователност от байтове е оставена изцяло на програмите, които използват файловете. Освен данни, файловете включват и метаданни, като например кой е собственикът на файла (т.е. данните), кой има право да чете данните и/или да ги променя и т.н. Тези метаданни се наричат файлов описател (inode), който включва и информация как да се стигне до същинските данни. Особеност на файловия описател е, че не съдържа името на файла. Обръщението към файлов описател е чрез номер на файлов описател.

За да могат програмите да намерят някой файл, те трябва да го търсят в директория. Директорията е списък от имена на файлове и номера на файловите им описатели. Това позволява един и същи файл да бъде в няколко различни директории в рамките на една файлова система. Директорията също е файл, но отбелязан като специален, понеже данните в този файл се интерпретират по точно определен начин от файловата система и програмите обикновено не могат пряко да променят тези данни.

Пълният потребителски поглед върху файловата система се получава, когато отбележим една директория за коренова директория на файловата система. В тази директория може да има други директории, като по този начин се получава едно дърво, в което обикновените файлове са листа, а вътрешните възли, както и някои листа, са директории. С цел удобство всяка директория съдържа две по-особени директории. Поддиректорията . (точка) сочи същата директория, т.е. използва номера на файловия описател на самата директория. Поддиректорията .. (две точки) сочи родителската директория.

Всичко това е образът на една файлова система. В UNIX обаче към директориите могат да се монтират други файлови системи, като по този начин точката на монтиране е реална директория на едната файлова система, но всяко обръщение към нея се обработва все едно е обръщение към кореновата директория на монтираната файлова система.

Всички файлови системи се разполагат върху област от диска, обикновено наричана дял (partition). Тази област винаги се разделя на равни по големина блокове. Една от характеристиките на всяка файлова система е големината на блока й.

Представители на първото поколение файлови системи са SysV, Xenix, Coherent и Minix. Те са със сравнително проста структура. Големината на блока е 512 или 1024. Първият блок е запазен за програмата за начално зареждане (bootloader). Вторият блок е главният блок (superblock) и съдържа важна информация за цялата файлова система. Както файловият описател съдържа информация за данните, така и главният блок съдържа информация за файловата система като цяло. Следват последователност от блокове, където се съдържат само файлови описатели. Още при създаването броят на файловите описатели е определен и не може да се променя по-късно. Остатъкът от дяла се заема от данните на файловете.

Файловите описатели трябва да съдържат и номерата на блоковете, където са данните. Понеже в UNIX още от начало се насърчава използването на малки файлове, номерата на първите 10 блока от данните на файла са записани във файловия описател. Ако това не стигне, използва се допълнителен блок от пространството на файловата система за данни, където да се запишат номерата на останалите блокове от файла. Такъв блок се нарича косвен блок. Ако това не стигне, добавя се още един косвен блок, който обаче съдържа номера на още косвени блокове, а не номера на блокове с данни. Такъв блок се нарича двоен косвен блок. Ако и това не стигне, използва се троен косвен блок.

Създаването и изтриването на файлове, както и допълването и отрязването на данни, изискват подходящи структури от данни, за да могат да се заделят и освобождават както файлови описатели, така и блокове от данни. За тази цел в главния блок има кратък списък на номера на свободни файлови описатели. Ако този списък се изчерпи, чрез линейно търсене се запълва пак. Колкото до свободните блокове, в главния блок има номер на свободен блок. Първите байтове на този свободен блок сочат друг свободен блок, и така всички свободни блокове са в един голям едносвързан списък.

В края на 70-те и началото на 80-те в университета в Бъркли (САЩ) правят много промени в тяхното копие на UNIX-а, закупен от AT&T. Една от тези промени е нова файлова система, наричана Fast File System (FFS), която е производна на файловата система SystemV. От нея се заражда Unix File System (UFS) на Berkley Software Distribution (BSD), която файлова система се използва и до днес във FreeBSD, NetBSD, OpenBSD, NeXTStep[1] и Solaris.

Причината за промяната на оригиналната файлова система SystemV е в наблюдението, че често данните и метаданните са разпиляни по целия диск, а времето за придвижване на главата на твърдите дискове е голямо. Решението е файловата система да се раздели на цилиндрови групи и винаги да се прави опит данните и метаданните на файл да бъдат в една цилиндрова група. Така пак може да има местене на главата, но се прави всичко възможно това местене да е най-малко.

Ext2 е много близка до UFS. По времето на създаването на Ext2 (началото на 90-те) е нямало реализация на UFS, която да е свободен софтуер. По това време BSD и AT&T се съдят за кода на BSD. Затова е взет кода на Minix, той е преправен и използвайки идеи от UFS, например цилиндрови групи, е създадена Ext2. По-късно ще бъде разгледан по-подробно формата на Ext2.

Както второто поколение файлови системи адресира бавното местене на главите на дисковете, така третото поколение адресира многократно нарастналите обеми на дисковете. Проблемните изисквания са следните:

  • Големи файлови системи, изискващи повече от 32 бита за адресиране.

  • Големи файлове, за които също не са достатъчни 32 бита. Това включва и поддръжката на файлове с дупки (sparse files), бързо заделяне на голямо количество последователни блокове за данни на файлове и бърз достъп до всяка част от данните, обикновено чрез използване на отрязъци (extents).

  • Голям брой файлове в една директория. При стотици хиляди файлове линейното търсене е значително бавно.

  • Голям брой файлови описатели. За целта обикновено се поддържа динамично заделяне на файлови описатели.

  • Промяна на големината на файловата система, като способността да се извърши това действие върху монтирана файлова система е плюс.

  • Журнална файлова система. След неочаквано спиране на операционната система, например при спиране на захранването, всички метаданни на файловите системи трябва да се обходят, за да се провери дали има несъгласуваност. При големи файлови системи времето за това е значително и решението е добавяне на журнал за всички действия.

През 2000 година за ядрото Linux се появиха 4 файлови системи, които малко или много се справят с тези изисквания: Ext3, reiserfs, XFS и JFS. Подробностите за всяка една от тях следват по-надолу.

Всички операции върху файлове като правило „докосват“ много места на файловата система. Тези промени на различни места по необходимост трябва да се извършат последователно – съвременните твърди дискове нямат способност да приемат заявка от типа: „запиши тези няколко разпръснати блока едновременно“, винаги има явна или неявна последователност. Проблемът се явява, ако по някаква причина, например спиране на тока, се прекъсне променянето на тези различни места. Тогава част от промените остават записани, а друга част остават незаписани. Появява се несъгласуваност (inconsistency) на файловата система. В компютрите обаче работата с такава несъгласувана файлова система почти сигурно води до още по-неприятни последици. Затова при зареждане на операционната система се проверява дали файловата система е затворена нормално. Ако не е, значи трябва да направим пълна проверка на всички метаданни на файловата система, за да бъдат поправени, доколкото може, всички несъгласуваности. Всяка файлова система се придружава и със съответната програма, която прави тази проверка. В UNIX тези програми се наричат под общото име fsck.

От друга страна, обемът на твърдите дискове расте стремително. Фантастични обеми в миналото са долна граница на дисковете на съвременните компютри. Това се превръща в проблем, когато трябва да се провери съгласуваността на някоя файлова система. Например метаданните на файловата система ext2 са приблизително 1%. Това са 1G от 120G файлова система. Значи fsck трябва да премине през 1G само за да открие какво е развалено от незавършената операция.

Елегантно решение на този проблем е използване на транзакции, които са развити доста в базите данни. Транзакцията е поредица от операции, които или трябва всички да се изпълнят, или никоя да не се изпълни. В случая под „операция“ разбираме промяна в блок от файловата система. За целта трябва да имаме предварителен пълен списък на промените, без те да са записани на съответните места във файловата система. Тези промени се записват само в журнал. След като са записани там, файловата система се променя според промените.

Смисъл на всичко това проличава, когато имаме непредвидено прекъсване на системата. Ако прекъсването е по средата на записването на промените в журнала, ние имаме напълно съгласувана файлова система, защото самата файлова система все още не е докосната от тези промени. Ако прекъсването е по средата на прилагането на промените върху файловата система, имаме несъгласувана файлова система. Обаче ние имаме пълния списък на промените в журнала. Простото повтаряне на тези промени от началото до края ни гарантира, че ще получим съгласувана файлова система.

В общи линии това е действието на всеки журнал. Проверката на съгласуваността на цялата файлова система се заменя с проверка дали има транзакции в журнала, чиито промени да се приложат във файловата система.

В базите данни свойствата на транзакциите се обобщават съкратено като ACID (Atomicity, Consistency, Isolation, Durability). Atomicity изисква атомарност, неделимост на промените. Или цялата промяна трябва да се извърши, или нищо от нея. Consistency изисква транзакцията да остави структурите от данни в напълно съгласуван вид. Isolation изисква всяка от едновременно работещи транзакции да вижда данните в напълно съгласуван вид. Обикновено транзакциите във файловите системи са последователни, така че те лесно се справят с това изискване. Durability гарантира, че при завършек на транзакцията ефектът й ще остане и при внезапен срив на системата.

Решетка на заетостта

Решетката (bitmap) е вектор от битове, в който всеки бит показва дали ресурсът е зает или свободен. Възможните ресурси са блокове и файлови описатели. Като правило, решетката се избягва в новите файлови системи, защото забавя заделянето на ресурси.

Косвени блокове

Косвените блокове са обяснени в Раздел 2.2.1, „Първо поколение файлови системи“.

Отрязъци

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

B+ дървета

B+ дърветата са вариант на B-дърветата, които са балансирани дървета, оптимизирани за съхраняване върху външен носител, а не в паметта. B+ дърветата са много по-бързи от използването на списъци.

По-надолу са представени журнални файлови системи, които са кандидати за реализация за Hurd. Всички те са с отворен код, което позволява изучаване в най-големи подробности на файловата система. UFS не е истинска журнална файлова система, но реализира интересен подход към проблема с възстановяване насъгласуваността на файлова система след внезапен срив на системата. Останалите 4 файлови системи са в Linux и са включени там по едно и също време – 2000 година. Всички поддържат ACLs (Access Control Lists) и квота.

Въпреки че файловата система UFS няма журнал, за нея е разработена специална технология, наричана Soft Updates[softdep]. При нея промените върху метаданните на файловата система се изпълняват в точно определен ред, така че при срив на системата да има минимални разрушения, а също програмата за възстановяване на съгласуваността да е максимално улеснена. В допълнение, FreeBSD поддържат фонова проверка на файлова система[bg-fsck], при която файловата система се монтира и й се прави „снимка“. Програмите продължават да работят върху действителната файлова система, а снимката се проверява за грешки.

Големите обеми на файловите системи са решени в UFS чрез създаването на UFS2, която е част от FreeBSD и NetBSD от 2003 година. Единствените промени в UFS2 спрямо UFS са следните:

  • Навсякъде се използват 64-битови полета за номера на блокове и файлови описатели, вместо 32-битови полета. Като резултат, файловият описател нараства два пъти и стига 256 байта.

  • Малки промени в подреждането на метаданните, за да се поддържат по-добре блокове с променлива големина, както и „отрязъци“ в бъдеще.

  • Поддръжка на разширени атрибути (Extended Attributes) на файлов описател. Това включва списъците за контрол на достъпа (Access Control Lists, ACL).

  • Забавено инициализиране на файлови описатели, което прави създаването на файлова система по-бърза операция.

Интересно е, че големите обеми не са били причината да се започне разработката на UFS2. Целта е била да се поддържат разширени атрибути за файлови описатели. Тогава се е решило, че е добре да се добавят гореизброените промени към UFS2.

Това е една от най-обсъжданите файлови системи за Linux. Проектът reiserfs има далечни цели, които ще позволят използването на файловата система да бъде толкова ефективно, колкото и база данни. Използването на много малки файлове (например от по няколко байта), разхвърляни в дълбока йерархия от директории, е доста бързо.

Технически reiserfs има няколко важни особености. Директорията съдържа не само имената на файловете, но и цялата метаинформация за тях. При това файловете са подредени в дърво, така че намирането на файл е много бързо. Присъствието на метаинформацията в дървото означава, че веднъж като се намери, можем направо да започнем да работим с файла, без да се обръщаме допълнително към друго място на диска, откъдето да взимаме метаинформация.

Важно подобрение е специалното отношение към опашките на файловете. Всяка файлова система разделя пространството си на равни по големина блокове. Когато се запазва файл, последният блок почти винаги съдържа празно пространство, просто защото големината на файла не се дели точно на големината на блок. Данните на файла в последния блок се наричат опашка на файла. Във файловата система reiserfs опашката на блока се записва в метаинформацията, т.е. в директорията. Това позволява още по-голямо ускоряване на достъпа, особено за малки файлове. Излиза, че с достъпа до файл, по-малък от блок, вече е прочетен и самият файл!

Както виждаме, reiserfs ще е особено силен, когато директориите съдържат голямо количество файлове, също и когато файловете са малки. Но даже и да не е така, reiserfs е много бърза.

В сайта на XFS тя е определена като високопроизводителна журнална файлова система. Целите при конструирането на тази файлова система са били различни от тези на reiserfs. Тя е създадена с мисъл за мащабируемост на капацитета и производителността, поддържайки много големи файлови системи. Това включва боравене с много големи файлове, голям общ брой файлове, големи директории и високопроизводителна входно-изходна система.

За тези цели се стъпва силно върху употребата на B+ дървета. Те се използват за следното:

  • Директориите са B+ дървета на файлове.

  • Отрязъците с блокове на всеки файл се държат в B+ дърво.

  • Индексите на динамично заделените файлови описатели са B+ дърво.

XFS е напълно 64-битова: за брой на блоковете и файловете например. Обаче пространството е разделено на allocation groups, където за някои неща се използват локални 32-битови числа. Тези allocation groups се използват и при промяна на големината на файлова система.

За висока производителност е обърнато внимание на следните неща:

  • Стремеж блоковете на файловете да бъдат последователни. Фрагментираността води до забавяния. Използва се предварително заделяне на последователност от блокове, още преди те да бъдат поискани от програмите, блоковете на файла се представят групирани в отрязъци (extents), големината на блок във файлова система може да достигне до 64K.

  • Групиране на четенето и записването на последователни блокове, което понякога ускорява много операциите. При четенето това подразбира и четене на блокове, които се очакват да се поискат.

  • Преки входно-изходни операции (Direct I/O), които позволяват блоковете да се записват направо от адресното пространство на програмите, без да се минава през ядрото.

  • Оптимизации за случая, когато много файлове четат и пишат в един файл. Това включва по-пестеливо използване на заключване и отключване на структурите от данни на файла в ядрото.

Поддържат се гарантирана производителност на входа/изхода за файлове (Guaranteed-Rate Input/Output, GRIO). Това се използва например при видеобработка. Тази поддръжка обаче не е прехвърлена в Linux порта.

През 2000 година 4 журнални файлови системи влизат като част на ядрото Linux. Всяка от тях е създадена поради специфични нужди. По-долу е представено таблица с основните свойства на всяка от файловите системи.

Забелязва се, че XFS и JFS си приличат. Измежду основните цели на UFS2 и ext3 е обратната съвместимост и това изпъква на фона на останалите файлови системи. Въпреки че в тази таблица reiserfs3 и reiserfs4 са представни с едни и същи свойства, reiserfs4 е забележимо по-добра във всяко отношение.

За разбиране на файловите системи под Linux е нужно да бъде ясно как програмите работят с файлове. Програмите се обръщат към ядрото чрез системни извиквания. Те са ограничен брой, обикновено 100–200, и трябва да обхващат всичко, което една програма може да поиска от ядрото. За програмите това са обикновени извиквания на функции.

Определена част от тези системни извиквания се отнасят до файлове. Именно те трябва да могат да бъдат обработвани от всяка от файловите системи. Само че в самите системни извиквания няма информация за коя файлова система става въпрос. Затова по средата стои виртуалната файлова система (Virtual File System, VFS). Тя поема заявките за файлове и ги пренасочва към кода на съответната файлова система. В UNIX „файл“ може да означава много неща – мрежова връзка, генерирано от ядрото съдържание (например файловете в /proc) и др. VFS поема общите части в заявките към тези файлове, като се грижи и за структурите от данни в ядрото, свързани с файловите системи.

Основните обекти, с които борави VFS, са: файлова система, файлов описател и отворен файл. Всички системни извиквания, касаещи VFS, в крайна сметка се отнасят до точно един тип обект измежду тези трите. Всеки такъв обект се поддържа от VFS, но в данните за него има и вектор на функции, които изпълняват конкретни действия, като например четене на байтове от отворен файл. Затова всички отворени файлове са като една голяма група от един и същ клас (VFS) обекти, но векторите им на действията сочат към различни файлови системи, както при виртуалните методи на клас в езика C++.

Над файловата система е VFS, която чрез векторите на действията вика файловата система. Отдолу е кешът на буферите, чрез който файловата система чете и променя самата файлова система. Кешът на буферите има две основни функции: взимане на буфер, съдържащ определен блок от файловата система, и връщане (записване) на този буфер. Използването на кеш на буфер дава огромно ускорение и удобство в сравнение с това файловата система пряко да работи с дискове. Прякото взаимодействие с дискове е оставена на кеша.

Пространството в заделения за файловата система дял се използва за два основни вида данни: данни и метаданни. По необходимост всяка операция с файл променя няколко блока от метаданните и няколко блока от данните. Естествено, това става в някакъв ред и ако, например, внезапно спре захранването на компютъра, операцията ще бъде наполовина изпълнена и файловата система ще остане в несъгласувано състояние. Затова такива случаи се отбелязват и при пръв удобен случай файловата система се проверява за всевъзможни грешки, резултат на такива наполовина свършени операции. Изискванията на потребителите обаче, и малки, и големи, са тези бавни проверки за грешки на файловите системи да се избегнат максимално, а ако може – и въобще да изчезнат. Това се постига с използването на някакъв вид транзакции, както в базите данни. Общото наименование на такива файлови системи, които използват транзакции, е журнална файлова система. Името идва от използването на журнал, в който се записват всички промени още преди те да се извършат в действителност. След това се извършват и самите промени. Ако по средата на промените процесът се прекъсне, промените се извършват по-късно, по време на възстановяването, според информацията в журнала. И така, ext3 не е нищо друго, освен ext2, но с добавен журнал. Това е направено съвсем съзнателно с цел максимално да се използва кода на стабилната и добре тествана файлова система ext2. Друга причина е възможността ext2 да чете и пише върху файлова система ext3, което е било важно за много потребители.

Файловата система ext2 е второ поколение файлова система. Дялът за ext2 се разделя на блокови групи, всяка от които има собствени файлови описатели, блокове за данни, решетки за свободните файлови описатели и блоковете за данни.

Главният блок се намира във втория 1024-байтов блок на дяла. Там се съдържат основните данни за файловата система като големина на блока, брой на блоковете и файловите описатели, брой на свободните блокове и файлови описатели. Понеже трябва все пак през определено време да се проверява файловата система, поддържат се брояч на монтиранията, време на последната проверка, както и настройка при какъв брой на монтиранията или след колко време да се извърши нова проверка.

От първия блок веднага след главния блок започват описателите на всички блокови групи. Всеки описател съдържа информация колко е голяма блоковата група, броя на всичките и свободните блокове и файлови описатели в тази блокова група, както и в кои блокове са решетката на свободните блокове и решетката на файловите описатели.

В началото на всяка блокова група винаги стоят главен блок и описатели на блоковите групи. Обаче промяната им винаги е само в първата блокова група, освен ако при монтирането на файловата система не е зададено друго. В останалите информацията не е актуална и съвпада само в непроменливите части. Например във всички копия на описателя на дадена блокова група има верен общ брой на блоковете, но само в един описател ще има актуален брой на свободните блокове. Цялото това излишество на важна метаинформация е за да може в случай на разваляне на първата блокова група, все пак използвайки останалите блокови групи, да се възстанови донякъде файловата система. Неактуалността на променливата метаинформация не е толкова важна, защото тя може да се възстанови. Например броят на свободните блокове може да се възстанови просто като се преброят тези блокове в решетката на свободните блокове.

В блоковата група са нейните решетка на свободните блокове, решетка на свободните файлови описатели и масив от файлови описатели. Решетката на свободните блокове и файлови описатели са от по един блок, в който всеки блок или файлов описател от блоковата група е представен от един бит, показващ дали е свободен или не. Разположението на тези решетки изцяло се определя от описателя на блоковата група.

Почти всичко от информацията във файловия описател може да се получи от програмите чрез системното извикване stat. Във файловия описател обаче информацията е представена в един по-„разпръснат“ вид. Например големината на файла се съдържа в две 32-битови полета, i_size и i_size_high, които съвсем не са едно до друго физически на диска. Това е резултат от стремежа за съвместимост с по-старите версии на ext2.

От полетата, които не се връщат от stat, най-важен е векторът с номерата на блоковете с данни. Първите 12 номера са на първите 12 блока с данни. 13-ият номер е на косвен блок, който изцяло е вектор с номера на блокове с данни. 14-ият номер е на косвен блок, който изцяло е вектор с номера на косвени блокове, приличащи на 13-ия. 15-ият номер е на косвен блок, който изцяло е вектор с номера на блокове, приличащи на 14-ия. По този начин могат да бъдат представени наистина много големи файлове, но колкото е по-малък файлът, толкова по-малко ще заема и метаинформацията за него. Номер 0 на блок означава празен блок, който трябва да се запълни с нули, ако се изиска. Това са така наречените дупки във файла. Те не са нещо специфично за ext2, а се поддържат от почти всички файлови системи за UNIX.

Алгоритмите за заделяне са важна част от ext2. Принципът е блоковете и файловите описатели да са близо един до друг, ако се очаква да се използват заедно. По отношение на файловите описатели това значи файловият описател да е в същата блокова група, където е и съдържащата го директория. По отношение на блоковете функцията за заделяне на нов блок приема като параметър блок-цел, около която да се задели новия блок. Първо се пробва дали има свободен блок малко по-напред, после малко по-назад, след това изобщо някакъв блок в същата блокова група, и чак когато всичко е неуспешно, се търси из другите блокови групи. Едно от усъвършенстванията е предварителното заделяне на блокове. Когато се заделя блок, с това се заделят по специален начин и следващите 7 блока. При следващото искане на блок направо се връща вече заделения следващ блок.

Ext3 е създадена така, че реализация на ext2 да може да чете и пише върху файлова система ext3. По отношение на дисковия формат, файловата система ext3 има просто добавен един скрит файлов описател за журнала си. Освен този скрит файлов описател, единствената друга разлика е, че има флаг (EXT3_FEATURE_COMPAT_HAS_JOURNAL) за наличието на този файлов описател на журнала. Когато ext2 чете и пише върху такава файлова система, журналът просто се игнорира. Обаче правилата са такива, че ако в журнала има недовършени транзакции, ext2 няма да монтира файловата система.

Има и други добавки, които не са включени в реализацията за Hurd. Това са например хеширането на директориите и списъците за контрол на достъпа (Access Control Lists, ACL). И двете са реализирани по същия начин – ext2 може да монтира и променя такава файлова система, като просто няма да вижда разширенията.

Обикновено журналът се използва с модифицирана логика. Вместо да се записват и данни, и метаданни, само метаданните се слагат в журнала. Това ускорява журналната файлова система, като тя пак си остава съгласувана. Но съгласувани остават метаданните, не и данните. В повечето случаи това е приемливо, защото една от основните цели на журнала е да се избегне проверяването на файловата система след всяко неочаквано прекъсване на системата. Това проверяване е изцяло за метаданните, но не и за данните.

Обаче с тази промяна има един случай, при който може да има разваляне на данни. Той може да се прояви само ако в един момент блок, който е бил с метаданни, се използва за данни. Такива метаданни са косвените блокове (indirect blocks), които съдържат номерата на блокове с данни или на други косвени блокове. Когато някой файл се изтрие или се смали, такива косвени блокове могат да бъдат освободени и по-късно да бъдат използвани за данни. Ако системата обаче се прекъсне, в журнала може да има транзакции, които да презапишат стария косвен блок върху новите данни. Нека проследим постъпково какво се случва:

  1. В транзакция x в косвен блок y се записва нещо.

  2. По време на транзакция x+1 косвен блок y е освободен.

  3. В транзакция x+2 блок y е използван за данни. Според правилата на журнала, данните се записват преди да е завършил предварителния запис.

  4. Транзакция x+3 е прекъсната. Същинският запис на всички транзакции от x нататък не е завършен.

  5. При преиграването на транзакция x върху блок y се записва съдържанието на стария косвен блок, по този начин изтривайки новите данни в блока.

Това положение може да се постигне и с по-малко транзакции, но тук са представени повече за по-голяма яснота.

Това е еднственият проблем, свързан с отделянето на данните от журнала. Решението е просто – поддържа се списък на освободените блокове (revoked blocks) и те не се преиграват. Точният алгоритъм се състои от следните правила:

  • Транзакциите, освен предварителен запис на блокове с метаданни, съдържат и списък с освободени блокове. В този списък са блоковете, които са освободени в съответната транзакция.

  • При преиграване преди всичко друго се преминава през всички транзакции и се прави краен списък на освободените блокове. За да влезе блок в този краен списък, трябва да са изпълнени следните условия:

    1. Блокът да е в списъка на освободените блокове на някоя транзакция.

    2. Блокът да отсъства от предварителните записи на всички транзакции, които са след транзакцията, в която е обявен за освободен.

След като имаме крайния списък на освободените блокове, по време на преиграването на предварителните записи никога не са записва върху тези блокове. Това е и причината за второто условие – един освободен блок може да се използва по-късно пак за метаданни. Тогава въпреки че в по-ранна транзакция е обявен за освободен, този блок трябва да се включи в преиграването.

Всички останали описания на блокове започват с общото начало.

Таблица 2.3. Главен блок

Тип Поле Описание
uint32_t s_blocksize Големина на блок
uint32_t s_maxlen Общ брой на блоковете
uint32_t s_first Първият блок, който може да се използва за транзакции
uint32_t s_sequence Номер на първата транзакция за преиграване. Всички номера под този ще се игнорират.
uint32_t s_start Номер на блок от журнала, откъдето да започне преиграването.
int32_t s_errno Номер на грешка при абортиране на журнала
uint32_t s_feature_compat Обратно съвместими възможности
uint32_t s_feature_incompat Обратно несъвместими възможности
uint32_t s_feature_ro_compat Обратно съвместими възможности, но само за четене
uint8_t s_uuid[16] 128-битов UUID на журнала
uint32_t s_nr_users Брой на файловите системи, които си споделят този журнал
uint32_t s_dynsuper Номер на блок на динамичното копие на главния блок
uint32_t s_max_transaction Граница на журналните блокове за транзакция
uint32_t s_max_trans_data Граница на блоковете с данни за транзакция
uint8_t s_users[16*48] UUID на файловите системи, които си споделят журнала

Самият журнал представлява последователност от транзакция. Смесени са revoke и descriptor блоковете, а последният блок е commit блокът. Без commit блок цялата транзакция е невалидна.

Самият descriptor блок има само обща част и последователност от тагове на блокове с данни.

Последният таг има флаг JFS_FLAG_LAST_TAG. Всеки таг трябва да има UUID на файловата система, на който е блокът. Това UUID се записва в 16 байта веднага след тага. Ако то е същото като на предишния таг, може просто да се използва флага JFS_FLAG_SAME_UUID и да не се добавя след тага.

Веднага след descriptor блока има по един блок за всеки таг. Там е записано новото съдържание на блока от файловата система. Това съдържание не трябва да започва с магическата стойност JFS_MAGIC_NUMBER, защото иначе блокът ще бъдат разпознат като специален. В такъв случай там се записва 0 и това се отбелязва чрез флага JFS_FLAG_ESCAPE. При преиграване нулата ще се замени обратно с истинската стойност.

Revoke блоковете имат само едно поле r_count от тип uint32_t. След него следват толкова на брой номера на блокове, които са обявени като вече несъдържащи метаданни на файловата система.

От потребителска гледна точка задачата на операционната система е да осигурява среда за изпълнение на потребителските програми – приложенията. Освен библиотеки и програми, ОС се грижи и за разпределение на ресурсите на компютъра. В архитектурата на класическия UNIX, както и на всички негови производни и подобия, централно място заема ядрото. То стои между програмите и целия останал свят – други програми, файлова система, мрежа и т.н. Ядрата са много големи програми, които често имат способността да се добавя и изкарва функционалност от тях. Контрастирайки на тях, в Hurd ядрото е малко и затова се нарича микроядро.

Микроядрото Mach е първото популярно микроядро, което и до днес има силно влияние, макар и не в оригиналния си вид. Всичко започва през 1975 в Университета на Рочестър. Там е било демонстрирано как операционните системи могат да се построят на модулен принцип, като общуването между процесите се осъществява чрез предаване на съобщения. Тази абстракция на общуването позволява програмите да са на различни машини в мрежа, но самите програми да не знаят това. Това и не ги засяга, понеже това предаване на съобщения е абстракция, която скрива тези детайли.

Един от инженерите в проекта е бил Ричард Рашид. През 1979 той се премества в Университета Карнеги Мелон, където продължава изследванията си върху операционните системи за предаване на съобщения, и през 1981 излиза резултатът – системата Accent.

Системата е била популярна в CMU, но през 1984 е изместена от UNIX. Тогава Рашид решава да работи върху третото поколение на проекта, което той нарича Mach. Една от целите на Mach е да може да се използва огромното количество софтуер, написано за UNIX. Други подобрения спрямо предишните проекти са въвеждането на нишки, подобрено общуване между процесите, многопроцесорна поддръжка и съвременна система за виртуална памет. По това време DARPA (същите, които са финансирали реализацията на TCP/IP) търсят многопроцесорна операционна система и харесват Mach. Така проектът си осигурява финансиране и е добавена поддръжка на 4.2BSD направо в ядрото, правейки го доста голямо, но за сметка на това съвместимо с UNIX.

По това време започват проблемите на BSD с лицензите на AT&T, която е имала силно влияние тогава. Големи компании като IBM и DEC създават OSF (Open Software Foundation), опитвайки се да се справят с проблема. Те взимат Mach 2.5 и издават операционната система OSF/1. В крайна сметка този клон не става популярен, но има своето влияние в развитието на операционните системи. Ядрото Darwin на MacOS X е произлязло от OSF/1 и FreeBSD. Може би това е най-популярната операционна система в момента, използваща в крайна сметка Mach.

Решението на CMU относно лицензните проблеми е да отдели BSD функционалността в отделен процес на системата, изчиствайки по този начин Mach от всички лицензионни проблеми. Тази версия е 3.0 и излиза през 1989. Интересно е, че системата за виртуална памет на Mach по-късно е прехвърлена върху BSD. По-късно този клонинг се развива до UVM, който обаче не е върнат обратно в Mach.

От 1991 година Ричард Рашид започва работа в Microsoft. Много други разработчици излизат от университета и отиват да работят, и по този начин фактически разработката спира.

Развитието на Mach продължава в Университета на Юта, където през 1994 излиза Mach 4, която е и последната версия на оригиналния Mach. Там е пренаписана поддръжката на компютърната архитектура IA-32 (x86) на Intel, както и са включени драйверите на версия 2.0 на изгряващата звезда Linux. От Mach 4 е създаден GNU Mach 1, който се използва от Hurd. През това време в Университета на Юта, използвайки опита си с Mach, създават библиотеката за операционни системи oskit, където се използват драйверите на Linux 2.2. Започвайки като частен проект, през 1999 Роланд МакГрад променя GNU Mach да използва изцяло oskit. По-късно е обявено, че това ще е GNU Mach 2, но към настоящия момент този клон е нестабилен и не се развива от никой.

Mach използва някои понятия, които са по-бедни на съдържание от обикновено. Основната единица е задача, която обикновено се нарича процес, което е по-технически термин на програма или приложение. Задачата в Mach не е нищо повече от контейнер на останалите понятия: адресно пространство, нишки, портови права.

По-усложнени са портовете. Бидейки първото микроядро, в Mach предаването на съобщения е доста натоварен процес, за разлика от по-съвременни ядра от ново поколение, като L4 например. Порт е опашка от съобщения вътре в адресното пространство на Mach. Понеже е опашка, съобщенията се обработват в дисциплината FIFO (First-In First-Out). Единственият начин задача да се обърне към порт е да притежава портово право (port right), което, както и File Descriptor в UNIX, е едно число, което може да се използва само и единствено в задачата, на която е дадено портовото право. Портовите права, както и нишките, принадлежат на точно една задача.

Портовите права са няколко вида:

  • За добавяне (изпращане) на съобщение (send right)

  • За изваждане (получаване) на съобщение (receive right)

  • За еднократно добавяне (изпращане) на съобщение (send-once right) Веднага след първото добавяне на съобщение, портовото право автоматично става невалидно.

Само една задача може да притежава правото за изваждане на съобщения, но различни нишки вътре в тази задача могат да чакат получаване на съобщение. При появяване на съобщение, по случаен начин една от чакащите нишки получава съобщението.

Има и още един специален вид портови права, наричани портови групи. Те са група от портови права за изваждане на съобщения и не могат да включват портови права от друг вид. От портова група може само да се изваждат съобщения, като портът, от който ще се извади съобщение, не може да се определи предварително. Портовите групи служат за задачи-сървъри, които трябва да обработват заявките на много клиенти, които идват чрез много портове. Вместо да има поне по една нишка за клиент, може всички портови права за изваждане да се включат в портова група и тя да чака съобщения (заявки) от клиентите. За по-голяма отзивчивост на сървъра може няколко нишки да чакат съобщение от една портова група.

Всички обекти, разглеждани тук – задачи, нишки, адресни пространства, – се управляват чрез портове, или по-точно – портови права за изпращане. Например създаването на нишка в задача е изпращане на съобщение към портовото право за изпращане на задачата. По този начин една задача може буквално да има пълен контрол върху друга задача, ако притежава портови права към всички Mach обекти на задачата. Получаването на тези портови права обаче е изцяло във властта на задачата, която ще бъде управлявана.

Съобщенията могат да съдържат и портови права. При създаването на портовите права в задачата, която изважда съобщението, естествено числата на портовите права са различни и специфични само за новата задача. Освен копиране на портово право, може да се извършва и преместване на портово право, при което то допълнително се изважда от задачата, която изпраща съобщението. Друга способност на такова предаване на портови права е, че те могат да се трансформират. Възможностите са следните:

  • Портово право за изваждане се превръща в портово право за добавяне. Така обикновено сървърите предават портови права на клиенти.

  • Портово право за изваждане се превръща в портово право за еднократно добавяне. Понеже предаването на съобщения винаги е еднопосочно, чрез това превръщане се дава възможност на другата страна да даде еднократен отговор.

  • Портово право за добавяне се превръща в портово право за еднократно добавяне.

Като следствие на правилата за портовите права, следните ограничения са в сила:

  • При предаване в съобщение на портово право за изваждане, това трябва да бъде предаване чрез преместване, понеже не може две задачи да имат право за изваждане от един и същ порт.

  • При предаване в съобщение на портово право за еднократно добавяне, това трябва да бъде предаване чрез преместване, понеже иначе би се позволило към този порт да се добавят 2 съобщения, използвайки отначало само едно право за еднократно предаване на съобщение.

Всяка задача има точно едно адресно пространство, което е разделено на равни по големина страници, като големината им изцяло зависи от компютърната архитектура. Някои страници се намират във физическата памет, а някои въобще не се намират там. Така задачата може да си мисли, че има много памет на разположение, но колко от тази памет реално е готова за ползване е друг въпрос.

Страниците от адресното пространство винаги са отражение на нещо зад тях. Затова тези страници, които в момента са във физическата памет, се наричат кеширани страници и винаги са свързани с някой обект за памет (memory object). Тези обекти за памет доставят и връщат обратно страници. Отвъд тези операции, Mach не знае откъде се появяват и изчезват страниците. Подразбиращият се обект за памет просто записва страниците в swap мястото.

Обектите за памет не взимат никакви решения – те просто изпълняват заявките на Mach. Самите решения се взимат от микроядрото, което разполага с цялата физическа памет, т.е. с целия кеш за страници, и по този начин има пълен поглед кои страници се използват и кои – не. Тази информация помага в решенията кои страници да се изхвърлят („evict“) и кои да останат. При достъп до страница, която не е кеширана, т.е. не е във физическата памет, микроядрото спира съответната нишка, изпраща заявка към обекта за памет да даде страницата, изчаква и след като я получи, „монтира“ я във физическата памет и адресното пространство на нишката (т.е. адресното пространство на задачата на нишката) и продължава изпълнението на тази нишка. Дори може обектът за памет да върне грешка при доставяне на страницата и тази грешка да се предаде на задачата. Това става чрез изключенията на Mach, които са подобни на сигналите, използвани в UNIX.

Реализирането на обекти за памет обаче не е лесна работа. Трябва да се изпълни строгия протокол на Mach и да се вземат впредвид всякакви странични ефекти. Затова в Hurd е създадена специална библиотека за управление на обекти за памет – libpager. Използвайки тази библиотека, потребителят може да дефинира страницатор (pager), който използвайки потребителски функции за четене и записване на страници, да може да се използва като обект за памет. Всички детайли около взаимодействието с Mach са оставени на библиотеката. По този начин асоциирането на части от адресното пространство с някакъв постоянен склад за данни става лесна задача за всяка програма.

Това е използвано в оригиналния ext2fs, за да може да се асоциира целия дял на файловата система с непрекъсната област от адресното пространство. И понеже това пространство е ограничено от 2G[2], транслираните дялове могат да бъдат до максимум около 1.8G[3].

Системата за виртуална памет на Mach е важна част от цялостната среда, доставяна от микроядрото. Това е използвано и във формата на съобщенията. Както видяхме по-рано, освен масив от байтове, едно съобщение може да съдържа и предавани портови права. Освен тях допълнително съобщението може да съдържа и страници от адресното пространство на задачата, която е добавила съобщението към опашката на порта. Който изважда съобщението, получава и виртуални копия на тези страници, т.е. двете пространства съдържат страница, която отразява една и съща страница от даден обект за памет. Ако някоя от двете задачи промени страницата, микроядрото прихваща това и първо прави копие на страницата. По този начин много ефективно могат да се предават големи количества данни, използвайки съобщения.

Както и случая с обектите за памет, прякото програмиране чрез предаване на съобщения е трудна задача, изискваща много внимание и програмиране. Затова често се използва генератора на код MiG (Mach Interface Generator). Приликите с COM и CORBA не са случайни – имат общи корени. Мисленето при този подход се променя към обектно-ориентирано. Вместо да се предават съобщения, имаме манипулатори на обекти (object references). Чрез тези манипулатори програмите извикват методи на обектите. Списъкът на методите на един обект се нарича интерфейс. Ето пример за извикване на метод:

Първият аргумент на метода е манипулатора на обекта, който всъщност е порт. По традиция тези методи се наричат отдалечени извиквания на процедури (Remote Procedure Calls), защото отвънка изглежда просто като извикване на функция, но зад това извикване стоят много действия, докато заявката стигне до същинската функция в някоя друга задача или някоя друга машина и резултатът се върне.

Остава да разгледаме въпроса как така от клиентското извикване на io_read се стига до trivfs_S_io_read, включително и връщането на резултата обратно. Всичко това се изпълнява от генериран от mig код. Този код генерира две библиотеки: една за сървъра и една за клиенти. Всички подробности по приемането и изпращането на съобщения се съдържа в тези библиотеки. За целта обаче mig трябва да има подробна информация за отдалечените процедури. Тя се доставя от файл, написан на специален език за определяне на интерфейси (Interface Definition Language). Този език е специфичен за mig, както са специфични и езиците на COM и CORBA, които също се наричат IDL. Ето примерно описание на интерфейс, взето от hurd/io.defs:

Както вече беше казано, всичко в Mach се управлява чрез портове. С цел удобство обаче, интерфейсите към тези Mach обекти са стандартния начин да се извършат действия. Затова всички действия се извършват чрез RPC на mig.

Стандартни интерфейси на Mach

mach/bootstrap.defs

Използва се само от първата задача, пусната от Mach. Тази задача извиква единствения метод на интерфейса, за да вземе привилигерованите портове, с които може да се управлява всичко останало.

mach/default_pager.defs

Управление на подразбиращия се обект за памет, който обикновено използва swap дял.

mach/default_pager_helper.defs

Добавяне на място към подразбиращия се обект за памет.

mach/exc.defs

Предаване на Mach изключение към задача

mach/mach4.defs

Профайлинг на задачи.

mach/mach.defs

Управление на задачи, нишки, адресни пространства, портове и обекти за памет. Това е главният файл с интерфейси.

mach/mach_host.defs

Управление на процесора и политиката му по отношение на виртуалната памет („заковаване“ на страници) и нишките (например приоритети).

mach/mach_norma.defs

NORMA позволява съобщенията да се предават между задачи на различни машини, свързани в мрежа. Не се използва в GNU Mach.

mach/mach_port.defs

Допълнителни действия с портове.

mach/memory_object_default.defs

Специфични за подразбиращия се обект за памет методи.

mach/memory_object.defs

Управление на обекти за памет.

mach/norma_task.defs

Създаване на задача в среда на неизползваната от GNU Mach NORMA.

mach/notify.defs

Извиквани от ядрото потребителски процедури за уведомяване относно събития.

Mach е първият успешен опит за реализиране на операционна система, основана изцяло на предаване на съобщение. За съжаление системата за предаване на съобщения е доста сложна. Поставянето на BSD ядрото в адресното пространство на микроядрото е оставило своето неблагоприятно влияние върху сложността на Mach. Въпреки това Mach е използвано в много проекти на IBM, DEC, HP, NeXT и Apple, което е признание на новаторските му идеи. Например чрез Mach добиват популярност нишките, гъвкавото управление на адресното пространство, многопроцесорните и многокомпютърните системи, хетерогенни системи и разпределени системи.

С помощта на MiG предаването на съобщения между програми на C е сравнително лесна задача. Обаче проблемът с търсенето на портове, на които да се предават съобщения, не се решава от самото микроядро. Както ще бъде показано по-късно, за тази цел в Hurd се използва файловата система.

Може да се каже, че UNIX наложи дървовидното категоризиране на файлове, както е разпространено навсякъде сега. По този начин всички файлове са разпръснати в директории[4]. От своя страна директориите се водят като различен вид файлове, които са вътре в други директории. Една директория е по-специална: кореновата директория – началото на всички други директории.

Тази проста представа за файловете и директориите обаче не е достатъчна. В компютрите понякога има няколко диска, особено в сървърите, и всеки диск има по няколко дяла, които съдържат файлови системи. Затова някои директории на файловата система, така, както се виждат от потребителите, всъщност препращат към файлова система на друг дял. Тези директории се наричат точки на монтиране и е нормално в монтирана файлова система да има друга точка на монтиране. По този начин потребителят все още продължава да има единен поглед върху всички файлови системи на всички дялове, или поне тези, които са монтирани.

Концепцията за монтиране на файлови системи има интересни развития. Например не е задължително да се монтира дял от диск, а например споделена директория от друг компютър, чрез протокола NFS. Или пък направо FTP директория на някой сървър. А може и въобще да не става въпрос за физически файлове, а за генерирано съдържание, например /proc/123/cmdline в Linux съдържа командния ред, довел до стартирането на процес 123. Или стандартният начин за включване на маршрутизирането в Linux чрез командата echo 1 > /proc/sys/net/ipv4/ip_forward.

Продължавайки в същия дух, стигаме до понятието транслатор, както се употребява в Hurd. Транслаторът е директория или файл, заявките към който се изпълняват от обикновена програма, обикновено наричана сървър на транслатора. От своя страна в транслираната файлова система може да има още транслатори, които да препращат към сървъри.

Така получаваме една „разпределена“ файлова система, в смисъл, че обработката на файловите операции, като четене и запис например, се извършва между разпръснати процеси. Транслатори може да поставя всеки потребител, което значи, че един потребител може влезе в транслирана от друга потребител част от файловата система. Несъмнено това поставя нови предизвикателства пред безопасността на цялата операционна система, които още не са добре проучени.

Както беше посочено в началото, микроядрото осигурява само и единствено общуването между отделните потребителски процеси[5]. Затова голяма част от Hurd-а се заема от разнообразни библиотеки, които улесняват системното програмиране. Важните за нашето разглеждане са следните:

Библиотеки за правене на файлови системи

libstore

В Hurd под „store“ се обозначава това, което в UNIX-света се нарича „block device“. Само че store-ът не е задължително да съответства на дял от диска, а може да е, например, резултат от слепването на други два store-а.

libdiskfs

Реализира общите части на файловите системи, които са разположени върху диск. Не се използва за, например, споделени директории от мрежата.

libpager

Страницатор, който може да управлява части от адресното пространство на процеси. Микроядрото има интерфейс за това, но libpager доставя тази способност по много по-удобен за програмите начин.

Всички общувания между процесите в Hurd се осъществяват чрез интерфейси. В тях има изброени функциите и техните входни и изходни параметри. Описват се във файлове с разширение defs. Следните интерфейси се използват пряко във файловите системи:

Интерфейси, използвани във файловите системи

fsys.defs

Управление на файловата система като цяло, например чрез fsys_startup и fsys_syncfs.

fs.defs

Операции върху файловете като цяло, като например file_exec и file_chmod.

io.defs

Основни входно-изходни операции с отворен файл, например io_read и io_select.

Всички тези интерфейси се реализират от libdiskfs и са точно отражение на основните единици във виртуалната файлова система (VFS) в Linux. Библиотеката се опитва да свърши максимално работа, използвайки callback функции на програмата. От своя страна програмата се очаква да е получила store, който да достъпва с libstore.

Примерни callback функции[6], използвани от libdiskfs за промяна на списъка на файловете в директория.

diskfs_lookup_hard

Търси име на файл в директория. Задава се явно какво ще се прави с този файл, т.е. коя от следващите функции се очаква да се извика. В резултат се получава манипулатор[7], който се използва като параметър на функциите по-долу.

diskfs_drop_dirstat

Затваря върнатия от diskfs_lookup_hard манипулатор. Използва се, когато първоначалното намерение за употребата на манипулатора не може да се осъществи поради странични причини.

diskfs_null_dirstat

Връща такъв dirstat, който да се игнорира от diskfs_drop_dirstat.

diskfs_direnter_hard

Добавяне файл.

diskfs_dirrewrite_hard

Преименуване на файл.

diskfs_dirremove_hard

Изтриване файл.

Понеже кодът е писан около 1995–1996, целият store на файловата система се изобразява (memory mapping) върху адресното пространство на сървърския процес за файловата система. Заявките към това адресно пространство се управляват чрез libpager, който от своя страна работи със store-а. И понеже цялото адресно пространство е 32-битово, имаме 4G, половината от които обаче се използват за системни цели. Затова за изобразяването на целия store могат да се използват един–два гигабайта, но не повече.

Сериозна трудност е, че навсякъде кодът на ext2fs разчита цялата файлова система да бъде изобразена в адресното пространство и веднъж като се получи указател към данни, този указател трябва винаги да е валиден. Всички решения на проблема трябва да се съобразяват с тази особеност.

Бяха разгледани кандидатите за журнална файлова система за Hurd. Въпреки че не е най-напредналата файлова система, ext3 печели лесно заради обещаващата леснота на реализацията – ext2fs е стабилната файлова система на Hurd, а ext3 просто добавя журнал за промените в блоковете. Самият журнал на ext3 е с прост формат, което предполага и проста реализация.

Архитектурата на Hurd контрастира с тази на Linux и другите варианти на UNIX. Вместо централизиране на управлението на всички общи ресурси, в Hurd се използват множество сървърски процеси с точно определени отношения помежду си, наричани интерфейси. Общуването между сървърите се осъществява от микроядрото Mach, което е ветеран в областта на операционните системи, използващи изключително предаване на съобщения за всяко взаимодействие. Файловата система е централна структура в Hurd, понеже основният начин за свързване със сървър е чрез транслатора на сървъра, който се намира във файловата система.

Реализацията на файлови системи за Hurd е улеснено от множество библиотеки, като например libdiskfs. Те обаче въобще не са проектирани за журнални файлови системи, което изисква добавяне на такива способности към libdiskfs.

Разгледан беше проблемът на ext2fs с файлови системи, по-големи от 2G. Решаването на този проблем изисква изцяло нов подход към достъпа на блокове на диска, който да е сходен с т.нар. block layer на другите ядра на операционни системи.

В Linux ext3 е реализиран чрез библиотека за поддържане на журнал JBD (Journal Block Device). Тази библиотека обаче е твърде обвързана с Linux и трудно може да бъде използван кодът й, затова се налага нова реализация на JBD.

В заключение, въпреки привидната простота на избора на ext3, нужно е да се направят немалки промени в Hurd, за да се поддържа журнал, а също така е нужно да се направи нова библиотека за работа с журнал.



[1] Тази операционна система отдавна не се използва.

[2] Другите 2G от 32-битовото адресно пространство се използват за системни цели.

[3] Все пак има и други неща, които да се слагат в това пространство от 2G.

[4] Наричани още каталози и папки.

[5] Разбира се, някои от тези процес са чисто администраторски, например сървърът proc, който се грижи за съответствието процес–PID (микроядрото няма понятие за Process IDentifier), както и за UNIX–сигналите.

[6] Част от конкретните примери завършват на _hard, защото обикновено не се извикват направо, а чрез съответните функции без наставка.

[7] Структурата dirstat, която трябва да се дефинира от потребителя на libdiskfs.