Перейти к содержанию

Основы процессов в Linux

Что такое процесс

Процесс — это экземпляр выполнения программы. Это изолированная единица, которой ядро операционной системы выделяет ресурсы и управляет её жизненным циклом.

Процесс — это живой объект в памяти, появляющийся при запуске программы. Программа — статичный ELF-файл на диске; процесс — её исполнение с собственными ресурсами. Одной программе может соответствовать сколько угодно параллельных процессов.

Каждый процесс обладает следующими атрибутами:

  • адресное пространство — виртуальная память: сегменты кода, данных, стека и кучи;
  • контекст выполнения — значения регистров процессора, счётчик команд, состояние памяти;
  • привилегии — UID, GID, effective UID, capabilities;
  • таблица файловых дескрипторов — набор открытых файлов, сокетов, пайпов;
  • сигнальная маска — список ожидающих и заблокированных сигналов;
  • текущая рабочая директория (CWD).
PCB (Process Control Block) — что хранит ядро о процессе
struct task_struct в Linux kernel

┌─────────────────────────────────────────────────────────┐
│                    task_struct                          │
├──────────────────────────┬──────────────────────────────┤
│  Идентификация           │  Состояние                   │
│  ┌────────────────────┐  │  ┌────────────────────────┐  │
│  │ pid   (PID)        │  │  │ state: R/S/D/T/Z       │  │
│  │ tgid  (thread grp) │  │  │ exit_code              │  │
│  │ ppid  (PPID)       │  │  └────────────────────────┘  │
│  └────────────────────┘  │                              │
├──────────────────────────┼──────────────────────────────┤
│  Адресное пространство   │  Планировщик                 │
│  ┌────────────────────┐  │  ┌────────────────────────┐  │
│  │ mm_struct ──▶      │  │  │ prio / nice            │  │
│  │  .text / .data     │  │  │ sched_class            │  │
│  │  .heap / .stack    │  │  │ cpus_allowed           │  │
│  │  mmap list         │  │  └────────────────────────┘  │
│  └────────────────────┘  │                              │
├──────────────────────────┼──────────────────────────────┤
│  Файловые дескрипторы    │  Привилегии                  │
│  ┌────────────────────┐  │  ┌────────────────────────┐  │
│  │ files_struct ──▶   │  │  │ uid / gid              │  │
│  │  fd[0] stdin       │  │  │ euid / egid            │  │
│  │  fd[1] stdout      │  │  │ capabilities           │  │
│  │  fd[2] stderr      │  │  └────────────────────────┘  │
│  │  fd[3] ...         │  │                              │
│  └────────────────────┘  │                              │
├──────────────────────────┼──────────────────────────────┤
│  Сигналы                 │  Прочее                      │
│  ┌────────────────────┐  │  ┌────────────────────────┐  │
│  │ pending signals    │  │  │ CWD (fs_struct)        │  │
│  │ blocked mask       │  │  │ rlimits                │  │
│  │ signal handlers    │  │  │ cgroups                │  │
│  └────────────────────┘  │  └────────────────────────┘  │
└─────────────────────────────────────────────────────────┘

/proc/<pid>/status — удобный способ увидеть большинство этих полей

Идентификаторы процесса: PID и PPID

Каждый процесс в системе идентифицируется уникальным числом:

  • PID (Process ID) — уникальный идентификатор процесса, выдаваемый ядром при его создании. Остаётся неизменным на протяжении всей жизни процесса.
  • PPID (Parent Process ID) — PID родительского процесса, который создал данный процесс через fork().

Все процессы в системе образуют дерево с корнем в init (PID = 1) или systemd.

Получить PID и PPID из программы:

#include <unistd.h>
#include <stdio.h>

int main() {
    pid_t mypid     = getpid();   // мой PID
    pid_t parentpid = getppid();  // PID родителя
    printf("PID=%d, PPID=%d\n", mypid, parentpid);
    return 0;
}

Из командной строки:

echo $$              # PID текущей оболочки (shell)
ps                   # список процессов с их PID и PPID

Дерево процессов

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

pstree              # дерево всех процессов
pstree -p           # с отображением PID
pstree -p -s <pid>  # цепочка предков для конкретного процесса
pstree <user>       # только процессы конкретного пользователя

Альтернативно, команда ps поддерживает режим отображения в виде дерева:

ps aux --forest     # ASCII-дерево процессов
ps -ejH             # с колонкой PPID для иерархии

Мониторинг использования ресурсов

Команда ps позволяет получить снимок состояния процессов:

ps aux                         # все процессы с %CPU и %MEM
ps aux --sort=-%mem            # отсортировано по памяти (убывание)
ps aux --sort=-%cpu            # отсортировано по CPU (убывание)
ps -eo pid,user,vsz,rss,comm   # кастомный формат

Два ключевых показателя использования памяти:

Метрика Описание
VSZ (Virtual Memory Size) Размер всего виртуального адресного пространства процесса, включая незагруженные в RAM страницы
RSS (Resident Set Size) Объём физической оперативной памяти, реально занятой процессом

Для интерактивного мониторинга в реальном времени используется top или htop:

top              # интерактивный монитор
top -p <pid>     # только конкретный процесс
htop             # более удобная версия top

Идентификаторы пользователя: UID, EUID и CWD

Каждый процесс имеет набор идентификаторов пользователя, влияющих на его права доступа:

  • UID (Real User ID) — реальный ID пользователя, который запустил процесс. Обычно не меняется в течение жизни процесса.
  • EUID (Effective User ID) — идентификатор, который ядро использует при проверке прав доступа к файлам и другим ресурсам. Именно EUID определяет, может ли процесс открыть файл, привязаться к порту или выполнить привилегированную операцию.
  • CWD (Current Working Directory) — текущая рабочая директория процесса; используется при разрешении относительных путей.

В обычной ситуации UID == EUID, однако они могут расходиться при использовании SUID-битов, вызовах setuid() или при управлении capabilities. Классический пример: программа /usr/bin/passwd запускается обычным пользователем (UID = 1000), но на её исполняемом файле установлен SUID-бит, поэтому её EUID равен 0 (root).

Получить идентификаторы из программы:

#include <unistd.h>
#include <stdio.h>

int main() {
    printf("UID=%d, EUID=%d\n", getuid(), geteuid());

    char cwd[256];
    if (getcwd(cwd, sizeof(cwd)) != NULL) {
        printf("CWD=%s\n", cwd);
    }

    return 0;
}

Из командной строки:

ps -o pid,uid,euid,comm
readlink /proc/<pid>/cwd    # CWD — симлинк на рабочую директорию

Изменить идентификатор пользователя или директорию из программы:

#include <unistd.h>

// Изменить UID (обычно требует привилегий root)
if (setuid(1000) == -1) {
    perror("setuid failed");
}

// Установить только EUID (для SUID-бинарей)
if (seteuid(0) == -1) {
    perror("seteuid failed");
}

// Сменить текущую директорию
if (chdir("/tmp") == -1) {
    perror("chdir failed");
}

Из командной строки:

cd /tmp                 # изменить CWD текущей оболочки
sudo -u someuser ./prog # запустить с другим UID

Файловая система /proc

Каждый живой процесс представлен в псевдофайловой системе /proc в виде директории /proc/<pid>/. Это основной интерфейс для интроспекции процессов без использования системных вызовов.

Ключевые файлы и директории:

Путь Содержимое
/proc/<pid>/status Имя, PID, PPID, UID, GID, состояние, потребление памяти
/proc/<pid>/cmdline Командная строка запуска (аргументы разделены нулевым байтом)
/proc/<pid>/environ Переменные окружения процесса
/proc/<pid>/maps Карта виртуальных регионов памяти (сегменты, библиотеки, стек, куча)
/proc/<pid>/fd/ Символические ссылки на все открытые файловые дескрипторы
/proc/<pid>/exe Символическая ссылка на исполняемый файл
/proc/<pid>/cwd Символическая ссылка на текущую рабочую директорию
/proc/<pid>/task/ Поддиректории для каждого потока (по TID)

Примеры:

cat /proc/self/status          # информация о текущем процессе
ls -la /proc/self/fd/          # открытые файловые дескрипторы
cat /proc/self/maps            # карта памяти процесса
readlink /proc/self/exe        # путь к текущему исполняемому файлу

Подробнее о карте памяти и виртуальных регионах см. в статье Виртуальная память.

Связанные темы

Источники

  • man 1 ps — документация по команде ps
  • man 1 pstree — документация по pstree
  • man 1 top — документация по top
  • man 2 getpid — системные вызовы getpid, getppid
  • man 2 getuid — getuid, geteuid, getgid, getegid
  • man 2 setuid — setuid, seteuid
  • man 2 chdir — изменение рабочей директории
  • man 5 proc — файловая система /proc