Сага о POSIX-системах
(C) Алексей Федорчук, 2004

Назад|Содержание|Вперед

Истина третья, или три кита POSIX'ивизма

И понял я после стакана -
Земля стоит на трех баранах:
Сперва гульба, потом пальба,
Вот вам единство и борьба.
Тимур Шаов

Третья из моих истин - комбинированная, ибо сводится к трем пунктам: всё, что существует в системе статически, суть файлы; всё, что существует в системе в динамике, суть процессы; все, кто тем или иным образом взаимодействует с системой - ее процессами и ее файлами, - суть пользователи. Файлы, процессы и пользователи - это те три кита, на которых зиждется дивный POSIX-мир...

Содержание

Преамбула

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

Получается нечто вроде известной сказки про белого бычка, которая, как уже говорилось, в мире Unix задумчиво называется рекурсией. И определить понятие процесса без представления, что такое файл и пользователь, столь же трудно, как определить понятие файла, не имея представления о процессе. Однако мы, POSIX'ивисты, не боимся трудностей и, подобно Александру Филипповичу (Македонскому) разрубим этот узел, чисто волюнтаристически установив в нем точку отсчета. В качестве каковой резонно было бы принять себя, любимого, сиречь пользователя.

Все для блага человека

- Расскажи, что слышал, что видел?
- Много хорошего слышал:
"Все для человека!
все для блага человека!!
все для счастья человека!!!
И человека того чукча видел...
Рассказ чукчи,
делегата XXIV съезда КПСС

Время от времени в компьютерной прессе появляются сообщения о выходе очередного Linux-дистрибутива - наконец-то "с человеческим лицом". Не верьте им: лицо у POSIX-систем было человеческим всегда. Ибо одно из краеугольных понятий, на которых стоял, стоит и стоять будет POSIX-мир - это понятие пользователя. А какое лицо может быть более человеческих, чем собственная морда?

В компенсацию собственного эгоцентризма заметим, что круг пользователей собой, любимым, не исчерпывается. Потому что в POSIX-системе, даже если вы не делите ее с ближними (по дому или по работе) обязательно присутствует еще один пользователь - всемогущий root-оператор (именуемый в народе также администратором или суперпользователем). Правда, в условиях локального (особенно домашнего) десктопа в его роли, скорее всего, выступает наша же персона.

Однако есть в системе и пользователи, никакой реальной личности не соответствующие. Но, тем не менее, способные выполнять в этой системе некоторые вполне реальные действия. Мистицизм такого явления отражен в общем их названии - демоны (daemon). На самом деле ничего мистического в них нет, но это - тема особого разговора. Если же добавить, что существуют еще и т.н. псевдопользователи, типа почтовых или ftp-клиентов, то может показаться, что понятие пользователя утрачивает всякую определенность.

И потому принимаем волевое решение - считать пользователем любого, кто имеет учетную запись в соответствующей базе данных (нетрудно догадаться, что называется она незатейливо - базой данных пользователей). Без всякой дискриминации в отношении реальных юзеров, псевдоюзеров или даже демонов. Почему, строго говоря, и правильней говорить не о пользователях, а об их учетных записях (по заграничному, аккаунтах; встречается еще и выражение пользовательские бюджеты) или, как более изящно выразился Иван Паскаль, учетных карточках. Однако фраза типа "учетная карточка пользователя имя_рек выполняет такое-то действие" по русски звучит несколько странно (я бы даже сказал, зловеще). И потому будем по прежнему применять личную форму - пользователь, он же юзер. Тем более, что в этой главе речи о демонах или псевдопользователях не будет.

Каждый пользователь имеет фиксированный набор закрепленных за ним атрибутов, собственно и составляющий содержание его учетной записи в базе данных. Каких? А давайте подумаем, какие атрибуты пользователю необходимы.

И тут самое время вспомнить, что даже на суперперсональной машине пользователей всегда больше одного. И, значит, система должна их как-то различать между собой. Как - да вполне по человечески, по именам. И потому первый атрибут пользователя - это его имя, именуемое также login. Именно предложение ввести свое пользовательское имя - это первое, что видит пользователь после загрузки системы.

Конечно, не нужно чересчур очеловечивать компьютеры (говорят, что они этого не любят). И считать, что он волшебным образом опознает вас по имени, данному при рождении (или крещении). Нет, имя пользователя - это просто некий набор (почти) любых символов. Конечно, и собственное имя в этом качестве использовать не возбраняется. Ведь единственное общее к нему требование - уникальность. Правда, в некоторых системах существует ограничение на длину login (иногда - не менее M и не более N символов). И обычно принято ограничиваться символами алфавитно-цифровыми.

Так что имя пользователя в системе - условно. Более того, для системы по большому счету имя это абсолютно до лампочки. Потому что опознает она пользователя на самом деле не по нему, а по поставленному ему в соответствие числовому идентификатору (UID - User IDentificator). Как правило, это - просто порядковый номер пользовательской учетной записи, причем для обычного пользователя - начиная с некоего минимального числа. В одних системах это может быть 100, в других 500, в большинстве Linux-дистрибутивов - 1000. Младшие идентификаторы система резервирует для своих целей - для тех самых виртуальных юзеров, о которых говорилось ранее. И еще - идентификатор 0 всегда и везде в POSIX-мире бронируется для поминаемого выше всуе имени божьего - то есть для root'а.

Стоит добавить, что и само имя пользователя не обязательно, достаточно, чтобы аккаунт имел уникальный числовой идентификатор. Соответствие же его какому-либо имени обеспечивается не самой системой, а одной из дополнительных (хотя и необходимых) программ (системной библиотекой glibc - просто запомним пока это слово, как колдовское заклинание).

Так почему же все-таки имя пользователя считается непременным атрибутом его аккаунта? Да все потому, что лицо у Unix - человеческое. И создатели системы прекрасно понимали, что запомнить имя alv для пользователя гораздо проще, чем идентификатор 1276. Вот ему и пошли на встречу.

Итак, для вхождения пользователя в систему ему необходимо указать свое имя (login). Однако как система опознает, что пользователь имя_рек - именно тот, за кого себя выдает? И не возникнет ли ситуация, как в хармсовском анекдоте, когда Гоголь переоделся Пушкиным, а Державин решил, что это и вправду Пушкин, и сходя в гроб, благословил его?

Нет, не возникнет. Потому что система, с бдительностью часового советских границ, запросит у пользователя подтверждения того, что он именно тот, за кого себя выдает. По аналогии с вопросом часового, легко догадаться, что подтверждение это именуется паролем (по ихнему, по буржуинскому, - password). В качестве какового выступает опять же некая последовательность символов (не обязательно чисто алфавитно-цифровых - пущей секретности ради рекомендуется разбавлять их специальными символами, а для литер применять смесь нижнего и верхнего регистра), которую, теоретически рассуждая, никто, кроме пользователя, знать не может. И которую пользователь должен затвердить, как "Отче наш, иже еси в винной смеси" ((C) "Всепьянейшая литургия", из вагантов). Этот пароль являет собой третий непременный атрибут пользователя.

В данном случае фраза "никто не должен знать" понимается в буквальном смысле - даже всемогущий root в общем случае пользовательский пароль знать не должен и узнать обычными способами не может. Потому что пароль хранится в односторонне зашифрованном виде, и из его шифрованного представления вычислить вводимую последовательность символов нельзя (хотя можно подобрать - но это уже из области "грязного крэка").

Так вот, при входе пользователя в систему (название этого процесса - авторизация, регистрация, существует и еще несколько эвфемизмов) она (система) сначала, в ответ на ввод логина, проверяет, а зарегистрировано ли это имя (точнее, соответствующий ему численный идентификатор) в базе данных ее пользователей. А затем, после ввода пароля - по той же базе данных определяет, соответствует ли этот пароль зафиксированному для данного пользователя. И в благоприятном случае - разрешает вход в сокровенные свои недра, иначе же - после более или менее длительного промежутка времени предлагает повторить процедуру авторизации (в некоторых системах количество возможных повторов ограничено).

О правилах выбора пароля написано немерянно - в том числе и экспертами по компьютерной безопасности. Поэтому я не буду отвлекать читателя своими дилетантскими рассуждениями. Замечу только, что даже самый простой с точки зрения устойчивости к взлому пароль - на несколько порядков лучше, чем его полное отсутствие. Хотя в принципе в Linux (и других системах) можно и обойти необходимость ввода пароля, и учредить беспарольный вход для любого пользователя (в том числе и для root'а). Однако делать это можно только на машине сугубо персонального назначения, не подключенной ни к какой сети вообще. Да и то - не стоит, дабы не выработалась вредная привычка. Если же ввод пароля ну очень напрягает - есть несколько возможностей автоматизировать процесс регистрации, не погружаясь в болото беспарольного плюрализма (об одном из таких способов рассказывается в Заметках о Linux-консоли).

А пока посмотрим, что же происходит после успешного завершения авторизации. Ясно, что пользователь входит в систему не для чего-то там нибудь, а дабы выполнить какие-то действия (немного поработать, например, - пока кушать не позовут). А для этого ему требуется какая-то рабочая среда. И программа. таковую обеспечивающая, запускается системой после принятия логина и пароля. В подавляющем большинстве случаев такой программой будет т.н. командная оболочка, о чем подробно рассказывается в соответствующем разделе сайта. Пока же отметим только, что имя этой командной оболочки - и есть следующий атрибут пользовательского аккаунта.

Скорее всего деятельность пользователя в системе включает в себя ввод и обработку каких-либо данных. Которые нужно куда-то записывать. Поскольку POSIX-системы - многопользовательские по своей сути, данные нашего пользователя не должны путаться с данными других таких же юзеров (и особенно суперюзера). Из чего следует, что после успешной авторизации наш пользователь должен получить некоторое место для записи своих данных, куда другие, без его дозволения, соваться не моги. И действительно, такое место, именуемое домашним каталогом пользователя (символически обозначаемым как ~/ или $HOME - это несколько разные вещи, но об этом - как-нибудь в другой раз) - следующий атрибут учетной записи.

И последнее. Многопользовательская система предполагает не только защиту пользователей от зловредного влияния других пользователей, но и обмен данными между группой пользователей-товарищей. Которая так и называется - группой пользователей, или просто группой. И любой пользователь по умолчанию включается минимум в одну группу - может быть, свою собственную, не имеющую других членов. Хотя в большинстве Linux-дистрибутивов такая умолчальная группа обычно называется users и включает в себя всех реальных пользователей, кроме root'а (а во FreeBSD, например, каждый пользователь по умолчанию приписывается к своей собственной, одноименной ему, группе). И имя этой группы - еще один непременный атрибут учетной записи пользователя.

Как и в случае с именем пользователя, имя группы - лишь одна из метафор, очеловечивающих поля пользовательского аккаунта. И потому в последнем фигурирует, собственно, не имя (например, users), а соответствующий ей численный идентификатор. Опять же аналогично идентификаторам пользователей, это обычно - порядковый номер вновь создаваемой группы. И начальной точкой отсчета выступает какое-либо число. Например, во FreeBSD номера пользовательских групп начинается с 1001 - все, что меньше, резервируется для системных псевдопользователей (мы же помним, что в отношении их не допускается никакой дискриминации, и они тоже являются членами всяких разных групп). А в Linux группа реальных пользователей users обычно имеет идентификатор 100.

Каким образом пользователь может ознакомиться с атрибутами своей учетной записи? Ответ, как обычно в POSIX-системах, будет таким: разными способами. Правда, для этого ему нужно не только авторизоваться в системе (чему мы как-будто бы уже научились), но и суметь дать несколько команд. Собственно, команды - тема следующей главы, и потому пока я дам несколько примеров на уровне заклинаний (надеюсь, что скоро они перестанут казаться чем-то сверхъестественным).

Команда, позволяющая пользователю получить информацию о самом себе - это echo. Она просто выводит на экран те данные, которые получила в качестве ввода (например, с клавиатуры). Однако если за ней дать некоторые специальные символы (забегая вперед, скажу, что они называются переменными), то echo (как это ей и положено по званию) отразит на экране значения этих переменных.

Раз уж речь, опережая события, зашла о командах - начинающему пользователю (а читают эти строки, скорее всего, именно совсем начинающие - прочим уже давно надоело пережевывание известных им вещей) очень рекомендуется понемногу запоминать их - все они потребуются в ходе дальнейшего изложения.

Итак, первый резонный вопрос юзера - кто я? Для ответа на него командуем:

$ echo $USER

где символ $ после команды echo и пробела и обозначает, что все следующие символы являются переменной, а USER - имя данной конкретной переменной. Так вот, на эту команду мы незамедлительно получим ответ:

alv

свидетельствующий, что давший ее пользователь носит имя (login) alv. Ответом на запрос о программе, выполняющей роль пользовательской среды

$ echo $SHELL

будет ее имя, вместе с путем к исполняемому файлу:

/bin/zsh

А если пользователя заинтересует место хранения собственных данных, следует спросить так:

$ echo $HOME
/home/alv

где /home/alv - и есть домашний каталог пользователя alv (совпадение логина пользователя и имени его домашнего каталога - не обязательно, но, как правило, имеет место быть).

Однако наиболее полную информацию о себе пользователь может получить прямым просмотром базы пользовательских аккаунтов. База данных хранится в файле passwd каталога /etc (вопросы, что такое файл, каталог и почему его имя должно предваряться символом слэша - пока замнем для ясности). Для наших целей достаточно знать, что /etc/passwd - простой текстовый файл, доступный для чтения кому угодно - был бы инструмент для чтения. Забегая вперед, скажу, что, как и следовало ожидать, такой инструмент в POSIX-системах предоставляется, и даже не один. Хотя сейчас нам много не нужно - достаточно одного, наиболее удобного. Таковым в Linux выступает обычно команда less, а во FreeBSD - команда more. И та, и другая требуют указания еще и имени просматриваемого файла. То есть в нашем случае это будет выглядеть так:

$ less /etc/passwd

или

$ more /etc/passwd

Вывод данной команды может выглядеть таким образом (на примере Archlinux, где этот файл умолчально устроен проще всего):

root:x:0:0:root:/root:/bin/zsh
bin:x:1:1:bin:/bin:
daemon:x:2:2:daemon:/sbin:
mail:x:8:12:mail:/var/spool/mail:
ftp:x:14:11:ftp:/home/ftp:
nobody:x:99:99:nobody:/:
alv:x:1000:100::/home/alv:/bin/zsh
lis:x:1001:100::/home/lis:/bin/zsh

Из чего можно видеть, что мы действительно имеем дело с базой данных, хотя и очень простой по структуре. Любая же база данных, как известно, состоит из уникальных записей (или строк), относящихся к одному объекту базы, и составляющих запись полей, характеризующих свойства (сиречь атрибуты) этого объекта. Поля записи обязательно разделяются чем либо - в зависимости от формата базы это могут быть пробелы, символы табуляции, двоеточия (colon), точки с запятой (semicolon) и что-то еще.

Так и здесь: каждая строка представляет запись одного пользовательского аккаунта - первым идет запись для суперпользователя, как ему по должности положено, следующие - это те самые псевдопользователи, которых мы договорись пока не трогать. а вот последняя запись - это уже аккаунт реального пользователя.

Наборы символов, разделенные символом двоеточия (:) - поля этой записи, описывающие каждый атрибут учетной записи (то есть colon - разделитель полей в нашей базе). Считаем - атрибутов этих оказывается 7 (хотя и не все поля, как скоро станет ясно, обязательны к заполнению; и, напротив, некоторых атрибутов пользовательского аккаунта в этой базе мы не увидим). Некоторые атрибуты нам уже знакомы, некоторые - нет. Тем не менее, пройдемся по ним по всем - ведь повторенье мать ученья.

На первом месте каждой записи, в соответствие с гуманистической ориентацией POSIX-систем, стоит имя пользователя, реального или виртуального, - не важно. Важно, что одинаковых мы среди них не найдем (в принципе можно создать пользователей с одинаковыми именами и разными численными идентификаторами, но ничего, кроме путаницы, это не даст).

Второе поле отведено под пароль пользователя. В данном случае оно заполнено символом x (или * - что почти равноценно). Это не значит, что нас посылают по известному на Руси адресу. Просто в файле /etc/passwd, вопреки его названию, реальных паролей не содержится - к этому вопросу мы еще вернемся.

Итак, с паролями пока проехали. Третье поле - самое важное: это и есть числовой идентификатор пользователя, служащий для однозначного его опознавания системе. Аналогичен и смысл четвертого поля - только здесь стоит численный идентификатор основной группы, к которой пользователь приписан.

Пятое поле записи - так называемый комментарий. Для root'а и псевдопользователей здесь стоят некие условные значения, совпадающие с их именами. Что на деле отнюдь не обязательно - форма заполнения этого поля свободная. Так, для реальных пользователей здесь часто указываются их всамделишние ФИО по паспорту и любые другие данные, типа номера телефона. В приведенном примере для реальных пользователей "пункт пятый" просто оставлен пустым. так как своего имени и фамилии я пока не забыл. А во FreeBSD некоторые утилиты создания учетных записей пользователей обязательно потребуют придать этому полю какое-либо значение.

Шестое поле - домашний каталог пользователя. Для псевдопользователей тут опять же стоят некие непонятные имена - в некоторых случаях оно может быть и пустым. А вот для пользователей реальных здесь указываются каталоги. куда они и в самом деле пишут свои данные. Суперпользователь в этом отношении похож на обычных пользователей - его домашний каталог /root также предназначен для собственных данных администратора. Как будет ясно в дальнейшем, выполнять от лица root'а какие-либо всамделишние пользовательские задачи - занятие крайне нездоровое, и потому домашний каталог суперюзера заполняется (обычно) исключительно его личными конфигурационными файлами (до которых тоже со временем дойдет дело).

И наконец, последнее поле записи - это имя той самой программы, которая выполняет роль среды обитания пользователя, запускаясь первой сразу же по его авторизации (опять несколько опережу события - в общем случае эта программа называется login shell). Можно видеть, что у псевдопользователей это поле пусто - они в такой среде не нуждаются. А вот root и тут проявляет свою человеческую сущность - и у него login shell имеется.

Вообще говоря, и для обычных пользователей заполнять седьмое поле записи не обязательно: в этом случае в качестве login shell для них будет запущена некоторая предопределенная по умолчанию среда (в POSIX-системах она всегда носит имя /bin/sh, хотя в реальности это могут быть разные программы).

В общем, из рассмотрения содержимого файла /etc/passwd мы узнали о пользователях, имеющих право на вхождение в данную систему, почти все. Однако слабо освещенным остался вопрос о группах - ведь вместо человечьего имени их в соответствующем поле стоит лишь нечленораздельный идентификатор. Что ж, дело легко поправимо - сведения о группах также хранится в специально предназначенном для этого файле, который называется (кто бы мог подумать) group и находится в том же каталоге /etc, где мы на него и посмотрим:

$ less /etc/group

Вивисекция нашего подопытного кролика (системы, на которой я в данный момент работаю) он выглядит следующим образом (многоточиями я заменил группы псевдопользователей, которые в данный момент нас не волнуют - на самом деле здесь полторы дюжины записей):

root::0:root
...
wheel::10:root,alv,lis
...
users::100:alv,lis
sound:x:101:alv,lis

Можно видеть, что структура этого файла аналогична базе данных пользователей, но еще более проста - в каждой записи лишь четыре поля. Первое, как и следовало ожидать, - имя группы пользователей. Второе поле - пустое. В принципе оно предназначалось когда-то для пароля группы, однако по своему назначению ни в одной POSIX-системе, насколько мне известно, не используется (на причинах этого останавливаться не буду - но поверьте, что резоны к тому есть). Третье поле - числовой идентификатор группы, тот самый, который мы уже видели в четвертом поле файла /etc/passwd.

Ну а здесь четвертое поле - это список пользователей, входящих в каждую группу. Как уже говорилось, каждый пользователь является членом минимум одной группы, которая называется основной. Однако это не мешает ему состоять, при необходимости, и в каких-либо иных группах. И это - очень эффективный механизм разграничения доступа к данным (или напротив, организации их совместной обработки разными пользователями).

Легко видеть, что root-оператор является единственным членом своей собственной группы (это характерно для большинства псевдопользовательских групп). А группа users объединяет двух пользователей, которые в моей системе являются реальными - и это их основная группа. Кроме того, оба они состоят еще и членами группы sound - без этого, как будет показано дальше, эти юзеры были бы лишены счастья слушать мою коллекцию авторской песни (и любые другие звуки - тоже).

Правда, и тот, и другой пользователь представляют собой мою скромную персону. Только не подумайте, что я страдаю раздвоением личности: просто alv - это тот пользователь, от лица которого я выполняю свою работу (например, пишу эти строки), а аккаунт lis предназначен для всяких экспериментов с системой, более или менее нездоровых (зачем - станет ясным из одного из последующих разделов).

Особого внимания заслуживает группа wheel. Во многих Linux-дистрибутивах (и, добавлю, во всех BSD-системах) временно получить права администратора могут только члены этой группы (как - скажу чуть позже). В других системах это не так - впрочем, положение легко изменить как в ту, так и в другую сторону.

Осталось прояснить вопрос с паролями. Мы уже видели, что в файле /etc/passwd никаких паролей на самом деле нет. На вопрос "почему" легко ответить словами Балбеса из "Операции Ы" - чтоб никто не догадался. Ведь если мы, будучи простым пользователем, легко получили доступ к содержимому столь, казалось бы, важного файла, то и для злоумышленника это труда не составит. Ибо право чтения файла /etc/passwd имеет любой пользователь (хотя вносить изменения в большинство записей и полей может только root). И потому для хранения паролей в большинстве дистрибутивов Linux предусмотрено специальное потайное место - файл /etc/shadow.

Впрочем, просмотреть его сразу нам не удастся - ведь мы зашли в систему в качестве обычного пользователя, а ему доступ к /etc/shadow запрещен под любым соусом (в том числе и просто для чтения). Что делать?

Парой абзацев выше я обмолвился о том, что есть возможность временно получить права администратора. Для этого предназначена специальная команда - su (что иногда трактуют как аббревиатуру от Super User, но на самом деле означает Set UID, потому что она позволяет получить права не только администратора, но и любого другого пользователя). Данная в самой простой форме, как

$ su

она, однако, предоставляет доступ именно к суперпользовательскому аккаунту, что чего сначала будет запрошен соответствующий пароль. Если мы его знаем и введем правильно - казалось бы, ничего не произойдет, никаких сообщений не последует. Правда, если присмотреться - окажется, что изменился вид приглашения командной строки (как - зависит от настроек командной оболочки, на которых пока задерживаться не будем). И это должно послужить сигналом к повышенному вниманию - с этого момент наш обычный пользователь наделен полномочиями root'а.

Почему требуется повышенное внимание? Да потому, что полномочия root'а, как будет рассказано в разделе о файлах и их атрибутах, практически не ограничены. И он легко может по ошибке совершить какие-нибудь непоправимые действия, вплоть до полного разрушения системы.

Мы, однако не собираемся делать ничего брутального (или даже потенциально опасного). Нам бы только на файл с паролями полюбоваться - ведь теперь мы имеем на это право. Так что

$ less /etc/shadow

и вперед, удовлетворять свое любопытство (к тому же не без пользы).

Что же мы видим в нашем /etc/shadow? Да примерно такую же базу данных, что и раньше, о восьми полях. Первое поле - имя пользователя, реального или виртуального. Второе у псевдопользователей пусто. А у реальных (в том числе и у root'а) заполнено нечленораздельным набором символов. Это и есть пароль. Его представление в базе не имеет ничего общего с тем, что вводит пользователь при авторизации. Ибо пароль помещается в базу в односторонне зашифрованном виде. То есть по его представлению восстановить последовательность символов для ввода невозможно (как говорят эксперты по безопасности - невозможно в принципе; глядя на свой /etc/shadow, охотно им верю). И односторонняя шифровка пароля - это первый эшелон обороны в Linux.

Если пароль нельзя расшифровать, то его можно подобрать. И представление зашифрованного пароля может помочь в этом черном деле - каким образом эта абракадабра поможет злодею-хацкеру, ума не приложу, но так говорят эксперты. А потому доступ к файлу /etc/shadow закрыт для всех, кроме root'а - даже для просмотра. Что являет второй эшелон обороны Linux-системы.

Я не случайно в последних абзацах подчеркиваю, что речь здесь идет именно о Linux. Потому что такой механизм защиты (он называется механизмом теневых паролей - shadow passwords) принят именно в этой ОС (по крайней мере, в большинстве ее дистрибутивов). Как обстоит дело в проприетарных Unix'ах, не знаю. А вот в BSD-системах механизм теневых паролей не используется, а эшелонированная оборона достигается другим способом. Там в качестве дополнительного кольца защиты используется пара файлов: /etc/master.passwd и /etc/passwd. Первый из них, доступный только для root'а, содержит все данные о пользователе, включая его пароль, во втором, открытом для всеобщего обозрения, есть все то же самое - но без пароля.

К слову сказать, во FreeBSD оба эти файла существуют в двух вариантах - текстовом (собственно /etc/master.passwd и /etc/passwd), доступном для просмотра, и неудобочитаемом бинарном (/etc/master.spwd.db и /etc/spwd.db) Именно последние используются для идентификации пользователей: изменения, внесенные (скажем, в обычном редакторе) в текстовые варианты баз пользовательских аккаунтов, должны быть соответствующим образом преобразованы. Что, кроме всего прочего, может рассматриваться как еще один эшелон обороны.

Завершая разговор о паролях, еще раз обратимся к файлу /etc/master.passwd. Просмотр его показывает, что кроме полей, общих с файлом /etc/passwd, в нем можно видеть еще три. Они хранят информацию о времени последнего изменения пароля, а также могут определять всякие дополнительные сроки действия пароля, Например, здесь можно задать дату, когда пароль должен быть изменен, или дату, после которой вход по этому паролю в систему невозможен. Неконец, очень важное поле class (например, russian) позволяет определить некоторые свойства, общие для ряда пользователей.

Таким образом мы плавно подошли к вопросу о том, как создаются и изменяются пользовательские аккаунты. Или, иными словами, кто определяет пользователей системы, и каким образом.

В большинстве дистрибутивов Linux в ходе инсталляции создаются минимум две учетные записи - администратора (поскольку имя его фиксировано - root, то при создании ее запрашивается только пароль) и обычного пользователя, с определением его имени и пароля (в некоторых случаях для такого пользователя можно задать его еще и основную группу). И потому может показаться, что пользователи возникают в системе сами собой, по принципу непорочного зачатия. Однако это не так.

Правда, пользователь root действительно существует в системе от века, аналогично господу богу. Вернее, пользователь с UID 0 - как уже говорилось, сама по себе система об именах пользователей не имеет никакого понятия - за их соответствие идентификаторам отвечает главная системная библиотека glibc, И в процессе установки Linux возможна ситуация (а иногда она и действительно возникает - например, в ходе самостоятельной сборки from Scratch), когда система еще не догадывается о существовании пользователя с именем root. И что же? Она прекрасно справляется с его распознаванием администратора по его нулевому идентификатору, наделяя соответствующими полномочиями.

Все же прочие пользователи являются порождением нашего бога из машины - root-оператора. Причем косвенно это относится даже к псевдопользователям-демонам (почему - станет ясно со временем). А уж обычные реальные и виртуальные (почтовые и ftp-клиенты) пользователи порождаются root'ом непосредственно.

Впрочем, сейчас нас волнуют только реальные пользователи. Для их создания (то есть учреждения соответствующих учетных записей) в Linux существует ряд инструментов (вплоть до обычного текстового редактора), на которых сейчас останавливаться неуместно. Замечу только, что в процессе создания аккаунта обязательно задается только его имя и (или) идентификатор, Хотя дополнительно можно (а обычно и нужно - в смысле, хорошо бы) определить также его домашний каталог, пользовательскую среду, основную и дополнительные группы, а также пароль.

В рамках элементарного введения в предмет интересней вопрос, а что может сделать сам пользователь в отношении своей учетной записи? Немногое - фактически только сменить пароль и пользовательскую среду (последнюю - не всегда). Первое делается командой /usr/bin/passwd, второе - /usr/bin/chsh.

На деталях обеих процедур я пока останавливаться не буду. Потому что в текущем контексте предлагаю задаться вопросом: а каким образом пользователь в принципе способен на выполнение этих действий? Ведь обе команды вносят изменения в файлы, открытые для изменения только администратору (а /etc/shadow к тому же не доступен юзеру даже для чтения). Чтобы на этот вопрос ответить, следует рассмотреть ознакомиться с понятиями файлов и их атрибутов, чем мы займемся в ближайшее время. А пока обратимся к понятию процесса.

Процесс пошел

Над нами солнце встает,
Процесс сам по себе идет...
Тимур Шаов

Понятие процесса принадлежит к тем сущностям, кажущимся интуитивно ясными, и которым, тем не менее, нелегко дать строгое (и при этом удобопонятное) определение. Наиболее распространенное их них - это представление процесса как программы в стадии ее выполнения (Андрей Робачевский. Операционная система Unix. СПб: БХВ-Петербург, 2000).

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

Все сказанное может показаться настолько кучерявым, что вызывает естественный вопрос: а за каким зеленым обычному (=конечному) пользователю-POSIX'ивисту вообще нужно знать о каких-то там процессах? Ведь пользователь MacOS (BSD-системы в своей основе - то есть тоже имеющей понятие процесса) спокойно обходится без этих знаний. Да и в Windows процессы, скорее всего, протекают:-), однако побуждений знакомиться с ними у пользователя не возникает.

Причин к рассмотрению понятия процесса с позиций пользователя несколько. Первая, гносеологическая, - просто не кажется лишним знать, что там у этого пользователя в системе происходит (а, как уже говорилось, все, что происходит в системе - пользователя ли, админа, или программера - суть процессы).

Однако есть и несколько более практически важных моментов. Во-первых, понимание взаимоотношений между процессом и запущенной программой очень не вредно при всякого рода настроечных меропрятиях - дабы не удивляться, почему командная оболочка bash (для примера) в консоли ведет себя одним образом, запущенная в окне терминала - другим, а в некоторых иных случаях - вообще третьим (например, категорически отказывается находить исполняемые файлы). А во-вторых, представление о процессах и способах влияния на них часто оказывается незаменимым в аварийных ситуациях. Так что давайте уделим этому вопросу некоторое внимание.

Итак, повторюсь: процесс - это все, что происходит в системе. А все происходящее в системе - либо порождено ею самой, либо - одним из ее пользователей. Соответственно этому, процессы бывают системные и пользовательские. На первых мы здесь задерживаться не будем. Скажу только, что запуск системных процессов осуществляется ядром ОС (не важно, какой - Linux там, или FreeBSD), и их задача - управление ресурсами машины (доступом к процессору, памятью и т.д.). Важно только помнить, что с системными процессами не коррелируют никакие программы в обычном понимании этого слова (кроме ядра, конечно, которое само по себе - программа). То есть: в системе не существует отдельного исполнимого файла, ответственного за запуск программы, исполняемой системным процессом (внятно выразился?) - все они запускаются ядром, которое тоже являет собой просто исполнимый файл, хотя и не совсем обычный по своим функциям.

Из сказанного выше существует единственное исключение, и это - процесс init, прародитель всех прочих процессов, почему его и относят к системным процессам. Хотя он и имеет ассоциированный с ним исполнимый файл (имя его всегда и везде в POSIX-системах - /sbin/init, хотя реально это могут быть разные программы), который вызывается по окончании загрузки системы и порождает все остальные процессы. В том числе - процессы getty, открывающий терминал, и login, ответственный за авторизацию пользователя на этом терминале. То есть процесс login - тот самый, который предлагает ввести пользователю свое имя и пароль.

Процесс login относится уже к пользовательским процессам. Которые, в отличие от системных, всегда ассоциированы с неким исполнимым файлом. Для процесса getty это будет, например, /usr/libexec/getty, а для процесса login - нечто вроде /usr/bin/login.

Процессы типа getty и login относятся к категории неинтерактивных, то есть тех, которые не привязаны к какому-либо терминалу, и на которые пользователь не может влиять непосредственно (общение с ними возможно только посредством т.н. сигналов, о которых будет говориться несколько позже). Неинтерактивные процессы именуются иногда демонами (daemon). Термин этот не несет в себе мистического смысла, представляя простую аббревиатуру (Disk And Execution MONitor). Они инициируются самой системой при ее загрузке обычным образом (запуском соответствующих программ) и функционируют в фоновом режиме, активизируясь при необходимости совершения некоего действия - печати, доставки почты и т.д.

Есть еще и процессы интерактивные, отличающиеся тем, что привязаны к некоторому терминалу, через который пользователь может с ними взаимодействовать. Так, авторизовавшись в системе, пользователь (реальный - о виртуальных далее речи не будет) запускает тем самым свой первый процесс - свою "умолчальную" командную оболочку (login shell; в Linux это скорее всего bash, в BSD-системах - /bin/sh или вариации на тему C-Shell). Это - процесс интерактивный, он привязан к определенному терминалу, на котором авторизовался пользователь. И последний может с ним взаимодействовать определенным образом (вводить команды, например).

Как же процесс соотносится с запустившим его пользователем? Для ответа нужно ознакомиться с атрибутами процесса. Сразу следует отметить, что любой процесс, системный ли, пользовательский, интерактивный или неинтерактивный, имеет следующие атрибуты:

А вот для рассмотрения их смысла нам придется обратиться к команде ps, которая и выводит информацию о процесса в виде, доступном пониманию пользователя.

Команда ps имеет множество опций, из которых нам сейчас потребуется одна: -u (впрочем, символ дефиса в этой команде можно опустить), она выведет весьма подробную информацию о процессах, запущенных данным пользователем на текущей виртуальной консоли (с помощью иных опций можно получить сведения о чужих процессах и о процессах на иных консолях, но для нас пока это не важно). Опять же, далеко не все из этих сведений нам сейчас интересны, поэтому задержим внимание только на существенных в контексте данной статьи.

Итак, сразу после авторизации пользователя в ответ на команду ps -u будут выведены две строки, состоящие из нескольких колонок, в числе которых - следующие:

USER       PID           COMMAND
alv       5408            -zsh
alv       5598            ps -u

Первая колонка указывает на имя хозяина процесса - как правило (хотя и не обязательно), таковым выступает пользователь, этот процесс запустивший. Вторая колонка - уникальный идентификатор процесса, третья - имя программы, породившей процесс; очевидно, что в момент сразу после авторизации это будет только командная оболочка - -zsh (символ дефиса перед именем показывает, что это - не просто некая программа, а login shell). Ну и, конечно, сама команда ps - ей ведь тоже соответствует свой процесс, хотя и завершившийся. Так вот, именем хозяина определяются отношения процесса к файлам и иным процессам. То есть процесс получает те же привилегии, которые имеет его хозяин.

Сказанное относительно принадлежности процессов относится не только к интерактивным пользовательским процессам, но и к демонам. Хотя их, казалось бы, никто не запускает, но и они имеют своих хозяев. Это - те самые псевдопользователи, о которых я упоминал в предыдущем разделе.

Однако не обязательно хозяином процесса является запустивший его пользователь (или псевдопользователь). Это легко проверить, дав от лица обычного пользователя команду passwd для смены его пароля, и посмотрев атрибуты соответствующего процесса:

root      5617          passwd

Как могло получиться, что обычный, непривилегированный, пользователь запустил процесс, хозяином которого оказался root? Это станет понятно из дальнейшего. Пока же скажу, для чего это нужно. Ясно, что любой пользователь должен иметь возможность сменить свой пароль. Однако для этого ему придется внести изменения в базу пользовательских аккаунтов, а файл /etc/passwd, как мы помним, открыт для изменения только администратору. И потому пользовательский процесс, соответствующий команде passwd, наделяется правами, в обычной жизни этому пользователю недоступными.

Более строго это можно описать так. Как было сказано, система работает не с именем пользователя (по большому счету оно ей до лампочки), а с его идентификатором (UID). Именно он обычно наследуется процессом в качестве идентификатора "хозяина" (и потому называется реальным UID). Однако у процесса есть еще и т.н. эффективный идентификатор "хозяина" (EUID), который собственно определяет привилегии данного процесса. Обычно UID и EUID совпадают. Если посмотреть их с помощью команды ps -l для процесса zsh, в поле UID мы увидим цифру 1000 (в данном случае - это UID юзера alv). Однако для процесса passwd поле это будет содержать значение 0, то есть эффективный идентификатор его унаследован не от пользователя, а от root-оператора.

Кроме реального и эффективного идентификатора пользователя, для процесса устанавливаются еще два атрибута принадлежности - реальный и эффективный идентификаторы группы (GUID и EGUID, соответственно). Смысл их аналогичен, но они наследуют права доступа процесса для группы, к которой принадлежит запустивший процесс пользователь, о чем уже говорилось в предыдущем разделе.

Каждый пользовательский процесс порождается каким-либо другим процессом - в конечном счете, в первооснове их всех лежит процесс init. Как нетрудно догадаться, порождающий процесс называется также родительским, или материнским, а порожденный - дочерним (по русски получается несоответствие грамматических родов, но "сыновний" звучит не очень привычно). И идентификатор родительского процесса (PPID) - также один из важных атрибутов пользовательского процесса, в чем мы немедленно и убедимся.

Каждый процесс в своем существовании проходит стадии зарождения, исполнения (часто с порождением новых процессов) и завершения (отмирания). Как все это выглядит в реальности, можно рассмотреть на примере самого первого пользовательского процесса, запускающего его командную оболочку по умолчанию (login shell). Для этого опять обратимся к выводу команды ps в форме

$ps l

для того, чтобы видеть идентификаторы родительских процессов (за их показ отвечает опция l). Дополнительно отфильтруем (что это такое, будет объясняться в одной из следующих глав) процессы, относящиеся только к одной из виртуальных консолей, на которой в данный момент никто не авторизован - для определенности, второй по счету (устройство v1):

$ ps l | grep v1

Для пустой, то есть не несущей пользовательского сеанса, консоли вывод последней команды будет примерно таким (я опускаю столбцы, содержание которых в данный момент для нас не существенно):

UID	PID	PPID	COMMAND	
0	491	1	/usr/libexec/getty

То есть мы имеем в этой консоли единственный процесс getty, принадлежащий суперпользователю (UID 0), имеющий идентификатор 491 и порожденный процессом init (PPID 1 - я забыл сказать, что этот процесс-первопредок всегда имеет идентификатор 1).

После же авторизации вывод той же команды приобретет такой вид:

UID	PID	PPID	COMMAND	
0	491	1	login
1001	1949	491	-zsh

Он свидетельствует, что в нашей экпериментальной консоли запущено уже два процесса - login, показывающий, что на ней зарегистрирован некий пользователь, и командная оболочка последнего - zsh; символ дефиса перед именем последней указывает, что это - не просто некий экземпляр его, а login shell,

И еще прошу обратить внимание: PID процесса, ассоциированного с командой login, тот же, что и для процесса, исполнявшего перед этим команду getty, Это свидетельствует, что авторизация пользователя не привела к запуску нового процесса - напротив, замещение одной команды другой произошло в рамках одного и того же процесса (одна из причин, почему не следует отождествлять процесс и программу). А вот идентификатор для zsh - другой: командная оболочка исполняется в порожденном (см. значение PPID) процессе.

Вытеснение одной программы другой в рамках единого процесса - типичный способ запуска новой программы. Так, если из командной строки zsh мы запустим, например, текстовый редактор joe, то в ответ на

$ ps l | grep v1

увидим следующую картину:

UID	PID	PPID	COMMAND	
0	491	1	login
1001	1949	491	-zsh
1001	2208	1949	joe

которая может создать впечатление, будто бы процесс zsh непосредственно породил процесс joe. Однако это не так. К сожалению, проиллюстрировать динамику зарождения процесса не представляется возможным (по крайней мере, я не знаю, как), поэтому прошу поверить на слово - не мне даже, а авторам фундаментальных руководств по Unix.

На самом деле родительский процесс перво-наперво порождает свою собственную копию - в данном случае еще один экземпляр командной оболочки zsh (не потому ли первый Shell Борна и получил свое имя, что вызов программы из оболочки напоминает последовательную серию скорлупок?). И различаются в момент зарождения родительский и дочерний процессы фактически только своими PPID (ну и собственными идентификаторами, разумеется). И лишь затем в рамках дочернего процесса осуществляет запуск на исполнение собственно нашей новой программы.

В свою очередь, новый процесс также может выступать в качестве родительского. Так, редактор joe обладает способностью запуска из своей среды программ оболочки. В образовавшемся имитаторе командной строки можно запустить какую-либо другую программу, скажем, поиск файла командой find. Если в процессе поиска посмотреть распределение процессов (например, с другой виртуальной консоли), можно будет увидеть, что процесс joe породил как бы свою копию - но уже с иными идентификаторами (при этом PPID копии, как и следовало ожидать, равен PID оригинала).

Как станет ясным из дальнейшего, понимание механизма создания процессов и запуска программ очень важно. В одной из следующих глав речь пойдет о командных оболочках и вызываемых из них командах. Так вот, большинство команд запускается именно таким образом, как описано выше. Что влечет за собой то, что запущенная программа наследует свойства (т.н. программное окружение) не той командной оболочки, которая выступает в качестве пользовательской, а ее копии, которую команда заместила в рамках нового процесса. И вполне возможно, что программное окружение материнского и дочернего шеллов окажется разным, что не может не сказаться на выполнении команды.

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

Управлять процессами можно двояко. Первый спопоб - это отдача прямой команды. Например, выход из редактора joe автоматически приводит к отмиранию соответствующего ему процесса, комбинация клавиш Control+Z в большинстве случаев переводит процесс в фоновый режим, и так далее.

Очевидно, что прямое управление возможно только в отношении интерактивных пользовательских процессов, имеющих реально запустившего их хозяина и управляющий терминал, с которого этот процесс запущен. С процессами-демонами же пользователь может взаимодействовать только посредством посылки им неких сигналов. Сигналы - это также единственный способ ликвидировать безнадежно зависший процесс, который невозможно остановить штатными средствами.

Посылка сигналов осуществляется командой kill. Она требует указания имени сигнала (или соответствующего ему численного значения) и номера процесса, на который нужно оказать воздействие. К этому вопросу нам еще придется вернуться. Пока же запомним, что зависший процесс можно попробовать убить следующим образом:

$ kill -15 ### 

где ### - номер (PID) убиваемого процесса, а 15 - численное значение для сигнала TERM (от terminated). Соответственно, команда эта может быть дана и в форме

$ kill -TERM ### 

По получении сигнала TERM программа, ассоциированная с процессом ###, пытается по возможности корректно завершить свою работу с записью несохраненных данных.

Однако это ей не всегда удается - в этом случае процесс, соответствующий зависшей программе, продолжает существовать. И тут приходится прибегать к более жесткой форме:

$ kill -9 ### 

или

$ kill -KILL ### 

Сигнал KILL не может быть проигнорирован никаким процессом - его получение знаменуется немедленным и неизбежным завершением работы зависшей программы. Разумеется, ни о каком сохранении данных при этом речи идти не может, поэтому применение этого сигнала - крайняя мера.

Есть и еще один способ управления процессами - изменение их приоритета с целью перераспределения ресурсов машины. Однако на практике пользователю с этим почти не приходится сталкиваться, и потому здесь мы не будем затрагивать эту тему.

В завершение же разговора о процессах отметим, что пользователь может управлять только теми процессами, хозяином которых он является - то есть собственноручно запущенными интерактивными. Администратор системы же располагает правами на управление всеми запущенными процессами, в том числе и неинтерактивными демонами.

Все суть файлы

Все сказанное выше может показаться пока не совсем понятным. Однако надеюсь - этот раздел внесет ясность в вопрос о китах POSIX'ивизма (а равно баранах и прочих кашалотах). Для чего нам только потребуется замкнуть разорванный в начале раздела круг, и поговорить о файлах. Раздел этот придется разбить на три субраздела. В первом будет рассказываться об устройстве файлов самих по себе, во втором - об их организации в файловые системы, в третьем - о логической иерархии файлов и каталогов.

Файл как он есть

Как уже говорилось, все, что существует в POSIX-системе в статическом виде, являет собою файлы. Собственно, использование файловой системы как универсального интерфейса доступа ко всему, чему угодно - от устройств, физически подсоединенных к машине, до процессов, в системе протекающих, - и есть один из критериев Unix-подобия ОС (и ее соответствия стандартам POSIX). Так что пора рассмотреть вопрос о том,

Что такое файл

Помнится, на заре моего приобщения к компьютерам этот вопрос доставил мне немало пищи для размышлений. Встреченное где-то (не у Фигурнова ли?) определение файла как именованной области на диске для хранения данных казалось а) интуитивно непонятным, и б) не соответствующим собственным впечатлениям. И по мере освоения POSIX-систем (сначала Linux'а, а потом и FreeBSD) оказалось, что интуиция меня не подвела. Ибо файл - это отнюдь не область, расположен он не всегда на диске и уж ни в коей мере не привязан к какому-то имени.

В самом общем виде файл можно определить (это - чисто ИМХО) как некую последовательность байтов, идентифицируемую тем или иным образом. Причем имя файла, как скоро станет ясным, отнюдь не единственный и даже не главный способ его идентификации. А то, где хранится эта самая байт-последовательность, и какая именно информация передается теми самыми байтами, - дело уже десятое.

Предложенное определение может быть применено к любой операционной системе. Конкрентно же в Unix'ах всякого рода (сиречь POSIX-совместимых ОСях) к этому следует прибавить, что файл состоит как бы из двух частей, не обязательно находящихся в одном месте (а в общем случае - как раз разобщенных).

Первая часть файла - так называемая область метаданных, именуемая по английски inode (index node или information node - мне встречались обе расшифровки). Нормального русского эквивалента для этого термина не придумано (выражения типа "информационный узел" врял ли прояснять существо дела, а "индексный дескриптор" назвать русским выражением трудно), и потому далее он будет использоваться без перевода. В inode содержатся некоторые сведения о файле (именуемые атрибутами файла), как то:

Все эти характеристики файла будут рассмотрены в следующих параграфах этого раздела.

Область данных файла заключает в себе собственно ту информацию, для хранения которой файл и предназначен. Она может быть самой различной - текстовые символы, двоичные данные и многое, многое другое. Однако характер этой информации для операционной системы POSIX-типа безразличен (чего нельзя сказать о пользовательских приложениях) - она воспринимает их просто как последовательность байтов.

Область данных не является непременной принадлежностью файла. Существуют файловые системы, которые умудряются разместить данные маленьких файлов в области метаданных, чем достигается как быстрота доступа к хранимой информации, так и экономия дискового пространства. Потому что, не смотря на приведенную выше оговорку, файлы в основном все же хранятся на дисковых устройствах.

Внимательный читатель, привыкший под влиянием DOS/Windows к трактовке файлов в качестве именованных документов, обратил внимание на то, что среди атрибутов файлов нет одного, казалось бы, самого важного, - имени файла. Как же различаются друг от друга?

Цели идентификации файлов служат числа, которые, как это ни странно, так и называются - идентификаторы. Таковых - два: идентификатор устройства и собственно идентификатор файла. Со вторым все более-менее понятно - это просто порядковый номер (с некоторыми оговорками). И номер этот - уникален для файловой системы. Именно по нему ОС считывает информацию из конкретного файла - то есть из области его данных, поставленных в однозначное соответствие с файловым ID из inode.

А понятие идентификатора устройства требует некоторого пояснения. Когда речь шла о второй из вечных истин, вскользь было упомянуто о том, что Linux или FreeBSD могут быть установлены на несколько дисковых разделов. И каждый из этих разделов будет нести свою файловую систему, образующую отдельные ветви единого файлового древа.

Так вот, нумерация файлов в каждом таком разделе - независима от других, начинаясь с 2 (идентификатор "локального" корня). И потому в каждой ветви файловой системы могут быть (и неизбежно будут) файлы с одинаковыми идентификаторами. Чтобы отличать их друг от друга, и введен такой атрибут - идентификатор устройства.

Файлы существуют в том числе и для того, чтобы пользователь с ними работал :-) - открывал, просматривал, редактировал, и т.д. А из предыдущего раздела мы уже знаем, что все эти действия осуществляются процессами. То есть каждый процесс имеет некий набор открытых им файлов. Однако легко представить себе ситуацию, при которой два и более процессов открывают один и тот же файл. Как они различаются? - ведь inode открытого файла по определению один, а имя файла в контексте процесса также не фигурирует.

Для этой цели предназначены т.н. файловые дескрипторы, которые не следует путать с дескрипторами индексными (сиречь inodes). Файловые дескрипторы (в дальнейшем условимся называть их дескрипторами просто) - это также числовые идентификаторы, которые присваиваются файлам по мере открытия их данным процессом. Причем нумерация их для каждого процесса - отдельная. То есть файл с inode xxx, открытый процессом A, получит дескриптор M, а в процессе B ему же будет присвоен дескриптор N.

Однако одинаковые дескрипторы для одних и тех же файлов также не запрещаются. Примером чему - имеющие особое значение имеют дескрипторы 0, 1 и 2. В любом процессе они присваиваются фиксированным файлам устройств - стандартного ввода, стандартного вывода и стандартного потока ошибок ( /dev/stdin, /dev/stdout, /dev/stderr, соответственно). К ним мы еще вернемся, а пока достаточно запомнить, что в случае настольной персоналки для ввода данных в процесс стандартно служит устройство под названием "клавиатура", а вывод процесса осуществляется на экран монитора (куда по умолчанию валятся и все сообщения об ошибках).

Классификация файлов

Старый пользователь DOS/Windows, не погрязший окончательно в метафоре файлов-документов, возникшей в MacOS (где, надо отдать ей должное, метфора эта вполне оправдана) и внедренной в Windows начиная с версии 95, помнит о сущестовании типов файлов, отличающихся своими расширениями, - исполняемых (*.exe, *.com, *.bat), текстовых (*.txt), изображений в различных форматах (*.tiff там, или *.gif), и так далее. И потому будет весьма удивлен, когда узнает, что в Unix'ах все они не типизированы никак. И действительно - как ОС может отличить их друг от друга, если имени файла (и, следовательно, расширения) в атрибутах его нет и в помине? В этом смысле говорят, что в Unix нет понятия типизации файла.

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

Тем не менее, под понятие файла в POSIX-системах подпадают столь разнородные объекты, что они неизбежно требуют классификации. Другое дело, что классификация эта основана на совершенно других принципах. А именно, выделяются следующие типы файлов (и тип в данном случае - это атрибут, описанный в соответствующем поле inode каждого файла):

Пользователю постоянно приходится иметь дело с обычными файлами и каталогами, довольно часто - с символическими ссылками, время от времени - с файлами устройств. А вот с каналами и сокетами он практически не сталкивается (по крайней мере, у меня за пять лет карьеры POSIX-пользователя, такой необходимости ни разу не возникло). Пропорционально этому мы и уделим внимание перечисленным типам файлов. Начнем мы, однако, не с обычных файлов, с коими пользователь общается наиболее тесно, а с каталогов - и сейчас же станет ясно, почему.

Каталоги

Итак, каталоги (по английски directory) - в некотором смысле они противопоставляются всем другим типам. Это - специальные файлы, объединяющие другие файлы (и подкаталоги, называемые также вложенными каталогами) в обособленные друг от друга группы (по крайней мере, так они видятся пользователю в специальных программах управления файлами - файловых менеджерах).

В русскоязычной литературе по DOS/Windows каталоги обычно именуются директориями, а в последнее время в обиход вошел термин "папка" (по английски folder). Однако для Unix-систем термин "каталог" предпочтительнее, и я сейчас постараюсь объяснить, почему.

Принятая в Windows (и идущая исторически от MacOS) метафора каталога как папки с документами способна создать впечатление (и подчас его создает), что каталог - это некое физическое вместилище включенных в него файлов (на память приходит "зиккурапия" классиков нашей фантастики). Но это - не так. Каталоги - суть не более чем списки имен входящих в них файлов и тех самых числовых идентификаторов, о которых речь шла в прошлом параграфе (и по которым собственно данные и находятся системой). И потому метафора библиотечного каталога (или книжного оглавления) как нельзя лучше отражает физический смысл соответствующего понятия.

Роль каталогов трудно переоценить. Потому что имя файла в файловой системе Unix'ов существует только в составе каталога, и никак иначе. Собственно же содержимое файла ставится в соответствие имени через идентификатор его inode, подобно тому, как это осуществляется в реляционных базах данных. Механизм соотнесения имени файла, его метаданных и данных носит название жесткой ссылки (hardlink), чтобы отличить его от ссылок символических - файлов особого типа, о которых речь пойдет в следующем подпараграфе. Количество жестких ссылок и есть значение поля счетчика в inode файла. Очевидно, что возможный его минимум для большинтсва типов файлов - единица.

Из сказанного следует несколько важных свойств имен файлов. Идентификатора устройства, несущего данный файл, в каталоге нет. И потому имя файла (то есть элемент данных включающего его каталога) может существовать только в той же физической файловой системе (дисковом разделе), что и inode, с которым он связан жесткой ссылкой (и, разумеется, в той же, что и собственно область данных файла).

Во-вторых, на один inode с его данными может быть создано произвольное количество жестких ссылок (вспомним о соответствующем атрибуте - счетчике ссылок). То есть файл с одним и тем же физическим содержанием может выступать под рядом имен, и все они будут равноправны между собой. Или, напротив, под одним и тем же именем входить в состав разных каталогов.

Редактирование файла, вызванного под любым из его имен или из разных каталогов (например, текста в текстовом редакторе) будет приводить к изменению области данных файла, не зависящим от имени, и, следовательно, содержимое любого из таких файлов-двойников будет оставаться идентичным. Более того, любое из имен такого файла можно удалить (то есть изменить содержимое включающего его каталога), что не окажет никакого влияния на остальные имена файла (и, тем более, на его реальное содержимое).

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

В третьих, удаление файлов в Unix происходит совершенно иначе, чем в DOS/Windows. А именно, файл считается удаленным, когда уничтожены все имена, ссылающиеся на идентификатор данного inode (то есть файл исключен из файловой системы), и закрыта последняя программа, к нему обращающаяся (то есть завершен процесс, загрузивший данные файла в память, и уничтожен индексный декриптор файла в этом процессе). В описании атрибутов файла это выражается в том, что счетчик ссылок его inode обнуляется. Разумеется, сами по себе данные, составляющие содержание файла, физически могут продолжать существовать на диске, но для системы они уже недоступны. А поскольку содержание файла оторвано от его имени, восстановление случайно удаленного файла по фрагменту имени (на чем основаны DOS-утилиты типа UNERASE и UNDELETE) оказывается невозможным.

Пока любой файл открыт, то есть существует ссылающийся на него процесс, он продолжает существовать, даже если имя его исключено из всех каталогов, и может быть записан, скопирован, переименован, и т.д. То есть открытый каким либо процессом дескриптор данного файла - гарантия его существования, по крайней мере до завершения процесса. Именно поэтому я ранее сказал, что файл не обязательно имеет имя: в случае удаления открытого файла из каталога он некоторое время существует как бы безымянным, для поддержания его бытия достаточно открытого дескриптора, ассоциированного с inode.

Однако вернемся к каталогам. Как мы только что увидели, любой файл доступен для системы в том только случае, если он включен, пардон за двойную тавтологию, в файловую систему. То есть имя его приписано к определенному каталогу. Но ведь каталог - это тоже файл, и он тоже должен быть приписан к некоему каталогу более высокого уровня, который носит название родительского. И этот родительский каталог (символически он обозначается как ..) - один из элементов любого, даже не содержащего как бы файлов, каталога. А второй его непременный элемент - это он сам, то есть текущий каталог (что символизируется так - .). То есть для каталога минимум возможных связей (значение поля счетчика в inode), в отличие от всех прочих типов файлов, равно двойке.

Итак, каждый файл входит в какой-либо каталог, который, в свою очередь, включен в каталог более высокого уровня, и так далее. Вплоть до самого высокого каталога, не вполне логично именуемого корневым, за которым зарезервирован символ /. Вся эта конструкция называется деревом файловой системы (хотя на иллюстрациях в большинстве книжек это дерево растет обычно сверху вниз:-)), файловой системой просто (как скоро станет ясным, это - один из самых многозначных терминов во всей околокомпьютерной литературе) или, наконец, файловой иерархией (т.е. иерархией файлов и каталогов). На последнем термине мы и остановимся, а к разъяснению его сути обратимся немного позже.

Корневому каталогу файловой системы всегда присваивается идентификатор 2. Очевидно, что он не имеет родительского каталога - выше его в файловой иерархии не лежит ничего. Тем не менее, счетчик его ссылок также имеет два значения - ибо он выступает сам в качестве своего родителя. То есть ссылка на родительский каталог - есть ссылка на него же.

Отдельные ветви файловой системы, расположенные на самостоятельных дисковых разделах, имеют свои корневые каталоги, также имеющие в качестве идентификатора двойку. Однако, поскольку они различаются идентификаторами устройств (то есть разделов), путницы между ними и "главным" корневым каталогом не происходит (подробнее об этом речь пойдет в подразделе о файловой иерархии).

Символические ссылки

Как уже было сказано, жесткая ссылка на файл может существовать только в той же файловой системе, в которой размещен его inode. Однако подчас возникает необходимость поместить имена одних и тех же файлов в каталогах, лежащих в разных файловых системах (и из параграфа о файловой иерархии станет ясно, зачем). Конечно, такие файлы можно просто скопировать. Однако это, помимо напрасной траты дискового пространства, влечет за собой и другие неудобства. В частности, нельзя гарантировать идентичности содержимого таких файлов-копий - а в ряде случаев именно идентичности от них и требуется.

Для разрешения этой коллизии был придуман особый тип файлов - символические ссылки (symlinks), часто называемые просто ссылками (links). Будем так поступать и мы. А для отличия от них жестких ссылок последние будут именоваться полность. Вообще-то, в русском языке для однозначности за жесткими ссылками хорошо бы закрепить термин "связь", но он почему-то не прижился.

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

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

Очевидно, что если исходный файл по каким-либо причинам недоступен (например, лежит на удаленной машине, связь с которой в данный момент отсутствует), никакое обращение к нему окажется невозможным - последует просто сообщение об ошибке. Аналогично будет и при обращении к симлинку, исходный файл которого был удален - такие ссылки называются мертвыми.

Роль символических ссылок в Unix'ах очень велика. Файловые иерархии ОСей этого класса складывались исторически, и, не смотря на ряд общих черт, со временем стали различаться в отдельных клонах. В то же время мы помним, что все POSIX-совместимые системы должны уметь исполнять одни и те же программы, удовлетворяющие соответствующим стандартам. А программы эти могут искать требующиеся им для работы компоненты (в частности, системные библиотеки) в совершенно определенных каталогах. Которые в разных Unix-клонах вполне могут оказаться не только в разных местах, но и на разных файловых системах. И тут символические ссылки оказываются незаменимыми - достаточно продублировать ими требуемые ветви файловой системы в соответствующих местах, чтобы данное приложение спокойно находило все, что ему нужно.

Есть и другое применение символических ссылок. Некоторые программы требуют для своей работы не просто определенные компоненты, чаще всего - библиотеки, но и фиксированные их версии. В то же время установленная в данной системе версия может быть иной - более старой или более новой. В первом случае все ясно - программа, запрашивающая библиотеку имя_рек.5, с версией имя_рек.4, скорее всего, работать не будет (хотя бывают и исключения).

А что делать, если версия библиотеки - более новая? Ведь большинство разработчиков программ для POSIX-систем обычно свято придерживаются принципа обратной совместимости. То есть функциональность библиотеки имя_рек.6 будет, как правило, заведомо достаточной для работы программы, требующей версии имя_рек.5 (исключения и здесь бывают, но они не так уж часты). Однако просит-то наша программа именно версию имя_рек.5, и, не обнаружив ее, выдает сообщение об ошибке.

Конечно, теоретически правильно написанная программа должна требовать версии не ниже некоторой определенной, однако на практике такая ситуация встречается сплошь и рядом. И разрешается она часто достаточно легко - посредством механизма тех же символических ссылок. То есть обычно достаточно создать симлинк библиотеки имя_рек.5 на имя_рек.6, чтобы обмануть наше приложение и заставить его работать правильно.

Файлы устройств

Файлы устройств соответствуют различным присутствующим в системе устройствам :-). Устройства эти могут быть реальными (жесткие диски, принтеры и т.д.), а могут - т.н. псевдоустройствами, с которыми не ассоциировано никакого "железа" (например, знаменитое устройство /dev/null, не содержащее ничего, или /dev/zero, содержимое которого - сплошные нули).

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

Сказанное в предыдущем абзаце до недавнего времени было верно для всех Unix-подобных систем. Однако ныне, например, во FreeBSD, произошел отказ от поддержки блочных устройств (о резонах к тому мы поговорим в соответствующем разделе BSD-цикла). Это не значит, конечно, что диски и прочие накопители с блочной организацией в этой ОС более не поддерживаются. Просто теперь они предстают перед системой в виде тех же символьных устройств, что и всякого рода порты. Хотя, каюсь, детали этого дела для меня все еще не ясны - буду признателен за комментарии.

Для файлов устройств, кроме обычной идентификации, есть и собственная система опознавания - по номерам - основному, или старшему (major), определяющему класс устройств (например, 4 - старший номер устройств, относимых к классу виртуальных консолей), и дополнительному, или младшему (minor), который обычно является просто порядковым номером данного устройства в своем классе (например, для певрой виртуальной консоли он будет равен 1, для второй - 2, и так далее).

Файлы устройств могут быть фиксированными и динамическими. В первом случае они создаются (как правило, при инсталляции системы) в комплекте на все случаи жизни, и включают в себя почти все, что теоретически может быть подсоединено к машине. Например, на машине с обычным IDE-контроллером, имеющим два канала на два устройства каждое, можно обнаружить четыре файла, соответствующие жестким дискам, для каждого - по четыре файла, отвечающие теоретически возможным первичным разделам, немернянное количество файлов терминальных и псевдотерминальных устройств, и т.д. Хотя большая часть из этих файлов не соответствует никаким реально присутствующим устройствам.

В то же время некоторые на самом деле задействованные устройства могут не иметь ассоциированных с ними файлов. В этом случае пользователь вынужден пополнять их список, для чего предназначены специальная команда (mknode) и построенный на ее основе сценарий (MAKEDEV.

Динамическое создание файлов устройств применяется в тех системах, которые используют т.н. файловую систему устройств (devfs - подробнее о ней пойдет речь в следующем подразделе). Оно происходит каждый раз заново в процессе старта машины на основе тестирования наличного обрудования. И тут уже файлы устройств сохдаются только для тех, которые присутствуют на самом деле (и, что немаловажно, поддерживаются текущей конфигурацией ядра данной операционки - в противном случае устройства все равно что нет).

Понятие файла устройств в ОС Unix-семейства позволяет пользователю (и программам) общаться с железом, не вдаваясь в подробности устройства оного. В разделе о командах будет показано, что вывод программ может быть перенаправлен в произвольный файл. Или, напротив, содержимое файла может быть считано в качестве входных данных. А поскольку устройства - это такие же файлы, то понятие перенаправления распространяется и на них. То есть для распечатки некоего текста его достаточно перенаправить в файл печатающего устройства (например, /dev/lpr - принтер на параллельном порту).

Каналы и сокеты

Специальные виды файлов - именованные каналы (named pipe) и сокеты (socket), - предназначены для обмена данными между процессами. Они важны для разработчиков ПО, пользователь с ними, как правило, напрямую не общается. Однако знать о факте их существования полезно.

Обычные файлы

Наконец-то мы подобрались к типу, наиболее важному для пользователей - обычным, или регулярным, файлам. Рассмотрение их я отложил напоследок по одной причине - дать определение обычного файла можно только методом исключения. А именно, к типу обычных файлов отнесено все, что не является каталогами, символическими ссылками, файлами устройств, каналами и сокетами.

Легко понять, что множество обычных файлов охватывает все то, что в DOS/Windows рассматривалось бы как самостоятельные типы - исполняемые бинарные файлы, файлы сценариев оболочки, обычные тексты, файлы изображений, файлы в форматах специальных программ (например, документы ворд-процессоров или электронных таблиц). Общее между ними одно: содержимое всех их может быть непосредственно просмотрено либо стандартными командами типа cat, less, more, либо программами, в которых они были созданы. Тогда как непосредственный просмотр содержимого файлов прочих типов, как правило, невозможен - доступ к ним можно получить только косвенными путями.

Однако под понятие регулярных файлов подпадают и весьма неожиданные вещи. Выше я упоминал, что в Unix'ах в качестве файлов предстают не только устройства (сущности статические), но даже протекающие в системе процессы. Для представления последних задействуется т.н. файловая система процессов - procfs. Так вот, как это ни странно, файлы процессов суть также обычные файлы, многие из которых можно легко просмотреть с помощью обычных средств доступа к файлам - командами типа cat, more, less.

К слову сказать, существование файлов процессов опровергает еще один момент из приведенного в начале параграфа определения файла - будто бы это обязательно область на диске. Файловая система процессов со всем своим содержимым никакого места на диске не занимает, являюсь лишь отображением происходящего в системе в виде, пригодном для восприятия универсальным интерфейсом файловой системы (подробности - в следующем подразделе).

Еще раз об именах

Как можно заключить из изложенного выше, имя файла не несет в POSIX-системах того сакрального смысла, который вкладывался в него со времен DOS. Не имеется здесь и расширений имен (типа приснопамятной DOS-маски 8.3), предопределяющих отношение ОС к данным из конкретного файла.

На самом деле в Unix'ах на длину и формат имени файла накладывается весьма мало ограничений. Длина имени определяется форматом каталога, принятым в данной файловой системе (подробнее - в следующем подразделе). И ныне большинство их реализаций поддерживают имена длинной до 255 символов.

В именах файлов абсолютно запрещенными к использованию символами, с точки зрения ОС, являются лишь прямой слэш (/), зарезервированный как имя собственное корневого каталога и элемент пути к файлу (разделитель каталогов), и NULL, то есть отсутствие всякого символа. Все прочие символы теоретически могут входить в имена.

Однако практически на имена файлов накладывают свои ограничения еще и некоторые прикладные программы. В первую очередь это - командные оболочки, которые целый ряд символов, не принадлежащих к числу алфавитно-цифровых, интерпретируют по-своему - как коды управления, регулярные выражения, и так далее. В главе о командах будет показано, что в принципе специальное значение символов можно отменить. Тем не менее, такие знаки, как *, !, #, @ и прочие из верхнего ряда клавиатуры, за исключением подчеркивания (_), не рекомендуется включать в имена файлов. К слову, сам символ отмены специального значения - обратный слэш (\), - также в именах лучше не использовать...

Зато точек имя Unix-файла может содержать сколько угодно - ведь мистического значения (отделения собственно имени от расширения) в этот символ не вкладывается. И потому здесь обычными являются имена типа archive.tar.gz или archive.tar.bz2 - файлы архивов, образованных утилитой tar и компрессированных программами gzip или bzip2, соответственно (в Unix'ах понятия архивации и компрессии суть вещи, обычно, разные).

Поскольку одноименные файлы (с разным или одинаковым содержанием) вполне могут существовать в разных каталогах, имя файла само по себе не позволяет однозначно идентифицировать его положение в файловой системе. Отсюда возникает понятие пути к имени, по английски - pathname, что иногда переводится как полное, или составное, имя. Оно, помимо собственно именования файла, указывает всю цепочку подкаталогов, отделяющих его либо от корневого каталога (абсолютный путь к имени), либо от каталога текущего (относительный путь).

Абсолютный путь отсчитывается от "главного" корневого каталога файловой системы вообще, и имеет следующий вид:

/dir1/filename

где dir1 - подкаталог корневого каталога (первый /). Относительный путь отсчитывается от каталога, выступающего текущим для данного процесса. В предположении, что текущим является каталог /dir1, относительный путь к файлу /dir2/filename будет выглядеть так:

../dir2/filename

Элементы пути к имени (промежуточные подкаталоги) в любом случае разделяются символами прямого слэша - именно поэтому он и является абсолютно запрещенным к употреблению собственно в именах.

На полную длину пути также накладываются системные ограничения. Впрочем, они весьма мягкие - верхний лимит pathname лежит обычно в районе нескольких тысяч символов (в Linux, если мне не изменяет память, он составляет 4096 символов). Так что путь к имени файла может быть сколь угодно длинным - в разумных пределах, разумеется. А вполне возможная в Windows (точнее, в одной из ее файловых систем - vfat) ситуация, когда создается файл, доступ к которому в дальнейшем оказывается невозможным вследствие превышения лимита длины пути, в Unix практически исключена.

Вспомним о символическихъ ссылках - именно путь к именам файлов любого другого типа и составляет содержимое файлов этого типа. Соответственно, ссылки могут быть абсолютными, определенными от корневого каталога, и относительными, задаваемыми от каталога, текущего в настоящий момент. Абсолютный симлинк всегда однозначно определяет файл, на который он ссылкается. Симлинк же относительный зависит от того, какой каталог выступал в качестве текущего каталога в момент его создания. И при перемещении относительной ссылки в иной каталог она станет неработоспособной.

И еще о текущем каталоге. Для пользователя, заставшего времен DOS, представляется очевидным, что любой файл (например, исполняемый экзешник) ищется в первую очередь в текущем каталоге. В Unix'ах это не так: в большистве систем поиск в текущем каталоге или не производится вообще, или осуществляется в последнюю очередь. И потому, если требуется вызвать исполняемый файл из текущего каталога, его положение должно быть указано в явном виде:

$ ./filename

Это имеет глубокое внутреннее обоснование - в том числе и с точки зрения безопасности, и к этому вопросу мы еще будем возвращаться.

Право на файл

В начале этого параграфа были вскользь упомянуты прочие атрибуты файла - принадлежности, доступа и режима. Это - очень важные понятия любой Unix-системы (как, впрочем, и любой иной системы, претендующей на звание всамделишней). И - одно из тех понятий, которые (знаю по собственному опыту) психологически трудны для понимания пользователя, пришедшего из мира DOS/Windows 3.X/9X/ME.

Впрочем, смысл атрибутов принадлежности интуитивно понятен: каждый файл в системе, а) имеет своего хозяина, б) приписан к какой-либо группе пользователей, и в) находится в неких отношениях со всеми прочими. В качестве хозяина файла обычно выступает его создатель. Точнее, хозяин процесса, этот файл создавшего. А еще точнее - пользователь, идентификатор которого был унаследован процессом, создавшим файл, в качестве эффективного UID. То есть если процесс этот по каким-либо причинам получил привилегии root-оператора, то и созданный им файл будет иметь своим хозяином его, а не пользователя, на самом деле процесс запустившего.

Далее, каждый файл приписывается по умолчанию к основной группе пользователя - хозяина файла, однако такое положение не является непременным. И именно отнесение файла к некоей группе широко используется для разграничения доступа, организации коллективной работы и прочих задач администрирования, время говорить о которых еще не наступило. Ну и все "прочие" также имеют некие права на файл - и не обязательно более узкие, чем члены его группы (или даже хозяин).

С атрибутами доступа дело несколько сложнее. Их также три: атрибут чтения, изменения, исполнения, причем смысл их различается для каталогов и всех прочих файлов. Начнем со вторых.

Право на чтение (read) файла - это возможность просмотреть его командой less (естественно, только для обычного файла, файлы устройств, сокеты или каналы просмотреть таким образом нельзя, а попытка просмотра символической ссылки вызовет ее файл-источник), открыть в текстовом редакторе или какой-либо прикладной программе. Атрибут изменения (write) также понятен - он дает право изменить содержимое файла, вплоть до полного удаления (содержимого, но не файла! - никакие атрибуты файла не имеют отношения к его удалению). А атрибут исполнения (execute) - возможность запустить откомпилированный бинарник или скрипт: именно присвоение этого атрибута волшебным образом превращает набранную в текстовом редакторе последовательность команд в сценарий оболочки.

Для каталога же атрибут чтения позволяет просмотреть командой ls (или каким бы то ни было иным способом) его содержимое. А по предыдущему параграфу мы помним, что все содержимое каталога - это просто список имен входящих в него файлов. И потому право на изменение каталога - это возможность этот самый список модифицировать - например, удалить из него какое-либо имя, или создать новое (путем перемещения ли, копирования файла, или записи некоторых данных из программы). А поскольку больше имя файла нигде не фигурирует, лишившись его, файл становится недоступным для системы - то есть удаленным. При этом прошу обратить внимание: никаких особенных прав на сам файл не требуется, в общем случае можно удалить файл, не имея на него прав не только изменения, но даже чтения, достаточно обладать правом на изменение каталога. При этом, зная точно имя удаляемого файла и полный путь к нему, можно не иметь и права на чтение каталога, к которому файл приписан.

Наконец, атрибут исполнения для каталога дает право входить в него командой cd. Всего-то навсего, но без этого права какие-либо операции внутри каталога становятся затруднительными. Хотя и не невозможными: по аналогии с правом чтения легко догадаться, что файл из каталога можно удалить, не входя в него - достаточно знать имя и путь.

Атрибуты доступа сцеплены с атрибутами принадлежности. То есть для каждой категории обладателей файла (даже прочих - они тоже в определенной мере обладатели файла) может быть установлено свое (и теоретически любое) сочетание прав на доступ к нему. Так, юзер может иметь право на чтение, изменение и (если это оправдано по смыслу) исполнение файла или каталога (очевидно, что право исполнения TIFF-файла ни малейшего смысла не имеет), группа и прочие - иметь права чтения и, если нужно, исполнения: это схема, по которой обычно по умолчанию распределяются атрибуты вновь создаваемого файла.

Можно запретить любой доступ к своим файлам для прочих, оставив их только для группы особо избранных товарищей. А можно, напротив, запретить право чтения и исполнения для группы, сохранив их для прочих: в этом случае группа товарищей превращается в группу, скажем так, не-товарищей. Ведь права доступа проверяются при обращении к нему в строгом порядке: юзер -> группа -> прочие. И потому, если чтение файла запрещено для членов группы, к которой файл приписан, их попытки ознакомиться с ним будут отвергнуты, тогда как все прочие прочтут файл беспрепятственно...

Кроме атрибутов принадлежности и доступа, которые присваиваются файлу (или, напротив, отнимаются у него) в обязательном порядке, он может иметь и три дополнительных атрибута, называемых иногда атрибутами режима. Для названия их удобочитаемого русского перевода не существует. А на вражьей мове это атрибут SUID (Set User IDentificator), иногда именуемый битом "суидности" (не путать с суицидом), SGID (Set Group IDentificator) и sticky (что можно трактовать как атрибут "липкости" или "сохранности"). Первые два имеют смысл только для исполняемых обычных файлов, последний - в основном для каталогов.

Атрибут "суидности" обеспечивает механизм, благодаря которому пользователю удается изменить свой пароль без помощи администратора. А именно: файл, которому он присвоен, при запуске на выполнение (именно потому он имеет смысл только для файлов с атрибутом исполнения) порождает процесс, наследующий эффективный UID не юзера, его запустившего, а хозяина файла, правами доступа которого и определяются привилегии процесса. А поскольку хозяином подавляющего большинства файлов за пределами домашних каталогов является root, именно его-то права процессу обычно и достаются. Смысл атрибута SGID аналогичен, только тут процессом наследуется не эффективный UID пользователя, а эффективный GID группы, к которой приписан помеченный файл.

Атрибут sticky присваивается обычно каталогам, и влечет за собой невозможность удаления из него файла кем бы то ни было, за исключением владельца файла - ведь в обычном случае для этого достаточно иметь права доступа не к файлу, а к каталогу. Установка его целесообразна для каталогов, хранящих всякого рода временные данные (типа /tmp и некоторых подкаталогов ветви /var), права доступа к которым по умолчанию (и по смыслу) допускают их модификацию всеми пользователями. Однако, если право на запись временных файлов юзер вряд ли сможет использовать во вред, то удаление из таких каталогов чужих файлов вряд ли будет приветствоваться их хозяином. И именно для предотвращения такой ситуации предназначен атрибут sticky.

Сведения обо всех атрибутах файла можно получить посредством команды ls в "длинном" формате (с опцией -l). Или - от какого-либо файлового менеджера. Хотя, на мой взгляд, команда ls ничуть не менее информативна и выразительна.

В обычном выводе команды ls -l имена пользователя и группы находятся в третьем и четвертом полях, а атрибуты доступа объединены в первом. Он имеет вид

-rwxrwxrwx

что расшифровывается следующим образом. Первая позиция последовательности - тип файла (символ дефиса, -, в примере означает, что мы имеем дело с обычным файлом, для каталога там был бы символ d - от directory). Следующие три символа определяют атрибуты доступа для хозяина файла: r - чтение, w - изменение, x - исполнение. Две последние тройки символов - суть тоже самое, но для группы и прочих, соответственно. То есть в данном примере фигурирует исполняемый файл, открытый на чтение, запись и запуск для всех. Если же у кого-либо какое-то право отнято - в соответствующей позиции мы увидим символ дефиса. Так, атрибуты нового текстового файла по описанной выше умолчальной схеме будут выглядеть как

-rw-r--r--

Если файлу приписан атрибут суидности, в тройке владельца место символа x займет символ s, а при наличии атрибута SGID то же s окажется на месте x в тройке группы. Атрибут "липкости" маркируется символом t в последней позиции (вместо x для "прочих"). То есть вывод команды

ls -l /usr/bin/passwd

будет выглядеть так:

-rwsr-xr-x

а прочие варианты предлагается домыслить самостоятельно.

Выше при описании прав доступа использована т.н. символьная нотация, простая и мнемонически понятная (r - от read, o - от other, и т.д.). Однако наряду с ней существует (и активно используется) нотация цифровая, где права доступа обозначаются числами типа 644. Поначалу она кажется загадочной. Однако при ближайшем рассмотрении - ничего подобного. Первая цифра соответствует правам хозяина файла, вторая - правам членов группы, третья - правам разных там прочих, как и при символьной нотации. А сама цифра представляет собой простую арифметическую сумму прав, также выраженных численно - только в двоичной системе счисления, трансформированной в восьмеричную для компактности. А именно: наличию любого права соответствует двоичная единица, отсутствию - двоичный ноль. То есть символьной форме rwxrwxrwx будет соответствовать двоичная 111 111 111, что в восьмеричном пересчете и даст "число юзверя" - 777 (полный доступ для всех атрибутов принадлежности). Образуется она из суммы прав чтения (восьмеричное 4), изменения (восьмеричное 2) и исполнения (восьмеричная 1) в трех позициях. Из чего можно догадаться, что число 000, напротив, означает отсутствие прав на любые действия у кого бы то ни было.

Арифметические вычисления того, какие числа соответствуют каким соотношениям прав, я предоставляю читателю (слаб стал в устном счете с тех пор, как бросил преферанс). Скажу только, что "умолчальная" атрибутика новорожденного файла rw-r--r-- в численной нотации будет выглядеть как 644.

В численной нотации определяются обычно и права, даруемые файлу при рождении. Для этого существует команда umask, помещаемая в общесистемный профильный файл (о чем подробнее будет говориться в соответствующей главе). И ее аргумент в численной форме показывает, какие права из их суммы должны быть отняты (на этот раз - именно отняты) от совокупности исходных прав у файла. То есть для получения стандартных (в большинстве систем) прав 644 этот аргумент должен быть равен 022.

Догадались, почему в первой позиции стандартной совокупности прав фигурирует 6, хотя в аргументе команды umask видим 0? Правильно, потому, что ни один создаваемый непосредственно пользователем файл не рождается как исполняемый: этот атрибут должен быть присвоен ему принудительно. А от рождения право на исполнение обретают только двоичные файлы, создаваемые компилятором (например, gcc).

О времени и о файле

В заключение - несколько слов о временных атрибутах файла, которые, как станет ясным из главы про управление пакетами, в некоторых случаях оказываются очень важными. Их - опять же три: время доступа (atime - от Access Time), время модификации (mtime - от Modification Time) и время изменения (ctime - от Change Time). Первый атрибут фиксирует время последнего обращения к файлу - любого, например, просмотра его командой less. Атрибут этот на практике используется редко, а пересчет времени доступа для изобилия файлов на древе Unix-системы отъедает ресурсы. И потому часто он (пересчет, конечно, а не атрибут) отменяется, о чем будет разговор в разделе о иерархии файлов и монтировании файловых систем.

Атрибут mtime устанавливает время последнего изменения файла, вернее - изменения области его данных (например, правки текста в редакторе). А атрибут изменения, ctime, (по русски лучше для определенности добавлять - изменения статуса) фиксирует время изменения метаданных файла - например, прав доступа, владельца, группы и т.д.

Характерно, что ни один из временных атрибутов файла не отражает непосредственно времени его создания. В качестве такового можно рассматривать атрибут ctime - но только в том случае, если в дальнейшем статус файла не изменялся (например, если файлу текста скрипта не был присвоен атрибут исполнения). В противном случае определить истинное время создания файла невозможно.

Файловые системы

Файловая система - один из самых многозначных терминов в околокомпьютерной литературе. Им обозначается и подсистема ядра, отвечающая за управление операциями с файлами, и способ физической организации данных на носителях, и логическая структура файлов и каталогов. В настоящий момент нас будут интересовать два последних понятия. В этом подразделе мы рассмотрим физику размещения файлов, а логику их организации отложим до следующего раза.

В современных операционках POSIX-семейства поддерживается множество файловых систем. В первом приближении их можно разделить на две группы - реальные, или базирующиеся на дисках (точнее, на блочных устройствах, block based file systems), и виртуальные, под которыми никаких реальных физических устройств не обнаруживается. Есть и промежуточный вид файловых систем - на виртуальных дисках в оперативной памяти (т.н. RAM-дисках). Основным предметом нашего рассмотрения будут файловые системы первой группы - о виртуальных файловых системах я скажу несколько слов под занавес.

Дисковые накопители

Как следует из названия, block based файловые системы размещаются на блочных устройствах, в качестве которых выступают обычно диски и их разделы. И потому рассмотрению ФС этого типа следует предпослать введение о дисковых накопителях.

Дабы не повторять это в дальнейшем, сразу оговорюсь, что почти все сказанное далее относится к дискам с интерфейсом IDE (ATA). SCSI-диски имеют свою специфику, но я с ними дела не имел и собственных впечатлений на сей предмет у меня нет. Да и для пользователя, с ростом объема и скорости ATA-дисков, падением их цены и распространением внешних накопителей с USB- и FireWire-интерфейсами, SCSI становится все менее актуальным. Хотя эту тему и придется затронуть - но не в отношении дисков, а устройств иного типа. Ибо и в Linux, и BSD-системах все не-ATA накопители почему-то любят прикидываться SCSI-устройствами...

Для начала вспомним, что дисковые накопители, как и любые другие устройства, в любой POSIX-совместимой системе предстают перед пользователем в виде файлов специального типа - файлов устройств. Файлы эти расположены в каталоге /dev, и номенклатура их подчиняется правилам, совим для каждого представителя этого семейства ОСей.

В Linux ATA-диски традиционно именовались как /dev/hd?, где hd - общее имя устройств этого класса (рискну предположить, происходящее от hard disk), а символ ? - литера, идентифицирующая конкретный его представитель таким образом:

/dev/hda	Master на 1-м IDE-канале
/dev/hdb	Slave на нем же
/dev/hdc	Master на 2-м IDE-канале
/dev/hdd	Slave на нем же

Номенклатура устройств - стаична, то есть не зависит от физического их наличия: то есть диск, подключенный как Slave ко второй линии встроенного IDE-контроллера, всегда будет именоваться /dev/hdd, даже являясь единственным накопителем в системе (случай чисто гипттетический - трудно представить себе реальные основания для конфигурации такого рода).

Отдельно следует оговорить, что тем же правилам подчиняется именование не только дисков, но и дургих накопителей с интерфейсом ATA - CD ROM/R/RW, DVD любого вида или отходящих в прошлое внутренних Zip-приводов.

Однако в последнее время большинство "прогрессивных" дистрибутивов используют файловую систему устройств (devfs), вносящую свои коррективы в номенклатуру устройств, в том числе и дисковых.

При использовании devfs для файлов любых ATA-накопителей предназначен каталог /dev/ide (в некоторых дистрибутивах файловая система устройств монтируется в каталог /devices, а каталог /dev сохраняется для совместимости). Файлы накопителей на встроенном основном IDE-контроллере локализуются в подкаталоге /dev/ide/host0 (если используется еще и дополнительный IDE-контроллер, встроенный или внешний, можно увидеть и каталог /dev/ide/host1). А внутри него есть два подкаталога, соответствующие IDE-каналам - /dev/ide/host{/bus0,/bus1}, каждый из которых опять же может делиться надвое - на каталоги target0 и target1, по количеству подключенных устройств. Внутри каталога target0(1) имеется минимум еще один подкаталог lun0. А уж в нем размещаются непосредственно файл дискового устройства - disc.

Таким образом, полное обозначение дискового раздела будет выглядеть как

/dev/ide/host0/bus0/target0/lun0/disc

для первого диска на первом канале основного IDE-контроллера.

Как и в случае старой номенклатуры, правила ее распространяются и на не-дисковые ATA-накопители, в частности, CD. С той только разницей, что именование CD-привода в этой нотации (назовем ее полной) будет выглядеть так:

/dev/ide/host0/bus1/target0/lun0/cd

Предусмотрен и более краткий способ обращения к файлам устройств - через символические (изредка - жесткие) ссылки; назовем этот способ краткой нотацией. Для файлов дисковых накопителей (независимо от интерфейса - IDE или SCSI) они собраны в каталоге /dev/discs (для файлов CD-приводов, например, - в каталоге /dev/cdrom) с подкаталогами disc0, ... , disc#. И потому к приведенному в качестве примера диску можно обратиться и так:

/dev/discs/disc0/disc

а CD привод будет именоваться следующим образом:

/dev/cdroms/cdrom0

Краткая нотация имеет динамический характер, то есть первый в порядке подключения диск всегда будет представлен устройством /dev/discs/disc0/disc, а второй - /dev/discs/disc1/disc, вне зависимости от того, на каком канале или в каком качестве (Master или Slave) они реально подключены.

Наконец, для совместимости со старыми временами (и старыми привычками) в большинстве дистрибутивов поддерживается и номенклатура обратной совместимости, принятая до внедрения devfs. То есть все тот же дисковый накопитель из примера можно обозвать просто - /dev/hda.

Таким образом, подключение devfs - не препятствие для использования старой номенклатуры накопителей. Правда, это требует соответствующей настройки демона devfsd (точнее, его конфига), отвечающего за обратную совместимость. И, при желании, может быть отменено. В частности, такие дистрибутивы, как CRUX и Archlinux, на стадии установки обратной совместимости не используют - при подготовке дисков здесь именовать их приходится только в новой нотации - краткой или полной, без разницы.

При использовании файловой системы devfs имена файлов создаются только для реально существующих в системе устройств (в том числе, для устройств "горячего" подключения, типа PC-карт или USB-драйвов, - и "на лету"). Поэтому, если в системе имеется только единственное IDE-устройство, скажем, жесткий диск как мастер на первом канале, бесполезно было бы искать файлы устройств с именами, отличными от приведенного в примере. Что удобно, но в некоторых реализациях devfs может создавать сложности. Впрочем, как сказал бы Киса Воробьянинов, к теме, которую я в данный момент представляю, это не относится...

Несколько отлична номенклатура накопителей во FreeBSD. Здесь файлы устройств ATA-дисков именуются в общем как ad#. Как нетрудно догадаться, аббревиатура ad происходит от ATA Disk, а # - численный идентификатор, соответствующий номеру конкретного накопителя в порядке его подключения к IDE-разъемам. То есть:

/dev/ad0	Master на 1-м IDE-канале
/dev/ad1	Slave на 1-м IDE-канале
/dev/ad2	Master на 2-м IDE-канале
/dev/ad3	Slave на 2-м IDE-канале

Во FreeBSD по умолчанию также принята статичная нумерация файлов дисковых устройств, хотя это можно изменить при перекомпиляции ядра.

В отличие от Linux, во FreeBSD для приводов CD ROM принята своя номенклатура - файлы этих устройств именуются как /dev/acd0, /dev/acd1 и (если таков реально бывает) так далее, вне зависимости от контроллера, к которому они подключены.

Во FreeBSD 5-й ветки также используется файловая система устройств devfs. Однако влияния на номенклатуру устройств она не оказывает: все низкуровневые утилиты в этой ОС разрабатываются совместно с ядром, и потому задача обратной совместимости тут не ставится. Единственно, что в каталоге /dev установленной FreeBSD 5-й ветки будут присутствовать только файлы реально имеющихся устройств - например, /dev/ad0 и /dev/acd0. Тогда как в более старых ветвях мы увидели бы также созданные "про запас" файлы /dev/ad0, /dev/ad1 и /dev/ad2, хотя попытка обращения к ним ничего, кроме сообщения об ошибке, и не дала бы.

Наконец, прочие BSD-системы (Net- и OpenBSD) также располагают собственной терминологией в отношении дисковых устройств. Соответствующие им файлы именуются так:

wd0	1-й диск
wd1	2-й диск
wd2	3-й диск
wd3	4-й диск

Причем, в отличие и от Linux, и от FreeBSD, в OpenBSD номенкрлатура дисков по умолчанию динамична - то есть диски нумеруются по порядку, вне зависимости от линии контроллера и положения на ней (Master или Slave). Хотя, как и во FreeBSD, такой порядок может быть изменен при переконфигурировании ядра. Как обстоит дело в NetBSD по умолчанию - честно говоря, не помню:-).

Жесткие диски - не единственные накопители, которые можно обнаружить в современной машине: все более возрастает роль разного рода сменных носителей, вытеснивших, наконец, из этой ниши аксакала - трехдюймовый дисковод. Накопители такие бывают двух типов - с интерфейсом FireWire и USB. О первых не могу сказать ничего определенного - не только не пользовался, но и даже не видел. А вот USB-драйвы (в просторечии - флэшки) заслуживают нескольких слов.

Потому что это - и есть те дивайсы из числа распространенных, которые изображают из себя SCSI-диски. И соответствующие им файлы устройств подчиняются правилам, установленным для последних. То есть в Linux по старой номенклатуре они будут именоваться как /dev/sda, /dev/sdb и так далее. По новой же в полной нотации - точно как SCSI-диски:

/dev/scsi/host0/bus0/target0/lun0/disc

Впрочем, в краткой нотации USB-привод будет выглядеть обычным винчестером:

/dev/discs/disc1/disc

Во FreeBSD USB-нкопители также предстают в качестве SCSI-дисков, и, в соответствие с принятыми здесь правилами, имена соответствующих им файлов устройства будут выглядеть как /dev/da1, /dev/da2 и так далее.

К слову сказать, точно так же, как и флэшки будут обычно выглядеть (и в Linux, и во FreeBSD) и цифровые камеры с USB-интерфейсом (точнее, встроенные в них носители информации).

Теоретически ничто не мешает создавать файловые системы непосредственно на дисковых устройствах. Однако практически так почти никогда не поступают - диски принято делить на разделы (partitions в терминах DOS/Windows и Linux, slices в терминологии FreeBSD). Однако прежде чем говорить о разбиении диска, необходимо сказать

Немного о "геометрии"

Слово "геометрия" в заголовке рубрики взято в кавычки не случайно. Дело в том, что с тех пор, как объем дисков перевалил за 500 с небольшим мегабайт (ограничение старых BIOS персональных, ранее именовавшихся IBM-совместимыми, компьютеров), с реальной их геометрией пользователь никогда не сталкивается. Софт, прошитый в дисковой электронике (т.н. firmware) преобразует ее к виду, доступному восприятию BIOS - на деталях, как именно это делается, останавливаться не буду за некомпетентностью (да и вряд ли кто, кроме производителей дисков, эти детали знает хорошо).

А доступная BIOS геометрия диска описывается в терминах цилиндр/головка/сектор (cylinders/heads/sectors, C/H/S). Фигурально говоря (а, повторяю, все, относящееся к дисковой геометрии, ныне следует понимать исключительно фигурально, аллегорически или метфорически), головки считывают информацию с концентрических магнитных дорожек (tracks), на которые поделена каждая дисковая пластина. Вертикальная совокупность треков с одинаковыми номерами на всех пластинах, составляющих диск как физическое устройство, и образует цилиндр. А сектора нарезают пластину, вместе с ее треками, на радиальные фрагменты, именуемые блоками. То есть это можно представить себе таким образом, что блок лежит на пересечении (в пространстве) цилиндра, трека и сектора.

Число треков и секторов в современных дисках обычно фиксировано (вернее, предстает таковым в BIOS): 255 треков нарезается на 63 сектора каждый, что в совокупности дает 16065 блоков на цилиндр. А количество цилиндров определяется объемом диска (в арифметические вычисления вдаваться не буду). Важно здесь только то, что головки диска механически двигаются синхронно по поверхности всех пластин. То есть если на одной пластине информация считывается с 1-го трека, то и все прочие головки перемещаются на ту же дорожку - каждая на своей пластине.

Повторяю, все это условно - хотя бы потому, что понятие цилиндра в геометрическом смысле слова очень трудно применить к современным дискам, часто не то что однопластинным, а даже, если так можно выразиться, полупластинным (то есть только с одной задействованной стороной единственной пластины). Но разбираться с этой геометрией - дело firmware и BIOS, для нас же интересны именно цилиндры - совокупность треков, к которым осуществляется синхронный доступ, и блоки - минимальные кванты дискового пространства.

Образующие цилиндры треки создаются при первичной заводской разметке диска - т.н. низкоуровневом форматировании. Из сказанного выше очевидно, что доступ к данным в пределах одного цилиндра или группы соседних будет выполнен быстрее, чем к данным, записанным частично на первый и, скажем, на последний цилиндр диска. Этот случай не столь уж невероятен, как может показаться: в DOS'е, где пространство, занятое стертыми файлами, помечается как неиспользуемое, но реально перезаписывается только тогда, когда по настоящему свободное место на диске вообще исчерпано, такая ситуация вполне могла бы возникнуть.

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

Где кончается один раздел и начинается другой? Резонные люди из Одессы сказали бы, что полиция кончается именно там, где начинается Беня Крик. Однако для нас очевидно, что для каждого из разделов следует хранить сведения о его начале и конце (то есть номера первого и последнего из задействованных в нем цилиндров). Где их хранить? Для ответа на этот вопрос следует обратиться к понятию блока.

Как и треки, дисковые блоки (или физические - есть еще блоки логические, но это относится уже к файловым системам, о которых речь пойдет в следующем параграфе) создаются при низкоуровневом форматировании, и пользователь влиять на них не может. Размер их также всегда одинаков и равен 512 байтам. Вернее, таким он видится BIOS'у персоналки - каков он на самом деле, одному Аллаху ведомо.

Однако то, что обмен данными с диском возможен минимум 512-байтными порциями - объективная реальность, как и то, что любой, сколь угодно маленький, объем информации, записанный на него, будет занимать целый блок - вне зависимости от реального своего размера. То есть мелкие текстовые файлики размером в пару символов (сиречь байт) все равно захватят под себя аж 512 байт, не меньше. С другой стороны, считывание данных блоками по 512 байт будет происходить быстрее, чем если бы при каждом обращении головки к диску данные считывались бы побайтно. Однако и это относится уже к теме файловых систем.

Пока же нас интересен один-единственный блок, образованный первым сектором на первом треке первого цилиндра. Он резервируется под служебную область диска, именуемую главной загрузочной записью (MBR - Master Boot Recodr), которая и считывается BIOS'ом при старте машины. Очевидно, что по прямому назначению MBR используется только в том случае, если диск определен в Setup'е BIOS'а как загрузочный (или просто является единственным в системе). Однако поскольку использование каждого конкретного диска остается на усмотрение пользователя, место под него отводится всегда.

Внутри нулевого блока, помимо прочего (в частности, кода какого-либо начального загрузчика, который может быть туда записан) есть еще один зарезервированный участок. Он предназначен для BIOS'овской таблицы разделов (Partition Table), под которую испокон веков (со времен самой первой IBM PC, кажется) отведено 64 байта. В эту таблицу записываются (или могут быть записаны) данные о разделе (разделах) в определенном, доступном пониманию BIOS'а, формате.

А формат этот предусматривает указание для каждого раздела его стартового блока, размера в байтах, идентификатора типа файловой системы (это, вопреки названию, совсем не то же самое, что файловая система, о которой речь пойдет в одном из последующих параграфов) и (только для одного из разделов) флага активности (то есть помечающего данный раздел как загрузочный). Последнее необходимо для некоторых операционок типа DOS, хотя и Linux'у, и любой BSD-системе флаг этот глубоко безразличен.

Всего информации, необходимой для описания дискового раздела, набегает 16 байт. А поскольку, как мы помним, под всю таблицу разделов этих байт отведено лишь 64, без калькулятора можно подсчитать, что предельное количество разделов на диске - 4. Эти разделы называются первичными или, не совсем точно, физическими. Так как в большинстве случаев такие разделы могут быть также поделены на части - разделы логические (о чем речь впереди).

Повторю еще раз - это относится только к машинам с PC BIOS, то есть обычным персоналкам. На всякого рода PowerPC, Sparc'ах и тому подобных станциях все может быть совсем по другому (хотя как именно - честно говоря, не Ганнибал, не знаю).

Как можно заметить, в описание раздела входит идентификатор файловой системы. Это - некоторое число (во FreeBSD обычно в десятичном представлении, в Linux'е, например, - в шестнадцатеричном), которое ставится в соответствие с файловой системой операционки, планируемой к размещению на диске. Так, раздел, предназначенный для FreeBSD (тип его - 4.2BSD), имеет идентификатор 165 (десятичный) или A5, раздел для Linux (Linux native) - 131 (или 83), FAT16 - 6, расширенный раздел (т.н. DOS Extended) - 5, и так далее. В следующей таблице приведены значения наиболее важных для пользователя Linux и BSD-систем идентификаторов типов разделов (по выводу Linux'ового fdisk, то есть в шестнадцатиричном исчислении) с их краткой характеристикой.

Таблица значений идентификаторов типов разделов
Значение, hex Название Характеристика
1 FAT12 Файловая система DOS-дискет
4 FAT16<32M Файловая система старых (ниже 4-й) версий DOS
5 Extended Расшиненный раздел DOS
6 FAT16 Обычный раздел DOS (до 2 Гбайт)
b W95 FAT32 Раздел Windows 95 (от OSR2 и выше)
63 GNU HURD Раздел ОС HURD
81 Minix Раздел ОС Minix, принят на дискетах Linux
82 Linux swap Раздел подкачки Linux
83 Linux Родной (native) раздел Linux
8e Linux LVM Раздел для использования менеджером логических томов
a5 FreeBSD Раздел FreeBSD (4.2BSD)
a6 OpenBSD Раздел OpenBSD
a8 Darwin UFS Раздел MacOS X
a9 NetBSD Раздел NetBSD
fd Linux raid auto Раздел для использования в программных RAID

Для использования в Linux непосредственно предназначены, кроме Linux native и Linux swap, так же разделы типа Minix (хотя с отмиранием дискет актуальность его теряется), Linux LVM и Linux raid auto. Кроме того, без всяких дополнительных ухищрений возможен доступ из Linux к разделам FAT любого рода. Прочие из перечисленных типов предназначены для одноименных ОС.

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

Собственно о разделах

Итак, следствием установлено, что на одном физическом диске может быть создано до 4 (включительно) разделов, каждый их которых может быть приписан к отдельной операционной системе. А что дальше? А дальше следует изучить вопрос стилей разметки разделов.

Стили разметки разделов именуются Disk Label, что не следует путать с метками дисков (disk label) - произвольными именами, которые в DOS (и не только) можно присвоить дисковому разделу. Стили же разметки - это формат вторичной таблицы разделов, записываемой в первый блок раздела первичного. Эта таблица и определяет характер доступных действий над данным первичным разделом.

Пользователи Windows обычно не имеют причин задумываться над проблемой стилей разметки. Однако стилей таких существует немало - чтобы убедиться в этом, достаточно зайти в меню конфигурации ядра Linux, в подраздел Partition Types раздела File systems. Однако я опять забегаю вперед - нынче из всего этого изобилия нас будут интересовать только два стиля разметки - DOS и BSD.

В DOS/Windows используется (как ни удивительно:-)) DOS-стиль разметки разделов. Он основывается на BIOS-таблице, задействованной лишь частично. А именно - из четырех доступных записей Partitions Table заполняются только две (вернее, только два раздела можно создать средствами стандартного FDISK из DOS/Windows 96/98/ME; как обстоит дело в NT/2000/XP - просто не знаю).

В записи для первого раздела можно указать идентификатор типа файловой системы (например, FAT16 или FAT32), второму же разделу автоматически присваивается идентификатор типа Extended DOS. А уж Extended-раздел может быть далее поделен на логические разделы (Logical Partitions). В терминологии DOS/Windows они именуются логическими томами (Logical Volume). Однако мы зарезервируем этот термин для системы LVM (Logical Volumes Manager), к разговору о которой придется вернуться позднее.

Расширенный раздел выступает в качестве контейнера, в который последовательно, как в матрешку, вкладываются один логический раздел и еще один расширенный раздел. Последний, в свою очередь, выступает контейнером второго уровня, и может включать еще один логический раздел и следуюущий по очереди расширенный, - и так до бесконечности. Правда, аналогия с матрешкой - не совсем строгая, потому что для пользователя все эти вложенные разделы видятся как равноправные части "головного" Extended-раздела. Да и на счет бесконечности - тоже несколько преувеличено - на самом деле больше 63 (кажется) логических разделов создать не удается.

В Linux также используется DOS-стиль разметки. Только тут уж BIOS-таблица задействуется по полной программе - стандартными средствами этой ОС (Linux'овым fdisk, имеющим весьма мало общего со своим тезкой из DOS/Windows, его front-end'ом (оболочкой) cfdisk или (почти) универсальной программой parted можно создать все четыре первичных раздела и пользовать их в свое удовольствие. Правда, опять же лишь один из них можно объявить расширенным и, соответственно, поделить на разделы логические.

Разделы в Linux представляются перед системой (и пользователем) в качестве таких же файлов блочных устройств, как и диски. И номенклатура их подчинаяется тем же правилам. В старой (до-devfs'ной) нотации к обозначению целого диска (например, /dev/hda) просто добавляется численный идентификатор раздела (его порядковый номер, начиная с единицы. Идентификаторы с 1-го по 4-й зарезервированы за первичными разделами. Если один из них определен как расширенный, то логические разделы внутри него нумеруются, начиная с 5-го (даже если первичных разделов на диске нет и в помине. То есть типичная картина разбиения диска под Linux будет выглядеть примерно так:

/dev/hda1	1-й первичный раздел
/dev/hda2	2-й первичный раздел
/dev/hda3	3-й первичный раздел,
			определенный как расширенный
/dev/hda5	1-й логический раздел внутри расширенного
/dev/hda6	2-й логический раздел внутри расширенного
...

и так далее.

При использовании devfs файлы устройств, соответствующих дисковым разделам, именуются part#, где # - порядковый номер (с 1-го по 4-й - для первичных разделов, с 5-го и далее - для расширенных). То есть в полной нотации это будет выглядеть так:

/dev/ide/host0/bus0/target0/lun0/part1 
/dev/ide/host0/bus0/target0/lun0/part2   /dev/ide/host0/bus0/target0/lun0/part3
/dev/ide/host0/bus0/target0/lun0/part5
/dev/ide/host0/bus0/target0/lun0/part6
...

а в краткой - таким образом:

/dev/discs/disc0/part1
/dev/discs/disc0/part2
/dev/discs/disc0/part3
/dev/discs/disc0/part5
/dev/discs/disc0/part6
...

Интересен вопрос о разделах USB-накопителей (и встроенных носителей цифровых камер). Они поставляются в фабрично отформатированном под vfat виде, однако эта файловая система накладывается на самые разные схемы разбиения. Мне, в частности, приходилось видеть, что форматированию подвергалось все устройство, без разделов (в этом случае обращаться к флэшке из под Linux'а следует - /dev/sda). В иных же случаях файловая система создавалась на 1-м или 4-м первичных разделах - файлы устройств при этом именуются как /dev/sda1 или /dev/sda4, соответственно.

Впрочем, при использовании devfs имя файла устройства, соответствующего флэшке, легко определить методом ползучего эмпиризма. Для этого достаточно просмотреть содержимое каталога /dev до и после подключения USB-драйва: файл, добавившийся во втором случае, и будет искомым именем устройства.

Особенности BSD-разметки

Совершенно иначе (как по существу, так и с точки зрения именования файлов устройств) выглядит BSD-стиль разметки (BSD Label), используемый во FreeBSD, Net- и OpenBSD. Здесь также может быть использована BIOS-таблица, заполнение которой создаст четыре первичных раздела. В терминологии FreeBSD они именуются слайсами (slices - наиболее точным переводом будет "отрезки"), чтобы отличать их от партиций (partitions) BSD-разметки. Слайсы в номенклатуре файлов устройств маркируются добавлением к имени файла диска литеры s и порядкового номера (в отличие от дисков, начиная с единицы), что для Master'а на 1-м IDE-канале будет выглядеть так:

/dev/ad0s1 - 1-й первичный раздел
/dev/ad0s2 - 2-й первичный раздел
/dev/ad0s3 - 3-й первичный раздел
/dev/ad0s4 - 4-й первичный раздел

Если одному или нескольким из слайсов будет присвоен идентификатор BSD-системы - 165 в десятичном исчислении (как уже говорилось, он называется 4.2BSD), то в его начальный блок запишется собственно BSD-таблица разделов (BSD Label). В соответствие с ее форматом, каждый слайс с ID 165 абсолютно равноправен и может быть поделен на логические разделы (собственно partitions, в терминологии FreeBSD).

Для партиций в BSD-таблице предусмотрено восемь записей. Соответствующие им разделы номенклатурно маркируются добавлением к имени файла слайса литеры - от a до h. То есть таких логических разделов, казалось бы, может быть создано восемь. Однако практически это не совсем так (вернее, совсем не так).

Начать с того, что одна из записей (третья по счету, маркируемая литерой c) резервируется для описания всего слайса в целом - например, ad0s1c, необходимость чего станет ясной в дальнейшем. Далее, первая запись таблицы, соответствующий которому файл устройства маркируется как ad#s#a, отводится для описания корневого раздела файловой системы. А очевидно, что на конкретной локальной машине корневой раздел может быть только один, вне зависимости от количества дисковых разделов и даже физических дисков.

Наконец, вторая запись (файл устройства - ad#s#b) предназначена исключительно для описания раздела подкачки (swap-раздела), который, во-первых, не может содержать данные, и во-вторых, является единственным на весь диск (ясно, что создавать по свап-разделу в каждом слайсе бессмысленно, хотя при наличии двух физических дисков поделить между ними пространство подкачки - идея вполне здоровая). В итоге на четырех слайсах физического диска может быть создано 22 раздела - 1 корневой, один раздел подкачки и 20 разделов для хранения данных.

Практически, однако, так никто, насколько я знаю, не делает. Создание слайсов преследует своей целью разместить на диске более чем одну операционку и сохранить возможность обмена данными между ними (теоретически к BSD-разделам можно обращаться из Linux'а, если пересобрать его ядро должным образом; хотя обратная процедура - обращение к ext2fs разделу из FreeBSD, - гораздо проще).

Если же весь наличествующий диск планируется отдать на растерзание FreeBSD, то проще создать один-единственный слайс на (почти) весь его объем, оставив записи в BIOS-таблице для остальных неиспользованными. Ну а семи позиций BSD-таблицы обычно более чем достаточно для обособления всех необходимых ветвей файловой системы, таких, как /usr, /tmp, /var и /home (о чем будет говориться в следующем подразделе).

Разметка диска, использующая записи в BIOS-таблице первого блока, называется разметкой в режиме совместимости. Вне зависимости от того, создается ли один слайс для FreeBSD или несколько отдельных - для каждой операционки, в режиме совместимости в начале диска резервируется пространство в размере 63 блоков (всего около 30 Кбайт), в котором не только сохраняется в неприкосновенности "умолчальный" MBR, но и остается место для записи кода какого-либо стороннего загрузчика. В итоге диск остается доступным для других операционных систем, по крайней мере теоретически.

Однако использование режима совместимости и BIOS-таблицы разделов во FreeBSD не является обязательным. Вполне допустимо записать в MBR, вместо таблицы BIOS, непосредственно BSD-таблицу разделов. В этом случае понятно, что слайсов как таковых не создается, а все дисковое пространство представляет собой как бы единый слайс, и может быть разбито на BSD-партиции по тем же правилам, что и отдельный слайс. И тут становится ясной необходимость резервирования третьего поля BSD-таблицы - именно в ней и описывается весь наш диск, целиком отведенный под FreeBSD.

Такое обращение с диском именуется режимом эксклюзивного использования, или Dangerously Dedicated. Вопреки названию, в нем не таится никакой опасности ни для данных пользователя, ни для его здоровья. А единственная подстерегающая его опасность - это то, что диск в эксклюзивном режиме не будет опознан никакой другой операционной системой, установленной на данном компьютере (обращению к диску по сети он препятствий не составит). Однако это - чисто теоретическое неудобство, потому что ни одна из известных мне операционок все равно не умеет толком работать с BSD-разделами и файловой системой FreeBSD (особенно современной - UFS2). А, скажем, при наличии на другом физическом диске мультизагрузчика GRUB, FreeBSD с "эксклюзивного" диска вполне может быть им загружена.

В документации по FreeBSD встречаются указания, что "эксклюзивные" диски иногда не могут быть загрузочными, вероятно, потому, что BIOS не сможет опознать нестандартные записи в MBR. Однако, видимо, это относится к каким-либо старым версиям BIOS - мне с таким сталкиваться не приходилось, хотя я часто прибегал к эксклюзивному режиму при возможности отдать под FreeBSD целый физический диск.

Тем не менее, в документах проекта FreeBSD всегда подчеркивается, что эксклюзивный режим - в частности, из-за грошовой экономии дискового пространства, - следует использовать лишь в исключительных случаях. Один из резонов к такому использованию - несоответствие "геометрии" диска, видимой из BIOS, и того представления о ней, которое складывается у FreeBSD (подробнее на эту тему можно прочитать в официальном FreeBSD FAQ).

Схема разметки диска в BSD-стиле принята и в других ОС этого семейства, с той разницей, что термин слайс ни в OpenBSD, ни в NetBSD не применяется, и имена файлов разделов именуются - /dev/wd0a, /dev/wd0b и так далее. А в NetBSD, кроме литеры c, предназначенной для описания всего диска, резервируется еще и литера d - файл устройства /dev/wd0d предназначается для описания слайса целиком.

Общие черты файловых систем POSIX-семейства

Итак, файловые системы обычно размещаются на отдельных дисковых разделах. Соотношение между файловыми системами не вполне однозначно. На одном разделе может быть размещена одна файловая файловая система. Однако последняя, в общем случае, может располагаться более чем на одном разделе. Такие технологии, как LVM (Logical Volumes Managers) или программный RAID, позволяют объединить несколько дисковых разделов в единое логическое устройство, которое целиком занимается одной файловой системой. Впрочем, после такого объединения (конкатенации) прежде самостоятельные разделы видятся системой точно так же, как и обычные разделы или слайсы диска (см. подробности о LVM, программных RAID в Linux и их аналогах во FreeBSD).

Физически все файловые системы в ОС, соответствующих стандарту POSIX, организованы сходным образом. И в основе их лежит разделение на три пространственно разобщенные области - суперблок, область метаданных и блоки данных.

Суперблок - это первый (после загрузочного) блок раздела, занятого данной файловой системой. Он содержит наиболее общую информацию о нейе - размер логического блока (который следует отличать от физического дискового блока), их суммарное количество, число блоков данных, количество свободных и занятых блоков. Кроме того, в нем записывается и информация, специфичная для определенных файловых систем.

Содержимое области метаданных составляет таблица inodes - это данные о данных файловой системы. Для каждого файла она содержит: его уникальный идентификатор, по которому он находится системой, число ссылок на него (фигурально говоря, количество имен файла), размер, атрибуты принадлежности, доступа, времени, и еще некоторые.

Адреса блоков данных файла, то есть собственно физического расположения его контента на диске, также описываются в таблице inodes. Ну а сами блоки данных, как легко догадаться, и составляют наполнение одноименной области.

Описанным способом была организована первая файловая система первозданного Unix, получившая позднее название s5fs (то есть файловая система System V). Как уже говорилось в историческом обзоре, в самой ее приоде были заложены существенные ограничения, не способствовавшие ни надежности, ни быстродействию, ни эффективности использования дискового пространства. И потому разработчики всех более поздних файловых систем POSIX-совместимых ОС, приняв за основу организацию s5fs, вносили в нее те или иные усовершенствования, направленные на исправление указанных недостатков.

Борьба за повышение быстродействия файловых операций осуществлялась дроблением файловой системы на более или менее независимые части, получившие название групп цилиндров (в ОС BSD-семейства), групп блоков (в файловой системе Linux), вплоть до обособления allocation groups в файловой системе XFS, где они представляют фактически самостоятельные подсистемы. В результате этого появилась возможность размещения логически связанной информации в физически смежных областях диска, что минимизировало перемещение дисковых головок, чем и способствовало скорости доступа к данным.

Расчленение файловой системы одновременно способствовало и росту их надежности. Так как давало возможность в ее частях дублировать критически важную информацию - а именно, суперблок, утрата которого (вследствие, например, физического повреждения дисковой поверхности) ранее влекла за собой невозможность доступа к данным вообще.

Экономия в использовании дискового пространства достигалась прямо противоположным приемом - так называемой внутренней фрагментацией, то есть разделением на части логического блока файловой системы. И теперь маленькие файлы могли занимать уже не блок целиком, а только его часть. Венцов творения в этой области явилась файловая система ReiserFS, в которой даже данные маленьких файлов удается целиком вписать в область ее метаданных.

Особым направлением в повышении надежности файловых операций явилось развитие так называемых журналируемых файловых систем. Журнал -- это часть файловой системы, отведенная под протоклирование выплненных и предстоящих файловых операций, а также под сохранение данных о текущем (на некие моменты времени) ее состоянии. Что, в случае аварийного завершения работы, нарушившего целостность данных и (в первую очередь) метаданных, откатиться назад, в последнее "нормальное" состояние.

Детали устройства отдельных файловых систем будут предметом особого рассмотрения. Пока же перечислю и кратко охарактеризую наиболее используемые в свободных ОС POSIX-семейства их разновидности.

Файловые системы BSD-клана носят общее наименование FFS (Fast File System). Название это отражает их большее быстродействие относительно первознанной s5fs, хотя среди современных файловых систем они - в числе самых медленных (см. сравнительное исследование быстродействия).

Во FreeBSD (и в последних версиях NetBSD) применяются собственные реализации FFS - UFS (по умолчанию в версиях 4-й ветки) и ее 64-битная разновидность, UFS2 (в пятой ветке). Фактически FFS и разновидности UFS - единственные нативные файловые системы для ОС BSD-семейства, хотя на уровне обмена данными в них можно использовать файловые системы FAT-семейства и, с некоторыми оговорками, файловую систему Linux.

В Linux длительное время также имелась только одна нативная файловая система, носящая имя ext2fs. Хотя в принципе Linux можно установить и на FAT-раздел, и даже запускать с оного. А для дискет, отформатированных в этой ОС, по сию пору используется тяжелое наследние старого режима - файловая система Minix.

Однако в настоящее время Linux как родные воспринимает широкий спект файловых систем различного происхождения. Во-первых, это ext3fs - журналируемое расширение традиционной ext2fs, сохраняющее полную с ней совместимость (вплоть до возможности трансформации одной в другую, даже без перезагрузки машины).

Во-вторых, все большее распространение приобретает журналируемая файловая система ReiserFS - разработка Ханса Райзера и его фирмы Namesys. Она отличается эффективным механизмом работы с (очень) маленькими файлами, хорошим быстродействием и надежностью.

В третьих, начиная с версии ядра 2.6.X, Linux приобрел штатную поддержку XFS - также джурналируемой файловой системы, первоначально разработанной фирмой SGI для своей ОС IRIX, ориентированной на работу с мультимедийными данными и потому хорошо приспособленной к (очень) большим файлам и дисковым разделам.

И, наконец, эпоним журналируемых файловых систем, JFS (разработка IBM для собственной версии Unix, AIX), уже долгое время поддерживается ядром Linux в качестве нативной. Однако по ряду причин широкого распространения здесь она не получила.

На уровне обмена данными и Linux, и BSD поддерживают множество файловых систем, некоторые - только в режиме чтения, другие же - и записи тоже. Важнейшими для пользователя являются файловые системы CD- и DVD-дисков (ISO9660 и UDF, соответственно) и вариации на тему FAT, а также NTFS (последняя - практически только в режиме чтения).

Кроме того, Linux и BSD теоретически поддерживают файловые системы друг друга. теоретически - потому, что прочитать из Linux раздел с UFS - задача нетривиальная (раздел с UFS2 - пока невозможная), а запись на него, как пишут в документации, дело рискованное для сохранности данных. FreeBSD же поддерживает чтение и запись для файловых систем ext2fs и ext3fs без особых сложностей (по крайней мере, я с проблемами здесь не сталкивался), но - только для них: журналируемые системы ReiserFS и XFS из нее недоступны, а на счет JFS мне встречались противоречивые указания.

Все упомянутые выше файловые системы могут располагаться не только на реальных блочных устройствах (дисках и дисковых разделах), но и на виртуальных дисках - т.н. RAM-дисках. Впрочем, кроме того, что они располагаются в оперативной памяти (занимая некий фиксированный ее участок), в остальном они ведут себя точно так же, как и обычные дисковые разделы. Тем не менее, они знаменуют собой плавный переход к явлению, именуемому -

Виртуальные файловые системы

До сих пор речь шла о файловых системах, размещаемых на блочных устройствах (block devices based) или, иначе говоря, "реальных". Однако POSIX-совместимые операционки поддерживают еще и несколько типов виртуальных файловых систем. Они не занимают места на диске (или - почти не занимают), располагаясь в оперативной памяти, и слудат для специальных целей.

Исторически первой из таких виртуальных файловых систем была файловая система процессов - procfs. Как можно догадаться из названия, она представляет протекающие в системе процессы в виде файлов каталога /proc, откуда и извлекают информацию о проессах команды типа ps и top. Однако и пользователь может извлечь оттуда немало полезной для себя информации.

В каталоге /proc каждому процессу соответствует подкаталог, именем которого является идентификатор процесса (просто номер в порядке запуска). Внутри такого подкаталога обнаруживается набор файлов (разный, в зависимости от ОС). Содержимое некоторых из них можно посмотреть обычным способом и обнаружить там информацию об имени запустившей процесс команды, используемых ею адресах памяти и так далее.

Множество важной информации содержится в файлах корня /proc. Из них обычными командами просмотра можно извлечь свдения о процессоре (/proc/cpuinfo), текущей конфигурации ядра системы (/proc/config.gz), загруженных его модулях (/proc/modules), устройстах, подсоединенных к шине PCI (/proc/pci) - в частности, иногда только тут можно узнать подробности о своей сетевой карте и внутреннем модеме, необходимые для правильной их настройки.

Чуть выше я говорил, что виртуальные файловые системы не занимают места на диске. Однако в отношении procfs это не совсем верно - она хранит на диске свою структуру вложенных подкаталогов, каждый из которых хоть несколько байт, но занимает. В этом легко убедиться командой

$ du /proc

которая выведет нечто вроде следующего:

0       /proc/asound/card1/pcm0c/sub0
...
2       /proc/1
...
12      /proc/2106
508394  /proc

Приведенный вывод относится к Linux, во FreeBSD он имеет чуть другой вид.

Следуюущей виртуальной файловой системой является файловая система устройств - devfs. Она по умолчанию задействована во FreeBSD 5-й ветки и во многих (хотя и далеко не всех) современных дистрибутивах Linux. Чтобы понять ее назначение, нужно вспомнить, как происходило обращение с файлами устройств ранее.

А происходило оно так. Некий набор файлов устройств генерировался при начальной установке системы. Каждый файл устройства характеризовался своим старшим (major) и младшим (minor) номерами. Первый определял класс устройств, например, диски, терминалы и псевдотерминалы, параллельные или последовательные порты, и так далее. Младший же номер был идентификатором конкретного устройства в данном классе. Очевидно, что сочетание старшего и младшего номеров должно быть уникальным.

Файлы устройств генерировались в некотором соответствии с реальностью, но по принципу явной избыточности. Например, при наличии IDE-контроллера, поддерживающего до 4-х устройств, создавались файлы для всех теоретически подключаемых к нему дисков, даже если в наличии имелся только один. То же и с псевдотерминалами - файлов вида pty* в каталоге /dev можно было обнаружить немерянно (и понятно, почему - ведь каждый запущенный в X-сессии экземпляр эмулятора терминала вроде xtrem требовал своего такого устройства). В результате каталог /dev приобретал объем просто необозримый.

И тем не менее, подчас наличных файлов оказывалось недостаточно для представления всех нужных устройств. И тогда пользователю приходилось их создавать самостоятельно. Файлы для большинства распространенных устройств можно было создать с помощью специально предназначенного сценария /dev/MAKEDEV. Однако иногда он оказывался бессильным (особенно для новых устройств, сценарием еще неучтенных), и приходилось прибегать к чисто ручному созданию файлов устройств командой mknod. Что было не так уж и трудно само по себе, но требовало знания старшего номера создаваемого устройства (младший при этом вычислялся легко).

Появление devfs избавило пользователя от этих забот. Отныне файлы устройств пересоздаются автоматически при старте системы - и только в соответствие с наличным (и поддерживаемым текущей конфигурацией ядра) железом, определяемым в ходе загрузки. Если же в ходе работы какого-либо устройства из числа поддерживаемых недостанет - соответствующий ему файл будет создан сам собой.

Более того, devfs сделала очень легким "горячее" подключение устройств (типа карт PCMCIA, USB-накопителей, цифровых камер и сканеров с этим интерфейсом). Достаточно воткнуть USB-драйв в соответствующий разъем - и в каталоге /dev можно будут наблюдать появление соответствующего ему файла.

В отличие от procfs, файловая система устройств вообще не задействует дискового пространства, что нам подтвердит та же команда du /dev: в выводе ее против имен файлов и каталогов будут сплошные нули.

В настоящее время в Linux файловая система устройств постепенно заменяется системой поддержки динамического именования устройств - udev, однако использование ее не стало еще общепризнанным.

Во FreeBSD и многих дистрибутивах Linux используется также файловая система в оперативной памяти, именуемая mfs в первом случае и tmpfs - во втором. Реализованы они по разному, но с точки зрения пользователя выглядят почти одинаково - как дисковые разделы, смонтированные в некоторые каталоги. И на самом деле они заменяют собой блочные устройства там, где требуется быстрая, но не обязательно долговременная, запись. То есть их целесообразно использовать для всякого рода промежуточных каталогов при архивации/разархивации, пакетной конвертации графических файлов, а также компиляции программ.

Следует подчеркнуть, что, в отличие от файловых систем на RQAM-дисках, виртуальные файловые системы в оперативной памяти не несут под собой никаких блочных устройств. Следствием чего является то, что они не нуждаются в создании (о чем будет говориться в заключительном параграфе этого подраздела).

Файловая иерархия

Одной из отличительных особенностей логического устройства файловой системы операционок POSIX-семейства является их иерархическая, или древовидная, организации (правда, как я уже говорил дерево выглядит это немного старанно:-)). То есть здесь нет, как в DOS или Windows любого рода, обозначений (например, буквенных, или каких-либо иных) для отдельных носителей и их разделов: все они включаются в единую структуру в качестве подкаталогов главного каталога. называемого корневым. Процесс включения файловых систем на самостоятельных физических носителях (и их разделах) назвается монтированием, а подкаталоги, содержимое которых они составляют, именуются точками монтирования.

Исторически в Unix сложилась определенная структура каталогов, весьма сходная в разных предстивтелях этого семейства в общих чертах, но несколько различающаяся в деталях. В частности, файловая иерархия в BSD-системах почти идентична, отличаясь от таковой в Linux. А в последней существенные отличия обнаруживаются между разными дистрибутивами. Вплоть до того, что структура файловой иерархии является одним из дистрибутив-специфичных признаков.

Такое положение дел затрудняет сочинение кросс-платформенных приложений. И потому существует и активно развивается проект стандартизации файловой иерархии - FHS (Filesystem Hierarchy Standard), основополагающий документ которого ныне доступен в русском переводе (за что спасибо Виктору Костромину).

Проект FHS был направлен первоначально на упорядочивание структуры каталогов в многочисленных дистрибутивах Linux. Позднее он был приспособлен для других Unix-подобных систем (в том числе и BSD-клана). И ныне есть шанс, что он станет для POSIX-систем стандартом не только по имени, но и фактически.

Стандарт FHS покоится на двух основополагающих принципах - четком отделении в файловой иерархии каталогов разделяемых и неразделяемых, с одной стороны, и неизменяемых и изменяемых - с другой.

Противопоставление разделяемых и неразделяемых каталогов обусловлено изначально сетевой природой Unix. То есть данные, относящиеся к локальной машине (например, файлы конфигурирования ее устройств) должны лежать в каталогах, отдельных от тех, содержимое которых доступны с других машин в сети, локальной или глобальной (примером чему являются не только пользовательские данные, но и программы).

Суть противопоставления неизменяемых и изменяемых каталогов легко пояснить на примере. Так, те же общие пользовательские программы по природе своей должны быть неизменяемыми (вернее, доступными для модификации только администратору системы, но не самому пользователю, применяющему их в своей работе). В то же время эти программы при своей работе генерируют не только файлы данных, скажем, тексты или изображения (изменяемая их природа ясна без комментариев), но всякого рода служебную информацию, типа log-файлов, временных файлов и тому подобного). Каковая и должна группироваться в каталогах, отделенных от собственно исполнимых файлов программ, необходимых для их запуска библиотек, конфигурационных файлов и т.д.

Четкое следовании концепции отчленения разделяемых и неразделяемых, неизменяемых и неизменяемых каталогов друг от друга позволяет, в рамках единой древовидной файловой иерархии, обособить отдельные ее ветви физически - то есть в виде самостоятельных файловых систем, размещенных на изолированных устройствах (дисках, дисковых разделах, слайсах, партициях etc.). Резонов к тому много - и повышение быстродействия, и увеличение надежности, и просто соображения удобства, - но и о них сейчас речь не пойдет. Потому что в данный момент для нас важно только то, что эти ветви файлового древа должны быть инкорпорированы в общую файловую систему.

Собственно говоря, для функционирования абсолютно необходимо наличие лишь одной файловой системы - той, что монтируется в корневой каталог файлового древа (своего рода аналог мирового дерева Иггдрассиль:-)). Корневой каталог и его непременные ветви обязательно должны составлять единую файловую систему, расположенную на одном носителе - диске, дисковом разделе, программном RAID-массиве или логическом томе в понимании LVM. И в нем должны располагаться все компоненты, необходимые для старта системы и, в идеале, - ничего сверх того.

Просмотреть состав корневого каталога можно командой

$ ls -1 /

которая в любой POSIX-системе покажет некий минимальный джентльменский набор каталогов::

bin/
boot/
etc/
lib/
root/
sbin/

Именно в них собраны все файлы, без которых система не может существовать. Прочие каталоги - примерно такие:

home/
mnt/
opt/
tmp/
usr/
var/

Они а) не обязательны (по крайней мере, теоретически - практически обойтись без них трудновато) б) присутствуют не во всех системах и дистрибутивах, и в) каждый из них может быть (и часто является - если все делать по уму) точкой монтирования собственной ветви файлового древа.

Кроме этого, в большинстве случаев в корне файловой системы POSIX-совместимых ОС присутствуют еще два подкаталога:

dev/
proc/

Это обычно - точки монтирования виртуальных файловых систем - устройств и процессов, соответственно.

Корневая файловая система

Корневая файловая система является неразделяемой (то есть не предназначенной для совместного использования разными машинами сети) и неизменяемой (то есть изменения в нее может вносить только администратор системы, но не пользовательские программы и, тем более, не пользователи). Причем крайне не рекомендуется создавать в ней подкаталоги сверх предусмотренных стандартом (и пересичленных выше).

Наполнение корневой файловой системы подбирается с таким рассчетом, чтобы машина могла стартовать и сохраняла бы минимальную функциональность даже при аварийной загрузке (или в однопользовательском режиме), когда все остальные файловые системы не монтируются (и, соответственно, такие ее ветки, как /usr или /var, могут оказаться недоступными.

В соответствие с этим старт машины обеспечивается файлами каталогов /boot и /etc. В первом размещаются ядро системы - исполнимый файл "особого назначения", - и все, что требуется для его загрузки: в Linux, например, это системная карта (файл /etc/System.map), а во FreeBSD - загружаемые модули ядра. Впрочем, подчас ядро размещается непосредственно в корне файловой системы, и тогда каталог /boot может отсутствовать вовсе.

Каталог /etc предназначен для общесистемных конфигурационных файлов, определяющих условия ее загрузки. Содержимое его очень сильно зависит от системы (а в Linux - еще и от дистрибутива), и потому рассматривать его здесь я не буду - к этой теме придется еще не раз возвращаться.

Минимально необходимая функциональность обеспечивается содержимым каталогов /bin и sbin - в них собраны исполнимые файлы важнейших пользовательских и системных программ, соответственно, тех самых, которые позволят выполнить комплекс ремонтно-спасательных мероприятий и привести машину в человеческий вид после сбоя.

Разнесение системных и пользовательских программ по подкаталогам корня - достаточно условно. Ни одна из команд этих для решения пользовательских задач по настоящему не предназначена. Просто в каталоге /bin собраны команды администрирования, к которым время от времени обращается (или может обратиться) и обычный пользователь, а каталог sbin предназначен для команд, о которых пользователю и знать-то не положено. И которыми он, в большинстве случаев, все равно не сможет воспользоваться по причине отсутствия соответствующих полномочий (например, требуемых прав доступа к файлам устройств).

Для запуска POSIX-программ (в том числе и тех, что собраны в каталогах /bin и sbin), как правило, требуется доступ к функциям общесистемных библиотек (в первую очередь - главной библиотеки glibc). И потому (почти) непременный компонент корневого каталога - подкаталог /lib, в коем они и собраны.

В Linux каталог /lib служит еще одной важной цели - в его подкаталоге (/lib/modules) собраны загружаемые модули ядра (во FreeBSD их место - каталог /boot/kernel).

Во FreeBSD каталога /lib в корневой файловой системе не обнаруживается - соответствующие компоненты здесь размещаются в /usr/lib (см. далее). Это связано с тем, что исторически во FreeBSD важнейшие общесистемные программы собирались так, что требуемые им библиотечные функции встраивались в их исполнимые файлы (так называемая статическая линковка, о которой речь пойдет со временем). Во FreeBSD 5-й ветки программы из каталогов /bin и /sbin линкуются динамически, то есть при отсутствии каталога /usr (а во Free это почти всегда отдельная ветвь файловой системы) они не функционируют. В компенсацию чего предусмотрен выходящий за рамки стандартов каталог /restore, содержащий те же программы, но слинкованные статически (как следует из имени каталога, единственным назначением его содержимого являются аварийно-спасательные работы).

И, наконец, /root. Это - обычный домашний каталог одноименного пользователя, сиречь администратора системы. Поскольку никакой практической работы он не делает (или, по крайней мере, делать не должен), содержимое его - конфигурационные файлы (пользовательской командной оболочки, любимого редактора и так далее).

Ветвь /usr

Исторически каталог /usr предназначался для пользовательских программ и данных. Ныне эти функции распределены между каталогами /usr/local и /home (хотя и сейчас во FreeBSD по умолчанию последний представляет собой символическую ссылку на /usr/home). Каталог же /usr - не изменяемый, но разделяемый, - служит вместилищем основной части прикладных программ и всего, что к ним относится - исходных текстов, конфигурационных файлов, разделяемых библиотек, документации и тому подобного хозяйства.

Состав каталога /usr существенно различается в BSD-системах и в Linux. В первых в него помещаются только неотъемлемые части операционной системы (того, что во FreeBSD объединяется понятием Distributions). Приложения же, устанавливаемые из портов или пакетов, имеют место своей прописки подкаталог /usr/local, который может представлять отдельную ветвь файлового древа.

В Linux каталог /usr служит вместилищем всех программ (и их компонентов), шататно включенных в состав дистрибутива. А подкаталог /usr/local предназначается обычно для программ, самостоятельно собираемых из исходников.

В любом случае, обычный состав каталога /usr следующий (по выводу команды ls -1):

bin
etc
include
lib
libexec
local
sbin
share
src
X11R6

Как уже говорилось, подкаталог /usr/local - отдельная ветвь файлового древа, и потому будет рассмотрен отдельно же. Назначение же прочих каталогов таково:

Кроме этого, в каталоге /usr могут обнаружиться подкаталоги /usr/var и /usr/tmp - обычно символические ссылки на соответствующие ветви корневого каталога. А внекоторых дистрибутивах Linux непосредственно в /usr помещается и основная общесистемная документация - man-страницы (в подкаталог /usr/man).

Наконец, в BSD-системах и некоторых Source Based дистрибутивах Linux (например, Gentoo) в каталоге /usr размещается подкаталог для системы управления пакетами - портов FreeBSD и OpenBSD (/usr/ports), их аналогов в других системах (/usr/portage в Gentoo). Хотя с точки зрения следования духу стандарта FHS (сам он о портах и подобных системах не упоминает ни словом), более логичным местом их размещения был бы каталог /var (см. ниже).

Ветвь /usr/local

Как уже было сказано, ветвь /usr/local в Linux предназначена для самостоятельно собираемых из исходников (не входящих в данный дистрибутив) программ. А во FreeBSD она служит вместилищем большей части пользовательских приложений - всего того, что выходит за рамки Distributions и устанавливается из пакетов или портов. Соответственно этому, структура каталога в целом повторяет таковую ветви /usr (с понятными исключениями):

bin
etc
include
lib
man
sbin
share

Содержимое подкаталогов также аналогично: исполнимые файлы программ (/usr/local/bin и /usr/local/sbin), их конфиги (/usr/local/etc), библиотеки, с которым они связаны, и их заголовочные файлы (/usr/local/lib и /usr/local/include, соответственно), man-страницы (/usr/local/man) и всякая архитектурно независимая всячина (/usr/local/share), в том числе и документация в иных форматах.

Ветвь /opt

Каталог /opt предусмотрен стандартом FHS, но реально используется не во всех дистрибутивах Linux, а в BSD-системах и вовсе отсутствует. Тем не менее, все больше программ пишутся в рассчете на умолчальную инсталляцию именно в него.

А вообще-то назначение каталога /opt - размещение больших самодостаточных программных комплксов, таких, как библиотека Qt, KDE со всеми его компонентами и приложениями, OpenOffice.org и тому подобных. Структура каталога должна быть такой: /opt/pkg_name. Вот как выглядит она а в моей системе (Archlinux):

$ ls -1 /opt
gnome/
kde/
OpenOffice.org1.1.2/
qt/

Каждый из подкаталогов имеет собственную внутреннюю структуру:

$ ls -1 /opt/*

/opt/gnome:
bin/
lib/
man/
share/

/opt/kde:
bin/
etc/
include/
lib/
share/

/opt/OpenOffice.org1.1.2:
help/
LICENSE
LICENSE.html
program/
README
README.html
setup@
share/
spadmin@
THIRDPARTYLICENSEREADME.html
user/

/opt/qt:
bin/
doc/
include/
lib/
mkspecs/
phrasebooks/
plugins/
templates/
translations/

Назначение подкаталогов внутри /opt/pkg_name легко угадывается по аналогии с /usr и /usr/local. Например /opt/kde/bin предназначается для исполнимых файлов системы KDE и ее приложений, /opt/kde/etc - для конфигурационных ее файлов, /opt/kde/include - для файлов заголовков, /opt/kde/lib - для библиотек и /opt/kde/share - для разделяемых файлов, в том числе и документации. В KDE нет документации в man-формате, если же она имеется, то (как в случае Gnome - я его не ставил, это то, что потянули Gimp и тому подобные Gtk-приложения) можно вижеть подкаталог /opt/pkg_name/man.

Ветвь /var

Как явствует из названия, каталог /var предназначен для хранения изменяемых файлов, генерируемых в ходе нормальной жизнедеятельности различных программ - программных (например, браузерных) кэшей, log-файлов, спулинга печати и почтовых систем, почтовых ящиков, описаний запущенных процессов и так далее. В частности, именно в каталог /var помещаются так называемые дампа - слепки состояния оперативной памяти, генерируемые при аварийном завершении работы для выявления причин оного. Отличительная особенность всех этих компонентов - их изменчивый в процессе сеанса работы характер и то, что они, тем не менее, должны сохраняться при перезагрузке системы.

Внутренняя структура /var очень сильно меняется от системы к системе, и поэтому на деталях ее устройства я задерживаться не буду. Замечу только, что этот каталог - логичное место для помещения компонентов всякого рода портообразных систем управления пакетами, как это сделано, например, в дистрибутиве Archlinux, где под нее отведен подкаталог /var/abs (abs - Archlinux Building System).

Каталог /mnt

Каталог /mnt предназначен для монтирования временно используемых файловых систем, располагающихся, как правило, на сменных носителях. В всежеустановленной системе он обычно пуст, и структура его никак не регламентирована. Пользователю вольно создать в нем подкаталоги для отдельных видов носителей. Например, в моей системе это /mnt/cd, /mnt/dvd, /mnt/usb и /mnt/hd - для дисков CD, DVD, флэшки и съемного винчестера.

Во FreeBSD штатными каталогами для монтирования CD и дискет являются /cdrom и /floppy непосредственно в корневом каталоге.

Ветвь /home

Каталог /home предназначен для помещения домашних каталогов пользователей. Содержимое его никак не регламентировано, но обычно он имеет вид вроде /home/{username1,...,username#}. Хотя в крупных системах с большим количеством пользователей их домашние каталоги могут быть объединены в группы.

В каталоге /home могут располагаться домашние каталоги не только реальных, но и некоторых виртуальных пользователей. Так, если машина используется в качестве web- или ftp-сервера, можно видеть такие подкаталоги, как /home/www или /home/ftp, соответственно.

Ветвь /tmp

Осталось поговорить только о каталоге для хранения временных файлов - /tmp. Как и компоненты /var, они генерируются различными программами в ходе нормальной их жизнедеятельности. Но, в отличие от /var, для компонентов /tmp не предполагается их сохранения вне текущего сеанса работы. Более того, все руководства по системному администрированию рекомендуют регулярно (например, при рестарте машины) или периодически очищать этот каталог. И потому в качестве /tmp целесообразно монтировать файловые системы в оперативной памяти - tmpfs (в Linux) или mfs (во FreeBSD). Кроме того, что это гарантирует очистку его содержимого при перезагрузке, так еще и способствует быстродействию, например, компиляции программ, временные продукты которой не записываются на диск, а помещаются в виртуальный каталог типа /tmp/obj.

Во многих системах можно увидеть каталоги вроде /usr/tmp и /var/tmp. Это, как правило, символические ссылки на /tmp.

В заключение разговора о файловой иерархии следует подчеркнуть, что гарантированно на одной файловой системе (фигурально говоря, на одном дисковом разделе, хотя это и не совсем точно) должны находиться только каталоги, перечисленные в параграфе Корневая файловая система. Все же прочие каталоги - /usr, /opt, /var, /tmp и, конечно же, /home могут представлять точки монтирования самостоятельных файловых систем на отдельных физических носителях или их разделах.

Более того, в локальной сети каталоги эти вполне могут располагаться даже на разных машинах. Так, один компьютер, выполняющий роль сервера приложений, может содержать разделяемые в сети каталоги /usr и /opt, другой - файл-сервер, - вмещать все домашние каталоги пользователей, и так далее.

Практические следствия

В заключение - несколько слов об инструментарии для создания дисковых разделов, файловых систем и управления ими. Полный цикл подготовки носителя к использованию его в какой-либо POSIX-совместимой операционке включает в себя следующие стадии:

Для выполнения каждой стадии предназначены соответствующие инструменты, несколько различающиеся в Linux и в BSD-системах. Подробное их описание будет предметом особого рассмотрения, пока же дам им лишь краткую характеристику.

В Linux для разбиения диска можно использовать несколько программ:

Linux поддерживает в качестве нативных несколько файловых систем, и потому для работы с каждой из них существует собственный набор инструментов: ext2fsprogs - для файловых систем ext2fs и ext3fs, reiserfsprogs, xfsprogs и jfsutils - для файловых систем ReiserFS, XFS и JFS, соответственно. Каждая из этих файловых систем может быть создана командой mkfs с опцией -t, значением которой выступает желаемый тип файловой системы, и аргументом - именем файла устройства (дискового раздела).

Однако сама по себе команда mkfs - лишь оболочка, служащая для вызова специфичной команды, создающей файловую систему определенного типа: mk2efs или mkfs.ext2 - ext2fs или ext3fs (в последнем случае обе они требуют опции -j, предписывающей создать журнал), mkreiserfs - ReiserFS, mkfs.xfs - XFS, mkfs.jfs - JFS. Выглядит это примерно так:

$ mke2fs /dev/hda1

создасть файловую систему ext2fs на первичном разделе /dev/hda1,

$ mke2fs -j /dev/hda3

создаст файловую систему на первичном разделе /dev/hda3

$ mkreiserfs /dev/hda5
$ mkfs.xfs /dev/hda6
$ mkfs.jfs /dev/hda7

создаст поименованные файловые системы на соответствующих логических разделах в Extended partition (каковой будет, например, /dev/hda4). При использовании файлойловой системы устройств агрументы команд семейства mk* могут (а иногда - и должны) указываться по ее номенклатурным правилам, в краткой или полной нотации - без разницы.

Для монтирования файловых систем любых типов в Linux предназначена универсальная утилита mount. В общем случае она требует двух аргументов - имени монтируемого устройства и точки монтирования. Например, команда

$ mount /dev/hda1 /boot

смонтирует ранее созданную файловую систему в каталог /boot.

Утилита mount, как правило, безошибочно распознает поддерживаемые ею типы файловых систем (а в их числе - все нативные файловые системы Linux, все вариации на тему FAT и файловые системы CD и DVD-дисков). Если же по каким-то причинам делать это отказывается (а такое иногда случается для FAT и CD-дисков), тип файловой системы нужно задать в явном виде с помощью опции -t, например:

$ mount -t vfat /dev/sda /mnt/usb

для монтирования USB-накопителя с файловой системой VFAT (вариант FAT, поддерживающий длинные имена и принятый в Windows, начиная с версии 95).

Команда mount в Linux - очень гибка и имеет много опций. Она позволяет смонтировать одну и ту же файловую систему в разные каталоги с помощью опции --bind. Например, tmpfs таким образом может быть смонтирована не только в каталог /dev/shm (штатное ее место с точки зрения стандарта POSIX), но и в каталог /tmp:

$ mount --bind tmpfs -t tmpfs /dev/shm ;
$ mount --bind tmpfs -t tmpfs /tmp

К слову сказать - файловая система tmpfs не нуждается в создании с попощью команды семейства mk* (тем более, что такой команды в природе не существует): для ее использования достаточно поддержки в ядре и монтирования.

Более того, в разные точки можно смонтировать и ветвь каталога, не представляющую отдельной файловой системы. Это целесообразно, например, для совместного использования репозитория исходных текстов разными Source Based дистрибутивами.

Наконец, в качестве файловой системы можно смонтировать и отдельный файл, например, ISO-образ CD-диска, для чего служит опция --loop.

Впрочем, подробный разговор о монтировании у нас впереди. А пока отмечу только непременное требование (относящееся и к BSD-системам, описанным ниже): каталог, выступающий в качестве точки монтирования, должен уже существовать до отдачи команды mount (создать таковой она почему-то не в состоянии:-) - это следует сделать заблаговременно командой mkdir dirname). И желательно, чтобы он был пуст: монтирование в каталог с файлами фатальных последствий нынче не влечет (раньше - влекло, вплоть до общей паники системы), но все содержимое его станет недоступным вплоть до размонтирования файловой системы (о размонтировании я расскажу в самом конце). Правда, и никуда не денется тоже.

Отдельно стоит предостеречь от монтирования tmpfs в каталог /tmp в ходе рабочего сеанса: это вызовет мгновенный крах некоторых программ, в частности - оконной системы Икс. Как же быть, если нужно задействовать tmpfs именно в этом качестве? - спросите вы меня. И на это я отвечу под занавес.

А пока - о BSD-системах, конкретно - о FreeBSD. Здесь первая стадия (то есть разбиение диска) распадается на два шага: сначала (с помощью программы fdisk) создается BSD-слайс (то есть первичный раздел, по схеме DOS/Linux), который затем (программой bsdlabel) разделяется на логические партиции.

Относительно программы fdisk - сами создатели отмечают в ней единственный баг: то, что интерфейс ее мог бы быть и подружественней. И действительно - это неинтерактивная утилита командной строки, изменения в разметку диска вносятся в реальном времени, так что любая ошибка чревата потерей данных. В сущности, она гораздо более похожа на sfdisk, нежели на свою тезку из Linux. В компенсацию чего она, как и sfdisk, обладает несравненной гибкостью.

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

FreeBSD в качестве нативных использует всего две близкородственные файловые системы - UFS и UFS2, и потому необходимости в большом количестве команд для их создания нет: с этой задачей вполне справляется единственная утилита newfs - по умолчанию она нынче создает именно UFS2, но указанием соответствующей опции может сделать это и для старой UFS.

Некоторая сложность использования fdisk и bsdlabel обуславливает то, что во FreeBSD для подготовки носителей широко применяется (а для начинающих пользователей - рекомендуется) универсальная утилита sysinstall, подробно описанная в соответствующем разделе BSD-цикла. Она позволяет добиться поставленной цели - разбиения диска, создания файловой системы и ее монтирования, - быстро и с минимальным риском для имеющихся данных, а при понимании сути своих действий - сделать это (почти) также гибко, как и посредством специализированных утилит. Важно только понимать, что именно последние-то при этом и работают, тогда как sysinstall - только надстройка над ними.

BSD-инструментарий для монтирования файловых систем отличается от Linux'ового тем, что здесь отсутствует команда mount. Вернее, она есть - но не носит универсального характера, предназначаясь только для монтирования родных файловых систем - UFS и UFS2. Умолчальная ее форма:

$ mount /dev/ad#s#? /mount_point

только их и в состоянии смонтировать. Для монтирования чуждых файловых систем из числа поддерживаемых предназначены специальные команды: mount_msdos - для FAT-разделов, mount_ext2 - для файловых систем Linux (как я уже говорил, только для ext2fs и ext3fs, причем в последней журналирование просто игнорируется), mount_cd9660 - для CD-дисков. Тот же результат может быть достгнут и командой mount -t type_fs - но это лишь опосредованный вызов специальной команды для данного типа файловой системы.

Все варианты команды mount во FreeBSD существенно беднее своей Linux-коллеги по возможностям. Здесь не допускается ни раздельное монтирование одной и той же файловой системы в разные точки, ни монтирование ветвей файловой системы. А монтирование файла как устройства требует некоторых дополнительных телодвижений (см. материал о записи CD во FreeBSD).

Выше был описан процесс ручного монтирования файловых систем и в Linux, и во FreeBSD. И действительно, к ручному монтированию подчас прибегать приходится. Однако постоянно используемые файловые системы (такие, как /usr, /home и так далее, не говоря уже о корне) целесообразно монтировать автоматически в ходе загрузки машины. За этот процесс отвечают соответствующие системы инициализации. А вот то, что должно монтироваться, описывается в файле /etc/fstab.

Формат его одинаков в большинстве POSIX-операционок, представляя собой простую базу данных, строки которой описывают каждое требующее монтирования устройство, а поля - следующие (разделитель полей - пробел или пробелы в любом количестве, или символы табуляции):

Указания в поле опций монтирования значения default подразумевает, в том числе, и то, что файловая система будет смонтирована автоматически при старте системы. Однако это не подходит для сменных носителей, каковыхв момент загрузки может не быть в приводе. И потомув строках, описывающих CD ROM, USB-драйвы и тому подобные дивайсы, значение этого поля должно укамзываться как noauto. А сама по себе запись в /etc/fstab для них при звана в дальнейшем упростить процедуру монтирования: в качестве аргумента команда mount потребует только имя каталога - точки монтирования.

Для пояснения всего сказанного приведу свой файл /etc/fstab для системы Archlinux:

/dev/discs/disc0/part1 / reiserfs notail,noatime,nodiratime 0 0
/dev/discs/disc0/part3 /var reiserfs notail,noatime,nodiratime 0 0
/dev/discs/disc0/part4 /home reiserfs notail,noatime,nodiratime 0 0

Можно видеть, что в поле опций монтирования у меня приведено довольно много значений (через запятую, без пробела). К ним мы еще вернемся, нынче отмечу лишь, что опции noatime,nodiratime запрещают обновление того самого временного атрибута - времени последнего доступа, о котором говорилось ранее, и призваны несколько повысить производительность файловых операций (впрочем, именно для ReiserFS это повышение вполне реально, как показано в соответствующем иссследовании).

По завершении использования файловой системы она в обязательном порядке подлежит размонтированию. Для постоянно смонтированных файловых систем, описанных в /etc/fstab, это делается автоматически при перезагрузке системы (или подготовке ее к выключению - Unix-машину крайне не рекомендуется выключать просто обесточиванием, на то есть специальные команды).

А вот файловые системы на сменных носителях перед извлечением оных должны быть размонтированы руками. Для чего служит команда umount с единственным аргументом - точкой сонтирования (или именем файла устройства - без разницы, но не обоих вместе). То есть любая из команд типа

$ umount /mnt/cd

или

$ umount /dev/cdroms/cdrom0

одинаково успешно размонтирует файловую систему на диске в CD-приводе, без чего извлечь его оттуда можно только с помощью скрепки.

По умолчанию монтирование и размонтирование файловых систем - прероготива исключительно root'а. Однако часто целесообразно разрешить эту операцию и обычному пользователю - по крайней мере, в отношении сменных носителей. В Linux это достигается просто - указанием значения user в поле опций монтирования соответствующей строки файла /etc/fstab, например, для CD:

/dev/cdroms/cdrom0 /mnt/cd iso9660 ro,user,noauto,unhide 0 0

Во FreeBSD - несколько сложнее, требуется изменение прав доступа к соответствующим устройствам, что описывать мне уже откровенно лень (предлагается в качестве домашнего задания). Потому что впереди у меня еще

Несколько слов о своппинге

Строго говоря, свопиинг не имеет отношения ни к файловым системам, ни к их монтированию. Но сказать о нем нужно, а лучшего места я придумать не смог.

Итак, ранее вскользь упоминалось о разделе, предназначенном для своппинга - то есть области диска, куда выгружается содержимое оперативной памяти при ее переполнении (это - в Linux, во FreeBSD несколько иначе, но как - сейчас не существенно).

Для того, чтобы использовать область подкачки, нужно для создать соответствующий раздел (первичный или логический - без разницы, главное - с соответствующим иденитификатором, каковой в Linux будет 82, хотя и это не обязательно). Рекомендованный его размер - удвоенный объем оперативной памяти, что при нынешних RAM, как правило, - излишество (но и не внапряг при нынешних дисках).

Своп-раздел не несет на себе никакой файловой системы, однако его качество должно быть задано явным образом специальной командой, например:

mkswap /dev/hda2

Здесь нужно быть внимательным: применение этой команды к разделу с файловой системой приведет к уничтожению последней (и, соответственно, всех ее данных).

В монтировании своп-раздел также не нуждается, вместо этого его нужно активизировать:

$ swapon /dev/hda3

При старте машины активация свопа происходит автоматически, при наличии в файле /etc/fstab строки вида:

/dev/discs/disc0/part2 swap swap defaults 0 0

Обратной процедуре - дезактивации области своппинга, - служит команда

$ swapoff /dev/hda3

Она также выполняется автоматически при перезагрузке или подготовке к выключению.

Вот и подошлел к концу затянувшийся рассказ о трех китах POSIX'ивзма - жирные, однако, кашалоты получились:-). Впереди - разговор о том, как наш самый главный кашалот - то есть пользователь, - взаимодействует с двумя другими, каким образом он запускает процессы и общается с файлами.

Назад|Содержание|Вперед


Сайт управляется системой uCoz