Порождение процессов и нитей, запуск программ Linux

Автор: | 02.12.2018

Несмотря на очевидные различия, историю возникновения и развития, нити и процессы объединяет общее назначение  они являются примитивами выполнения некоторого набора последовательных инструкций. Откровенно говоря, нити, в общем, появились в операционных системах раньше, чем изолированные UNIX-процессы, в которые со временем вернулись UNIX-нити…

Процессы выполняют или разные последовательные программы целиком, или ветви одной параллельной программы, но в изолированном окружении со своим «частным» (private) набором ресурсов. Нити, наоборот, выполняют ветви одной параллельной программы в одном окружении с «общим» (shared) набором ресурсов.

В многозадачном ядре Linux вообще используется универсальное понятие «задача», которая может иметь как общие ресурсы (память, открытые файлы и т. д.) с другими задачами, так и частные ресурсы для своего собственного использования.

Порождение нового процесса реализуется при помощи системного вызова fork, в результате которого ядро, операционной системы создает новый дочерний (child) процесс PID2 — полную копию (COPY) процесса-родителя (parent) PID1.

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

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

Статус завершения status, переданный дочерним процессом через аргументы exit, будет сохраняться ядром до момента его востребования родительским процессом при помощи системного вызова wait, а весь этот промежуток времени дочерний процесс будет находиться в состоянии Z (zombie) (см. столбец STAT).

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

На самом деле этого не происходит, потому что «осиротевшим» процессам назначается приемный родитель, в качестве которого выступает прародитель всех процессов init с идентификатором PID=1.

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

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

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

Для запуска новой программы в новом процессе используются оба системных вызова fork и ехес, согласно принципу fork-and-exec «раздвоиться и запустить». В примере из листинга в статье дерево процессов сформировано именно на основе дочерне-родительских отношений между процессами, формирующимися при использовании принципа fork-and-exec.

Напримtр, командный интерпретатор bash по командам ps fx или man ps порождает дочерние процессы  и замещает их программами ps и man. Тем же образом действует графический эмулятор терминала gnome-terminal — запуская новый сеанс пользователя  на каждой из своих вкладок, он замещает свои дочерние процессы программой интерпретатора bash.

Листинг ниже иллюстрирует команду интерпретатора, запущенную в «фоновом» режиме при помощи конструкции асинхронного списка. Аналогично всем предыдущим командам, интерпретатор использует fork-and-exec для запуска программы в дочернем процессе с идентификатором 23228, но не дожидается его завершения при помощи системного вызова wait, как обычно, а немедленно продолжает интерактивное взаимодействие с пользователем, сообщив ему PID по-рожденного процесса и «номер задания»  команды «заднего фона».

Оповещение о завершении своего дочернего процесса интерпретатор получит позже, при помощи сигнала SICCHLD, и отреагирует соответствующим сообщением об окончании команды «заднего фона».

Фоновое выполнение программ

fitz@ubuntu:~$ dd ifs/dev/dvd of=plan9.iso 

[1] 23228

fitz@ubuntu:~$ ps f

PID  TTY         STAT       TIME    COMMAND

23625   pts/1      S               0:00     -bash

23228   pts/1      R               1:23      \_ dd if=/dev/dvd of=plan9.iso

23236    pts/1     R+            0:00      \_ ps f

fitz@ubuntu:~$

fitz@ubuntu:~$ 586896+0 записей получено

586896+6 записей отправлено

скопировано 366496752 байта (300 МВ), 14,6916 с, 26,5 МВ/с

 

[1]+ Готово                                         dd xf=/dev/dvd of=plan9.iso

В листинге ниже показана конвейерная конструкция интерпретатора, при помощи которой осуществляется поиск самого большого файла с суффиксом .html вниз по дереву каталогов, начиная с /usr/share/doc.

Эта конструкция реализуется при помощи fork-and-exec четырьмя параллельно порожденными дочерними процессами интерпретатора, в каждом из которых запущена программа соответствующей части конвейера, при этом дочерние процессы связаны неименованным каналом pipe — простейшим средством межпроцессного взаимодействия.

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

Параллельный запуск взаимодействующих программ

fitz@ubuntu:~$ find /usr/share/doc -type f -name ‘*.html’ | xargs -n1 wc -l | sort -k 1 -nr | head -1 &
[1] 12827
fitz@ubuntu:~$ ps fj
PPID     PID   PGID     SID   TTY          TPGID    STAT      UID        TIME       COMMAND

11715     11716 11716    9184   pts/0         14699    S            1006           0:01       -bash

11716    12824 12824  9184   pts/0         14699    S            1006          0:00        \_ find … -type f -name *.html

11716    12825 12824  9184   pts/0          14699   S            1006          0:00         \_ xargs -n1 wc -l

11716    12826 12824  9184   pts/0         14699    S            1006          0:00         \_ sort -к 1 -nr

11716    12827 12824  9184    pts/0        14699     S           1006          0:00         \_ head -1
11716    14699 14699  9184    pts/0        14699    R+         1006          0:00         \_ ps fj

fitz@ubuntu:~$ wait

41235 /usr/share/doc/libxll-dev/libX11/libX11. html

[1]+      Готово      find … -type f -name ‘*.html’ | xargs -n1 wc -l | sort -k 1 -nr | head -1

 

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *