- •И.Н.Акуленок, а.В.Акуленок
- •Часть I. Основы операционной системы unix Утверждено советом университета
- •Введение
- •Глава 1. История создания ос unix
- •Реализации oc unix
- •Unix на платформе Intel
- •Доля компьютеров с ос unix (1993 год)
- •Продажи unix–серверов (III квартал 2007 года)
- •1.1. Первые шаги по созданию unix
- •1.2. Исследовательские версии unix
- •1.3. Основные стандарты
- •1.3.1. Основные задачи стандартизации
- •1.4. Разработчики операционных систем
- •1.4.1. Версии at&t
- •1.4.2. Версии Microsoft/sco
- •1.4.3. Версии университета Беркли
- •1.4.4. Версии компании Sun
- •1.4.5. Версии компании Nowell
- •1.4.6. Популярные версии unix
- •1.4.7. Свободно распространяемые системы unix
- •1.5. Реализация ядра unix
- •1.5.1. Микроядро Mach
- •1.5.2. Микроядро Chorus
- •1.6 Характеристики oc unix
- •1.6.1.Файловая система
- •1.6.2. Многозадачность
- •1.6.3. Многопользовательский режим
- •1.6.4. Мобильность
- •1.6.5. Виртуальная память
- •1.6.6. Связь между задачами
- •1.6.7. Внешние устройства
- •1.6.8. Связь между компьютерами
- •1.6.9. Графический пользовательский интерфейс
- •1.6.10. Безопасность
- •1.6.11. Поддержка баз данных
- •1.6.12. Наличие стандартов
- •1.6.13. Открытость
- •1.6.14. Разработка программного обеспечения
- •1.7. Контрольные вопросы
- •1.8. Тесты
- •Глава 2. Функционирование ос unix
- •2.1. Ядро
- •2.1.1. Функции ядра
- •2.1.2. Структура ядра
- •2.1.3. Файловая подсистема
- •2.1.4. Подсистема управления процессами
- •2.1.5. Подсистема ввода/вывода
- •2.2. Командный процессор Shell
- •2.3. Программы–утилиты
- •2.4. Контрольные вопросы
- •2.5. Тесты
- •Глава 3. Процессы
- •3.1. Контекст процесса
- •3.3. Типы процессов
- •3.3.1. Системные процессы
- •3.3.2. Демоны
- •3.3.3. Прикладные процессы
- •3.4. Атрибуты процесса
- •3.4.1. Идентификатор процесса
- •3.4.2. Идентификатор родительского процесса
- •3.4.3. Приоритет процесса
- •3.4.4. Терминальная линия
- •3.4.5. Реальный и эффективный идентификаторы пользователя
- •3.4.6. Реальный и эффективный идентификаторы группы
- •3.4.7. Идентификатор терминальной группы
- •3.5. Иерархия процессов
- •3.6. Взаимодействие процессов
- •3.6.1. «Отцы», «дети», «сироты», «зомби»
- •3.7. Системные вызовы
- •3.7.1. Механизм создания процесса и запуска программы
- •3.7.2. Графический пример дерева процессов
- •3.8. Связи между процессами
- •3.8.1. Сигналы
- •Сигналы posix 1.1
- •3.8.2. Очереди сообщений
- •3.8.3. Семафоры
- •3.8.4. Совместная память
- •3.8.5. Программные каналы
- •3.8.6. Программные гнезда
- •3.9. Контрольные вопросы
- •3.10. Тесты
- •Глава 4. Файловая система unix
- •4.1. Имена файлов
- •4.2. Структура файловой системы
- •4.2.1. Загрузочный блок
- •4.2.2. Суперблок
- •4.2.3 Дескрипторы файлов
- •4.2.4. Блоки данных и свободные блоки
- •4.3. Типы файлов
- •4.3.1. Обычные файлы
- •4.3.2. Каталоги
- •4.3.4. Символические связи
- •4.3.5. Fifo – Именованные каналы
- •4.3.6. Сокеты
- •4.3.7. Обозначение типов файлов
- •Типы файлов
- •4.4. Дескриптор обычного файла
- •4.5. Дескриптор каталога
- •4.6. Дескриптор специального файла
- •4.7. Системная таблица файлов
- •4.8. Монтирование файловых систем
- •4.9. Демонтирование файловых систем
- •4.10. Проверка и восстановление файловых систем
- •4.11. Журналирование файловых систем
- •4.12. Контрольные вопросы
- •4.13. Тесты
- •Глава 5. Этапы начальной загрузки ос Unix
- •5.1. Загрузка и инициализация ядра
- •5.2. Распознавание и конфигурирование устройств
- •5.3. Создание спонтанных процессов
- •5.4. Выполнение команд оператора
- •5.5. Выполнение командных файлов запуска системы
- •5.6. Переход в многопользовательский режим
- •5.7. Контрольные вопросы
- •5.8. Тесты
- •Глава 6. Обзор командных файлов
- •6.1. Процесс init
- •6.1.1. Формат файла inittab
- •6.1.2. Уровни выполнения
- •Уровни выполнения
- •6.1.3. Дисциплины обработки процесса
- •Дисциплины обработки процесса
- •6.1.4. Запуск и этапы работы процесса init
- •6.2. Процесс rc
- •6.2.1. Сценарии запуска системы Solaris
- •6.3. Процесс cron
- •6.4. Процесс регистрации пользователей
- •6.5. Контрольные вопросы
- •6.6. Тесты
- •Глава 7. Останов системы
- •7.1. Выключение питания
- •7.2. Команда shutdown
- •7.3. Команда halt
- •7.4. Изменение уровня выполнения процесса init
- •Глава 8. Задачи системного администрирования
- •8.1. Инструменты администрирования
- •8.1.1. Администрирование aix
- •8.1.2. Администрирование hp-ux
- •8.1.3. Администрирование Solaris
- •8.1.4. Администрирование Linux
- •8.2. Пользователь root
- •8.2.1. Команда su
- •8.3. Добавление новых пользователей в систему
- •8.3.1. Файл /etc/passwd
- •Идентификаторы пользователей
- •8.3.2. Файл /etc/group
- •8.4. Контрольные вопросы
- •8.5. Тесты
- •Литература
- •Содержание
- •Глава 1. История создания ос unix 6
- •Глава 2. Функционирование ос unix 51
- •Глава 3. Процессы 75
- •Глава 4. Файловая система unix 116
- •Акуленок Ирина Николаевна Акуленок Анатолий Васильевич
- •Часть I. Основы операционной системы unix
3.7.1. Механизм создания процесса и запуска программы
Рассмотрим следующий пример (рис. 3.4). Пользователь, работая в командном режиме, запускает команду ls(1). Текущий процесс sh() делает вызов fork(2), порождая вторую копию shell. В свою очередь, порожденный shell вызывает exec(2), указывая в качестве параметра имя исполняемого файла, образ которого необходимо загрузить в память вместо кода shell.
Код ls(1) замещает код порожденного shell'a, и утилита ls(1) начинает выполняться. По завершении работы ls(1) созданный процесс «умирает». Пользователь вновь возвращается в командный режим.
Описанная процедура запуска новой программы называется fork–and–exec.
Системный вызов fork() служит для создания процессом своей собственной копии. Он представляет собой единственное средство для увеличения числа процессов.
Оба процесса разделяют между собой открытые файлы, и каждый из них в состоянии определить, родитель он или приемник.
Рис. 3.4. Создание процесса и запуск программы
С помощью вызова wait() интерпретатор shell организует выполнение программ в приоритетном (привилегированном) режиме – foreground. В этом случае shell выдает вызов fork(), далее порожденный процесс посредством вызова exec() вызывает процесс, затребованный пользователем, а процесс–родитель (shell) ожидает завершения последнего. Как только это происходит, shell выдает подсказку на ввод следующей команды. В фоновом режиме (background) shell аналогичным способом запускает необходимые процессы, но не ожидает их завершения.
Любой процесс в UNIX порождается с помощью системного вызова fork – вызова ветвления. При выполнении разветвления процесса проверяется, достаточно ли доступной памяти для данного процесса. Если да, то образ текущего процесса копируется в новый образ процесса, и в таблице процессов возникает новый элемент. Новый элемент создается и в таблице пользователя. Новому процессу присваивается новый уникальный идентификатор процесса, а разветвляющемуся процессу присваивается идентификатор родительского процесса.
3.7.2. Графический пример дерева процессов
Рассмотрим типичное «дерево» процессов в UNIX (рис. 3.5). Все процессы в UNIX создаются посредством системного вызова fork(). Запуск на выполнение новых задач осуществляется либо по схеме fork–and–exec (на рис. 3.5 эти связи показаны сплошными стрелками), либо с помощью exec() (на рис. 3.5 – это пунктирные стрелки).
Прародителем всех процессов является процесс init, называемый также распределителем процессов.
Рис. 3.5. Типичное дерево процессов в UNIX
Процессы shed и vhand являются системными процессами и формально не входят в иерархию.
Созданный процесс называется «процессом–ребенком», а процесс, вызвавший fork() – «процессом–родителем». Отметим, что по системному вызову fork() «процесс–родитель» не уничтожается. По окончании процедуры порождения нового процесса оба они – и старый, и новый – начинают конкурентную борьбу за ресурсы системы, например за время использования процессора.
Чаще всего системный вызов fork() используется для создания процессов, которые начинают свою работу с системного вызова exec().
В результате выполнения такой последовательности «родитель» создает новый процесс, а не просто порождает свой дубликат. В данном случае меняется не число процессов, а их количество. Иными словами, после вызова exec() вызывающий процесс заменяется некоторым другим. Идентификатор процесса и открытые файлы при этом остаются неизменными.
Именно такое типичное применение пары fork–exec демонстрирует shell, в котором с помощью указанного приема организуется выполнение почти всех вводимых вами команд. Shell («родитель») выполняет системный вызов wait() для ожидания завершения «процесса–ребенка».
Системный вызов kill() служит для передачи сигналов из одного процесса в другой. (Отметим, что имя kill, буквально означающее «убить», выбрано неудачно).
Сигнал – это определенное взаимными соглашениями значение, которым закодирована информация, передаваемая из одного процесса в другой. Так, одним из наиболее часто употребляемых сигналов является SIGKILL. Получив этот сигнал, соответствующий процесс завершает свою работу. В ОС UNIX применяются еще более десятка стандартных сигналов.
Пары kill–signal в совокупности образуют простую, но в то же время мощную систему коммуникаций между процессами.
В ОС UNIX имеется системный вызов exit(), который прекращает существование выдавшего его процесса. Этот вызов выполняется автоматически как реакция на некоторые сигналы или по достижению конца основного модуля программы на языке Си.
Явным образом системный вызов exit() может быть задан в произвольном месте программы. Выполнение этого вызова приводит к закрытию всех файлов, открытых процессом.
