Операционная система UNIX
Содержание
1. Основание и история
Проект операционной системы Multics: неудача с положительными последствиями
Возникновение и первая редакция ОС UNIX
Исследовательский UNIX
Первый перенос ОС UNIX
Седьмая редакция
Возникновение группы университета г. Беркли (BSD)
UNIX System III и первые коммерческие версии системы
AT&T System V Release 2 и Release 3
2. Основные понятия ОС UNIX и базовые системные вызовы
Основные понятия
Пользователь
Интерфейс пользователя
Привилегированный пользователь
Программы
Команды
Процессы
Перенаправление ввода/вывода
Основание и история
История ОС UNIX началась в недрах Bell Telephone Laboratories (теперь AT&T Bell Laboratories) и связана с известными теперь всем именами Кена Томпсона, Денниса Ритчи и Брайана Кернигана (два последних имени знакомы читателям и как имена авторов популярнейшей книги по языку программирования Си, издававшейся в нашей стране на русском языке).
Проект операционной системы Multics: неудача с положительными последствиями
С 1965 по 1969 год компания Bell Labs совместно с компанией General Electric и группой исследователей из Масачусетского технологического института участвовала в проекте ОС Multics. Целью проекта было создание многопользовательской интерактивной операционной системы, обеспечивающей большое число пользователей удобными и мощными средствами доступа к вычислительным ресурсам. В этом курсе мы не ставим задачу познакомить слушателей с ОС Multics. Это могло бы быть темой отдельного большого курса. Однако отметим хотя бы некоторые идеи, которые содержались в проекте MAC (так назывался проект ОС Multics).
Во-первых, эта система основывалась на принципах многоуровневой защиты. Виртуальная память имела сегментно-страничную организацию, разделялись сегменты данных и сегменты программного кода, и с каждым сегментом связывался уровень доступа (по выполнению для сегментов команд и уровень чтения и записи для сегментов данных). Для того, чтобы какая-либо программа могла вызвать программу или обратиться к данным, располагающимся в некотором сегменте, требовалось, чтобы уровень выполнения этой программы (точнее, сегмента, в котором эта программа содержалась, был не ниже уровня доступа соответствующего сегмента). Такая организация позволяла практически полностью и с полной защитой содержать операционную систему в системных сегментах любого пользовательского виртуального адресного пространства.
Во-вторых, в ОС Multics была спроектирована и реализована полностью централизованная файловая система. В централизованной файловой системе файлы, физически располагающиеся на разных физических устройствах внешней памяти, логически объединяются в один централизованный архив или древовидную иерархическую структуру, промежуточными узлами которой являются именованные каталоги, а в листьях содержатся ссылки на файлы. В том случае, когда при поиске файла в архиве по его имени оказывалось, что соответствующий накопитель (магнитный диск или магнитная лента) не был установлен на устройство внешней памяти, ОС обращалась к оператору с требованием установить нужный том внешней памяти. Естественно, такая дисциплина существенно облегчала операторскую работу и администрирование файловой системы, хотя и затрудняла выполнение таких рутинных действий как перенос части файловой системы с одного компьютера на другой. Позже мы увидим, какой изящный компромисс был выбран при реализации ОС UNIX.
Далее, наличие большой сегментно-страничной виртуальной памяти позволило использовать отображение файлов в сегменты виртуальной памяти. Другими словами, при открытии файла в виртуальной памяти соответствующего процесса образовывался сегмент, в который полностью отображался файл, располагающийся во внешней памяти. (Следует отметить, что в файловой системе ОС Multics на базовом уровне поддерживались файлы со страничной структурой. Более сложные организации являлись надстройкой). Дальнейшая работа с файлом происходила на основе общего механизма управления виртуальной памятью.
Операционная система Multics, хотя и не была полностью доведена до стадии коммерческого продукта, обогатила мировое сообщество системных программистов массой ценных идей, многие из которых сохраняют свою актуальность по сей день и используются применительно не только к операционным системам. Основным недостатком ОС Multics, который, по всей видимости, и помешал довести систему до уровня программного продукта, была ее чрезмерная сложность. Среди участников проекта Multics находились Кен Томпсон и Деннис Ритчи.
Решение о прекращении участия в проекте Multics было принято на самом верхнем уровне руководства Bell Labs, и сотрудники, по существу, были поставлены перед свершившимся фактом. Более того, руководство компании, разочарованное результатами весьма дорогостоящего проекта, вообще не желало больше вести какие-либо работы, связанные с операционными системами.
Возникновение и первая редакция ОС UNIX
Принято считать, что исходным толчком к появлению ОС UNIX явилась работа Кена Томпсона по созданию компьютерной игры "Space Travel". Он делал это в 1969 году на компьютере Honeywell 635, который до этого использовался для разработки проекта MAC. В это же время Кен Томпсон, Деннис Ритчи и другие сотрудники Bell Labs предложили идею усовершенствованной файловой системы, прототип которой был реализован на компьютере General Electric 645. Однако компьютер GE-645, который был рассчитан на работу в режиме разделения времени и не обладал достаточной эффективностью, не годился для переноса Space Travel. Томпсон стал искать замену и обнаружил, что появившийся к этому времени 18-разрядный компьютер PDP-7 с 4 килословами оперативной памяти и качественным графическим дисплеем вполне для этого подходит.
После того, как игра была успешно перенесена на PDP-7, Томпсон решил реализовать на PDP-7 разработанную ранее файловую систему. Дополнительным основанием для этого решения было то, что компания Bell Labs испытывала потребность в удобных и дешевых средствах подготовки и ведения документации. В скором времени на PDP-7 работала файловая система, в которой поддерживались: понятие inodes, подсистема управления процессами и памятью, обеспечивающая использование системы двумя пользователями в режиме разделения времени, простой командный интерпретатор и несколько утилит. Все это еще не называлось операционной системой UNIX, но уже содержало родовые черты этой ОС.
Название придумал Брайан Керниган. Он предложил назвать эту двухпользовательскую систему UNICS (Uniplexed Information and Computing System). Название понравилось, поскольку, помимо прочего, оно напоминало об участии сотрудников Bell Labs в проекте Multics. В скором времени UNICS превратилось в UNIX (произносится так же, но на одну букву короче).
Первыми реальными пользователями UNIX стали сотрудники патентного отдела Bell Labs. Однако существовали некоторые проблемы, связанные с PDP-7. Во-первых, эта машина не принадлежала компьютерной группе (была только во временном пользовании). Во-вторых, возможности этого компьютера не удовлетворяли потребности исследователей. Поэтому в 1971 году был приобретен новый 16-разрядный компьютер фирмы Digital Equipment PDP-11/20, и на него была перенесена UNIX. Существовавший к этому времени вариант системы был написан на языке ассемблера, так что можно представить, что перенос был совсем не простым делом. На PDP-11 система поддерживала большее число пользователей. Кроме того, была реализована утилита форматирования текстовых документов roff (тоже на языке ассемблера).
В ноябре 1971 года был опубликован первый выпуск документации по ОС UNIX ("Первая редакция"). В соответствии с этой "Первой редакцией" назвали и соответствующий документации вариант системы. Впоследствии это стало традицией: новая редакция ОС UNIX объявлялась при выходе в свет новой редакции документации.
Вторая редакция появилась в 1972 году. Наиболее существенным качеством "Второй редакции" было то, что система была переписана на языке Би ("B"). Язык и интерпретирующая система программирования были разработаны Кеном Томпсоном под влиянием существовавшего языка BCPL. Во второй редакции появились программные каналы ("pipes").
Появление варианта системы, написанного не на языке ассемблера, было заметным продвижением. Однако сам язык Би во многом не удовлетворял разработчиков. Подобно языку BCPL язык Би был бестиповым, в нем поддерживался только один тип данных, соответствующий машинному слову. Другие типы данных эмулировались библиотекой функций. Деннис Ритчи, который всегда увлекался языками программирования, решил устранить ограничения языка Би, добавив в язык систему типов. Так возник язык Си ("C"). В 1973 году Томпсон и Ритчи переписали систему на языке Си. К этому времени существовало около 25 установок ОС UNIX, и это была "Четвертая редакция".
В июле 1974 года Томпсон и Ритчи опубликовали в журнале Communications of the ACM историческую статью "UNIX Timesharing Operating System", которая положила начало новому этапу в истории системы. ОС UNIX заинтересовались в университетах. Этому способствовала политика компании Bell Labs, которая объявила о возможности бесплатного получения исходных текстов UNIX для использования в целях образования (нужно было платить только за носитель и документацию).
Появившуюся к этому времени "Пятую редакцию" ОС UNIX одними из первых получили Калифорнийский университет г. Беркли и университет Нового Южного Уэльса г. Сидней (Австралия).
Исследовательский UNIX
В 1975 году компания Bell Labs выпустила "Шестую редакцию" ОС UNIX, известную как V6 или Исследовательский UNIX. Эта версия системы была первой коммерчески доступной вне Bell Labs. К этому времени большая часть системы была написана на языке Си. Небольшие размеры языка и наличие сравнительно легко переносимого компилятора придавали ОС UNIX V6 новое качество реально переносимой операционной системы. Кроме того, потенциальное наличие на разных аппаратных платформах компилятора языка Си делало возможным разработку мобильного прикладного программного обеспечения.
Важный шаг в этом направлении был предпринят Деннисом Ритчи, который в 1976 году создал библиотеку ввода/вывода (stdio), ставшую фактическим стандартом различных систем программирования на языке Си. С использованием stdio стало возможно создавать мобильные прикладные программы, действительно независящие от особенностей аппаратуры процессора и внешних устройств.
Примерно в это же время Кен Томпсон во время своего академического отпуска посетил университет г. Беркли и установил там UNIX V6 на компьютере PDP-11/70. Билл Джой (основатель BSD - Berkeley Software Distribution, а впоследствии основатель и вице-президент компании Sun Microsystems) был тогда дипломником этого университета.
Первый перенос ОС UNIX
По-видимому, первый перенос ОС UNIX на компьютер с архитектурой, принципиально отличающейся от PDP-11, был произведен в 1977 году в Австралии. Это произошло вскоре после того, как в университете Воллонгонга была образована компьютерная кафедра. Джюрис Рейндфельдс, ставший заведующим новой кафедры, решил использовать ОС UNIX как основу обучения студентов. Он специально посетил университет г. Беркли и был вдохновлен возможностями, имеющимися в этом университете (PDP-11/40 с ОС UNIX V6). Однако выяснилось, что в университете г.Воллонгонг отсутствовали средства, достаточные для приобретения PDP-11.
Профессор Рейндфельдс был вынужден купить 32-разрядный компьютер Interdata 7/32, который был существенно дешевле, хотя и слабее по производительности. После нескольких попыток здравым образом дополнить "родную" операционную систему Interdata 7/32 OSMT/32 более развитыми средствами многопользовательского режима использования было принято решение попробовать перенести на эту 32-разрядную машину ОС UNIX V6.
Очень замысловатым образом (напомним, что в австралийском университете не было доступного компьютера PDP-11) путем обмена магнитными лентами с университетом г. Беркли Ричард Миллер (канадец, работавший в Австралии) смог к январю 1977 года получить компилятор языка Си, который мог успешно компилировать собственный исходный текст на Interdata 7/32. Это позволило уже через месяц получить некоторый вариант ОС UNIX, работающий на этой же машине.
Система Миллера представляла собой некий гибрид, основанный на ОС UNIX V6 и выполняемый "поверх" OSMT/32. Версия системы не включала собственных средств управления терминалами и обработки прерываний и поддерживала около восьми команд примитивного командного интерпретатора. Тем не менее, это была первая успешная (и быстро выполненная) попытка переноса ОС UNIX на компьютер с 32-разрядной архитектурой.
Седьмая редакция
После завершения своей работы Ричард Миллер отправился в Bell Labs с целью обсудить полученные результаты с Томпсоном и Ритчи. Незадолго до этого в Bell Labs был закуплен компьютер Interdata 8/32 (модель, следующая за Interdata 7/32). В принципе, компания Bell Labs была удовлетворена возможностями и ценой компьютеров семейства PDP-11. Однако 16-разрядная организация этих компьютеров ограничивала возможности ОС UNIX (слишком малый размер виртуальной памяти для разработки больших и сложных программ). Переход на 32-разрядные архитектуры позволял преодолеть эти ограничения.
Наличие 32-разрядного компьютера Interdata 8/32 и имеющийся положительный опыт Ричарда Миллера по переносу (хотя и не полному) ОС UNIX на Interdata привели к тому, что Томпсон и Ритчи решили произвести полный перенос UNIX на свою новую машину. Для начала требовалось развить язык Си, чтобы программисты могли использовать особенности 32-разрядных архитектур. Для этого Деннис Ритчи расширил систему типов языка Си типами union, short integer, long integer и unsigned integer. В дополнение к этому, в языке появились развитые средства инициализации переменных, битовые поля, макросы и средства условной компиляции, регистровые и глобальные переменные и т.д. Одним словом, язык Си стал таким, каким он описан в известнейшей книге Кернигана и Ритчи "Язык программирования Си" (сокращенно принято называть этот диалект языка K&R).
Однако одного расширенного языка Си было недостаточно для переноса UNIX, поскольку сама организация UNIX V6 была слишком ориентирована на особенности PDP-11. Пришлось полностью переписать подсистему управления оперативной и виртуальной памятью и изменить интерфейс драйверов внешних устройств, чтобы сделать систему более легко переносимой на другие архитектуры. Результатом работы стала "Седьмая редакция" UNIX (чаще ее называют UNIX Version 7). В состав новой версии системы входил компилятор нового диалекта языка Си PCC (Portable C-Compiler), новый командный интерпретатор sh, называемый также в честь своего создателя Bourne-shell, набор новых драйверов устройств и многое другое.
После выпуска UNIX Version 7 Деннис Ритчи поехал на конференцию в Австралию и взял с собой магнитную ленту с исходными текстами системы. В Мельбурнском университете был осуществлен полный перенос системы на Interdata 8/32. Позднее в Воллонгонге система была повторно перенесена на Interdata 7/32. Таким образом, в результате совместной плодотворной работы исследователей из США и Австралии было продемонстрировано одно из наиболее ярких качеств ОС UNIX - мобильность. Кроме того, стало ясно, что полезно привлекать к работе над ОС UNIX сотрудников и студентов университетов.
Возникновение группы университета г. Беркли (BSD)
Как мы упоминали выше, в 1976 году Кен Томпсон провел свой академический отпуск в университете г. Беркли и принял участие в проводившихся там исследованиях. Это привело к возникновению серьезного интереса к ОС UNIX среди профессоров и студентов. Появились местные знатоки системы, среди которых одним из наиболее сильных был Билл Джой.
Билл Джой собрал вместе с целью дальнейшего распространения большой объем программного обеспечения, включавший полный набор текстов UNIX V6, компилятор языка Паскаль, свой собственный редактор ex (потом его стали называть vi) и другие программы. Все это было названо Berkeley Software Distribution (BSD 1.0). Вокруг BSD сложилась небольшая, но очень сильная группа молодых программистов. Бытует мнение, что именно группа BSD смогла добиться практически полного устранения ошибок в UNIX V6. Не будучи удовлетворенной структурой и функциями ядра UNIX V6, группа BSD в своем втором выпуске (BSD 2.x) предприняла серьезную попытку переписать ядро системы.
В компьютерном отделении университета Беркли имелось несколько компьютеров семейства VAX компании Digital. Группа BSD при участии сотрудников Bell Labs Джона Рейзера и Тома Лондона произвела перенос UNIX Version 7 на 32-разрядную архитектуру VAX. Этот вариант UNIX назывался 32/V. В ядре системы появились новые свойства страничного замещения оперативной памяти и управления виртуальной памятью. Система стала основой третьего выпуска - BSD 3.x.
В группе BSD был разработан и впервые реализован стек транспортных протоколов TCP/IP (Transport Control Protocol/Internet Protocol). Эта работа финансировалась министерством безопасности США.
Bell Labs и университет Беркли заключили соглашение, в соответствии с которым группа BSD могла распространять свои версии ОС UNIX среди любых пользователей, которые располагали лицензией Bell Labs. Если учесть, что UNIX BSD исторически распространялся бесплатно (с исходными текстами!), а лицензия Bell Labs к этому времени стоила уже весьма недешево, то можно понять группу BSD, которая, начиная с первой версии BSD 4.1 (1980 год), стремилась к тому, чтобы освободить пользователей UNIX BSD от необходимости приобретать лицензию Bell Labs. Подробности этого процесса и возникшие коллизии мы рассмотрим в разделе, посвященном современному состоянию ОС UNIX.
UNIX System III и первые коммерческие версии системы
В 1978 году в Bell Labs специально для поддержки ОС UNIX была организована Группа поддержки ОС UNIX (UNIX Support Group - USG). Эта группа выпустила несколько версий системы, но они не имели хождения за пределами Bell Labs.
Однако, к этому времени большой интерес к ОС UNIX стали проявлять коммерческие компании-производители компьютеров и программного обеспечения. Это объясняется тем, что с развитием технологии электронных схем резко упала стоимость производства новых однокристальных процессоров. Поэтому наличие по-настоящему мобильной операционной системы, перенос которой на новую аппаратную платформу не занимал слишком много времени и средств, позволяло экономно оснастить новые компьютеры качественным базовым программным обеспечением. Появились компании, специализирующиеся на переносе UNIX на новые платформы.
Одной из первых была компания UniSoft Corporation, которая производила свою версию UNIX под названием UniPlus+. Microsoft Corporation совместно с Santa Cruz Operation (SCO) произвели вариант UNIX под названием XENIX. В результате к концу 70-х UNIX-подобные операционные системы были доступны на компьютерах, основанных на микропроцессорах Zilog, Intel, Motorola и т.д. Появились тысячи установок с ОС UNIX.
В 1982 году USG выпустила за пределы Bell Labs свой первый вариант UNIX, получивший название UNIX System III. В этой системе сочетались лучшие качества UNIX Version 7, V/32 и других вариантов UNIX, имевших хождение в Bell Labs.
AT&T System V Release 2 и Release 3
В начале 1983 года компания American Telephone and Telegraph Bell Laboratories (AT&T Bell Labs) объявила о выпуске UNIX System V. Впервые в истории Bell Labs было также объявлено, что AT&T будет поддерживать этот и все будущие выпуски System V. Кроме того, была обещана совместимость выпущенной версии System V со всеми будущими версиями. ОС UNIX System V включала много новых возможностей, но почти все они относились к повышению производительности (хеш-таблицы и кэширование данных). На самом деле UNIX System V являлась развитым вариантом UNIX System III. К наиболее важным оригинальным особенностям UNIX System V относится появление семафоров, очередей сообщений и разделяемой памяти.
В 1984 году USG была преобразована в Лабораторию по развитию системы UNIX (UNIX System Development Laboratories - USDL). В 1984 году USDL выпустила UNIX System V Release 2 (SVR2). В этом варианте системы появились возможности блокировок файлов и записей, копирования совместно используемых страниц оперативной памяти при попытке записи (copy-on-write), страничного замещения оперативной памяти (реализованного не так, как в BSD) и т.д. К этому времени ОС UNIX была установлена на более чем 100000 компьютеров.
В 1987 году подразделение USDL объявило о выпуске UNIX System V Release 3 (SVR3). В этой системе появились полные возможности межпроцессных взаимодействий, разделения удаленных файлов (Remote File Sharing - RFS), развитые операции обработки сигналов, разделяемые библиотеки и т.д. Кроме того, были обеспечены новые возможности по повышению производительности и безопасности системы. К концу 1987 года появилось более 750000 установок ОС UNIX, и было зарегистрировано 4,5 млн. пользователей.
На этом мы заканчиваем исторический обзор ОС UNIX, поскольку вплотную подошли к современному состоянию системы. Продолжим этот разговор в конце курса, а пока ограничимся таблицей 1.1 и рисунком генеалогического дерева ОС UNIX (заметим, что по поводу генеалогии существуют разные мнения).
Таблица
1.1.
Характерные
свойства версий AT&T UNIX начиная с 1982
года
1982 System III |
Именованные программные каналы |
|
Очереди запуска |
1983 System V |
Хеш-таблицы |
|
Кэши буферов и inodes |
|
Семафоры |
|
Разделяемая память |
|
Очереди сообщений |
1984 SVR2 |
Блокирование записей и файлов |
|
Подкачка по требованию |
|
Копирование по записи |
1987 SVR3 |
Межпроцессные взаимодействия (IPC) |
|
Разделение удаленных файлов (RFS) |
|
Развитые операции обработки сигналов |
|
Разделяемые библиотеки |
|
Переключатель файловых систем (FSS) |
|
Интерфейс транспортного уровня (TLI) |
|
Возможности коммуникаций на основе потоков |
1989 SVR4 |
Поддержка обработки в реальном времени |
|
Классы планирования процессов |
|
Динамически выделяемые структуры данных |
|
Развитые возможности открытия файлов |
|
Управление виртуальной памятью (VM) |
|
Возможности виртуальной файловой системы (VFS) |
|
Быстрая файловая система (BSD) |
|
Развитые возможности потоков |
|
Прерываемое ядро |
|
Квоты файловых систем |
|
Интерфейс драйвера с ядром системы |
Рис. 1.1. Генеалогическое дерево ОС UNIX
Основные понятия ОС UNIX и базовые системные вызовы
Основные понятия
Одним из достоинств ОС UNIX является то, что система базируется на небольшом числе интуитивно ясных понятий. Однако, несмотря на простоту этих понятий, к ним нужно привыкнуть. Без этого невозможно понять существо ОС UNIX.
Пользователь
С самого начала ОС UNIX замышлялась как интерактивная система. Другими словами, UNIX предназначен для терминальной работы. Чтобы начать работать, человек должен "войти" в систему, введя со свободного терминала свое учетное имя (account name) и, возможно, пароль (password). Человек, зарегистрированный в учетных файлах системы, и, следовательно, имеющий учетное имя, называется зарегистрированным пользователем системы. Регистрацию новых пользователей обычно выполняет администратор системы. Пользователь не может изменить свое учетное имя, но может установить и/или изменить свой пароль. Пароли хранятся в отдельном файле в закодированном виде. Не забывайте свой пароль, снова узнать его не поможет даже администратор!
Все пользователи ОС UNIX явно или неявно работают с файлами. Файловая система ОС UNIX имеет древовидную структуру. Промежуточными узлами дерева являются каталоги со ссылками на другие каталоги или файлы, а листья дерева соответствуют файлам или пустым каталогам. Каждому зарегистрированному пользователю соответствует некоторый каталог файловой системы, который называется "домашним" (home) каталогом пользователя. При входе в систему пользователь получает неограниченный доступ к своему домашнему каталогу и всем каталогам и файлам, содержащимся в нем. Пользователь может создавать, удалять и модифицировать каталоги и файлы, содержащиеся в домашнем каталоге. Потенциально возможен доступ и ко всем другим файлам, однако он может быть ограничен, если пользователь не имеет достаточных привилегий.
Интерфейс пользователя
Традиционный способ взаимодействия пользователя с системой UNIX основывается на использовании командных языков (правда, в настоящее время все большее распространение получают графические интерфейсы). После входа пользователя в систему для него запускается один из командных интерпретаторов (в зависимости от параметров, сохраняемых в файле /etc/passwd). Обычно в системе поддерживается несколько командных интерпретаторов с похожими, но различающимися своими возможностями командными языками. Общее название для любого командного интерпретатора ОС UNIX - shell (оболочка), поскольку любой интерпретатор представляет внешнее окружение ядра системы.
Вызванный командный интерпретатор выдает приглашение на ввод пользователем командной строки, которая может содержать простую команду, конвейер команд или последовательность команд. После выполнения очередной командной строки и выдачи на экран терминала или в файл соответствующих результатов, shell снова выдает приглашение на ввод командной строки, и так до тех пор, пока пользователь не завершит свой сеанс работы путем ввода команды logout или нажатием комбинации клавиш Ctrl-d.
Командные языки, используемые в ОС UNIX, достаточно просты, чтобы новые пользователи могли быстро начать работать, и достаточно мощны, чтобы можно было использовать их для написания сложных программ. Последняя возможность опирается на механизм командных файлов (shell scripts), которые могут содержать произвольные последовательности командных строк. При указании имени командного файла вместо очередной команды интерпретатор читает файл строка за строкой и последовательно интерпретирует команды.
Привилегированный пользователь
Ядро ОС UNIX идентифицирует каждого пользователя по его идентификатору (UID - User Identifier), уникальному целому значению, присваиваемому пользователю при регистрации в системе. Кроме того, каждый пользователь относится к некоторой группе пользователей, которая также идентифицируется некоторым целым значением (GID - Group IDentifier). Значения UID и GID для каждого зарегистрированного пользователя сохраняются в учетных файлах системы и приписываются процессу, в котором выполняется командный интерпретатор, запущенный при входе пользователя в систему. Эти значения наследуются каждым новым процессом, запущенным от имени данного пользователя, и используются ядром системы для контроля правомощности доступа к файлам, выполнения программ и т.д.
Понятно, что администратор системы, который, естественно, тоже является зарегистрированным пользователем, должен обладать большими возможностями, чем обычные пользователи. В ОС UNIX эта задача решается путем выделения одного значения UID (нулевого). Пользователь с таким UID называется суперпользователем (superuser) или root. Он имеет неограниченные права на доступ к любому файлу и на выполнение любой программы. Кроме того, такой пользователь имеет возможность полного контроля над системой. Он может остановить ее и даже разрушить.
В мире UNIX считается, что человек, получивший статус суперпользователя, должен понимать, что делает. Суперпользователь должен хорошо знать базовые процедуры администрирования ОС UNIX. Он отвечает за безопасность системы, ее правильное конфигурирование, добавление и исключение пользователей, регулярное копирование файлов и т.д.
Еще одним отличием суперпользователя от обычного пользователя ОС UNIX является то, что на суперпользователя не распространяются ограничения на используемые ресурсы. Для обычных пользователей устанавливаются такие ограничения как максимальный размер файла, максимальное число сегментов разделяемой памяти, максимально допустимое пространство на диске и т.д. Суперпользователь может изменять эти ограничения для других пользователей, но на него они не действуют.
Программы
ОС UNIX одновременно является операционной средой использования существующих прикладных программ и средой разработки новых приложений. Новые программы могут писаться на разных языках (Фортран, Паскаль, Модула, Ада и др.). Однако стандартным языком программирования в среде ОС UNIX является язык Си (который в последнее время все больше заменяется на Си++). Это объясняется тем, что во-первых, сама система UNIX написана на языке Си, а, во-вторых, язык Си является одним из наиболее качественно стандартизованных языков.
Поэтому программы, написанные на языке Си, при использовании правильного стиля программирования обладают весьма высоким уровнем мобильности, т.е. их можно достаточно просто переносить на другие аппаратные платформы, работающие как под управлением ОС UNIX, так и под управлением ряда других операционных систем (например, DEC Open VMS или MS Windows NT). Более подробно мы рассмотрим принципы мобильного программирования в среде ОС UNIX в четвертой части курса.
Приведем краткий обзор процесса разработки программы на языке Си (или Си++), которую можно выполнить в среде ОС UNIX. Любая выполняемая программа компонуется из одного или нескольких объектных файлов. Поэтому разработка программы начинается с создания исходных файлов, содержащих текст на языке Си. Эти файлы могут содержать определения глобальных имен переменных и/или функций (имен, которые могут быть видимы из других файлов), а также ссылки на внешние имена (объявленные как глобальные в одном из других файлов, которые будут составлять программу).
Текстовые файлы производятся с помощью одного из текстовых редакторов, поддерживаемых в среде UNIX. Традиционным текстовым редактором ОС UNIX является упоминавшийся в первом разделе редактор vi, исходная версия которого была разработана Биллом Джоем. Этот редактор достаточно старый, он может работать практически на всех терминалах и не является в полном смысле оконным.
В последние годы все большую популярность получает редактор Emacs (разработанный и непрерывно совершенствуемый президентом Free Software Foundation Ричардом Столлманом). Это очень мощный многооконный редактор, который позволяет не только писать программы (и другие тексты), но также и компилировать, компоновать и отлаживать программы (а также делать многое другое, например, принимать и отправлять электронную почту). Основным недостатком редактора Emacs является исключительно большой набор (более 200) функциональных клавиш. Следует, правда, заметить, что при использовании Emacs в оконной системе X он обеспечивает более удобный интерфейс.
Заметим также, что многие неудобства интерфейсов традиционных инструментальных средств ОС UNIX связаны с тем, что они ориентированы на использование и алфавитно-цифровых, и графических терминалов. Поэтому обычно эти средства поддерживают старомодный строчный интерфейс даже при наличии графического терминала. Естественно, в современных вариантах ОС UNIX все новые инструментальные средства поддерживают оконный графический интерфейс (и, следовательно, их невозможно использовать при наличии алфавитно-цифровых терминалов).
После того, как текстовый файл создан, его нужно откомпилировать для получения объектного файла. Наиболее популярными компиляторами для языка Си в среде ОС UNIX сейчас являются pcc (Ритчи и Томпсон) и gcc (Ричард Столлман). Оба эти компилятора являются полностью мобильными и обладают возможностью генерировать код для разнообразных компьютеров, т.е. эти компиляторы могут быть установлены практически на любой аппаратной платформе под управлением ОС UNIX.
Можно отметить следующие преимущества gcc. Во-первых, этот компилятор свободно, т.е. бесплатно (вместе со своими исходными текстами) распространяется Free Software Foundation. Во-вторых, gcc тщательно поддерживается и сопровождается. В-третьих, начиная с версии 2.0, gcc может компилировать программы, написанные на языках Си, Си++ и Objective C, а результирующая выполняемая программа может быть скомпонована из объектных файлов, полученных из текстовых файлов на любом из этих языков. В-четвертых, открытость исходных текстов gcc и тщательно разработанная структура компилятора позволяют сравнительно просто добавлять к gcc новые кодогенераторы. Относительным недостатком gcc является то, что используемый диалект языка Си включает слишком много расширений по сравнению со стандартом ANSI/ISO (однако имеется режим, в котором компилятор указывает все расширенные конструкции языка, встречающиеся в компилируемой программе).
Оба компилятора обрабатывают программу в два этапа. На первом этапе синтаксически правильный текст на языке Си преобразуется в текст на языке ассемблера. На втором этапе на основе текста на языке ассемблера генерируются машинные коды и получается объектный файл. Исторически в ОС UNIX использовались различные форматы объектных модулей. Для обеспечения совместимости с предыдущими версиями почти все они поддерживаются в современных версиях компиляторов. Однако в настоящее время преимущественно используется формат COFF (Common Object File Format). При желании можно остановить процесс компиляции после первого этапа и получить для изучения файл с текстом программы на языке ассемблера.
После того, как необходимый для построения выполняемой программы набор объектных файлов получен, необходимо произвести компоновку выполняемой программы. В ОС UNIX компоновщик выполняемых программ называется редактором связей (link editor) и обычно вызывается командой ld. Редактору связей указывается набор объектных файлов и набор библиотек, из которых нужно черпать недостающие для компоновки программы.
Процесс компоновки заключается в следующем. Сначала просматривается набор заданных объектных файлов. Для каждого внешнего имени ищется объектный файл, содержащий определение такого же глобального имени. Если поиск заканчивается успешно, то внешняя ссылка заменяется на ссылку на определение глобального имени. Если в конце этого этапа остаются внешние имена, для которых не удалось найти соответствующего определения глобального имени, то начинается поиск объектных файлов с нужными определениями глобальных имен в указанных библиотеках. Если, в конце концов, удается найти определения для всех внешних имен, все соответствующие объектные файлы собираются вместе и образуют выполняемый файл.
В ОС UNIX имеется несколько стандартных библиотек. В большинстве случаев наиболее важной является библиотека ввода/вывода (stdio). Грамотное использование стандартных библиотек способствует созданию легко переносимых прикладных программ (мы вернемся к обсуждению стандартных библиотек ОС UNIX в четвертой части курса).
Выполняемая программа может быть запущена в интерактивном режиме как команда shell или выполнена в отдельном процессе, образуемом уже запущенной программой.
Команды
Любой командный язык семейства shell фактически состоит из трех частей: служебных конструкций, позволяющих манипулировать с текстовыми строками и строить сложные команды на основе простых команд; встроенных команд, выполняемых непосредственно интерпретатором командного языка; команд, представляемых отдельными выполняемыми файлами (более подробно и точно командные языки рассматриваются в пятой части курса).
В свою очередь, набор команд последнего вида включает стандартные команды (системные утилиты, такие как vi, cc и т.д.) и команды, созданные пользователями системы. Для того, чтобы выполняемый файл, разработанный пользователем ОС UNIX, можно было запускать как команду shell, достаточно определить в одном из исходных файлов функцию с именем main (имя main должно быть глобальным, т.е. перед ним не должно указываться ключевое слово static). Если употребить в качестве имени команды имя такого выполняемого файла, командный интерпретатор создаст новый процесс (см. следующий подраздел) и запустит в нем указанную выполняемую программу начиная с вызова функции main.
Тело функции main, вообще говоря, может быть произвольным (для интерпретатора существенно только наличие входной точки в программу с именем main), но для того, чтобы создать команду, которой можно задавать параметры, нужно придерживаться некоторых стандартных правил. В этом случае каждая функция main должна определяться с двумя параметрами - argc и argv. После вызова команды параметру argc будет соответствовать число символьных строк, указанных в качестве аргументов вызова команды, а argv - массив указателей на переменные, содержащие эти строки. При этом имя самой команды составляет первую строку аргументов (т.е. после вызова значение argc всегда больше или равно 1). Код функции main должен проанализировать допустимость заданного значения argc и соответствующим образом обработать заданные текстовые строки.
Например, следующий текст на языке Си может быть использован для создании команды, которая выводит на экран текстовую строку, заданную в качестве ее аргумента:
#include
main (argc, argv)
int argc;
char *argv[];
{
if (argc != 2)
{ printf("usage: %s your-text\n", argv[0]);
exit;
}
printf("%s\n", argv[1]);
}
Процессы
Процесс в ОС UNIX - это программа, выполняемая в собственном виртуальном адресном пространстве. Когда пользователь входит в систему, автоматически создается процесс, в котором выполняется программа командного интерпретатора. Если командному интерпретатору встречается команда, соответствующая выполняемому файлу, то он создает новый процесс и запускает в нем соответствующую программу, начиная с функции main. Эта запущенная программа, в свою очередь, может создать процесс и запустить в нем другую программу (она тоже должна содержать функцию main) и т.д.
Управление процессами подробно обсуждается в третьей части курса. Тем не менее кратко опишем здесь общий подход. Для образования нового процесса и запуска в нем программы используются два системных вызова (примитива ядра ОС UNIX) - fork() и exec (имя-выполняемого-файла). Системный вызов fork приводит к созданию нового адресного пространства, состояние которого абсолютно идентично состоянию адресного пространства основного процесса (т.е. в нем содержатся те же программы и данные).
Другими словами, сразу после выполнения системного вызова fork основной и порожденный процессы являются абсолютными близнецами; управление и в том, и в другом находится в точке, непосредственно следующей за вызовом fork. Чтобы программа могла разобраться, в каком процессе она теперь работает - в основном или порожденном, функция fork возвращает разные значения: 0 в порожденном процессе и целое положительное число (идентификатор порожденного процесса) в основном процессе.
Теперь, если мы хотим запустить новую программу в порожденном процессе, нужно обратиться к системному вызову exec, указав в качестве аргументов вызова имя файла, содержащего новую выполняемую программу, и, возможно, одну или несколько текстовых строк, которые будут переданы в качестве аргументов функции main новой программы. Выполнение системного вызова exec приводит к тому, что в адресное пространство порожденного процесса загружается новая выполняемая программа и запускается с адреса, соответствующего входу в функцию main.
В следующем примере пользовательская программа, вызываемая как команда shell, выполняет в отдельном процессе стандартную команду shell ls, которая выдает на экран содержимое текущего каталога файлов.
main()
{if(fork()==0) wait(0); /* родительский процесс */
else execl("ls", "ls", 0); /* порожденный процесс */
}
Перенаправление ввода/вывода
Механизм перенаправления ввода/вывода является одним из наиболее элегантных, мощных и одновременно простых механизмов ОС UNIX. Цель, которая ставилась при разработке этого механизма, состоит в следующем. Поскольку UNIX - это интерактивная система, то обычно программы вводят текстовые строки с терминала и выводят результирующие текстовые строки на экран терминала. Для того, чтобы обеспечить более гибкое использование таких программ, желательно уметь обеспечить им ввод из файла или из вывода других программ и направить их вывод в файл или на ввод другим программам.
Реализация механизма основывается на следующих свойствах ОС UNIX. Во-первых, любой ввод/вывод трактуется как ввод из некоторого файла и вывод в некоторый файл. Клавиатура и экран терминала тоже интерпретируются как файлы (первый можно только читать, а во второй можно только писать). Во-вторых, доступ к любому файлу производится через его дескриптор (положительное целое число). Фиксируются три значения дескрипторов файлов. Файл с дескриптором 1 называется файлом стандартного ввода (stdin), файл с дескриптором 2 - файлом стандартного вывода (stdout), и файл с дескриптором 3 - файлом стандартного вывода диагностических сообщений (stderr). В-третьих, программа, запущенная в некотором процессе, "наследует" от породившего процесса все дескрипторы открытых файлов.
В головном процессе интерпретатора командного языка файлом стандартного ввода является клавиатура терминала пользователя, а файлами стандартного вывода и вывода диагностических сообщений - экран терминала. Однако при запуске любой команды можно сообщить интерпретатору (средствами соответствующего командного языка), какой файл или вывод какой программы должен служить файлом стандартного ввода для запускаемой программы и какой файл или ввод какой программы должен служить файлом стандартного вывода или вывода диагностических сообщений для запускаемой программы. Тогда интерпретатор перед выполнением системного вызова exec открывает указанные файлы, подменяя смысл дескрипторов 1, 2 и 3.
Конечно, то же самое может проделать и любая другая программа, запускающая третью программу в специально созданном процессе. Следовательно, все, что требуется для нормального функционирования механизма перенаправления ввода/вывода - это придерживаться при программировании соглашения об использовании дескрипторов stdin, stdout и stderr. Это не очень трудно, поскольку в наиболее распространенных функциях библиотеки ввода/вывода printf, scanf и error вообще не требуется указывать дескриптор файла. Функция printf неявно использует stdout, функция scanf - stdin, а функция error - stderr.
Более подробно механизм перенаправления вывода одной программы на ввод другой программы будет рассмотрен в третьей части курса.