Кратко о процессах в Linux

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

Впервые понятие “процесс” появилось в операционной системе Multics — одной из первых систем с разделением времени. Процесс — выполняющаяся программа, которой выделено процессорное время. Сама по себе программа процессом не является.

Несколько процессов могут использовать одну программу или те же ресурсы типа открытых файлов или адресного пространства.

В процессы входят сегменты данных, в которых есть переменные: 

  • набор ресурсов (открытые файлы и сигналы, ожидающие обработки); 
  • адресное пространство;
  • количество потоков выполнения.

Потоки выполнения осуществляют операции внутри процесса. В них есть счётчик команд, стек их выполнения и набор регистров. Ядро системы работает с потоками, а не с процессами. 

Для работы процессов нужны 2 виртуальных ресурса: процессор и память. Первый заставляет процесс думать, что он использует всю систему, а вторая — что всю физическую память. Потоки пользуются одной на всех виртуальной памятью, а виртуальные процессоры могут создаваться на каждый из них.

Чтобы описать работу процесса, есть несколько моделей. Самая простая состоит из 3 стадий:

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

Есть модель посложнее, в которой состояний уже 5. Новые — рождение и смерть процесса:

  • рождение: пассивное состояние, при котором самого процесса нет, но уже формируется структура под его деятельность;
  • смерть: пассивное состояние, когда процесса уже нет, но оставшаяся структура ещё есть в списке; такие процессы называются зомби.

Какие действия могут происходить с процессами?

  • создание: переход от рождения к готовности;
  • уничтожение: от выполнения к смерти;
  • восстановление: от готовности к выполнению;
  • изменение приоритета: от выполнения к готовности;
  • блокирование: от выполнения к ожиданию;
  • пробуждение: от ожидания к готовности;
  • запуск или выбор: от готовности к выполнению.

Что нужно системе для создания процесса?

  • дать процессу имя;
  • внести данные о процессе в общий список;
  • назначить процессу приоритет;
  • создать блок управления им;
  • выделить процессу ресурсы.

Как живёт процесс?

Создание процесса

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

В Linux процесс создаётся через системный вызов fork (): он полностью копирует родительский процесс и делает дочерний. Порождающий продолжает выполняться, а порождённый начинает работать с места возврата из системного вызова.

Далее они выстраиваются в древовидную структуру во главе с процессом init (PID=1). Следующий за ним процесс — fork(2). Он полностью идентичен init(1), но возвращает ему свой PID и берёт себе значение 0, а PPID меняется на PID родителя. По системным ресурсам родитель и ребёнок тоже идентичны, но это не совсем так для памяти. 

В Linux работает функция copy-on-write. Страницы памяти доступны родительскому и дочернему процессу в режиме read-only. Если какой-то из процессов меняет данные на странице памяти, создаётся копия страницы, в которой эти изменения и происходят. Неизменённая страница привязывается к противоположному процессу и переходит в статус read-write.

Готовность процесса

Как только fork(2) выполнится, процесс переходит в статус “готов”. Он ожидает своей очереди, пока планировщик не выделит ему время в процессоре. 

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

Выполнение процесса

И вот планировщик выделил процессу квант времени. Статус изменится на “выполняется”. Процессор работает с ним либо весь квант времени, либо часть времени будет работать с другим процессом, если вы воспользуетесь системным вызовом sched_yield.

Ожидание процесса

Предположим, процессу нужен ввод данных для работы или, наоборот, необходимо их вывести. На этом моменте он перейдёт в стадию ожидания. После того как вы совершите ключевое действие, процесс перейдёт в стадию готовности.

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

Остановка процесса

Если вам нужно приостановить процесс, это можно сделать через сигнал SIGSTOP. После этого он перейдёт в ожидание и не выйдет из этого состояния, пока не получит сигнал SIGCONT (возобновить работу) или SIGKILL (умереть). До этого прочие сигналы станут в очередь.

Завершение процесса

Процессы не завершаются сами по себе, а делают запрос системе через системный вызов _exit. Или система завершит их по причине ошибки. Если вернуть число из функции main(), всё равно сработает вызов _exit. Хоть аргумент вызова примет значение int, как код возврата будет использован меньший байт числа.

Процессы-зомби

Если процесс завершился, ядро системы фиксирует данные об этом и делает его “зомби”. Это состояние, когда процесс умер, но память о нём осталась в ядре. А ещё “зомби” игнорирует SIGKILL: то, что мертво, умереть не может.

Забытие процесса

Теперь нам нужно забрать информацию о процессе-зомби из ядра системы через специальные системные вызовы, но сейчас не о них. Эту информацию можно втиснуть в данные типа int. Чтобы получить код возврата и ведомости о причинах завершения процесса, нужно использовать макросы из станицы man waitpid(2).

Иногда родительский процесс завершается раньше, чем дочерний. Родителем в этой ситуации станет init, который воспользуется вызовом wait(2) в нужное время.

Когда родитель заберёт информацию о смерти дочернего процесса, ядро сотрёт информацию о ребёнке, а вместо него появится другой процесс.

Вывод

Понятно, что далеко не всем нужна информация о том, как работает ядро их операционной системы. Но чтобы понимать инструмент, с которым ты работаешь каждый день, нужно знать все нюансы его устройства. А по-другому — никак.

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

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