учебники, программирование, основы, введение в,

 

Работа с внешними устройствами

Представление устройства в системе
В лекции 10 говорилось о том, что аппаратный профиль компьютера определяется ядром на ранних этапах загрузки системы или в процессе подключения модуля. Это не означает, что устройство, не распознанное ядром, задействовать невозможно. Если неизвестным ядру устройством можно управлять по какому-нибудь стандартному протоколу, вполне возможно, что среди пакетов Linux найдется утилита или служба, способная с этим устройством работать. Например, программа записи на лазерный диск cdrecord знает великое множество разнообразных устройств, отвечающих стандарту SCSI, в то время как ядро, как правило, только позволяет работать с таким устройством как с обычным лазерным приводом (на чтение) и передавать ему различные SCSI-команды.
К сожалению, иногда обратное неверно: если производитель создает новое устройство, управлять которым нужно по-новому, а распознается оно как одно из старых, ошибки неизбежны. Многие стандарты внешних устройств предусматривают строгую идентификацию модели, однако хорошего мало и тут: незначительно изменив схемотехнику, производитель меняет и идентификатор, и устройство перестает распознаваться до тех пор, пока автор соответствующего модуля Linux не заметит этого и не добавит новый идентификатор в список поддерживаемых.
Большинству распознанных устройств, если они должны поддерживать операции чтения/записи или хотя бы управления (ioctl(), описанный ниже), соответствует файл-дырка в каталоге /dev или одном из его подкаталогов. В зависимости от того, выбрана ли в системе статическая или динамическая схема именования устройств, файлов-дырок в /dev может быть и очень много, и относительно мало. При статической схеме именования то, что ядро распознало внешнее устройство, никак не соотносится с тем, что в /dev имеется для этого устройства файл-дырка:
[root@localhost root]# cat /dev/sdg14
cat: /dev/sdg14: No such device or address
Здесь Мефодий попытался прочитать что-либо из устройства /dev/sdg14, что соответствует четырнадцатому разделу SCSI-диска под номером 7. Такого диска в этой машине, конечно, нет, а файл-дырка для него заведен на всякий случай: вдруг появится? Поскольку появиться может любое из поддерживаемых Linux устройств, таких файлов "на всякий случай" в системе бывает и десять тысяч, и двадцать. Файл-дырка не занимает места на диске, однако использует индексный дескриптор, поэтому в корневой файловой системе, независимо от ее объема, должен быть изрядный запас индексных дескрипторов.
При динамической схеме именования применяется специальная виртуальная файловая система, которая либо полностью подменяет каталог /dev, либо располагается в другом каталоге (например, /sys), имеющем непохожую на /dev иерархическую структуру; в этом случае файлы-дырки в /dev заводит специальная служба. Этот способ гораздо удобнее и для человека, который запустил команду ls /dev, и для компьютера (в случае подключения внешних устройств, например, съемных жестких дисков, "на лету"). Однако он требует соблюдать дополнительную логику "привязки" найденного устройства к имени, иногда весьма запутанную из-за той же нечеткой идентификации. Поскольку происходить это должно в самый ответственный момент, при загрузке системы, динамическую схему именования используют с осторожностью.
Виртуальная файловая система. Механизм отображения в виде файловой системы любых иерархически организованных данных. Существенно упрощает доступ к таким данным, так как позволяет применять обычные операции ввода-вывода: открытие и закрытие файла, чтение и запись и т.п.
Файлы-дырки и другие типы файлов
Кое-какие идеи динамического именования устройств присутствуют и в статической схеме. Так, файлы /dev/mouse или /dev/cdrom на самом деле представляют собой символьные ссылки на соответствующие файлы-дырки. Если тип мыши или лазерного привода изменится, достаточно изменить эти ссылки и перезапустить соответствующие службы:
Файл-дырка не имеет размера: сколько в него ни записывай, в файл на диске ничего не попадет. Вместо этого ядро передает все записанное драйверу, отвечающему за файл-дырку, а тот по-своему обрабатывает эти данные. Точно так же работает и чтение из файла-дырки: все запрашиваемые данные в нее подсовывает драйвер. Большинство драйверов - дисковые, звуковые, последовательных и параллельных портов и т.п. - обращаются за данными к какому-нибудь внешнему устройству или передают их ему. Но есть и такие, которые сами все "выдумывают": это и /dev/null, черная дыра, в которую что угодно можно записать, и все пропадет безвозвратно, и /dev/zero, из которого можно считать сколько угодно нулей (на запись оно ведет себя как /dev/null), и /dev/urandom, из которого можно считать сколько угодно относительно случайных байтов.
Изучив выдачу команды ls -lL (ключ "-L" заставляет ls выводить информацию не о символьной ссылке, а о файле, на который она указывает), Мефодий обнаружил, что та вместо размера файла-дырки (который равен нулю) выводит два числа. Первое из этих чисел называется старшим номером устройства (major device number), оно, упрощенно говоря, соответствует драйверу, отвечающему за устройство. Второе называется младшим номером устройства (minor device number), оно соответствует способу работы с устройством, а для дисковых носителей - разделу. В частности, из примера видно, что устройствами /dev/random и /dev/urandom занимается один и тот же драйвер со старшим номером 1. При этом часть устройств (по преимуществу - дисковые) имеет тип "b", а другая часть - "c" (этот тип имеют, например, терминалы). Тип указан в атрибутах файла первым символом. Это блочные (block) устройства, обмен данными с которыми возможен только порциями (блоками) определенного размера, и символьные (character) устройства, запись и чтение с которых происходит побайтно. Блочные устройства, вдобавок, могут поддерживать команды прямого доступа вида "прочитать блок номер такой-то" или "записать данные на диск, начиная с такого-то блока".
Блочные и символьные устройства - полноправные объекты файловой системы, такие же, как файлы, каталоги и символьные ссылки. Есть еще два типа специальных файлов - каналы и сокеты. Канал-файл (или fifo) называют еще именованным каналом (named pipe): это такой же объект системы, как и тот, что используется командной оболочкой для организации конвейера (его называют неименованным каналом), разница между ними в том, что у fifo есть имя, он зарегистрирован в файловой системе. Это - типичный файл-дырка, причем дырка двухсторонняя: любая программа может записать в канал (если позволяют права доступа) и любая программа может оттуда прочитать. Создать именованный канал можно с помощью команды mkfifo:
Здесь важно, что утилита head показывает начало не "файла" hole, а именно последней записываемой порции данных, как и подобает трубе.
Канал Объект Linux, используемый в межпроцессном взаимодействии. Доступен в виде двух дескрипторов: один открыт на запись, другой - на чтение. Все данные, записываемые в первый дескриптор, немедленно можно прочитать из второго. Различают неименованный канал, уничтожаемый с закрытием обоих дескрипторов, и именованный канал (FIFO) - файл-дырку, создаваемый в файловой системе.
Что же касается сокетов, то это - более сложные объекты, предназначенные для связи двух процессов и передачи информации в обе стороны. Сокет можно представить в виде двух каналов (один "туда", другой "обратно"), однако стандартные файловые операции открытия/чтения/записи на нем не работают. Процесс, открывший сокет, считается сервером: он постоянно "слушает", нет ли в нем новых данных, а когда те появляются, считывает их, обрабатывает, и, возможно, записывает в сокет ответ. Процесс-клиент может подключиться к сокету, обменяться информацией с процессом-сервером и отключиться. Точно так же можно передавать данные и по сети - в этом случае указывается не путь к сокету на файловой системе (так называемый unix domain socket), а сетевой адрес и порт удаленного компьютера (например internet socket, если подключаться с помощью сети Internet).
Драйверы устройств
Как уже говорилось в лекции 10, часть системы, отвечающая за взаимодействие с каким-нибудь внешним устройством и называемая "драйвер", в Linux либо входит в ядро, либо оформляется в виде модуля ядра и подгружается по необходимости. Следовательно, файл-дырка, обращение к которому приводило к "no such device or address", вполне может и заработать (в этом одна из причин огромного количества устройств в /dev). Гуревич наотрез отказался объяснять Мефодию, как добавить новый драйвер, до тех пор, пока тот не будет лучше разбираться в архитектуре компьютеров вообще и в аппаратной части IBM-совместимых компьютеров в частности. Поэтому все, что смог понять Мефодий, не имея таких знаний, сводилось к следующему. Во-первых, если существуют различия между тем, как по умолчанию загружает модули система и тем, как на самом деле это необходимо делать, различия должны быть описаны в файле /etc/modules.conf. Во-вторых, после изменения этого файла, добавления нового устройства, обновления самих модулей и т.п. следует запускать утилиту depmod, которая заново выстраивает непротиворечивую последовательность загрузки модулей. В-третьих, интересно (но в отсутствие знаний - малопознавательно) запускать утилиту lspci, которая показывает список устройств (распознаваемых по стандарту PCI), найденных на компьютере.
Работа с устройствами
Все файлы-дырки подчиняются одним и тем же правилам работы с файлами: их можно открывать для записи или чтения, записывать данные или считывать их стандартными средствами, а по окончании работы - закрывать. Открытие и закрытие файла (системные вызовы open() и close()) в командном интерпретаторе не представлены отдельной операцией, а выполняются автоматически при перенаправлении ввода (открытия на чтение) или вывода (на запись). Это позволяет работать и с устройствами, и с каналами, и с файлами совершенно одинаково, что активно используется в Linux программами-фильтрами. Каждый тип файлов имеет свою специфику, например, при записи на блочное устройство данные накапливаются ядром в специальном буфере размером в один блок, и только после заполнения буфера записываются. Если при закрытии файла буфер неполон, он все равно передается целиком: часть - данные, записанные пользователем, часть - данные, оставшиеся от предыдущей операции записи). Это, конечно, не означает, что из файла, находящегося на блочном устройстве, легко по ошибке прочитать такой "мусор": длина файла известна, и ядро само следит за тем, чтобы программа не прочла лишнего.
Даже такие, казалось бы, простые устройства, как жесткие диски, поддерживают гораздо больше различных операций, чем просто чтение или запись. Пользователю, как минимум, может потребоваться узнать размер блока (для разных типов дисков он разный) или объем всего диска в блоках. Для многих устройств собственно передача данных - лишь итог замысловатого общения с управляющей программой или ядром. Скажем, для вывода оцифрованного звука на звуковую карту сначала необходимо настроить параметры звукогенератора: частоту, размер шаблона, количество каналов, формат передаваемых данных и многое другое. Для управления устройствами существует системный вызов ioctl() (iputoutput control): устройство надо открыть, как файл, а затем использовать эту функцию. У каждого устройства - свой набор команд управления, поэтому в виде отдельной утилиты ioctl() не встречается, а используется неявно другими утилитами, специализирующимися на определенном типе устройств.
Права доступа к устройствам
Некоторые устройства просто обязаны быть доступны пользователю на запись и чтение. Например, виртуальная консоль, за которой работает Мефодий, доступна пользователю methody на запись и на чтение, именно поэтому командный интерпретатор Мефодия может посылать туда символы и считывать их оттуда. В то же время терминал, за которым работает Гуревич, другому пользователю недоступен, а терминалы, за которыми не работает никто, доступны только суперпользователю:
methody@localhost ~ $ who
methody tty1     Dec 3 16:02 (localhost)
shogun ttyS0     Dec 3 16:03 (localhost)
methody@localhost ~ $ ls -l /dev/tty1 /dev/tty2 /dev/ttyS0
crw--w---- 1 methody  tty   4,  1 Дек 3 16:02 /dev/tty1
crw------- 1 root     root  4,  2 Дек 3 15:51 /dev/tty2
crw--w---- 1 shogun   tty   4, 64 Дек 3 16:03 /dev/ttyS0
methody@localhost:~ $ ls -l /usr/bin/write
-rwx--s--x 1 root tty 8708 Июн 25 14:00 /usr/bin/write
Права на владение терминалом передаются с помощью chown пользователю программой login после успешной регистрации в системе. Она же выставляет право записи на терминал членам группы tty. "Настоящих" пользователей в этой группе может и не быть, зато есть setGID-программы, например, write, которая умеет выводить сообщения сразу на все активные терминалы.
Множество устройств в системе, используемой как рабочая станция, также отдаются во владение - на этот раз первому пользователю, зарегистрировавшемуся в системе. Предполагается, что компьютер служит рабочей станцией именно этого пользователя, а все последующие доступа к этим устройствам не получат. Как правило, так поступают с устройствами, которые могут понадобиться только одному человеку, сидящему за монитором: звуковыми и видеокартами, лазерными приводами, дисководом и т.п.:
При этом для того чтобы обеспечить доступ и другим - псевдо- или настоящим - пользователям, такие устройства также принадлежат определенным группам с соответствующими правами. Практика "раздачи" устройств группам вообще очень удобна: даже если доступ к устройству имеет только суперпользователь, существует возможность написать setGID-программу, которая, не получая суперпользовательских прав, сможет до этого устройства добраться (а можно и просто включить опытного пользователя в такую группу).


Разметка диска и именование устройств
В начале лекции говорилось о том, что младший номер устройства, соответствующего жесткому диску, обычно указывает на определенный раздел этого диска. Поначалу Мефодию казалось, что смысла "пилить" диск на несколько разделов нет: известно, что один большой раздел файловой системы Linux вмещает чуть больше данных, чем несколько маленьких того же объема. Кроме того, разбивая диск на разделы, можно не предугадать подходящие размеры этих разделов, и тогда размещение на них файловой системы Linux окажется делом нелегким, если вообще возможным, так как структура дерева каталогов Linux строго определена стандартом FHS (см. лекцию 3).
Раздел диска. Часть жесткого диска, используемая под определенные задачи: файловую систему того или иного типа, область подкачки и т.п. Изменение содержимого и типа одного раздела никак не сказывается на других.
Впрочем, в том же FHS весьма наглядно обоснована необходимость разнесения всего дерева каталогов по разным разделам, каждый из которых будет иметь собственную файловую систему. Каталоги сильно различаются по тому, как часто приходится в них записывать, насколько надежность хранения данных в них важнее быстродействия и насколько ситуация переполнения файловой системы опасна и может помешать работе. Поэтому стоит держать каталог /tmp, требущий очень частой записи, но не требующий надежного хранения данных после перезагрузки, не на том же разделе, что и корневую файловую систему, запись в которую происходит редко (в каталог /etc), но требует повышенной надежности. В отдельный раздел можно поместить весь каталог /usr, так как он вообще не требует операций записи. Наконец, такие каталоги, как /var или /home, суммарный объем файлов в которых с трудом поддается контролю со стороны системы, тоже не следует размещать на том же разделе, что и корневую файловую систему, переполнение которой может быть болезненно воспринято Linux.
К тому же на компьютере может быть установлено несколько операционных систем, и каждой из них понадобится для корневой файловой системы отдельный раздел. В примерах этой и предыдущей лекций Мефодий работает именно за такой машиной: помимо Linux, на ней установлен FreeDOS для запуска одной-единственной программы.
Разметка диска IBM-совместимого компьютера
Таблица разделов, таблица разбиения диска, HDPT. Небольшая часть жесткого диска, описывающая геометрию и тип его разделов. Стандартная таблица разделов диска IBM-совместимого компьютера может содержать не более четырех разделов.
Разбиение диска на разделы - дело (теоретически) несложное: какая-то часть диска должна быть отведена под таблицу разделов, в которой и будет написано, как разбит диск. Стандартная таблица разделов для диска IBM-совместимого компьютера - HDPT (hard disk partition table) - располагается в конце самого первого сектора диска, после предзагрузчика (master boot record, MBR) и состоит из четырех записей вида "тип начало конец", описывающих очередной раздел диска (если раздела нет, поле тип устанавливается в 0). Разделы, упомянутые в HDPT диска, принято называть основными (primary partition). Устройство Linux, соответствующее первому диску компьютера, обычно называется /dev/hda (hard disk "a"). Второй диск получает имя hdb, третий - hdc и так далее. На типичном IBM-совместимом компьютере такое же имя получит и лазерный накопитель. Часто бывает, что жесткий диск - первый в системе (hda), а лазерный накопитель - третий (hdc); второго же вовсе нет. Устройства, соответствующие основным разделам диска, называются /dev/hdбукваномер, для первого диска - от hda1 до hda4. Просмотреть список разделов можно с помощью команды fdisk -l.
Совмещение нескольких схем разметки
На той самой - двухсистемной - машине fdisk обнаружила пятый, шестой и седьмой разделы, однако не показала ни третий, ни четвертый:
[root@localhost root]# fdisk -l
Disk /dev/hda: 2147 MB, 2147483648 bytes
128 heads, 63 sectors/track, 520 cylinders
Units = cylinders of 8064 * 512 = 4128768 bytes
Device  Boot Start End Blocks   Id  System
/dev/hda1  *     1   25 100768+   6  FAT16
/dev/hda2       26  520 1995840   5  Extended
/dev/hda5       26  282 1036192+ 83  Linux
/dev/hda6      283  334 209632+  82  Linux swap
/dev/hda7      335  520 749920+  83  Linux
Дело в том, что четырех разделов редко когда бывает достаточно. Куда же помещать дополнительные поля таблицы разбиения? Создатели IBM PC предложили универсальный способ: один из четырех основных разделов объявляется расширенным (extended partition); он, как правило, занимает все оставшееся пространство диска. Расширенный раздел разбивается на подразделы по тем же правилам, что и весь диск: в самом его начале заводится HDPT с четырьмя записями (соответствующие им разделы называются дополнительными (secondary partition), которые снова можно использовать, причем один из подразделов может быть, опять-таки, расширенным, со своими подразделами и т.д.
Чтобы не усложнять эту схему, при разметке диска соблюдают два правила: во-первых, расширенных разделов в таблице разбиения диска может быть не более одного, а во-вторых, таблица разбиения расширенного раздела может содержать либо одну запись - описание дополнительного раздела, либо две - описание дополнительного раздела и описание вложенного расширенного раздела. Соблюдение этого правила позволяет в Linux нумеровать разделы линейно: после четырех основных номер 5 получает дополнительный раздел в первом расширенном, 6 - раздел во втором расширенном, вложенным в первый, и т. п. Сами вложенные расширенные разделы при этом не нумеруются и никакому устройству в /dev/ не соответствуют. В действительности разбиение диска двухсистемной машины Мефодия выглядит как на.
И разделы, и таблицы разбиения принято размещать с начала цилиндра (термин, имеющий отношение к внутреннему устройству жесткого диска), так что при заведении каждого расширенного раздела на этом компьютере тратилось впустую по четыре мегабайта (таков, по сведениям fdisk, размер цилиндра).
Той же тактикой - разбиением не диска, а раздела - пользуются, когда таблица разбиения нестандартна для IBM PC. Например, UNIX-подобные системы семейства BSD используют собственный универсальный формат разбиения (он старше, чем сама идея об IBM PC!), для чего подобной системе выделяется один раздел, и она творит с ним все, что заблагорассудится.
Область подкачки
Итак, Linux на компьютере из примера использует три раздела: hda5, hda6 и hda7. Тип раздела hda6, "Linux swap", отличается от двух других - по словам Гуревича, "это вообще не файловая система". Это - так называемая область подкачки (swap space), пространство на диске, используемое системой для организации виртуальной памяти. Оказывается, областям оперативной памяти, которые запрашивают процессы у ядра, не всегда соответствуют части физической оперативной памяти. Если процесс долгое время не использует заказанную оперативную память, ее содержимое записывается на диск, в область подкачки - тем самым освобождается место в физической памяти для других процессов. Когда же он "вспомнит" об этой области памяти, ядро подкачает ее с диска, разместит в оперативной памяти (возможно, откачав другие области), и только тогда позволит процессу продолжить работу.
Вполне может сложиться ситуация, когда несколько процессов заказали оперативной памяти больше, чем ее есть в действительности, и преспокойно работают, потому что не используют все заказанное пространство сразу, позволяя системе откачивать неиспользуемые области. К тому же многие процессы (особенно демоны) не работают постоянно, а ждут наступления определенного события, и чем дольше они ждут, тем дольше не используют оперативную память, и тем выше вероятность, что ядро откачает ее.
Следует отдавать себе отчет в том, что если эти процессы вдруг захотят работать одновременно и со всеми областями памяти, ядру придется туго. Большую часть времени система будет проводить, откачивая и подкачивая данные, потому что дисковые операции чтения и записи работают в тысячи раз медленнее, чем запись и чтение из оперативной памяти. Чтобы хоть как-то облегчить ядру задачу, область подкачки размещают на отдельном разделе, обмен данными с которым работает быстрее, чем чтение и запись в файл, обслуживаемые файловой системой.

Файловая система
Из лекции 3 Мефодий узнал, как пользоваться файловой системой и какую структуру она имеет с точки зрения программы, работающей с файлами в ней. О том, как огранизована файловая система изнутри, написан ряд больших статей, защищено немало кандидатских (и несколько докторских) диссертаций. Разработка файловой системы - сложный и интересный процесс, требующий одновременно владения высшей математикой, статистикой, умения безошибочно программировать и досконального знания того, как работает то или иное дисковое устройство. Поэтому файловых систем не так много, и каждая из них устроена по-своему, в соответствии с тем, как решал тот или иной коллектив разработчиков задачу быстрого и надежного доступа к файлам.
Принципы организации данных на диске
Во всех файловых системах есть немало общего. Например, в каждой из них решается вопрос метаданных, то есть информации, не имеющей прямого отношения к содержимому, допустим, файла, но описывающей, как до этого содержимого добраться. В файловой системе обычно различается системная область, в которой записываются метаданные, и область данных, где хранятся собственно файлы. Системная область может составлять заметную долю общего дискового пространства, и вот почему.
Различают устройство последовательного доступа (например, накопители на магнитных лентах) и устройства прямого доступа (например, жесткие диски). Чтение (и запись) данных на устройства последовательного доступа идет последовательно: если сейчас записан первый блок носителя, то следующим будет доступен второй, за ним - третий и т.д. Если доступен пятый блок, а нужен первый или тысячный, выполняется длительная операция позиционирования, причем она тем длиннее, чем дальше отстоит нужный блок от текущего: лента перематывается. Работать с устройствами прямого доступа легче: каков бы ни был текущий прочитанный блок, время, за которое будет прочитан любой другой, примерно одинаковое.
Файлы на магнитной ленте удобнее хранить целиком, каждый файл - одним длинным куском. У такого способа есть один существенный недостаток: если на ленту объемом в один гигабайт записать 1024 мегабайтных файла, а потом удалить каждый второй, то образуется полгигабайта свободного места, но кусочками по мегабайту каждый. Тогда запись, скажем, двухмегабайтного файла потребует трех операций: сначала надо переписать какой-нибудь мегабайтный файл на свободное место, затем удалить старую его копию, и только затем записать на образовавшееся место большой файл.
На устройстве прямого доступа можно избежать этой неприятной ситуации, если постановить, что файл может размещаться на нем в области данных по частям, а карта размещения этих частей будет записана в системную область. Если, не мудрствуя, предположить, что в системную область записываются номера полукилобайтных секторов, в которых лежит файл (по 32 бита каждый номер), то выходит, что размер системной области, который может потребоваться, всего в 16 раз меньше файловой. Но в Linux в системную область записываются индексные дескрипторы, размер которых существенно больше. Количество индексных дескрипторов может быть намного меньше количества блоков, но все же системная область занимает примерно такую же (от пяти до десяти процентов) долю общего дискового пространства.
Индексный дескриптор, inode. Внутренний объект файловой системы Linux, однозначно определяющий принадлежащий ей файл. Индексный дескриптор содержит атрибуты файла, размер, указывает расположение файла на диске и т.п. Каждому индексному дескриптору соответствует единственный в данной файловой системе идентификатор - целое число.
На самом деле, даже на жестком диске блоки, расположенные подряд, считываются (и записываются) быстрее, чем блоки, расположенные как попало. Эффект связан с механическим устройством жестких дисков, пояснять которое Мефодию Гуревич не стал, ссылаясь на общеизвестность. Суть его в том, что задержки при чтении данных, находящихся на разных цилиндрах диска, растут линейно, как для ленты (чем дальше, тем дольше). Один из остроумных способов оптимизировать работу с диском состоит в том, чтобы разбить все цилиндры на группы, а внутри каждой группы выделить свою системную область и область данных. Тогда сами файлы и их индексные дескрипторы будут лежать, если это возможно, на соседних цилиндрах, и доступ ускорится.
Другое, более общее решение - использование кеширования, при котором данные с диска частично дублируются в памяти. Если какой-то процесс прочитал данные из файла, эти данные некоторое время находятся в памяти, на случай, если они ему (или кому-нибудь другому) опять понадобятся. Повторное обращение уже не дойдет до диска, система вернет процессу данные из кеша, раз уж они ничем не отличаются от тех, что на диске. Если процесс записал данные на диск, содержимое кеша обновляется, оставаясь актуальным.
Еще эффективней кеш на запись: операции записи накапливаются в памяти, а до диска добираются не сразу, и в том порядке, в каком быстрее пройдет запись, а не в том, в каком были выполнены. Если запись шла во временный файл, который, в конце концов, удалили, обращений к диску может вообще не случиться. Однако с кешированием операций записи следует обращаться бережно: а вдруг сбой в электропитании произойдет именно тогда, когда часть данных уже записана, а часть - еще нет? А если не полностью, кусочками, обновилась системная область, состояние файловой системы после того, как питание опять включат, может оказаться совсем плачевным - настолько, что даже утилита восстановления fsck может оказаться бессильной. Поэтому системные области либо вообще не кешируются на запись, либо делают это исключительно с помощью будущих кандидатов и докторов наук, которые рассчитывают безопасные алгоритмы обновления файловой системы из кеша на запись...
Работа с файловыми системами
Итак, Linux свободно работает (и даже предпочитает работать) с несколькими разделами диска, содержащими, возможно, разные типы файловых систем.
Монтирование и размонтирование
В лекции 3 было рассказано о том, что файловые системы на различных разделах "прививаются" в виде ветвей общего дерева каталогов, растущего из "/". Делается это при помощи команды mount -o настройки_монтирования устройство точка_монтирования, где устройство - это имя блочного файла-дырки, точка_монтирования (mountpoint) - полный путь к каталогу, а настройки_монтирования определяют особые параметры, разные для разных файловых систем. После выполнения этой команды содержимое файловой системы, размещенной на устройстве (как правило, дисковом разделе), становится доступным в виде дерева подкаталогов точки_монтирования. Посмотреть список всех смонтированных файловых систем можно с помощью команды mount без параметров:
root@localhost root]# mount
/dev/hda5 on / type ext3 (rw)
/dev/hda7 on /home type ext3 (rw)
/dev/fd0 on /mnt/floppy type subfs (rw,nosuid,nodev,sync)
/dev/hdc on /mnt/cdrom type subfs (ro,nosuid,nodev)
proc on /proc type proc (rw,gid=19)
devpts on /dev/pts type devpts (rw,gid=5,mode=0620)
[root@localhost root]# umount /home
[root@localhost root]# ls /home
[root@localhost root]# mount /dev/hda7 /home
[root@localhost root]# ls /home
methody shogun tmpuser

Оба Linux-раздела смонтированы при старте системы: /dev/hda5 образует корневую файловую систему, а /dev/hda7 используется для хранения пользовательских домашних каталогов). Суперпользователь может размонтировать файловую систему вручную с помощью команды umount точка_монтирования, если на ней не открыто никаких файлов и никто не использует какой-либо ее каталог в качестве текущего.
Для того чтобы файловые системы монтировались при старте, их описывают в файле /etc/fstab:
/dev/hda5 / ext3 defaults 1 1
devpts /dev/pts devpts gid=5,mode=0620 0 0
/dev/hda7 /home ext3 defaults 1 2
proc /proc proc gid=19 0 0
/dev/hda6 swap swap defaults 0 0
/dev/fd0 /mnt/floppy subfs fs=floppyfss,sync,nodev,nosuid
/dev/cdrom /mnt/cdrom subfs fs=cdfss,nodev,nosuid
Первое поле каждой строки этого файла - устройство или название виртуальной файловой системы, второе - точка монтирования, третье - тип файловой системы, четвертое - настройки монтирования, а пятое и шестое относятся к организации резервного копирования и процедуре проверки цельности. Содержимое fstab практически повторяет выдачу mount (dev/cdrom на этой машине - ссылка на /dev/hdc). Здесь указывается и область подкачки, которую ядро не монтирует, а использует напрямую. Утилита mount поддерживает усеченный вариант командной строки mount точка_монтирования, при котором она самостоятельно ищет в /etc/fstab, каким способом должна быть смонтирована точка_монтирования. Для того чтобы при старте системы какое-либо устройство не монтировалось, а усеченным mount его можно было смонтировать вручную, в поле "настройки монтирования" добавляется ключевое слово noauto.
Две последних строки относятся к монтированию съемных (removable) носителей: лазерного и гибкого дисков. Съемные носители приходится монтировать гораздо чаще несъемных - не во время старта системы, а всякий раз, когда носитель сменился, и содержимое нового необходимо пользователю. Мало того, надо разрешить выполнять операцию mount пользователю, который принес дискету и желает поработать с ней. С другой стороны, нельзя всем и каждому давать право запускать mount и особенно umount с любыми параметрами! Существует четыре способа разрешить возникающее противоречие:

  1. Воспользоваться усеченным вариантом mount (запись с настройкой noauto в fstab) и утилитой sudo, при помощи которой позволить пользователю выполнять, скажем, только команды mount /cdrom и umount /cdrom.
  2. Воспользоваться усеченным вариантом mount и настройкой owner в fstab, которая позволяет выполнять операцию монтирования хозяину устройства; при этом /dev/hdc отдается во владение первому зарегистрированному пользователю так же, как /dev/audio и прочие устройства персонального использования. Этот способ лучше предыдущего тем, что исключает ситуацию, когда один пользователь смонтирует диск, а другой немедленно его размонтирует.
  3. Воспользоваться специальным демоном из пакета autofs, который отслеживает обращения пользователей к некоторому каталогу (например, /mnt/cdrom/auto) и самостоятельно выполняет операцию mount, а если к содержимому носителя долгое время никто не обращался - umount. Этот способ лучше предыдущего тем, что пользователю вообще никаких дополнительных команд подавать не приходится.
  4. Воспользоваться специализированным модулем ядра (в примере - subfs), который всегда сообщает программе пользователя, что устройство смонтировано и готово к работе, а поменялся ли носитель, разбирается самостоятельно. Этот способ лучше предыдущего тем, что пользователю не приходится ждать, пока система соизволит размонтировать и "отдать" лазерный диск. Кроме того, subfs может снабжать пользователя данными из кеша, даже если диск давно уже вынут (речь идет, разумеется, об операциях чтения).

Во всех случаях, когда диск монтирует не системный администратор, стоит предпринять некоторые дополнительные действия. Например, диск должен монтироваться так, чтобы с него не работал запуск с подменой идентификатора (setUID), и чтобы на нем нельзя было создавать файлы-дырки (чтобы не потворствовать хулиганству, вроде запуска setUID-оболочки или записи прямо в устройство, соответствующее hda). За это отвечают настройки nosuid и nodev, упомянутые в /etc/fstab.
Кроме того, лазерные приводы имеют "защелку", не позволяющую извлечь диск, пока он используется, а дисководы или устройства USB Flash - нет (хотя, казалось бы, она как раз нужнее там, где происходит запись). Единственная надежда - на то, что пользователь не будет выдергивать дискету из дисковода, пока он занимается записью, и на нем горит зеленая лампочка. Чтобы каждая операция записи немедленно приводила к передаче данных, необходимо полностью отключить кеш записи, то есть использовать синхронный режим работы файловой системы. Это делается при помощи настройки sync.

Поддерживаемые Linux файловые системы
Если бы на компьютере из примера использовался способ монтирования лазерного диска 1 или 2, то в поле "тип" fstab было бы написано iso9660. Так называется тип файловой системы, обычно используемой на лазерных дисках. Что же касается жестких дисков, то на них может использоваться несколько типов файловых систем, даже на одном Linux-компьютере.
Основная файловая система в Linux называется "Ext2". Имя происходит от слова "extended" (расширенная) и появилось после того, как самая первая версия файловой системы ранних Linux, повторяющая возможности одного из вариантов файловой системы UNIX, окончательно устарела. Пришлось переписать соответствующую часть ядра, расширив уже имеющиеся возможности. Так появилась "ExtFS". Когда и она устарела, возможности снова расширили, и к названию добавилось число "2". Повсеместно используемая в дистрибутивах Linux файловая система "Ext3" - "трижды расширенная" - отличается от Ext2 поддержкой журнализации.
Журналируемая файловая система ведет постоянный учет всех операций записи на диск. Получающийся журнал обращений сам, в свою очередь, записывается на диск. Разница между записью журнала и записью самих данных в том, что данные следует записывать в строго определенное место, а журнал устроен так, чтобы записываться как можно быстрее. Выгода от такой двухступенчатой процедуры особенно остро ощущается после сбоя электропитания: все операции, записи, которые еще не успели завершиться, записаны в журнале, так что стоит после включения компьютера "проиграть" их еще раз, и файловая система войдет в норму! Если часть данных уже была записана на диск, повторная (по требованию журнала) запись тех же самых данных на то же самое место ничем повредить не может. Наконец, если операция записи не попала даже в журнал (что бывает редко), то файловая система все равно останется в рабочем состоянии, каким оно было до начала этой операции.
Журналирование поддерживается и другими файловыми системами, используемыми в Linux - XFS и ReiserFS. ReiserFS вообще похожа скорее на базу данных: внутри нее используется собственная система индексации и быстрого поиска данных, а индексные дескрипторы и каталоги в стиле Linux выполнены в виде одной из возможных надстроек над этой системой. Традиционно считается, что ReiserFS отлично подходит для хранения огромного числа маленьких файлов (что было одной из целей проекта), а XFS - для хранения очень больших файлов, в которых постоянно что-нибудь дописывается или изменяется.
В Linux поддерживается, кроме собственных, немало форматов файловых систем, используемых другими ОС. Если способ записи на эти файловые системы известен и не слишком замысловат, то работает и запись, и чтение, в противном случае - только чтение (чего нередко бывает достаточно). Полностью поддерживаются файловые системы FAT12/FAT16/FAT32 (тип vfat), используемые в MS-DOS и Windows, ранние версии UFS, используемые в системах семейства BSD. Новые версии UFS (например, UFS2 из FreeBSD5, обладающая свойствами, которые не входят в стандарт Linux) поддерживаются только на чтение, как и созданная на основе DEC VMS, но впоследствии многократно переработанная файловая система NTFS из Windows.
Для того чтобы смонтировать файловую систему, имеющую заданный тип, команде mount необходимо указать его с помощью ключа "-t":
 [root@localhost root]# fdisk -l
. . .
Device   Boot Start End Blocks   Id System
/dev/hda1   *     1   25 100768+   6 FAT16
/dev/hda2        26  520 1995840   5 Extended
/dev/hda5        26  282 1036192+ 83 Linux
/dev/hda6       283  334 209632+  82 Linux swap
/dev/hda7       335  520 749920+  83 Linux
[root@localhost root]# mount -t vfat /dev/hda1 /mnt/disk
[root@localhost root]# ls /mnt/disk
autoexec.bat   config.sys    fdconfig.sys    freedos.bss
command.com    fdconfig.old  fdos            kernel.sys
Виртуальные и сетевые файловые системы
В /etc/fstab Мефодий сразу заметил две строки, начинающиеся не с имени устройства, а с названия виртуальной файловой системы, содержимое которой доступно в соответствующей точке монтирования. Виртуальная файловая система обычно не обращается ни к какому внешнему устройству, а "придумывает" все дерево каталогов и находящиеся в них файлы сама. Такова, например, файловая система в памяти (ROMFS, или аналогичная ей TMPFS, поддерживающая операции записи), используемая в стартовом виртуальном диске. Как правило, виртуальные файловые системы используются для того, чтобы предоставить доступ по чтению/записи к некоторой иерархической структуре данных.
Во многих версиях UNIX программа ps работает непосредственно с устройством /dev/kmem (памятью ядра), чтобы добыть оттуда информацию о таблицах процессов; это сложная и даже опасная программа, имеющая доступ к святая святых системы. В Linux ps может быть переписана чуть ли не на shell, потому что таблица процессов и масса другой информации о системе доступны в виде дерева подкаталогов /proc:
В частности, подкаталоги /proc с числовыми именами содержат информацию о процессах с соответствующими PID. Файл exe такого подкаталога - символьная ссылка на запущенную программу, файл cmdline содержит командную строку, а environ - окружение процесса. Мефодий углубился в чтение man proc, руководства по PROCFS, и, как всегда, убедился, что для полного понимания всего, что есть в /proc, ему пока не хватает знаний.
Файловая система devpts - шаг навстречу динамическому именованию устройств. Она предназначена для эмуляторов терминала, таких как sshd, xterm или screen. Задача эмулятора терминала - предоставить пользователю полноценный интерфейс командной строки (с запуском командного интерпретатора, с распознаванием и передачей сигналов и т.п.) в отсутствие терминального оборудования - по сети или из графической подсистемы, или при необходимости сымитировать несколько терминалов. Раньше для этого использовались пары устройств /dev/pty## - /dev/tty##, где ## - двухсимвольный идентификатор. Программа-эмулятор начинала обмениваться данными (от пользователя или из сети) с первым свободным устройством (скажем, ptya2, которое, в свою очередь, было привязано к соответствующему терминальному устройству (ttya2)). Именно с этим устройством и взаимодействовал командный интерпретатор и прочие процессы Linux, находясь в полной уверенности, что это полноценный терминал.
Выходило, что пар tty##-pty## при статическом именовании устройств могло не хватить, даже если создать их очень много (достаточно запустить еще больше эмуляторов терминала). Поэтому придумали завести одно устройство типа pty - /dev/ptmx и виртуальную файловую систему /dev/pts для терминальных файл-дырок. Каждая программа, открывающая ptmx, получает свой дескриптор), а в pts/ заводится очередное терминальное устройство, имя которого совпадает с порядковым номером. Когда дескриптор закрывается, терминальное устройство исчезает.
Среди файловых систем есть и такие, что не выдумывают содержимое сами, а обращаются за ним еще куда-нибудь, например, в сеть. Так работают удаленные файловые системы, например, NFS (network file system), стандартная для всех UNIX-подобных ОС. Вместо поля "устройство" обычно указывается сетевой адрес компьютера-сервера и имя ресурса (название каталога), который необходимо смонтировать по сети. Поддерживается и работа с удаленными файловыми системами Windows, причем как на уровне модулей ядра, с помощью mount -t smbfs, так и без монтирования, с помощью утилиты smbclient. Linux и сама может служить сервером, предоставляющим удаленный доступ к файлам, причем служба samba, занимающаяся файловыми системами для Windows под управлением Linux, работает зачастую быстрее, чем Windows, запущенный на том же компьютере для тех же целей.
Возможности файловых систем этим не исчерпываются. Например, можно смонтировать образ устройства из файла, если вызвать команду mount с ключом -o loop. Образ устройства - это файл, содержимое которого в точности повторяет содержимое устройства; его можно, например, получить с помощью команды cat устройство образ. Именно образами устройств манипулируют программы записи на лазерные носители. Смонтировать образ бывает нужно для проверки или изменения содержимого перед записью, или для хранения содержимого нескольких дисков в исходном виде:
Пример 11.11. Монтирование содержимого файла при помощи mount -o loop
Как заметил Мефодий, mount создает для такого способа монтирования специальное устройство - /dev/loop0, и уже с его помощью работает с файлом.
Обширное поле для экспериментов - так называемая пользовательская файловая система (linux userland file system, LUFS). Это модуль ядра и набор библиотек, позволяющий организовать файловую систему, обращающуюся за информацией к обычному процессу Linux. Так организованы разнообразные сетевые "эмуляторы" файловых систем с использованием протокола FTP или Secure Shell. Так работает и доступ на запись к файловой системе NTFS: некоторая программа обращается к устройству, содержащему файловую систему, задействует драйвер NTFS, взятый из лицензионной копии самой Windows (это можно сделать с помощью библиотек wine, подсистемы, распознающей исполняемые форматы Windows), и обменивается данными с модулем LUFS, который и предоставляет обычный файловый доступ процессам.
Проверка файловой системы
Если доступная на запись файловая система не была размонтирована перед выключением компьютера, после включения она окажется в нештатном состоянии, независимо от того, испортилось на ней что-либо или нет. Проверкой цельности файловой системы занимается утилита fsck (file system check). На самом деле таких утилит несколько - по одной для каждого из основных типов файловых систем (есть fsck даже для VFAT). Как уже говорилось в лекции 10, fsck запускается при старте Linux, если файловая система находится в нештатном состоянии, или для профилактики, если файловую систему просто давно не проверяли.
В самом лучшем случае fsck не находит ничего подозрительного, и система продолжает загрузку. Чаще всего, даже если в файловой системе не все в порядке, ее журнал не испорчен, и fsck "проигрывает" его, после чего все опять приходит в норму. Запустить fsck можно и вручную, в виде fsck устройство или fsck точка_монтирования, однако прежде следует размонтировать файловую систему:
Со второго раза fsck) работать тоже отказалась, ссылаясь на то, что файловая система и так находится в штатном состоянии (ее аккуратно размонтировали). Пришлось применить ключ "-f" (force), который заставляет fsck работать - конечно же, никаких ошибок найдено не было. Сама процедура проверки довольно сложна - она состоит из пяти этапов, каждый из которых отнюдь не тривиален и в этой лекции не описывается. Кстати сказать, для того чтобы проверить корневую файловую систему, ее приходится сначала монтировать только на чтение, находить там /sbin/fsck, проверять и только после этого монтировать на чтение-запись. Если корневая файловая система испорчена настолько, что /sbin/fsck в ней найти невозможно, остается пробовать загрузку с других носителей (например, с установочного CD) и разбираться.
Если какая-то порча файловой системы все-таки обнаружилась, fsck может поступить двояко. Во-первых, все ошибки, которые не приводят к изменению данных на диске, можно попробовать исправить автоматически. Например, индексные дескрипторы, на которые не ссылается ни одно имя (так называемые потерянные файлы, unref files), помещаются в специальный каталог /lost+found под именами, соответствующими номерам этих индексных дескрипторов. Впоследствии администратор может посмотреть в эти файлы и решить, нужны они или нет. Во-вторых, когда fsck встречается с ошибкой, исправление которой приведет к изменению данных на диске, загрузка Linux останавливается, и система переходит в однопользовательский режим. Предполагается, что администратор сам запустит fsck: либо интерактивно (тогда каждому изменению в файловой системе будет требоваться подтверждение с клавиатуры), либо пакетно, с ключом "-y" (тогда считается, что на все запросы администратор заранее ответил "yes").
Когда-то такие запуски fsck -y производили катастрофические разрушения по вине неумелых администраторов, а нынче Мефодий, как ни нажимал "Reset" на многострадальной двухсистемной машине, не смог добиться ничего, кроме двух-трех мгновенных воспроизведений журнала и жестокого нагоняя от Гуревича.

 

 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г.Яндекс.Метрика