Немного о процессах

Немного о процессах В Linux существуют такие понятия, как процесс, задача, нить, поток, сигнал. Чем же они различаются? Что вообще это такое? На эти вопросы я попытаюсь ответить в данной статье. Я думаю, что данная информация даст ключ к пониманию, поможет вам глубже понять сущность операционной системы Linux.
Операционная система Linux базируется на процессах. Процесс — это какой-либо процесс в системе в полном смысле этого слова. Это какой-то выполняемый параллельно с другими код. Например, программа это процесс, но кроме того программа тоже может состоять из нескольких параллельно выполняемых частей, которые называют нитями, но об этом ниже.
Как же процессы появляются в системе? Процессы создаются таким образом: системный вызов Linux, создающий новый процесс и называемый clone, создает дочерний процесс, представляющий собой почти точную копию родительского. После он выполняет назначенную ему функцию, а исходный процесс — то, что написано в нем после вызова clone. Далее отличий может стать больше. Но если требуется этому воспрепятствовать, вызов clone позволяет задать флаги, указывающие, что порожденный процесс будет иметь со своим предком общие:
— адресное пространство (флаг CLONE_VM);
— информацию о файловой системе (флаг CLONE_FS): корневой и текущий каталоги, а также umask (User Mask);
— таблицу открытых файлов (флаг CLONE_FILES);
— таблицу обработчиков сигналов (флаг CLONE_SIGHAND);
— родителя (флаг CLONE_PARENT).
Нити, т.е. параллельно выполняемые части одной программы, в стандартной библиотеке поддержки многонитевых программ Linux реализованы просто как процессы, порожденные с указанием флага CLONE_VM, и с точки зрения ядра системы ничем не отличаются от любых других процессов. Однако в некоторых альтернативных реализациях многонитевых библиотек дело обстоит иначе.
Помимо процессов описанного выше вида бывают еще "урезанные", порождаемые с помощью функции kernel_thread для внутренних системных нужд. У них нет параметров командной строки, как правило, они не имеют открытых файлов и т.д. Поскольку, несмотря на свою урезанность, эти процессы все равно фигурируют в списке задач, в литературе иногда различают полноценные процессы, порожденные из "пространства пользователя" (userspace), и задачи, т.е. все процессы, включая внутренние процессы ядра. Так, если новый процесс является всегда копией существующего, то каким образом в системе работают разные программы? Откуда берется самая первая из них?
Процессы, выполняющие разные программы, образуются благодаря применению имеющихся в стандартной библиотеке Unix функций семейства exec: execl, execlp, execle, execv, execve, execvp. Эти функции отличаются форматом вызова, но в конечном итоге делают одну и ту же вещь: замещают внутри текущего процесса исполняемый код на код, содержащийся в указанном файле. Файл может быть не только двоичным исполняемым файлом Linux, но и скриптом командного интерпретатора, и двоичным файлом другого формата. В последнем случае способ его обработки определяется настраиваемым модулем ядра под названием binfmt_misc.
Таким образом, операция запуска программы в Linux разделена на две (в отличие от Windows, где это одно целое): сначала производится запуск, а потом определяется, какая программа будет работать. Смысл данного подхода заключается в том, что иногда (в принципе довольно часто) программа должна совершить некоторые действия еще до того, как начнется собственно ее выполнение. Аналогичного результата можно было бы добиться и при запуске программы за один шаг, но более сложным путем.
Первый процесс в системе запускается при инициализации ядра. Определяется выполняемая в этом процессе программа следующим образом: вначале делается попытка переключить процесс на файл, указанный в командной строке ядра, потом на файлы /sbin/init, /etc/ init, /bin/init и напоследок на /bin/sh.
Когда процесс закончит работу (нормально или аварийно), он уничтожается, освобождая все использовавшиеся им ресурсы компьютера.
Если родительский процесс по какой-то причине завершится раньше дочернего, последний становится "orphaned process" — осиротевшим. Таким процессам в качестве родителя автоматически ставится программа init, выполняющаяся в процессе с номером 1, которая и принимает сигнал об их завершении.
Если же потомок уже завершил работу, а предок не готов принять от системы сигнал об этом событии, то потомок не исчезает полностью, а превращается в zombie — "зомби"; в поле Stat такие процессы помечаются буквой Z. Зомби не занимает процессорного времени, но строка в таблице процессов остается, и соответствующие структуры ядра не освобождаются. После завершения родительского процесса осиротевший зомби на короткое время также становится потомком init, после чего окончательно умирает.
Также процесс может впасть в сон, который не удается прервать: в поле Stat это обозначается буквой D. Процесс, находящийся в таком состоянии, не реагирует на системные запросы и может быть уничтожен только перезагрузкой системы. Процессы общаются между собой посредством сигналов. Сигналы посылаются одними процессами другим с помощью команды со страшным названием kill, хотя в общем случае она никого не убивает. Все зависит от конкретного сигнала, и практически любой сигнал при необходимости может быть процессом проигнорирован. Исключение составляют сигналы KILL, который беспрепятственно уничтожает процесс, и STOP, который его аналогичным образом останавливает.
Правила о том, какой процесс какому имеет право послать сигнал, достаточно сложны. Суперпользователь, очевидно, может посылать сигналы любым процессам, а обычный пользователь — только своим. Работа с сигналами в нитях требует особого подход, так как одни сигналы должны посылаться всем нитям, а другие — посылаться индивидуально.
Процессы также обмениваются данными. Это происходит через неименованные и именованные каналы, а также гнезда. Для передачи обширных массивов данных между процессами служит системный вызов mmap, представляющий собой довольно неожиданное применение страничной виртуальной памяти. Он позволяет, грубо говоря, сказать: "я хочу обращаться к такому-то участку такого-то файла как к оперативной памяти". Данные, которые процесс читает из указанной области памяти, по мере надобности считываются из файла, а те, которые он туда пишет, когда-нибудь попадут на диск. Но процесс сам не работает с диском, этим занимается ядро.
Вызов mmap применяется также для "загрузки в память" исполняемых файлов и библиотек, так что если программа использует 25 библиотек общим объемом во много десятков мегабайт, это вовсе не значит, что она и в памяти будет занимать такое же количество мегабайт.
Для работы с информацией о процессах, которую выводят на терминал программы ps и top, в Linux используется достаточно необычный механизм: особая файловая система procfs. В большинстве дистрибутивов она монтируется при запуске системы как каталог /proc. Данные о процессе с номером 1 (обычно это /sbin/init) содержатся в подкаталоге /proc/1, о процессе с номером 777 — в /proc/777, и т.д. Все файлы, открытые процессом, представлены в виде символических ссылок в каталоге /proc/<pid> /fd, а ссылка на корневой каталог процесса хранится как /proc/<pid> /root.
Программа lsof позволяет получить полезную информацию о процессах, а именно список всех файлов, используемых в данный момент процессами, список каталогов, занятых теми же процессами, причем занятыми они считаются потому, что какой-либо процесс использует один из этих каталогов в качестве корневого. Также выводится список разделяемых библиотек, загруженных в память.

X-Stranger
xstranger@tut.by
www.linux4u.narod.ru



Компьютерная газета. Статья была опубликована в номере 01 за 2002 год в рубрике soft :: linux

©1997-2024 Компьютерная газета