"Спецвыпуск журнала «Хакер» #47, октябрь 2004 г." - читать интересную книгу автора (Хакер)

Отец демона и пингвина / Особенности архитектуры UNIX

«Linux в массы!», «FreeBSD на рабочий стол!» – эти лозунги все чаще можно увидеть в интернете. Народ захотел Open Source на свои домашние машины. Но что же объединяет Linux и BSD? Этого многие не понимают. По сути, Linux и все возможные клоны BSD происходят от одной системы – UNIX. Давай рассмотрим архитектуру этой ОС более подробно.

Когда мы были молодые, а компьютеры – большие

На дворе 1969 год. К компьютерам имеют доступ лишь избранные профессора крупнейших университетов. Время работы у терминала строго ограничено, и за каждой минутой загрузки машины ведется строгий учет. Стоимость одного вычислительного центра приближается к бюджету небольшой страны. Именно на такой машине, называвшейся PDP-7, программисты Денис Ритчи (Dennis Ritchie), Радд Кенедей (Rudd Canaday), Дуг Макилрой (Doug McIlroy) и Кен Томпсон (Ken Thompson) в течение месяца написали ОС, оболочку, ассемблер и редактор.

Следующей вехой развития UNIX считается его первое портирование на машину с другой архитектурой. На более производительном PDP-11/20 UNIX был полностью переписан с ассемблера на язык Би («B»). С 1970 до 1972 UNIX развивался компанией ATamp;T Bell Lab. В 1973 году Ритчи и Томпсон перевели операционную систему на язык С. К этому моменту UNIX был установлен на 25 машинах – немного, но если ты вспомнишь, сколько всего компьютеров было тогда в мире, то поймешь, что означали эти инсталлы новой системы. ОС обретала новое звучание в компьютерном мире, о ней начали говорить как о серьезном проекте. Пятая редакция проекта внесла огромный вклад в развитие системы в целом – исходные коды UNIX стали доступны студентам университетов. Началась эра массового увлечения *nix и его клонами. В университете г. Беркли собирается группа разработчиков и начинается выпуск клонов UNIX – BSD-систем. После этого происходит непрерывное совершенствование исходных кодов системы, но концепция операционной системы сложилась именно в 70-е годы двадцатого столетия. Последующие версии и клоны устраняли слабые места и увеличивали функциональность программной модели, но фундаментальных изменений не вносили.

Основные плюсы системы

Многопользовательская ОС

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

Многозадачность

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

Переносимость кода

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

Свободное распространение

Это один из основных плюсов UNIX-клонов наших дней. Изначально UNIX была платной и закрытой системой, но с течением времени все изменилось в лучшую сторону, и сейчас активно развиваются две основных лицензии для *nix: BSD (под ней выпускается FreeBSD) и GPL. Основное отличие BSD от GPL в том, что, в принципе, по инициативе разработчиков, возможно превращение продукта из свободного в закрытый, коммерческий. Самая демократичная и популярная на сегодняшний день – это GPL, под ней выпускается Linux и его клоны. Существуют также разного вида коммерческие лицензии, основанные на продаже ОС.

Нетребовательность к ресурсам PC

На сегодняшний день для х86 *nix-системы – самые малотребовательные относительно аппаратной стороны машины. Для работы роутера на *BSD достаточно 386-го процессора и 4 мегабайт памяти. Обеспечить данные функции на других распространенных системах при такой конфигурации PC просто невозможно. Причем даже на такой слабой машине, по нынешним меркам, UNIX показывает все свои лучшие стороны. Объяснить такое поведение можно, рассмотрев архитектуру ОС более подробно.

Аксиомы UNIX-like ОС

Собственная файловая система

Основными понятиями файловых систем *nix являются следующие:

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

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

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

Вся необходимая для работы с файлом информация хранится в особой системной таблице, которая является индексным дескриптором (inode) данного объекта. Индексные дескрипторы всех файлов равны по размеру – 64 байта. В них хранятся данные о типе файла, физическом расположении файла на диске, размере в байтах, дата создания, время последней модификации, последнего обращения к файлу, информация о привилегиях доступа. Все inode пронумерованы и содержатся в особом отделе файловой системы. Для ОС порядковый номер файла есть уникальное имя файла. Полное имя объекта по его номеру устанавливается с помощью таблицы иерархии каталогов.

Существует один главный администратор – root, и он бог данного хоста

Эта особенность полностью соответствует духу UNIX: если ты – админ, то это подразумевает твои обширные знания в данной области. Власти root’a хватит на любое действие в системе: от прочтения домашних каталогов пользователей до удаления всех файловых систем на жестком диске, причем он даже не получит ни одного предупреждения от системы. Концепция UNIX подразумевает грамотного админа-root’a, в отличие от творения Б.Г.

Юзеры могут делать только то, что явно разрешено

Одна из самых сильных сторон пользовательской модели *nix-систем. Администратор при создании учетной записи нового юзера дает ему определенные права и возможности для работы с системой. Разделение прав на файлы происходит с помощью атрибутов. Достаточно запретить чтение файла, установив соответствующий бит, и никто, кроме root’a, не сможет узнать содержимое. Кроме очень гибкой модели атрибутов, админ делит пользователей на реальных, то есть тех, которые могут заходить с терминала или удаленно по сети, и на специальных – тех, у кого есть права для выполнения какой-либо из определенных задач. Например, обычный пользователь Vasya, имея аккаунт на машине, может подключаться к ней с помощью клавы-монитора (как обычный юзер локального ПК), через ssh из любой точки планеты (если нет ограничений на место подключения), используя модем и терминальную программу (minicom, telemax, стандартный терминал Виндов). При любом способе подключения юзер будет находиться в системе, как будто он работает с физического локального терминала. Специальные пользователи (виртуальные), такие, как, nobody, ftp, anonymous, присутствуют в системе только как аккаунты, и вход с этих учетных записей через терминалы невозможен. Они используются для общесистемных сервисов с целью ограничения их прав: так демон ftp запускается от пользователя ftp, Apache – от nobody; это необходимо для повышения безопасности и стабильности системы.

У демонов минимально необходимые привилегии

В первоначальных редакциях UNIX этого не было, но с приходом сетей и хакеров разработчики задумались и доработали концепцию системы. На практике это выглядит так: у каждого крупного сервиса есть «свой» специальный пользователь, от его учетной записи запускаются все процессы данного сервера. Так, например, для web-сервера Apache практически всегда создается специальный пользователь nobody, на все рабочие каталоги ставится владельцем этот юзер и раздаются соответствующие права, после чего в init-скрипте прописывается логин «nobody». В результате – резкое повышение безопасности всего хоста: даже если будет найдена уязвимость в демоне, то взломщик получит права непривилегированного пользователя apache.

Наличие средств для выполнения простых действий

*nix-системы отличаются от множества других ОС тем, что любое самое сложное действие можно легко разбить на несколько более простых, реализуемых с помощью встроенных средств. То есть если пользователь хорошо знает возможности UNIX-архитектуры и четко представляет себе результат своих трудов, то добиться его будет очень легко. Примерами средств, заметно упрощающих реализацию любой задачи, могут служить такие общеизвестные приложения, как cron (периодический запуск задач), перенаправления выводов и вводов с терминала, syslog (логирование всех действий в системе), различные комбинации действий на ФС и многое другое.

Свопинг позволяет работать эффективней

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

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

Главное – загрузиться!

«Loading UNIX» – фраза, говорящая о многом. Рассмотрим два основных способа загрузки ядра системы. Почему ядра, а не всей ОС? Потому что будет отличаться только загрузка ядра, после того как оно будет в памяти, все остальное загружается стандартной и отлаженной процедурой. Самый простой вариант – это Boot-дискета. Ход загрузки системы при таком способе выглядит очень просто: после начального теста BIOS передает управление загрузочной области дискеты, где содержится код, распаковывающий ядро UNIX в оперативную память. После распаковки начинается стандартное монтирование корневого раздела. Но такой простой вариант применяется только для дискет без файловых систем. Для винчестеров используют несколько усложненный вариант загрузки. Причина этого достаточно простая: ядро не может быть записано в первые сектора, так как там находится таблица разметки жесткого диска и описания всех ФС, при порче этих данных использовать хард просто невозможно. Поэтому разработчики применили усложненный алгоритм инициализации ядра. После POST-теста управление, как обычно, передается загрузочной области жесткого диска, где хранится миниатюрная программа, вся работа которой сводится к запуску главного загрузчика ОС, обладающего достаточной функциональностью и гибкостью для запуска ядра. Таким «большим» загрузчиком может быть LILO, GRUB или стандартный BSD-loader. А уже этот загрузчик копирует ядро в память, передав ему необходимые параметры. Как видишь, при загрузке с винчестера используются двухуровневые программы.

Я хочу продолжить рассказ о схеме загрузки UNIX-систем, так как загрузка является еще одним архитектурным решением сообщества разработчиков ОС. Существует две основные схемы загрузки UNIX и его клонов: BSD и System V. BSD-тип применяется во многих BSD-системах и в некоторых дистрибутивах Linux (Gentoo, Slackware). Схема BSD проще System V, но и возможностей у нее меньше. Рассмотрим более прогрессивную System V. Сначала загружается ядро ОС, будь то UNIX, BSD-клон или Linux-клон, после этого ядро монтирует корневую файловую систему, ссылка на которую ему передана параметром загрузчика. При удачном завершении операции начинается поиск служебного каталога /sbin; если он не обнаруживается, то система выдает «Kernel panic». Затем происходит запуск главного процесса Init: отдается команда /sbin/init. После чего уже Init обращается к каталогу /etc и ищет там файл inittab, где указан необходимый уровень запуска. Осталось не так уж много: init читает и анализирует содержимое своего конфигурационного файла, а затем запускает необходимые сервисы, монтирует локальные файловые системы, поднимает сетевые интерфейсы, монтирует уваленные файловые системы и запускает оставшиеся сервисы. На завершение процесса загрузки укажет (если система загрузится не в multiuser mode) появившееся приглашение ввести логин и пароль пользователя.

Ядро – всему голова!

Главным, определяющим архитектуру системы звеном является ядро. Все ядра *nix-систем должны выполнять следующие функции:

– управление работой процессов: создание, завершение и организация взаимодействия между ними.

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

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

– предоставление высокоуровневого доступа к винчестеру и другим носителям информации. Ядро подключает файловые системы и дает простой интерфейс по взаимодействию с ними. Все это делается с учетом прав на файлы и квот для пользователя.

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

init 6

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

Книги о UNIX

http://ois.mesi.ru/html_docs/BACH/ – подробная книга, которую можно скачать в 1 zip-архиве.

http://linuxdoc.chat.ru/obsh/rukadmina/index.html – книга 95-го года, но своей актуальности она не потеряла.

NO WARRANTLY ABSOLUTED – девиз модели Open Source наших дней.

При анализе первого варианта UNIX, написанного на языке C, Ритчи указал на заметно возросший объем (20-40%) и на ухудшение производительности ОС в целом по сравнению с ассемблерным вариантом кода.

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