Для наблюдения за процессами мы будем использовать программу top.
В таблице отображается различная информация о процессе. Нас интересуют колонки PID (идентификатор процесса), USER (пользователь, запустивший процесс), STAT (состояние процесса) и COMMAND (команда, которая была введена для запуска процесса).
Колонка STAT может содержать следующие значения:
R - процесс выполняется или готов к выполнению (состояние готовности)
D - процесс в "беспробудном сне" - ожидает дискового ввода/вывода
T - процесс остановлен (stopped) или трассируется отладчиком
S - процесс в состоянии ожидания (sleeping)
Z - процесс-зобми
< - процесс с отрицательным значением nice
N - процесс с положительным значением nice (о команде nice мы поговорим позже)
Код процесса не обязательно должен выполняться в текущий момент времени, так как процесс может находиться в состоянии спящего. В этом случае выполнение кода такого процесса приостановлено.
Жизнь каждого процесса представлена следующими фазами:
Создание процесса – на этом этапе создается полная копия того процесса, который создает новый. Например, вы запустили из интерпретатора на выполнение команду ls. Командный интерпретатор создает свою полную копию.
Загрузка кода процесса и подготовка к запуску – копия, созданная на первом этапе заменяется кодом задачи, которую необходимо выполнить и создается ее окружение – устанавливаются необходимые переменные и т.п.
Выполнение процесса
Состояние зомби – на этом этапе выполнение процесса закончилось, его код выгружается из памяти, окружение уничтожается, но запись в таблице процессов еще остается.
Умирание процесса – после всех завершающих стадий удаляется запись из таблицы процессов – процесс завершил свою работу.
Каждому процессу в системе назначаются числовые идентификаторы (личные номера) в диапазоне от 1 до 65535 (PID – Process Identifier – идентификатор процесса) и идентификаторы родительского процесса (PPID – Parent Process Identifier – идентификатор родительского процесса). PID является именем процесса, по которому мы можем найти его в операционной системе при использовании различных средств просмотра и управления процессами. PPID определяет родственные отношения между процессами, которые в значительной степени определяют его свойства и возможности. Другие параметры, которые необходимы для работы программы, называют “окружение процесса”. Одним из таких параметров является управляющий терминал – имя терминального устройства, на которое процесс выводит информацию и с которого информацию получает. Управляющий терминал имеют далеко не все процессы. Процессы, не привязанные к какому-то конкретному терминалу называются “демонами” (daemons). Такие процессы, будучи запущенными пользователем, не завершают свою работу по окончании сеанса, а продолжают работать, так как они не связаны никак с текущим сеансом и не могут быть автоматически завершены. Как правило, с помощью демонов реализуются серверные службы, так например сервер печати реализован процессом-демоном cupsd.
Мы вплотную подошли к самому интересному - созданию процесса-зомби. Во многих статьях, посвященных процессам, пишется "зомби = не жив, не мертв". А что это означает на самом деле? При завершении процесса должна удаляться его структура из списка процессов. Иногда процесс уже завершился, но его имя еще не удалено из списка процессов. В этом случае процесс становится зомби - его уже нет, но мы его видим в таблице команды top. Такое может произойти, если процесс-потомок (дочерний процесс) завершился раньше, чем этого ожидал процесс-родитель. Сейчас мы напишем программу, порождающую зомби, который будет существовать 8 секунд. Процесс-родитель будет ожидать завершения процесса-потомка через 10 секунд, а процесс-потомок завершить через 2 секунды.
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <stdio.h>
int main() {
int pid;
int status, died;
pid=fork();
switch(pid) {
case -1: printf("can't fork\n");
exit(-1);
case 0 : printf(" I'm the child of PID %d\n", getppid());
printf(" My PID is %d\n", getpid());
// Ждем 2 секунды и завершаемся, следующую строку я закомментировал
// чтобы зомби "прожил" на 2 секунды больше
// sleep(2);
exit(0);
default: printf("I'm the parent.\n");
printf(" My PID is %d\n", getpid());
// Ждем завершения дочернего процесса через 10 секунд, а потом убиваем его
sleep(10);
if (pid & 1)
kill(pid,SIGKILL);
died= wait(&status);
}
}
Для компиляции данной программы нам нужен компилятор gcc:
gcc -o zombie zombie.c
После того, как программа будет откомпилирована, запустите ее: ./zombie. Программа выведет следующую информацию:
I'm the parent
My PID is 1147
I'm the child of PID 1147
My PID is 1148
Если успеть быстро переключиться на другую консоль, и там ввести top –p 1148, то увидим нашего зомби, который будет жить 10 недолгих секунд