Наши партнеры

UnixForum






Книги по Linux (с отзывами читателей)

Библиотека сайта rus-linux.net

Процессы в Linux

Колисниченко Денис, dhsilabs@mail.ru

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

Немного теории

Термин "процесс" впервые появился при разработке операционной системы Multix и имеет несколько определений, которые используются взависимости от контекста. Процесс - это:

  1. программа на стадии выполнения
  2. "объект", которому выделено процессорное время
  3. асинхронная работа

Для описания состояний процессов используется несколько моделей. Самая простая модель - это модель трех состояний. Модель состоит из:

  1. состояния выполнения
  2. состояния ожидания
  3. состояния готовности

Выполнение - это активное состояние, во время которого процесс обладает всеми необходимыми ему ресурсами. В этом состоянии процесс непосредственно выполняется процессором.

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

Готовность - это тоже пассивное состояние, процесс тоже заблокирован, но в отличие от состояния ожидания, он заблокирован не по внутренним причинам (ведь ожидание ввода данных - это внутренняя, "личная" проблема процесса - он может ведь и не ожидать ввода данных и свободно выполняться - никто ему не мешает), а по внешним, независящим от процесса, причинам. Когда процесс может перейти в состояние готовности? Предположим, что наш процесс выполнялся до ввода данных. До этого момента он был в состоянии выполнения, потом перешел в состояние ожидания - ему нужно подождать, пока мы введем нужную для работы процесса информацию. Затем процесс хотел уже перейти в состояние выполнения, так как все необходимые ему данные уже введены, но не тут-то было: так как он не единственный процесс в системе, пока он был в состоянии ожидания, его "место под солнцем" занято - процессор выполняет другой процесс. Тогда нашему процессу ничего не остается как перейти в состояние готовности: ждать ему нечего, а выполняться он тоже не может.

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

Из состояния выполнения процесс может перейти либо в состояние ожидания или состояние готовности. Почему процесс может оказаться в состоянии ожидания, мы уже знаем - ему просто нужны дополнительные данные или он ожидает освобождения какого-нибудь ресурса, например, устройства или файла. В состояние готовности процесс может перейти, если во время его выполнения, квант времени выполнения "вышел". Другими словами, в операционной системе есть специальная программа - планировщик, которая следит за тем, чтобы все процессы выполнялись отведенное им время. Например, у нас есть три процесса. Один из них находится в состоянии выполнения. Два других - в состоянии готовности. Планировщик следит за временем выполнения первого процесса, если "время вышло", планировщик переводит процесс 1 в состояние готовности, а процесс 2 - в состояние выполнения. Затем, когда, время отведенное, на выполнение процесса 2, закончится, процесс 2 перейдет в состояние готовности, а процесс 3 - в состояние выполнения.

Диаграмма модели трех состояний представлена на рисунке 1.


Рисунок 1. Модель трех состояний

Более сложная модель - это модель, состоящая из пяти состояний. В этой моделе появилось два дополнительных состояния: рождение процесса и смерть процесса. Рождение процесса - это пассивное состояние, когда самого процесса еще нет, но уже готова структура для появления процесса. Как говорится в афоризме: "Мало найти хорошее место, надо его еще застолбить", так вот во время рождения как раз и просходит "застолбление" этого места. Смерть процесса - самого процесса уже нет, но может случиться, что его "место", то есть структура, осталась в списке процессов. Такие процессы называются зобми и о них мы еще поговорим в этой статье.

Диаграмма модели пяти состояний представлена на рисунке 2.


Рисунок 2. Модель пяти состояний

Над процессами можно производить следующие операции:

  1. Создание процесса - это переход из состояния рождения в состояние готовности
  2. Уничтожение процесса - это переход из состояния выполнения в состояние смерти
  3. Восстановление процесса - переход из состояния готовности в состояние выполнения
  4. Изменение приоритета процесса - переход из выполнения в готовность
  5. Блокирование процесса - переход в состояние ожидания из состояния выполнения
  6. Пробуждение процесса - переход из состояния ожидания в состояние готовности
  7. Запуск процесса (или его выбор) - переход из состояния готовности в состояние выполнения

Для создания процесса операционной системе нужно:

  1. Присвоить процессу имя
  2. Добавить информацию о процессе в список процессов
  3. Определить приоритет процесса
  4. Сформировать блок управления процессом
  5. Предоставить процессу нужные ему ресурсы

Подробнее о списке процессов, приоритете и обо всем остальном мы еще поговорим, а сейчас нужно сказать пару слов об иерархии процессов. Процесс не может взяться из ниоткуда: его обязательно должен запустить какой-то процесс. Процесс, запущенный другим процессом, называется дочерним (child) процессом или потомком. Процесс, который запустил процесс называется родительским (parent), родителем или просто - предком. У каждого процесса есть два атрибута - PID (Process ID) - идентификатор процесса и PPID (Parent Process ID) - идентифкатор родительского процесса.

Процессы создают иерархию в виде дерева. Самым "главным" предком, то есть процессом, стоящим на вершине этого дерева, является процесс init (PID=1).

На мой взгляд, приведенной теории вполне достаточно, чтобы перейти к практике, а именно - "пощупать" все состояния процессов. Конечно, мы не рассмотрели системные вызовы fork(), exec(), exit(), kill() и многие другие, но в Сети предостаточно информации об этом. Тем более, что про эти вызовы вы можете прочитать в справочной системе Linux, введя команду man fork. Правда, там написано на всеми любимом English, так что за переводом (если он вам нужен) все-таки придется обратиться за помощью к WWW.

Практика

Для наблюдения за процессами мы будем использовать программу top.

15:03:11  up 58 min,  4 users,  load average: 0,02, 0,01, 0,00
52 processes: 51 sleeping, 1 running, 0 zombie, 0 stopped
CPU states:  0,8% user,  0,6% system,  0,0% nice,  0,0% iowait, 98,3% idle
Mem:   127560k av,  124696k used,    2864k free,       0k shrd,     660k buff
        13460k active,              17580k inactive
Swap:  152576k av,    8952k used,  143624k free                   28892k cached

 PID USER     PRI  NI  SIZE  RSS SHARE STAT %CPU %MEM   TIME COMMAND
 3097 den       15   0  1128 1128   832 R     2,8  0,8   0:00 top
    1 root       8   0   120   84    60 S     0,0  0,0   0:04 init
    2 root      12   0     0    0     0 SW    0,0  0,0   0:00 keventd
    3 root      19  19     0    0     0 SWN   0,0  0,0   0:00 ksoftirqd_CPU0
...

Полный вывод программы я по понятным причинам урезал. Рассмотрим по порядку весь вывод программы. В первой строке программа сообщает текущее время, время работы системы ( 58 min), количество зарегистрированных (login) пользователей (4 users), общая средняя загрузка системы (load average).

Примечание. Общей средней загрузкой системы называется среднее число процессов, находящихся в состоянии выполнения (R) или в состоянии ожидания (D). Общая средняя загрузка измеряется каждые 1, 5 и 15 минут.

Во второй строке вывода программы top сообщается, что в списке процессов находятся 52 процесса, из них 51 спит (состояние готовности или ожидания), 1 выполняется (у меня только 1 процессор), 0 процессов зомби и 0 остановленных процессов.

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

В таблице отображается различная информация о процессе. Нас сейчас интересуют колонки PID (идентификатор процесса), USER (пользователь, запустивший процесс), STAT (состояние процесса) и COMMAND (команда, которая была введена для запуска процесса).

Колонка STAT может содержать следующие значения:

    R - процесс выполняется или готов к выполнению (состояние готовности)
  • D - процесс в "безпробудном сне" - ожидает дискового ввода/вывода
  • T - процесс остановлен (stopped) или трассируется отладчиком
  • S - процесс в состоянии ожидания (sleeping)
  • Z - процесс-зобми
  • < - процесс с отрицательным значением nice
  • N - процесс с положительным значением nice (о командне nice мы поговорим позже)

Давайте просмотрим, когда же процесс находится в каждом состоянии. Создайте файл process - это обыкновенный bash-сценарий

#!/bin/bash
x=1
while [ $x -lt 10 ]
do
x=2
done

Сделайте этот файл исполнимым chmod +x ./process и запустите его ./process. Теперь перейдите на другую консоль (ALT + Fn) и введите команду ps -a | grep process. Вы увидите следующий вывод команды ps:

 4035 pts/1    00:00:15 process

Данный вывод означает, что нашему процессу присвоен идентификатор процесса 4035. Теперь введите команду top -p 4035

15:30:15  up  1:25,  6 users,  load average: 0,44, 0,19, 0,07
1 processes: 0 sleeping, 1 running, 0 zombie, 0 stopped
CPU states:  2,3% user,  0,6% system,  0,0% nice,  0,0% iowait, 96,8% idle
Mem:   127560k av,  124496k used,    3064k free,       0k shrd,    1208k buff
        15200k active,              16400k inactive
Swap:  152576k av,   15676k used,  136900k free                   27548k cached

  PID USER     PRI  NI  SIZE  RSS SHARE STAT %CPU %MEM   TIME COMMAND
 4035 den       15   0  1320 1320   988 R    99,9  1,0   0:31 process

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

Теперь приостановим наш процесс - состояние T. Перейдите на консоль, на которой запущен ./process и нажмите Ctrl + Z. Вы увидите сообщение Stopped.

  PID USER     PRI  NI  SIZE  RSS SHARE STAT %CPU %MEM   TIME COMMAND
 4035 den        9   0  1320 1320   988 T     0,0  1,0   0:51 process

Теперь попробуем "усыпить" наш процесс. Для этого нужно сначала "убить" его: kill 4035. Затем добавить перед циклом while в сценарии ./process строку sleep 10m, которая означает, что процесс будет спать 10 минут. После этого опять запустите команду ps -a | grep process, чтобы узнать PID процесса, а затем - команду top -p PID. Вы увидите в колонке состояния букву S, что означает, что процесс находится в состоянии ожидания или готовности - попросту говоря "спит".

Мы вплотную подошли к самому интересному - созданию процесса-зомби. Во многих статьях, посвященных процессам, пишется "зомби = не жив, не мертв". А что это означает на самом деле? При завершении процесса должна удаляться его структура из списка процессов. Иногда процесс уже завершился, но его имя еще не удалено из списка процессов. В этом случае процесс становится зомби - его уже нет, но мы его видим в таблице команды 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

16:04:22  up 2 min,  3 users,  load average: 0,10, 0,10, 0,04
1 processes: 0 sleeping, 0 running, 1 zombie, 0 stopped
CPU states:  4,5% user,  7,6% system,  0,0% nice,  0,0% iowait, 87,8% idle
Mem:   127560k av,   76992k used,   50568k free,       0k shrd,    3872k buff
        24280k active,              19328k inactive
Swap:  152576k av,       0k used,  152576k free                   39704k cached

  PID USER     PRI  NI  SIZE  RSS SHARE STAT %CPU %MEM   TIME COMMAND
 1148 den       17   0     0    0     0 Z     0,0  0,0   0:00 zombie <defunct>

Мы видим, что в списке процессов появился 1 зомби (STAT=Z), который проживет аж 10 секунд.

Мы уже рассмотрели все возможные состояния процессов. Осталось только рассмотреть команду для повышения приоритета процесса - это команда nice. Повысить приоритет команды может только пользователь root, указав соответствующий коэффициент понижения. Для увеличения приоритета нужно указать отрицательный коффициент, например, nice -5 process

Все ваши вопросы и комментарии рад буду выслушать по адресу dhsilabs@mail.ru. Свои вопросы вы также можете задать на форуме на сайте http://dkws.narod.ru.