Состояния процессов, wait и sleep¶
Состояния процесса в Linux¶
Каждый процесс в Linux в каждый момент времени находится в одном из нескольких состояний. Состояние отражает, что процесс делает сейчас: выполняется, ждёт, остановлен или завершён.
| Код | Состояние | Описание |
|---|---|---|
| R | Running | Процесс выполняется на CPU или находится в очереди готовых к выполнению |
| S | Interruptible Sleep | Процесс ждёт наступления события (I/O, таймер, сигнал); может быть разбужен сигналом |
| D | Uninterruptible Sleep | Процесс ждёт завершения операции (как правило, блочный I/O); не может быть прерван сигналом |
| T | Stopped | Процесс приостановлен (получил SIGSTOP или SIGTSTP) |
| Z | Zombie | Процесс завершился, но родитель ещё не прочитал его статус через wait() |
Помимо основного состояния, ps может показывать дополнительные флаги:
| Флаг | Значение |
|---|---|
< |
Высокий приоритет (отрицательный nice) |
N |
Низкий приоритет (положительный nice) |
I |
Idle — процесс спит более 20 секунд |
s |
Лидер сессии |
+ |
Процесс работает на переднем плане (foreground) |
l |
Многопоточный процесс |
w |
Процесс выгружен в swap |
stateDiagram-v2
[*] --> Runnable: fork() / clone()
Runnable --> Running: получил CPU
Running --> Runnable: вытеснён планировщиком
Running --> Sleep: блокирующий I/O или syscall
Running --> Stopped: SIGSTOP / SIGTSTP
Running --> Zombie: exit() или смертельный сигнал
Stopped --> Runnable: SIGCONT
Sleep --> Runnable: событие наступило / SIGCONT / I/O готов / таймер
Zombie --> [*]: wait() / waitpid() — reaped
S (interruptible) — ожидание I/O, таймера, блокировки; сигнал прерывает. D (uninterruptible) — ожидание блочного I/O; сигнал не прерывает.
Просмотр состояний:
ps aux # столбец STAT показывает состояние
ps -eo pid,state,comm # только PID, состояние, имя
# Состояние конкретного процесса:
cat /proc/<pid>/status | grep State
Управление выполнением: foreground и background¶
Приостановка и возобновление процесса¶
Из терминала, где работает процесс:
Из другого терминала:
kill -STOP <pid> # эквивалент SIGSTOP (нельзя игнорировать)
kill -19 <pid> # SIGSTOP = сигнал номер 19
Возобновить остановленный процесс:
Команды fg и bg¶
fg (foreground) — перевести фоновое или остановленное задание на передний план. Процесс получает доступ к терминалу.
./prog & # запустить в фоне (shell выводит [job_id] PID)
fg %1 # привести задание с job_id=1 на передний план
fg %prog # привести задание по имени
bg (background) — возобновить остановленное задание в фоновом режиме.
Посмотреть все фоновые задания текущей оболочки:
Orphan-процессы¶
Orphan (осиротевший) — процесс, чей родитель завершился раньше него. Ядро автоматически «усыновляет» осиротевший
процесс: его новым родителем становится init/systemd (PID 1). Это гарантирует, что когда orphan завершится, его
статус будет корректно прочитан и запись в таблице процессов — убрана.
Orphan-процессы используются намеренно для создания демонов: процесс вызывает fork(), родитель немедленно
завершается, дочерний (теперь orphan) продолжает работать в фоне под init.
Процессы-зомби¶
Зомби (zombie process) — процесс, который завершил своё выполнение, но запись о нём в таблице процессов ядра ещё не была удалена, потому что родитель не прочитал его статус выхода.
Механизм возникновения:
- дочерний процесс вызывает
exit()или получает смертельный сигнал; - ядро сохраняет статус завершения в структуре дескриптора процесса;
- если родитель не вызывает
wait()/waitpid(), запись остаётся в таблице со статусом Z; - зомби «живёт» до тех пор, пока родитель не прочитает статус или пока родитель не завершится (тогда зомби усыновляет
init/systemdи немедленно убирает).
Пример кода, создающего зомби:
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t child = fork();
if (child == 0) {
printf("Child: завершаюсь\n");
return 42; // дочерний процесс завершился
} else {
// Родитель не вызывает wait() — дочерний становится зомби
printf("Parent: жду ввода...\n");
getchar();
}
return 0;
}
Найти зомби в системе:
Устранение зомби:
- вызывать
wait()илиwaitpid()в родительском процессе; - установить обработчик
SIGCHLDдля автоматического сбора завершившихся дочерних процессов; - игнорировать
SIGCHLD:signal(SIGCHLD, SIG_IGN)— ядро само будет убирать дочерние процессы.
Системный вызов wait¶
wait() и waitpid() блокируют родительский процесс до завершения одного из дочерних и позволяют получить статус его
завершения.
wait¶
waitpid¶
#include <sys/wait.h>
int status;
// Ждать любого дочернего:
pid_t child_pid = waitpid(-1, &status, 0);
// Ждать конкретного дочернего:
waitpid(12345, &status, 0);
// Неблокирующая проверка (вернёт 0, если дочерний ещё жив):
waitpid(-1, &status, WNOHANG);
Макросы для анализа статуса¶
if (WIFEXITED(status)) {
int code = WEXITSTATUS(status); // код возврата
printf("Завершился с кодом %d\n", code);
}
if (WIFSIGNALED(status)) {
int sig = WTERMSIG(status); // номер сигнала
printf("Убит сигналом %d\n", sig);
}
if (WIFSTOPPED(status)) {
// Процесс был остановлен (при использовании WUNTRACED)
int sig = WSTOPSIG(status);
printf("Остановлен сигналом %d\n", sig);
}
Функции sleep¶
Для приостановки выполнения процесса на заданное время используются несколько функций.
sleep(n) — спать n секунд:
Функция может вернуться раньше, если процесс получит сигнал. В этом случае возвращается количество оставшихся секунд.
usleep(us) — спать заданное количество микросекунд (устарела):
nanosleep() — наиболее точный вариант с разрешением до наносекунды:
#include <time.h>
struct timespec req = {
.tv_sec = 1, // 1 секунда
.tv_nsec = 500000000 // 500 миллисекунд
};
struct timespec rem;
if (nanosleep(&req, &rem) == -1) {
// Прерван сигналом; rem содержит оставшееся время
}
Связанные темы¶
- fork и exec — как создаётся дочерний процесс и почему появляются зомби
- Сигналы —
SIGCHLD,SIGSTOP,SIGCONTи их роль в управлении процессами - rlimit — ограничения на количество процессов (
RLIMIT_NPROC)
Источники¶
man 2 wait— wait, waitpidman 2 kill— отправка сигналовman 1 ps— просмотр состояния процессовman 1 jobs— управление заданиями оболочкиman 1 fg,man 1 bg— foreground и backgroundman 3 sleep— функция sleepman 2 nanosleep— наносекундный sleep