← Назад к вопросам
В чем разница между процесс-сирота и зомби-процессом?
1.8 Middle🔥 181 комментариев
#Linux и администрирование
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Orphan процессы vs Zombie процессы
Это две разные проблемы в Unix/Linux которые часто путают. Обе связаны с управлением процессами, но имеют разные причины и последствия.
Orphan процесс - осиротевший процесс
Определение: Процесс родитель которого был завершён, но сам процесс продолжает работать.
Как происходит:
1. Parent process (PID=1000) создает child process (PID=1001)
2. Parent (PID=1000) завершается/убивается
3. Child (PID=1001) становится orphan'ом
Что происходит:
Orphan процесс автоматически переусыновляется init'ом (PID=1)
После этого PPID меняется на 1
Процесс продолжает работать, но родитель уже нет
Пример:
#!/bin/bash
# Parent скрипт
echo "PID: $$"
echo "Starting child..."
# Запустить child в background
sleep 9999 &
CHILD_PID=$!
echo "Child PID: $CHILD_PID"
echo "Killing parent..."
# Parent завершается, но child продолжает работать
exit 0
# Теперь child - orphan
# ppid изменится на 1
# Проверить
ps -o ppid,pid,cmd | grep sleep
1 1001 sleep 9999 # PPID изменилась на 1 (init)
Zombie процесс - зомби
Определение: Процесс который завершился, но entry в process table остаётся потому что parent не прочитал его exit status.
Как происходит:
1. Child process завершается
2. ОС отправляет SIGCHLD сигнал parent'у
3. Parent должен вызвать wait() или waitpid() для получения exit status
4. Если parent НЕ вызывает wait() → child становится zombie
5. Entry в процесс таблице остаётся
Что это означает:
Zombie процесс уже "мёртв" (не использует памяти для кода/data)
Но entry в kernel процесс таблице занимает место
Частота: обычно несколько зомби
Проблема: если много зомби - исчерпаем таблицу процессов
Пример:
import subprocess
import time
# Плохо - создаёт zombie
proc = subprocess.Popen(['sleep', '10'])
# НЕ вызываем wait/join
# Когда sleep завершится - станет zombie
time.sleep(15)
# Process завершён, но entry остаётся в таблице
# Хорошо
proc = subprocess.Popen(['sleep', '10'])
proc.wait() # Дождаться завершения
# Или
proc.communicate() # Альтернатива
Bash пример:
#!/bin/bash
# Плохо - создаёт zombie
/some/long/command &
PID=$!
# НЕ вызываем wait
exit 0 # Parent выходит
# Zombie процесс остаётся
# Когда Parent убит, zombie переусыновляется init'ом
# Init БУДЕТ вызывать wait() → zombie исчезнет
# Хорошо
/some/long/command &
PID=$!
wait $PID # Ждём завершения
exit 0
Сравнение
Orphan Zombie
────────────────────────────────────────────────────
Процесс живой? ДА (работает) НЕТ (мёртв)
Меморию использует? ДА НЕТ (только entry)
Причина Parent завершился Parent не вызвал wait()
Решение Переусыновляется init Parent вызывает wait()
Опасность Может зависнуть Утечка процесс таблицы
ПВИД zombies нет Orphans исчезают
Диагностика
Посмотреть zombie процессы:
# Zombie имеют статус Z
ps aux | grep Z
# Пример вывода
user 1234 0.0 0.0 0 0 pts/0 Z+ 10:15 0:00 [sleep] <defunct>
# ↑ Zombie процесс
# Более явно
ps aux --pid=$(pgrep -f 'parent_command')
# Посмотреть children и их статусы
# Информация о процессе
cat /proc/1234/status
# Показывает PPID, state, etc.
Посмотреть orphan процессы:
# Orphan'ы имеют PPID=1 (init)
ps -o ppid,pid,cmd -ef | awk '$1==1' | tail -20
# Видим процессы с PPID=1
# Проверить конкретный процесс
ps -o ppid,pid,cmd -p 1234
PPID PID CMD
1 1234 /path/to/command
# Если PPID=1 → это orphan или неправильный процесс
Практические примеры
Пример 1: Daemon создание (правильное)
import os
import sys
import time
def daemonize():
# Double fork для полного orphan
try:
pid = os.fork()
if pid > 0:
# Exit parent
sys.exit(0)
except OSError as err:
sys.exit(1)
os.chdir("/")
os.setsid() # Новая сессия
os.umask(0)
try:
pid = os.fork()
if pid > 0:
sys.exit(0) # Exit parent
except OSError as err:
sys.exit(1)
# Теперь process полностью orphan
# Переусыновлен init (PID=1)
# Использование
daemonize()
while True:
# Daemon работает
time.sleep(1)
Пример 2: Avoiding zombies (Python)
import subprocess
import signal
import os
# Плохо
proc = subprocess.Popen(['sleep', '10'])
print(f"Process PID: {proc.pid}")
# Если выполнить exit() здесь → zombie
# Хорошо - способ 1
proc = subprocess.Popen(['sleep', '10'])
proc.wait() # Ждём завершения
# Хорошо - способ 2
with subprocess.Popen(['sleep', '10']) as proc:
returncode = proc.poll()
# Автоматически вызовет wait
# Хорошо - способ 3: асинхронно
import atexit
proc = subprocess.Popen(['sleep', '10'])
atexit.register(proc.wait) # Гарантирует вызов wait
# Хорошо - способ 4: signal handler
def handle_sigchld(signum, frame):
os.waitpid(-1, os.WNOHANG) # Реапируем zombie
signal.signal(signal.SIGCHLD, handle_sigchld)
Влияние на систему
Orphan процессы:
+ Продолжают работать нормально
+ Автоматически очищаются init'ом
- Могут оставить файлы незакрытыми
- Могут держать ресурсы
Zombie процессы:
+ Почти не используют ресурсы (процесс таблица)
- Если много → исчерпаем PID space
- Не могут быть убиты (уже мертвы)
- Указывают на bug в parent коде
Мониторинг в production
# Скрипт для обнаружения проблем
#!/bin/bash
# Посчитать zombie процессы
ZOMBIES=$(ps aux | grep -c " Z ")
if [ $ZOMBIES -gt 10 ]; then
echo "WARNING: $ZOMBIES zombie processes detected"
# Отправить alert
fi
# Посчитать orphan процессы
ORPHANS=$(ps -o ppid=1 -e | wc -l)
if [ $ORPHANS -gt 100 ]; then
echo "WARNING: $ORPHANS orphan processes"
fi
# Проверить максимум процессов
MAX_PID=$(cat /proc/sys/kernel/pid_max)
CURR_PIDS=$(ps -e | wc -l)
USAGE=$((CURR_PIDS * 100 / MAX_PID))
if [ $USAGE -gt 80 ]; then
echo "WARNING: $USAGE% of PID space used"
fi
Выводы
Orphan процессы:
- Родитель умер, child продолжает работать
- PPID меняется на 1
- Нет проблемы - init позаботится
Zombie процессы:
- Child умер, parent не прочитал exit status
- Процесс таблица захламляется
- Нужно исправлять код (добавить wait/join)
Правило для DevOps:
- Мониторить zombie процессы
- Если много → bug в приложении
- Orphan'ы обычно OK
- Перезагрузка убивает всё (zombie и orphan)