Как сделать процесс системным

Добавил пользователь Cypher
Обновлено: 01.09.2024

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

Создание процессов fork()

Новые процессы создаются вызовом int pid=fork() , который создаёт точную копию вызвавшего его процесса. Пара процессов называются "родительский" и "дочерний" и отличаются друг от друга тремя значениями:

  • уникальный идентификатор процесса PID
  • идентификатор родительского процесса PPID
  • значение, возвращаемое вызовом fork() . В родительском это PID дочернего процесса или ошибка (-1), в дочернем fork() всегда возвращает 0.

После создания, дочерний процесс может загрузить в свою память новую программу (код и данные) из исполняемого файла вызовом execve(const char *filename, char *const argv [], char *const envp[]);

Дочерний процесс связан с родительским значением PPID. В случае завершения родительского процесса PPID меняется на особое значение 1 - PID процесса init.

Процесс init

В момент загрузки ядра создаётся особый процесс с PID=1, который должен существовать до перезагрузки ОС. Все остальные процессы в системе являются его дочерними процессами (или дочерними от дочерних и т.д.). Обычно, в первом процессе исполняется программа init поэтому в дальнейшем я буду называть его "процесс init".

В современных дистрибутивах классическая программа init заменена на systemd, но сущности процесса с PID=1 это не меняет.

При загрузке Linux ядро сначала монтирует корневую файловую систему на образ диска в оперативной памяти - initrd, затем создаётся процесс с PID=1 и загружает в него программу из файла /init. В initrd из дистрибутива CentOS начальный /init - это скрипт для /bin/bash. Скрипт загружает необходимые драйверы, после чего делает две вещи, необходимые для полноценного запуска Linux:

  1. Перемонтирует корневую файловую систему на основной носитель
  2. Загружает командой exec в свою память основную программу init

Для того, чтобы выполнить эти два пункта через загрузчик в начального init два параметра:

  • основной носитель корневой ФС. Например: root=/dev/sda1
  • имя файла с программой init. Например: init=/bin/bash

Если второй параметр опущен то ищется имя зашитое в начальный init по умолчанию.

Этот пример так же показывает, как получить права администратора при физическом доступе к компьютеру.

Каждый процесс имеет уникальный на данный момент времени идентификатор PID. Поменять PID процесса невозможно.

Значения PID 0 и 1 зарезервированы. Процесс с PID==0 не используется, PID==1 - принадлежит программе init .

Максимальное значение PID в Linux равняется PID_MAX-1. Текущее значение PID_MAX можно посмотреть командой:

По умолчанию это 2^16 (32768) однако в 64-разрядных Linux его можно увеличить до 2^22 (4194304):

*PID* назначаются последовательно. При создании нового процесса вызовом fork ищется *PID* , больший по значению, чем тот, который был возвращён предыдущим вызовом fork . Если при поиске достигнуто значение pid_max , то поиск продолжается с PID=2. Такое поведение выбрано потому, что некоторые программы могут проверять завершение процесса по существованию его PID. В этой ситуации желательно, чтобы PID не использовался некоторое время после завершения процесса.

UID и GID

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

Процесс может поменять своего владельца и группу в двух случаях:

  1. текущий UID равен 0 (соответствует пользователю root) и процесс обратился к системному вызову setuid(newuid) . В этом случае процесс полностью меняет владельца.
  2. процесс обратился к вызову exec(file) загрузив в свою память программу из файла в атрибутах которого выставлен флаг suid или sgid. В этом случае владелец процесса сохраняется, но права доступа будут вычисляться на основе UID и GID файла.
Прикрепленный файлРазмер
fork-exex-wait.jpg 25.8 КБ


Создание процесса

Вызов newpid=fork() создает новый процесс, являющейся точной копией текущего и отличающийся лишь возвращаемым значением newpid . В родительском процессе newpid равно PID дочернего процесса, в дочернем процессе newpid равно 0. Свой PID можно узнать вызовом mypid=getpid() , родительский – вызовом parentpid=getppid() .

Запуск программы

В оперативной памяти процесса находятся код и данные, загруженные из файла. При запуске программы из командной строки, обычно создается новый процесс и в его память загружается файл с программой. Загрузка файла делается вызовом одной из функций семейства exec (см. man 3 exec ). Функции отличаются способом передачи параметров, а также тем, используется ли переменная окружения PATH для поиска исполняемого файла. Например execl в качестве первого параметра принимает имя исполняемого файла, вторым и последующими – строки аргументы, передаваемые в argv[], и, наконец, последний параметр должен быть NULL, он дает процедуре возможность определить, что параметров больше нет.

Пример exec с двумя ошибками:

Ошибка 1: Первый аргумент передаваемый программе это имя самой программы. В данном примере в списке процессов будет видна программа с именем -l, запущенная без параметров.

Ошибка 2: Поскольку код из файла /bin/ls будет загружен в текущий процесс, то старый код и данные, в том числе printf("Программа ls запущена успешно\n"), будет затерты. Первый printf не сработает никогда.

Завершение процесса

Процесс может завершиться, получив сигнал или через системный вызов _exit(int status) . status может принимать значения от 0 до 255. По соглашению, status==0 означает успешное завершение программы, а ненулевое значение - означает ошибку. Некоторые программы (например kaspersky для Linux) используют статус для возврата некоторой информации о результатах работы программы.

_exit() может быть вызван несколькими путями.

  • return status; в функции main() . В этом случае _exit() выполнит некая служебная функция, вызывающая main()
  • через библиотечную функцию exit(status) , которая завершает работу библиотеки libc и вызывает _exit()
  • явным вызовом _exit()

Удаление завершенного процесса из таблицы процессов

После завершения процесса его pid остается занят - это состояние процесса называется "зомби". Чтобы освободить pid родительский процесс должен дождаться завершения дочернего и очистить таблицу процессов. Это достигается вызовом:

Вызов wait(&status); эквивалентен waitpid(-1, &status, 0);

waitpid ждет завершения дочернего процесса и возвращает его PID . Код завершения и обстоятельства завершения заносятся в переменную status. Дополнительно, поведением waitpid можно управлять через параметр options.

  • pid 0 - ожидание завершения любого дочернего процесса с указанным pid

Опция WNOHANG - означает неблокирующую проверку завершившихся дочерних процессов.

Статус завершения проверяется макросами:

  • WIFEXITED(status) - истина если дочерний процесс завершился вызовом _exit(st)
  • WEXITSTATUS(status) - код завершения st переданный в _exit(st)
  • WIFSIGNALED(status) - истина если дочерний процесс завершился по сигналу
  • WTERMSIG(status) - номер завершившего сигнала
  • WCOREDUMP(status)истина если дочерний процесс завершился с дампом памяти
  • WIFSTOPPED(status) истина если дочерний процесс остановлен
  • WSTOPSIG(status) - номер остановившего сигнала
  • WIFCONTINUED(status) истина если дочерний процесс перезапущен

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

Прерывания по таймеру происходят в соответствии с квантом времени, выделенному процессу. В Linux квант времени по умолчанию (DEF_TIMESLICE) равен 0,1 секунды, но может быть пересчитан планировщиком процессов ( sheduler ).

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

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

После завершения процесса вызовом _exit() или по сигналу все его ресурсы (память, открытые файлы) освобождаются, но запись в таблице процессов остаётся и занимает PID. Такой процесс называется "зомби" и должен быть явно очищен из таблицы процессов вызовом wait() в родительском процессе. Если родительский процесс завершился раньше дочерних, то всем его дочерним процессам приписывается значение PPID (parent pid) равное 1, возлагая обязательства по очистке от них таблицы процессов на особый процесс init с PID=1.

На диаграмме показаны различные состояния процесса


В Linux команда ps использует следующие обозначения состояния процесса:

  • R выполняется (в том числе в обработчике сигнала) или стоит в очереди на выполнение
  • S системный вызов ожидает ресурс, но может быть прерван
  • D системный вызов ожидает ресурс, и не может быть прерван (обычно это ввод/вывод)
  • T остановлен сигналом
  • t остановлен отладчиком
  • Z "Зомби" - завершён, но не удалён из списка процессов родительским процессом.

Планировщик процессов

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

Простейшая реализация очереди в виде FIFO очень быстра, но не поддерживает приоритеты и многопроцессорность. В Linux 2.6 воспользовались простотой FIFO, добавив к ней несколько усовершенствований:

  1. Было определено 140 приоритетов (100 реального времени + 40 назначаемых динамически), каждый из которых получил свою очередь FIFO. На запуск выбирается первый процесс в самой приоритетной очереди.
  2. В многопроцессорных системах для каждого ядра был сформирован свой набор из 140 очередей. Раз в 0,2 секунды просматриваются размеры очередей процессоров и, при необходимости балансировки, часть процессов переносится с загруженных ядер на менее загруженные
  3. Динамический приоритет назначается процессу в зависимости от отношении времени ожидания ресурсов к времени пребывания в состоянии выполнения. Чем дольше процесс ожидал ресурс, тем выше его приоритет. Таким образом, диалоговые задачи, которые 99% времени ожидают пользовательского ввода, всегда имеют наивысший приоритет.

Прикладной программист может дополнительно понизить приоритет процесса функцией int nice(int inc); (в Linux nice() - интерфейс к вызову setpriority() ). Большее значение nice означает меньший приоритет. В командной строке используется "запускалка" с таким же именем:

Процесс Idle

Если нет процессов готовых для выполнения, то планировщик вызывает нить (процесс) Idle. В Linux 2.2 однопроцессорная кроссплатформенная версия Idle выглядела так:

В аппаратно-зависимую реализацию idle() может быть вынесено управление энергосбережением.

В ранних версиях Linux процесс Idle имел PID=0, но, вообще говоря, Idle как самостоятельный процесс не существует.

Вычисление средней загрузки

Средняя загрузка (Load Average, LA) - усредненная мера использования ресурсов компьютера запущенными процессами. Величина LA пропорциональна числу процессоров в системе и на ненагруженной системе колеблется от нуля до значения, равного числу процессоров. Высокие значения LA (10*число ядер и более) говорят о чрезмерной нагрузке на систему и потенциальных проблемах с производительностью.

С каждым процессом Unix связаны два атрибута uid и gid - пользователь и основная группа. В принципе они могли бы определять права доступа процесса к ФС и другим процессам, однако существует несколько ситуаций, когда права процесса отличаются от прав его владельца. Поэтому кроме uid/gid (иногда называемых реальными ruid/rgid) с процессом связаны атрибуты прав доступа - эффективные uid/gid - euid/egid, чаще всего совпадающие с ruid/rgid. Кроме того, с uid связан список вспомогательных групп, описанных в файле /etc/group . euid/egid и список групп определяют права доступа процесса к ФС. Вновь создаваемые файлы наследуют атрибуты uid/gid от euid/egid процесса. Кроме того euid определяет права доступа к другим процессам (отладка, отправка сигналов и т.п.).

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

Примечание: в современных ОС особые привилегии процесса определяются через набор особых флагов - capabilities и не обязательно привязаны к euid=0.

(re)uid/(re)gid, а также вспомогательные группы, наследуются от родительского процесса при вызове fork(). При вызове exec() ruid/rgid сохраняются, а euid/egid могут быть изменены если у исполняемого файла выставлен флаг смены владельца. Для скриптов флаг смены владельца игнорируется т.к. фактически запускается интерпретатор, а скрипт передаётся ему в качестве параметра. В момент входа пользователя в систему программа login считывает из файлов /etc/passwd и /etc/group необходимые величины и устанавливает их перед загрузкой командного интерпретатора.

Список вспомогательных групп можно считать в массив функцией int getgroups(int size, gid_t list[]). Будет ли при этом в списке основная группа неизвестно - это зависит от реализации конкретной ОС. Максимальное число вспомогательных групп можно получить так: long ngroups_max = sysconf(_SC_NGROUPS_MAX); или из командной строки getconf NGROUPS_MAX . В моём Linux'е максимальное число групп - 65536.

Для инициализации вспомогательных групп в Linux можно воспользоваться функцией int initgroups(const char *user, gid_t group); эта функция разбирает файл /etc/group, а за тем обращается к системному вызову int setgroups(size_t size, const gid_t *list);.

Существуют несколько функций для управление атрибутами uid/gid. Для экономии места далее перечисляются только функции для работы с uid. Получить значения атрибутов можно с помощью функций getuid(), geteuid() Установить значения можно с помощью
setuid(id); - установить ruid и euid в id
seteuid(id); - установить euid в id
setreuid(rid,eid); - установить ruid и euid в rid и eid. -1 в качестве параметра означает, что значение не меняется

В Linux, HP-UX и некоторых других ОС дополнительно поддерживаются атрибут сохраненных прав процесса suid/sgid (не путать с одноименными атрибутами файла). Соответственно есть функция для установки всех трёх атрибутов setresuid(rid,eid,sid);

Если euid=0 или ruid=0 то ruid и euid могут меняться произвольно. Т.е. можно сделать euid<>0 или ruid<>0, а затем вернуться в состояние euid=ruid=0. Если оба атрибута не равны нулю, то возможно лишь изменение euid в ruid (отказ от дополнительных прав). Программа su получает euid=0 благодаря соответствующему атрибуту файла и использует возможности привилегированного процесса для запуска программ от имени произвольного пользователя (в том числе root). Веб-сервер apache, наоборот, стартует с ruid=euid=0, но затем отбирает у себя лишние права меняя ruid и euid на непривилегированные значения.

Что такое системный вызов в операционной системе?

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

Системный вызов предлагает услуги операционной системы пользовательским программам через API (интерфейс прикладного программирования). Системные вызовы являются единственными точками входа в систему ядра.

Из этого руководства по операционной системе вы узнаете:


Пример системного вызова

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

В интерактивной системе этот тип выполнения программы требует некоторых системных вызовов ОС.

  • Первый звонок — написать подсказку на экране
  • Во-вторых, чтобы прочитать с клавиатуры, символы, которые определяют два файла.

Как работает системный вызов?

Вот шаги для системного вызова:



Как вы можете видеть на приведенной выше диаграмме.

Шаг 1) Процессы, выполняемые в пользовательском режиме до тех пор, пока системный вызов не прервет его.

Шаг 2) После этого системный вызов выполняется в режиме ядра в приоритетном порядке.

Шаг 3) По завершении выполнения системного вызова управление возвращается в режим пользователя.,

Зачем вам системные вызовы в ОС?

Ниже приведены ситуации, которые требуют системных вызовов в ОС:

  • Чтение и запись из файлов требуют системных вызовов.
  • Если файловая система хочет создать или удалить файлы, требуются системные вызовы.
  • Системные вызовы используются для создания и управления новыми процессами.
  • Сетевые подключения требуют системных вызовов для отправки и получения пакетов.
  • Доступ к аппаратным устройствам, таким как сканер, принтер, требуется системный вызов.

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

Вот пять типов системных вызовов, используемых в ОС:

  • Контроль процесса
  • Управление файлами
  • Управление устройством
  • Информационное обслуживание
  • связи


Контроль процесса

Эти системные вызовы выполняют задачу создания процесса, завершения процесса и т. Д.

  • Конец и Прервать
  • Загрузить и выполнить
  • Создать процесс и завершить процесс
  • Ожидание и подписанное событие
  • Выделить и освободить память

Управление файлами

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

  • Создать файл
  • Удалить файл
  • Открыть и закрыть файл
  • Читать, писать и перемещать
  • Получить и установить атрибуты файла

Управление устройством

Управление устройствами выполняет работу с устройствами, такими как чтение из буферов устройств, запись в буферы устройств и т. Д.

  • Запрос и релиз устройства
  • Логически подключать / отключать устройства
  • Получить и установить атрибуты устройства

Информационное обслуживание

Он обрабатывает информацию и ее передачу между ОС и программой пользователя.

  • Получить или установить время и дату
  • Получить атрибуты процесса и устройства

Связь:

Эти типы системных вызовов специально используются для межпроцессного взаимодействия.

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

Вот общие общие правила для передачи параметров в системный вызов:

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

Важные системные вызовы, используемые в ОС

Подождите()

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

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

вилка ()

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

Этот системный вызов выполняется, когда исполняемый файл в контексте уже запущенного процесса заменяет старый исполняемый файл. Однако исходный идентификатор процесса остается, поскольку новый процесс не создается, но стек, данные, заголовок, данные и т. Д. Заменяются новым процессом.

убийство():

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

Выход():

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

Свойство Handle : возвращает дескриптор процесса

Свойство Id : получает уникальный идентификатор процесса в рамках текущего сеанса ОС

Свойство MachineName : возвращает имя компьютера, на котором запущен процесс

Свойство Modules : получает доступ к коллекции ProcessModuleCollection, которая хранит набор модулей (файлов dll и exe), загруженных в рамках данного процесса

Свойство ProcessName : возвращает имя процесса, которое нередко совпадает с именем приложения

Свойство StartTime : возвращает время, когда процесс был запущен

Свойство VirtualMemorySize64 : возвращает объем памяти, который выделен для данного процесса

Метод CloseMainWindow() : закрывает окно процесса, который имеет графический интерфейс

Метод GetProcesses() : возвращающий массив всех запущенных процессов

Метод GetProcessesByName() : возвращает процессы по его имени. Так как можно запустить несколько копий одного приложения, то возвращает массив

Метод Kill() : останавливает процесс

Метод Start() : запускает новый процесс

Получим все запущенные процессы:

Получим id процесса, представляющего Visual Studio:

Потоки процесса

Свойство Threads представляет коллекцию потоков процесса - объект ProcessThreadCollection , каждый поток в которой является объектом ProcessThread . В данном классе можно выделить следующие свойства:

CurrentPriority : возвращает текущий приоритет потока

Id : идентификатор потока

IdealProcessor : позволяет установить процессор для обработки потока

PriorityLevel : уровень приоритета потока

StartAddress : адрес в памяти функции, запустившей поток

StartTime : время запуска потока

Например, получим все потоки процесса Visual Studio:

Модули процесса

Одно приложение может использовать набор различных сторонних библиотек и модулей. Для их получения класс Prosess имеет свойство Modules , которое представляет объект ProcessModuleCollection . Каждый отдельный модуль представлен классом ProcessModule , у которого можно выделить следующие свойства:

BaseAddress : адрес модуля в памяти

FileName : полный путь к файлу модуля

EntryPointAddress : адрес функции в памяти, которая запустила модуль

ModuleName : название модуля (краткое имя файла)

ModuleMemorySize : возвращает объем памяти, необходимый для загрузки модуля

Получим все модули, используемые Visual Studio:

Запуск нового процесса

С помощью статического метода Process.Start() можно запустить новый процесс. Например:

При обращении к исполняемому файлу exe запускает приложение.

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

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

Управление процессами в ОС Unix

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

Программы и процессы

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

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

В то же время не следует отождествлять процесс с программой хотя бы потому, что программа может породить более одного процесса. Простейшие программы, например, команда who(l) или cat(l), при выполнении представлены только одним процессом. Сложные задачи, например системные серверы (печати, FTP, Telnet), порождают в системе несколько одновременно выполняющихся процессов.

Операционная система UNIX является многозадачной. Это значит, что одновременно может выполняться несколько процессов, причем часть процессов могут являться образцами одной программы.

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

Типы процессов

Системные процессы. Системные процессы являются частью ядра и всегда расположены в оперативной памяти. Системные процессы не имеют соответствующих им программ в виде исполняемых файлов и запускаются особым образом при инициализации ядра системы. Выполняемые инструкции и данные этих процессов находятся в ядре системы, таким образом они могут вызывать функции и обращаться к данным, недоступным для остальных процессов. Системными процессами являются: shed (диспетчер свопинга), vhand (диспетчер страничного замещения), bdfflush (диспетчер буферного кэша) и kmadaemon (диспетчер памяти ядра). К системным процессам следует отнести init, являющийся прародителем всех остальных процессов в UNIX. Хотя init не является частью ядра, и его запуск происходит из исполняемого файла (/etc/init), его работа жизненно важна для функционирования всей системы в целом.

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

Прикладные процессы. К прикладным процессам относятся все остальные процессы, выполняющиеся в системе. Как правило, это процессы, порожденные в рамках пользовательского сеанса работы. С такими процессами вы будете сталкиваться чаще всего. Например, запуск команды ls(l) породит соответствующий процесс этого типа. Важнейшим пользовательским процессом является основной командный интерпретатор (login shell), который обеспечивает вашу работу в UNIX. Он запускается сразу же после вашей регистрации в системе, а завершение работы login shell приводит к отключению от системы.

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

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

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

Атрибуты процесса

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

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

- Идентификатор родительского процесса Parent Process ID (PPID). Идентификатор процесса, породившего данный процесс.

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

- Терминальная линия (TTY). Терминал или псевдотерминал, ассоциированный с процессом, если такой существует. Процессы-демоны не имеют ассоциированного терминала.

- Реальный (RID) и эффективный (EUID) идентификаторы пользователя. Реальным идентификатором пользователя данного процесса является идентификатор пользователя, запустившего процесс. Эффективный идентификатор служит для определения прав доступа процесса к системным ресурсам (в первую очередь к ресурсам файловой системы). Обычно реальный и эффективный идентификаторы эквивалентны, т. е. процесс имеет в системе те же права, что и пользователь, запустивший его. Однако существует возможность задать процессу более широкие права, чем права пользователя путем установки флага SUID , когда эффективному иденти­фикатору присваивается значение идентификатора владельца исполняе­мого файла (например, администратора).

- Реальный ( RGID ) и эффективный ( EGID ) идентификаторы группы. Реальный идентификатор группы равен идентификатору первичной или текущей группы пользователя, запустившего процесс. Эффективный иден­тификатор служит для определения прав доступа к системным ресурсам по классу доступа группы. Так же как и для эффективного идентификатора пользователя, возможна его установка равным идентификатору группы владельца исполняемого файла (флаг SGID ).

Команда ps (1) ( process status ) позволяет вывести список процессов, выпол­няющихся в системе, и их атрибуты:

Читайте также: