Содержание
Файловая система
Представим себе, что мы решили создать свою операционную систему.
Мы хотим сделать её хорошей, чтобы она хотя бы вписывалась во все свойства UNIX, которые мы описали на прошлом занятии: она должна быть многопользовательской (она должна давать пользователям защищаться друг от друга, и она должна давать пользователям друг с другом взаимодействовать), и многозадачной (в ней всё время работает много программ – процессор по очереди исполняет по чуть-чуть из каждой и переключается на исполнение следующей).
И вот в нашем распоряжении оказалась аппаратура: процессор, память, и всяческие устройства взаимодействия с пользователем и окружающим миром (звуковая карточка, монитор, клавиатура, мышь, принтер, и т.п).
Памяти нам досталось три типа:
процессорный кэш (L1 Cache, L2 Cache) и регистры – память на триггерах, очень быстрая и очень дорогая; скорость работы этой памяти не меньше, чем скорость работы процессора (за каждый такт мы можем считать один кусок данных1).
оперативная память – память на конденсаторах, у неё уже есть довольно существенные ограничения по скорости, сейчас в зависимости от типа памяти и типа процессора за время считывания одного куска памяти может пройти до сотен тактов процессора.
жёсткий диск, флэш, SSD2 – в десятки или стони тысяч раз медленнее оперативной памяти в среднем. Плюс ещё у классических жёстких дисков начать читать (спозиционировать головку над дорожкой) и продолжить читать (ждать обороты диска, чуть-чуть подвигать головку, передавать считываемые данные) – это две раздельные операции, примерно равно долгие. То есть для жёстких дисков существенно быстрее сначала много прочитать в память, а потом много из памяти записать обратно, чем делать это по кусочкам. Но зато, жёсткие диски дешёвые и большие (правда, SSD пока маленькие и дорогие) и на них данные хранятся при выключенном компьютере.
Как нам использовать эти типы памяти?
Кэш операционной системы / OS Cache
Идея номер один. Давайте, наши программы никогда не будут работать непосредственно с жёстким диском. Когда программа хочет получить данные с диска, операционная система загружает их в память. Когда та же или другая программа захочет поменять этот же файл, она будет менять эту же самую временную память. И иногда, когда нам это будет казаться осмысленным, мы будем отправлять содержимое этой памяти на жёсткий диск.
Эта область памяти называется кэшом (Cache).
Чтобы такое сделать, мы нарезаем весь диск на маленькие кусочки (chunks), и заводим область памяти, которая тоже нарезана на такие кусочки (в них мы будем загружать кусочки с диска) и рядом с ними лежит указатель, какой кусочек диска где лежит.
Иногда нам может захотеться, чтобы все данные, которые сейчас есть в кэше, точно отправились на диск – для этого есть команда sync.
В этом аспекте Windows устроены так же, как и UNIX (да и вообще мало операционных систем, в которых это не так). Правда, в Windows нет команды sync или она очень глубоко запрятана и по-другому называется.
Своп / Подкачка / Swap
Если приглядеться, оказывается что у некоторых программ есть довольно много памяти, которую они загружают, а потом не используют. (Это бывает обычно по двум причинам: программу загрузили в память и ей не пользуются, или если у программы есть утечки памяти – она просит выделить себе память, ей дают память, программа ей пользуется, а потом забывает о том, что эта память ей больше не нужна, и не отдаёт её обратно – это ошибка программиста, разумеется, но программисты делают ошибки, мы не можем им в этом отказать)
Поэтому идея номер два: мы будем смотреть, какой кусок (оперативной) памяти когда последний раз использовался и самые древние неиспользуемые куски будем отправлять храниться куда-нибудь на диск – чтобы освободить побольше памяти другим программам и для кэша.
Этот механизм существенно сложнее (на практике), но основан он в точности на таком же нарезании памяти на кусочки и хранении указателя, где что лежит.
Эта идея воплощена и в Windows, и в многих UNIX. В разнообразных UNIX как правило есть ограничение, что своп лежит на отдельном разделе (а значит, место под него заведено заранее один раз), в Windows своп хранится в обычном файле (и это добавляет очень немало головной боли авторам ядра системы, но даёт немного больше свободы и спобности забить диск).
Разделы / partitions
Следующая идея – и тут я затрудняюсь ответить, откуда она взялась, – такая: давайте разделим диск на немного больших кусков, внутри каждого из которых будет, возможно, несколько своя организация данных.
Сейчас эта идея нужна хотя бы для двух вещей: во-первых, чтобы на одном компьютере с одним жёстким диском могли сосуществовать несколько ОС; во-вторых, чтобы если какая-нибудь программа постоянно что-нибудь пишет и пишет на диск, и мы за этим можем забывать следить, то мы можем эту программу посадить на отдельный раздел, и переполнение этого раздела не приведёт тогда к поломке всей системы. На самом деле, причин гораздо больше, но все они довольно мелкие и довольно незначительные.
Реализовано это очень просто – диск нарезается на кольца3. Эти кольца называются разделами (partitions).
<<Img(/~dendik/images/unix/2010-02-20-partitions.png)>>
Координаты начала и конца каждого кольца хранятся в очень маленькой таблице в самом самом начале диска – в таблице разделов. Таблица эта имеет фиксированный размер, в ней хватает места на четыре раздела. В таблице хранится начало раздела, конец раздела и тип раздела (число). Сначала всем казалось, что этого хватит. Потом начали появляться случаи, когда этого не хватает, и сделали возможность прилеплять дополнительную таблицу разделов на тех же правах, что и раздел. (Т.е. в таблице разделов появляется строка, в которой говорится, где дополнительная таблица разделов лежит, эта строка имеет тип "дополнительная таблица разделов").
- Об этом не надо было бы и упоминать, если бы люди, которые это
делали, спрятали детали от пользователя – но нет! Каждая программа разметки жёсткого диска4 обязательно будет у вас спрашивать, хотите вы сделать раздел первичным (primary partition) – т.е. хранящимся в первой таблице – или дополнительным (extended partition), т.е. хранящимся во второй таблице. При этом опять-таки все программы разметки умалчивают, что для того, чтобы создать дополнительный раздел, они куда-то (неизвестно, куда!, каждая программа по-своему) в первичную таблицу разделов добавляют ссылку на дополнительную таблицу разделов. Беда в том, что некоторые ограниченные программы загрузки ОС умеют работать только с первичными разделами (но если не все, то хотя бы самые популярные загрузчики Linux такими не являются); а также в том, что большинство программ разметки диска не предупредят вас, что когда вы создали уже 4 первичных раздела, добавить ссылку на дополнительную таблицу разделов уже некуда. Отсюда правило: если Windows хочет взять для C: первичный раздел, – пожалуйста; остальные разделы лучше сделать дополнительными.
В DOS, а затем и в Windows почему-то возникла странная идея делать разделы видимыми пользователю компьютера, притом видимыми под названием "дисков" (disk drives) и обозначать буквами: C:, D:, E:, ...
В Linux разделы не видны пользователю и заботиться о них нужно только администратору (в случае домашнего компьютера эти две роли, правда, совпадают). Разделы обозначаются цифрами после имени устройства жёсткого диска (что такое имя устройства см. далее): 1, 2, 3, 4 обозначают первичные разделы, 5 и далее – дополнительные. Т.е., например, если мы хотим дотянуться до первого дополнительного раздела на первом жёстком диске, подключённом через sata, то устройство жёсткого диска будет называться /dev/sda, а устройство раздела будет называться /dev/sda5.
В других UNIX приняты другие обозначения, да и в самом Linux можно наткнуться на другие.
LVM, logical volume manager
У традиционного подхода к разделению диска на разделы есть один недостаток: изменять размеры разделов становится почти невозможно.
Например, у нас есть два раздела и немного свободного места:
[ 1 | 2 | ]
И на разделе 1 кончилось место. У нас нету возможности добавить это свободное место в конец раздела 1! Точнее, есть, но ужасно болезненная: мы можем побайтно перекопировать раздел 2 немного подальше, свести конфигурацию к случаю [ 1 | | 2 ], а потом добавить это пространство к разделу 1. Такое действие невозможно совершать при работающем компьютере, оно будет длиться несколько часов, а если диск большой, то и дней, при этом если процесс прервать (см. закон Мэрфи, Мосэнерго), то систему потом можно будет собирать по маленьким кусочкам ценой большого труда. И даже если всё получится, это означает, что достаточно много времени компьютер не будет выполнять полезную работу.
Для домашнего компьютера это может быть допустимо, и даже потеря данных с одного раздела может быть допустимой (но неприятной), если заранее сделать бэкапы, а для серверов под рабочей нагрузкой это не годится.
Поэтмоу недавно (порядка десяти лет назад) появился другой способ разделить диск на разделы. Способ называется LVM (logical volume manager) и состоит в следующем:
Жёсткий диск или раздел в традиционном понимании делят на небольшие блоки (традиционно, по 4MB). Жёсткий диск или раздел – то место, где данные потом будут физически храниться – называют словом physical volume, PV. Блоки, на которые жёсткий диск нарезан, называют physical extent.
То, что ОС видит потом как раздел, называют logical volume, LV. Каждый logical volume также разбит на блоки такого же размера, как и PE, эти блоки зовутся logical extent, LE. Наконец, создают таблицу, в которой написано, какой LE является на самом деле каким PE.
Таким образом, раздел, как его видит ОС, может состоять из кусочков, которые на самом деле разбосаны по разным местам жёсткого диска.
<<Img(/~dendik/images/unix/2010-02-20-lvm.png)>>
(На самом деле, диск в этом случае тоже нарезается на кольца, а не на сектора – всё так же ради скорости. Но для понимания сути это не играет никакой роли).
К этому нужно добавить, что в LVM введено ещё одно понятие: volume group, VG – это набор из нескольких PV и LV. Это нужно для случаев, когда вы втыкаете в компьютер, на котором уже есть LVM чужой диск, на котором тоже есть LVM – чтобы LVM не запутался.
- Рассказываю об этом вот зачем: если вы настраиваете LVM, то первым вопросом у вас попросят создать хотя бы один VG. Создавайте ровно один VG и называйте его так же, как и компьютер, чтобы меньше было шансов случайно совпасть с кем-нибудь.
Файловая система в UNIX, на примере древнего ufs
Наконец, мы разобрались с тем, как разделить диск на разделы, и смотрим на один раздел.
Напоминаю, что мы хотим сделать многозадачную многопользовательскую ОС общего назначения. Это значит, что, видимо, нужно сделать какое-то деление этого раздела на небольшие части, в которых будут лежать программы или данные, с которыми они работают (например), – назовём их файлами. Наверное, было бы удобно этим частям дать какие-то имена, чтобы с ними можно было как-то осмысленно обращаться: удалять, копировать, заменять, переименовывать. Далее оказывается, что файлов-то дикое количество, и их хочется как-то группировать. Группу файлов мы будем называть директорией (и это прорывное изобретение, которое всем нам сейчас привычно, судя по Википедии, впервые возникло в ближайшем предшественнике UNIX, MULTICS'е). Но это мы пока что разобрались только с многозадачностью, тут мы вспоминаем про то, что система у нас многопользовательская, значит у нас где-то должно храниться, кому что можно, а кому что нельзя. Довольно логично такого типа информацию, которая относится к данному файлу, хранить где-то рядом с этим файлом. Почти так всё и устроено.
Каждая версия того, как всё это устроено на самом деле, и как именно какие данные отображаются в байтики внутри раздела, называется файловой системой. Файловых систем бывает много разных. Если файловая система разрабатывается для UNIX, то она обладает набором свойств, которые можно свести к одной формулировке: она похожа на самую первую файловую систему UNIX, имя которой ufs.
Поэтому посмотрим приблизительно, как она была устроена.
В первую очередь, раздел порезан на маленькие блоки (обычно от одного килобайта до нескольких мегабайт; типичный размер блока сегодня – 4K).
Первый из этих блоков называется superblock, и в нём хранится в первую очередь идентификация типа файловой системы.
Следом за ним выделено сколько-то пространства под блоки под названием inode. В них хранится информация о свойствах файла – размер, время, владелец и пр. И ещё в них хранится список ссылок на содержимое файла.
Наконец, содержимое файла разбито на блоки, которые лежат в третьей части раздела, за inode'ами.
[ superblock | inode | inode | ... | inode | data | data | data ]
Содержимое inode
inode – это очень важное понятие в UNIX, посмотрим на его содержимое поподробнее. Что же он содержит.
Длина файла
Длина файла – количество байт. Ничего неожиданного.
Ссылки на тело
Номера блоков, в которых лежит тело файла (если у файла есть тело). Вроде тоже ничего неожиданного.
Нас не интересует, как именно представляется список ссылок на блоки – это ни на чём ощутимо не сказывается, устроено везде по-разному.
Важное замечание: ни один из блоков тела файла сам по себе целиком файлом не является. И единственное место, где собраны ссылки на все блоки файла – это inode. Поэтому файлом является inode плюс все блоки тела. А чтобы на самом низком уровне в системе указать файл и достаточно, и необходимо указать номер блока с его inode.
Тип файла
В UNIX файлы бывают многих типов.
file – самый простой тип файлов. Файл обыкновенный. Это то, что вы всегда привыкли называть файлом.
directory – с точки зрения устройства файловых систем UNIX, директория – это тоже такой файл. По устройству ничем не отличающийся от обычного файла. В теле директории лежит табличка:
name |
inode |
'.' |
1234 |
'..' |
1233 |
'x.txt' |
1255 |
И это всё, что хранится в директории. Только имя и номер inode (то есть имя и ссылка на сам файл).
Теперь поглядим на такой пример:
name |
inode |
'.' |
1234 |
'..' |
1233 |
'x.txt' |
1255 |
'y.txt' |
1255 |
Мы видим в директории два имени, которые указывают на один и тот же файл. Такая ситуация называется hardlink (изредка говорят "жёсткая ссылка").
Это один из способов в UNIX отождествить два файла.
Самое частое известное мне применение этого способа – при работе с репозиториями исходных кодов программ (они же VCS, version control system, они же SCM, source code management). Когда вы клонируете чужой репозиторий – если он находится на той же файловой системе, то ту информацию, которую иметь общей безопасно, вы на самом деле не копируете, а создаёте hardlink на существующую.
Достаточно очевидное: если мы поменяли тело файла 'x.txt' в этом примере, то точно так же поменялось и тело файла 'y.txt' – ведь если у этих файлов общий inode, то и тело-то точно общее.
В UNIX жёсткие ссылки создаются командой ln откуда куда. (Если куда не указано, то это значит "под тем же именем, но в текущей директории").
$ echo hello > x.txt $ ln x.txt y.txt $ cat y.txt hello $ echo woo > x.txt $ cat y.txt woo
С жёсткими ссылками нужно всегда работать осторожно: во-первых, вы никогда не знаете, что работаете с жёсткой ссылкой, и можете случайно попортить те данные, которые портить-то не хотелось, а во-вторых, наоборот, очень легко незаметно для себя из двух файлов, являющихся жёсткими ссылками сделать два разных файла5.
Всякая директория в UNIX обязана иметь хотя бы две записи: '.' ссылается на ту же самую директорию (в нашем примере выше inode директории имеет номер 1234, и '.' ссылается на файл с inode 1234) и '..' – ссылка на родительскую директорию. В UNIX есть ровно одна директория, для которой '.' и '..' совпадают – это корневая директория, /
Следующий тип файлов: symlink (символьная ссылка). И это ещё один способ в UNIX отождествить два файла. У символьной ссылки в теле хранится текст: куда нужно пойти, чтобы получить содержимое файла. Однако, с точки зрения программ, которые работают с этим файлом как с обычным файлом, он ведёт себя в точности так же, как тот файл, на который он ссылается.
Символьные ссылки в командной строке создаются командой {{{ln -s откуда куда}}} (опять-таки, куда можно опускать).
$ ln -s w.txt z.txt $ cat z.txt cat: z.txt: No such file or directory $ echo hello > w.txt $ cat z.txt hello
Показываю сразу нетипичный пример использования символьных ссылок: первой строкой мы создали файл z.txt, который ссылается на w.txt, но w.txt ещё не существует! Такая ссылка называется битой (dangling link). Как видите, с точки зрения обычных программ такая ссылка выглядит как если бы мы просто пытались открыть несуществующий файл. Третьей командой мы создали файл w.txt и, о чудо, сразу же по имени z.txt нам доступно его содержимое.
На деле символьные ссылки используются очень часто. Многие программы устанавливаются с несколькими синонимами и меняют способ поведения в зависимости от того, под каким именем программу вызвали. Для многих стандартных программ в Debian есть несколько альтернативных реализаций – и в Debian есть система, которая выбирает, какую из реализаций использовать, и делает символьную ссылку на выбранную программу. Или ещё пример: я храню фотографии в домашней директории, но часть из них хочу выкладывать в public_html – я делаю из public_html ссылки на те из них, которые хочу опубликовать.
В Windows есть слегка аналогичное понятие: .lnk-файлы. (Они же shortcut, они же ярлыки). Разница здесь в двух вещах: во-первых, в .lnk кроме имени содержится туча всякой информации и настроек; и во-вторых, с точки зрения половины программ в Windows .lnk является файлом со всякой белибердой (ну то есть с содержимым ссылки), а не синонимом того файла, на который он ссылается. Так получается потому, что .lnk изначально придумывали не как универсальное средство отождествления файлов, а для того, чтобы делать ссылки на программы в Start-menu и на рабочем столе.
Следующий тип файлов: fifo. Этот тип файлов существует для взаимодействия мжеду программами: одна программа открывает этот файл на чтение, другая на запись, и он счастливо друг с другом общаются. Этот тип файлов используется достаточно редко, но возможно в следующей части курса я приведу пример его использования.
Думаю, очевидно, что файлам типа fifo никакое тело не нужно. (Им нужно сколько-то памяти компьютера, чтобы хранить то, что одна программа уже успела сказать, а другая ещё не успела услышать).
Следующий тип файлов: device node. У них тоже тело отсутствует. Этот тип файла говорит, какая часть ядра должна обрабатывать попытки писать в него и читать из него.
Приведу несколько примеров:
/dev/zero – это файл, который можно сколько угодно читать, и сколько вы его будете читать, столько ноликов вы из него прочитаете. (Я имею в виду байты со значением 0, а не буквы '0')
/dev/null – любая попытка прочитать этот файл сразу заканчивается концом файла; зато в него можно сколько угодно писать, и всё, что ы в него пишете, уйдёт в никуда.
/dev/urandom – сокращение от unfiltered random, видимо. Если читать этот файл, то вы получите поток случайных байт. При этом это не генератор псевдослучайных чисел – в ядре встроено в разных местах много точек, откуда разные сведения о поведении пользователя или самой системы, которые кажутся ядру достаточно хаотичными, отправляются в это устройство.
/dev/sda – чтение и запись в это устройство соответствует чтению и записи на жёсткий диск (первый жёсткий диск, воткнутый через sata или usb кабель). Последовательно, начиная с самого начала (в центре), вдоль всей поверхности.
Например,
# cat /dev/zero > /dev/sda
– это хороший способ за несколько часов избавиться от всех ваших данных6.
Наконец, полноты ради, но уже совсем без пояснений, последний тип файлов: socket. Это ещё один тип межпроцессных взаимодействий в UNIX, но не такой простой, как fifo.
Счётчик ссылок
С типами файлов мы разобрались, возвращаемся к обзору данных, которые лежат в inode.
А для этого возвращаемся к примеру с hardlink:
name |
inode |
'.' |
1234 |
'..' |
1233 |
'x.txt' |
1255 |
'y.txt' |
1255 |
Что случится, если мы сотрём файл x.txt? Что было бы естественно ожидать?
Мне видится два возможных поведения: либо при этом у нас должен остаться файл y.txt как ни в чём не бывало, с тем же содержимым, либо у нас при этом должен исчезнусь сразу одновременно и файл x.txt, и файл y.txt.
Согласитесь, это было бы несколько шокирующе, когда вы стираете один файл, а у вас сразу исчезает ещё сколько-нибудь где-нибудь (возможно в других директориях, о которых вы не знали).
То ли поэтому, то ли от бедности, но в UNIX из этих двух вариантов поведения выбрали первый. Поэтому на уровне программиста нету функции "стереть файл", а есть функция по имени unlink – отцепить одну из ссылок на файл. Если на файл больше нету ссылок и им никто не пользуется, файл будет удалён и программам можно будет поверх него писать.
Количество ссылок на файл хранится в inode как обычное число.
<<Img(/~dendik/images/unix/2010-02-20-lndir.png)>>
Теперь представьте картинку: у нас есть директория a, в ней поддиректория b, в ней директория c, а в ней жёсткая ссылка на директорию b. И теперь мы пытаемся из директории a стереть поддиректорию b. У нас граф распадается на две части: директория a и директории b, c. При этом на директорию b есть ссылка из c, а на директорию c есть ссылка из b. Получается, что у нас нигде количество ссылок не рано нулю. Мы не можем автоматом, не задумываясь, ничего удалять. (Мы могли бы каждый раз, когда удаляем директорию, обходить всё дерево поддиректорий в ней и проверять – но это было бы сложно; кроме того, в UNIX запрещено удалять непустые директории).
Чтобы от всех этих проблем избавиться, с достаточно древних времён в UNIX запрещено делать жёсткие ссылки на директории. (Хотя говорят, что на заре времён такого запрета не было).
Права доступа
Следующие три поля в inode мы подробнее рассмотрим на следующем занятии, поэтому сейчас я их просто упомяну: это идентификатор владельца файла (uid = user identifier), идентификатор группы пользователей файла (gid = group identifier) и права доступа к файлу (mode).
Времена
Следующие три поля хранят время последней работы с файлом:
atime – access time – время последнего чтения (или записи) файла.
mtime – modification time – время последнего изменения содержимого файла.
ctime – change time (но проще запомнить так: creation time) – время последнего изменения данных в inode файла.
Время представляется в виде количества секунд с начала "эпохи" (или, чуть скромнее "UNIX-эпохи"), 0:00 1 января 1970. Поэтому если у вас что-нибудь сильно заглючило в часах или как-нибудь очень странно побился архив, то не удивляйтесь, что у вас в системе стало именно такое время. (Казалось бы, такого типа глюков быть не должно, но ровно по одному разу на моей памяти каждый из этих глюков я видел).
Так как традиционно это время хранилось в 4-х байтах, а 4 байта – это не так уж и много, то наряду с популярной "проблемой 2000 года" есть несколько менее популярная "проблема 2038 года", когда 4 байт для хранения этого времени станет недостаточно и время внезапно станет отрицательным. Достаточно много где по этому поводу уже стали отводить 64 бита на время, но наверняка проблема вычищена ещё не отовсюду.
Вот, собственно, и всё, что обязано лежать в inode в любой файловой системе для UNIX.
Организация свободной памяти
Не практической пользы ради, а просто для удовлетворения любопытства, расскажу, как устроено управление памятью – это семейство похожих алгоритмов, которые применяются в равной мере и для жёстких дисков, и для оперативной памяти.
Основная идея очень простая: память нарезана на блоки (что в файловой системе уже есть), блоки есть занятые и есть свободные; а раз есть свободные блоки, то никто не мешает прямо в них хранить информацию о том, какая память свободна.
Простейший способ такой. В суперблоке мы храним ссылку на последний освободившийся блок. Когда у нас освобождается ещё один блок, мы в него пишем на предыдущий освободившийся диск, и меняем суперблок, чтобы в нём снова была ссылка на последний освободившийся. Когда от нас требуют дать блок памяти, мы пишем в суперблок ссылку на предпоследний освободившийся, и возвращаем адрес того блока, который был у нас записан до этого. (Ещё более просто: свободные блоки образуют односвязный список; когда новый блок освобождается, мы добавляем освободившийся блок в голову списка; когда требуется занять блок, мы вынимаем его из головы списка).
У этого подхода есть куча недостатков, и когда требуется тонкая оптимизация, его заменяют на что-нибудь более сложное: используют вместо односвязного списка более сложную структуру данных (двусвязный список, несколько односвязных списков, дерево), и меняют алгоритм поиска свободного блока (чтобы для большого файла выдавать с большей вероятностью много блоков подряд).
Итого, в суперблоке лежит:
- тип файловой системы
- ссылка на корневой inode
- ссылки на свободную память
- ну и может быть ещё какая-нибудь специфическая всячина
в inode:
- длина файла (в байтах)
- ссылки на блоки, в которых лежит тело файла
- тип файла
- счётчик ссылок
- права доступа к файлу: uid, gid, mode
- время последнего доступа к файлу по типу: atime, mtime, ctime
Чем отличаются современные файловые системы от древнего ufs
Основное нововведение последних пятнадцати лет – это журналирование файловой системы. Журналирование нужно для того, чтобы уменьшить вероятность поломки файловой системы. Как правило, современные жёсткие диски рассчитаны на неожиданное отключение питания, и если оно произошло, прекращают писать данные и паркуют головку, то есть неожиданное выключение компьютера приводит именно к тому, что мы прекращаем внезапно прекращаем записывать. (Раньше это могло приводить к тому, что головка могла приземлиться на поверхность диска или записать какой-нибудь мусор). В той схеме, которую мы рассмотрели выше, в зависимости от конкретной последовательности действий при случайном отключении питания может оказаться, что какой-нибудь inode указывает на освобождённый блок, или список свободной памяти оказывается разорванным или указывает на занятый блок, или может оказаться, что мы меняли содержимое директории в момент отключения питания и в директории оказалось две копии одного файла, или совсем простое: мы успели удалить файл из директории, но не успели внести его в список свободной памяти. Чтобы бороться с этим стали заводить отдельный участок памяти на диске, под названием журнала. Когда ОС хочет записать что-нибудь на диск, первым делом она пишет запись о том, что она хочет сделать, в журнал. (И только последним шагом в записи своих намерений она обозначает, что в журнале появилась новая запись – тогда это значит, что в журнале либо не появилось записи вовсе, либо появилась запись корректная). Затем, не обязательно сразу, ОС выбирает из журнала набор действий и воплощает их в жизнь. И лишь затем размечает запись как выполненную и удаляет из журнала. Таким образом при загрузке компьютера нам достаточно пройтись по журналу и проверить, все ли действия в журнале внесены в файловую систему, и если да, то мы уверены, что система находится в консистентном состоянии.
Кроме этого, можно заметить, что если нам случится повредить самое начало диска в той схеме, которую мы описали, то у нас теряются все inode'ы и суперблок, то есть все знания о том, как из блоков данных составить файлы и директории. А если повредится любая другая часть диска, то, в общем-то, это значит, что в худшем случае пострадает содержимое нескольких файлов и нескольких директорий. Против этого существует несколько приёмов борбьы: иногда делают несколько копий суперблока, иногда суперблок смещают в середину диска, блок inode'ов иногда тоже смещают в середину диска, иногда разбрасывают по поверхности вперемешку с блоками данных (примерно тем же подходом, как LVM может разбрасывать части разделов вперемешку).
Среди современных файловых систем у вас есть все шансы столкнуться с:
reiserfs – журналируемая файловая система. Она очень хорошо себя проявляет в случаях большого количества маленьких файлов. (В том смысле, что если у вас есть директория, в которой лежит 10000 файлов, то я вам настоятельно рекоммендую, чтобы эта директория была на reiserfs). reiserfs пользуется такой репутацией: пока оно работает, оно работает очень хорошо и не требует никакого внимания, но когда оно начнёт ломаться, оно сломается очень болезненно. (Имеется в виду износ жёсткого диска. В reiserfs очень плохая выносливость к ненадёжному носителю). Для меня, как администратора kodomo, эта файловая система не годится тем, что в ней нет возможности квотировать пользовательские данные.
xfs – также журналируемая файловая система, в которой хорошо реализованы квоты. Она в первую очередь славится тем, что с ней можно делать на используемом разделе: изменять размер раздела (правда, только увеличивать), дефрагментировать (т.е. собирать блоки данных одного файла поближе друг к другу на поверхности диска), делать копию (вопрос на засыпку: чем копия файловой системы отличается от копии всех файлов и директорий в ней). На kodomo именно она используется для хранения домашних директорий.
ext – с этой файловой системы начался Linux, и она похожа на то, что я рассказывал про ufs выше, но в ней ещё не хватало правильных отметок atime, mtime и ctime.
ext2 – поэтому почти сразу за ext возникла её вторая версия, и эта версия оказалась очень удачной.
ext3 – родилась в первую очередь ради добавления журналируемости к ext2. И так как она очень хорошо обратно-совместима с ext2 и процесс обновления с ext2 на ext3 хорошо отработан и не требует почти никаких усилий, то эта файловая система сейчас наиболее популярна. На мой вкус, она ломается несколько чаще, чем reiserfs, но зато для неё прекрасно отработаны механизмы починки.
ext4 – совсем недавно стала стабильной, и про неё я ничего уверенного сказать не могу. Вроде бы, это та же ext3 с некоторым количеством оптимизаций и защиты от ошибок.
ntfs – основная файловая система Windows. Журналируемая, очень сильно заоптимизированная. У фанатиков Linux ушлое примерно десять лет на то, чтобы сделать свою реализацию этой файловой системы, в которой (реализации) можно редактировать и переименовывать существующие файлы – но не создавать новые.
fat32 – основная файловая система для внешних накопителей сегодня. Не потому, что она для этого хорошо пригодна, а потому, что она примитивная и её умеют полноценно поддерживать все. 32 в названии символизирует количество бит, которые используются для обозначения ссылки на блок.
Я буду здесь и далее использовать это неформальное понятие. В случаях с памятью размер этого куска определяется тем, сколько бит одновременно можно передать из памяти в процессор. (1)
Solid State Drive -- самый флэш, оформленный в таком же корпусе, как жёсткий диск. (2)
Именно на кольца, а не на сектора. Из соображений эффективности, и это то место, где от них трудно отказаться. При чтении с жёсткого диска чтение данных, записанных подряд вдоль окружности самое быстрое -- по очевидным, надеюсь, причинам. (3)
Включая инталлятор Windows, про линуксовые инсталляторы я вообще молчу -- хотя их много разных и они могут вообще вопросов иногда не задавать. (4)
Впрочем, то, что я говорил на лекции, оказалось неправдой: все текстовые редакторы, которые я проверил, не расплетают жёстких ссылок. (5)
Их ещё можно будет попытаться тонким обородуванием восстановить по остаточной намагниченности, так что от спецслужб этим не защитишься; для защиты от спецслужб есть специальные программы шреддеры, которые переписывают поверхность диска не один раз, а несколько, и не ноликами, а случайными числами -- вот после этого никакое тонкое оборудование не скажет, что было на диске. (6)