div.main {margin-left: 20pt; margin-right: 20pt}
Немного о
процессах
В 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 http://www.linux4u.narod.ru/
|