Опубликован: 16.09.2004 | Уровень: специалист | Доступ: свободно | ВУЗ: Московский физико-технический институт
Лекция 2:

Процессы в операционной системе UNIX

< Лекция 1 || Лекция 2: 1234 || Лекция 3 >
Аннотация: Понятие процесса в UNIX, его контекст. Идентификация процесса. Состояния процесса. Краткая диаграмма состояний. Иерархия процессов. Системные вызовы getpid(), getppid(). Создание процесса в UNIX. Системный вызов fork(). Завершение процесса. Функция exit(). Параметры функции main() в языке С. Переменные среды и аргументы командной строки. Изменение пользовательского контекста процесса. Семейство функций для системного вызова exec().
Ключевые слова: Unix, контекст, пользовательский контекст процесса, контекст ядра, reallocation, исполняемый код, стек, системный контекст, регистровый контекст, kernel mode, блок управления процессом, PCB, идентификатор, идентификатор процесса, PID, идентификатор родительского процесса, PPID, операционная система, значение, диаграмма состояний, состояние процесса, исполнение в режиме ядра, исполнение в режиме пользователя, инструкция пользователя, прямой, дерево, системный вызов, getpid, getppid, fork(), parent process, child process, fork, исполнение, функция, exit, завершение процесса, параметр функции main(), прототип функции, командная строка, параметр, программа, имя программы, слово, аргумент командной строки, компилятор, исполняемый файл, файл, вывод, переменная среды, системная консоль, указатель, exec, счетчик, execlp, execvp, execl, execv, execle, execve, атрибут процесса, компиляция, запуск, порожденный процесс

Понятие процесса в UNIX. Его контекст

Все построение операционной системы UNIX основано на использовании концепции процессов, которая обсуждалась на лекции. Контекст процесса складывается из пользовательского контекста и контекста ядра, как изображено на рисунке 3-4.1.

Под пользовательским контекстом процесса понимают код и данные, расположенные в адресном пространстве процесса. Все данные подразделяются на:

  • инициализируемые неизменяемые данные (например, константы);
  • инициализируемые изменяемые данные (все переменные, начальные значения которых присваиваются на этапе компиляции);
  • неинициализируемые изменяемые данные (все статические переменные, которым не присвоены начальные значения на этапе компиляции);
  • стек пользователя;
  • данные, расположенные в динамически выделяемой памяти (например, с помощью стандартных библиотечных C функций malloc(), calloc(), realloc() ).

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

Контекст процесса в UNIX

Рис. 3-4.1. Контекст процесса в UNIX

Под понятием "контекст ядра" объединяются системный контекст и регистровый контекст, рассмотренные на лекции. Мы будем выделять в контексте ядра стек ядра, который используется при работе процесса в режиме ядра (kernel mode), и данные ядра, хранящиеся в структурах, являющихся аналогом блока управления процессомPCB. Состав данных ядра будет уточняться на последующих семинарах. На этом занятии нам достаточно знать, что в данные ядра входят: идентификатор пользователя — UID, групповой идентификатор пользователя — GID, идентификатор процессаPID, идентификатор родительского процессаPPID.

Идентификация процесса

Каждый процесс в операционной системе получает уникальный идентификационный номерPID (process identificator) . При создании нового процесса операционная система пытается присвоить ему свободный номер больший, чем у процесса, созданного перед ним. Если таких свободных номеров не оказывается (например, мы достигли максимально возможного номера для процесса), то операционная система выбирает минимальный номер из всех свободных номеров. В операционной системе Linux присвоение идентификационных номеров процессов начинается с номера 0, который получает процесс kernel при старте операционной системы. Этот номер впоследствии не может быть присвоен никакому другому процессу. Максимально возможное значение для номера процесса в Linux на базе 32-разрядных процессоров Intel составляет 231-1.

Состояния процесса. Краткая диаграмма состояний

Модель состояний процессов в операционной системе UNIX представляет собой детализацию модели состояний, принятой в лекционном курсе. Краткая диаграмма состояний процессов в операционной системе UNIX изображена на рисунке 3-4.2.

Сокращенная диаграмма состояний процесса в UNIX

Рис. 3-4.2. Сокращенная диаграмма состояний процесса в UNIX

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

Приведенная выше диаграмма состояний процессов в UNIX не является полной. Она показывает только состояния, для понимания которых достаточно уже полученных знаний. Пожалуй, наиболее полную диаграмму состояний процессов в операционной системе UNIX можно найти в книге [ 1 ] (рисунок 6.1.).

Иерархия процессов

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

Таким образом, все процессы в UNIX связаны отношениями процесс-родитель – процесс-ребенок и образуют генеалогическое дерево процессов. Для сохранения целостности генеалогического дерева в ситуациях, когда процесс-родитель завершает свою работу до завершения выполнения процесса-ребенка, идентификатор родительского процесса в данных ядра процесса-ребенка (PPID – parent process identificator) изменяет свое значение на значение 1, соответствующее идентификатору процесса init, время жизни которого определяет время функционирования операционной системы. Тем самым процесс init как бы усыновляет осиротевшие процессы. Наверное, логичнее было бы заменять PPID не на значение 1, а на значение идентификатора ближайшего существующего процесса-прародителя умершего процесса-родителя, но в UNIX почему-то такая схема реализована не была.

Системные вызовы getppid() и getpid()

Данные ядра, находящиеся в контексте ядра процесса, не могут быть прочитаны процессом непосредственно. Для получения информации о них процесс должен совершить соответствующий системный вызов. Значение идентификатора текущего процесса может быть получено с помощью системного вызова getpid() , а значение идентификатора родительского процесса для текущего процесса – с помощью системного вызова getppid() . Прототипы этих системных вызовов и соответствующие типы данных описаны в системных файлах <sys/types.h> и <unistd.h>. Системные вызовы не имеют параметров и возвращают идентификатор текущего процесса и идентификатор родительского процесса соответственно.

Системные вызовы getpid() и getppid()

Прототипы системных вызовов

#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
pid_t getppid(void);

Описание системных вызовов

Системный вызов getpid возвращает идентификатор текущего процесса.

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

Тип данных pid_t является синонимом для одного из целочисленных типов языка C.

Написание программы с использованием getpid() и getppid()

В качестве примера использования системных вызовов getpid() и getppid() самостоятельно напишите программу, печатающую значения PID и PPID для текущего процесса. Запустите ее несколько раз подряд. Посмотрите, как меняется идентификатор текущего процесса. Объясните наблюдаемые изменения.

< Лекция 1 || Лекция 2: 1234 || Лекция 3 >
лия логовина
лия логовина

организовать двустороннюю поочередную связь процесса-родителя и процесса-ребенка через pipe, используя для синхронизации сигналы sigusr1 и sigusr2.

Макар Оганесов
Макар Оганесов
Сергей Пархоменко
Сергей Пархоменко
Россия, Ростов-на-Дону, ЮФУ (ДГТУ), 2008