Добавить в корзинуПозвонить
Найти в Дзене

Ядро Линукс. Управление процессами

Ядро Линукс Управление процессами Процесс — это основная единица исполнения программного кода в операционной системе. Основные характеристики процесса: - Исполняет код программы.
- Оперирует системными ресурсами (файлы, память, устройства).
- Работает с виртуальной памятью.
- Может быть разбит на потоки (threads), которые позволяют исполняться параллельно на нескольких ядрах процессора. Каждый процесс (как и поток) описывается специальной структурой task_struct. Пример (упрощённый фрагмент): struct task_struct {
struct thread_info thread_info; // информация о потоке
long state; // текущее состояние процесса
void *id650111 (*stack); // указатель на стек
unsigned int cpu; // номер ядра, на котором исполняется
atomic_t usage; // статистика использования CPU
int prio; // приоритет
struct mm_struct *mm; // виртуальная память процесса
int exit_state; // состояние завершения
int exit_code; // код завершения
int exit_signal; // сигнал, который будет отправлен при завершении
pid_t pid; // идент

Ядро Линукс

Управление процессами

Процесс — это основная единица исполнения программного кода в операционной системе.

Основные характеристики процесса:

- Исполняет код программы.
- Оперирует системными ресурсами (файлы, память, устройства).
- Работает с виртуальной памятью.
- Может быть разбит на потоки (threads), которые позволяют исполняться параллельно на нескольких ядрах процессора.

Каждый процесс (как и поток) описывается специальной структурой task_struct.

Пример (упрощённый фрагмент):

struct task_struct {
struct thread_info thread_info; // информация о потоке
long state; // текущее состояние процесса
void *id650111 (*stack); // указатель на стек
unsigned int cpu; // номер ядра, на котором исполняется
atomic_t usage; // статистика использования CPU
int prio; // приоритет
struct mm_struct
*mm; // виртуальная память процесса
int exit_state; // состояние завершения
int exit_code; // код завершения
int exit_signal; // сигнал, который будет отправлен при завершении
pid_t pid; // идентификатор процесса (PID)
struct task_struct *id871537943 (*parent); // родительский процесс
struct list_head children; // список дочерних процессов
u64 start_time; // время запуска
const struct cred *cred; // права и учетные данные
struct files_struct
*files; // открытые файлы
struct thread_struct thread; // контекст выполнения
};

Жизненный цикл процесса

1. Создание — системный вызов `fork()` порождает новый процесс.
2. Готов к исполнению — процесс стоит в очереди планировщика.
3. Ожидание события — процесс «спит», ожидая данных (например, из сети). В этом состоянии он практически не потребляет ресурсов.
4. Исполнение — процесс назначается на ядро процессора и выполняется.
5. Приостановка — процесс снимается с CPU и ждёт своей очереди.
6. Возобновление — процесс снова попадает на процессор.
7. Завершение — процесс освобождает ресурсы, фиксируется его `exit_code`.

Процесс в Linux — это сущность с:
- собственным адресным пространством (своей виртуальной памятью);
- отдельным PID;
- своими дескрипторами файлов, сигналами, таблицами и прочей «обвязкой» ядра.

Когда мы создаём новый процесс через `fork()`, он получает копию адресного пространства родителя (copy-on-write). Сначала она помечается как доступная только для чтения для обоих процессов. Реальная копия страницы памяти создаётся только в момент, когда один из процессов пытается её изменить. Это огромная оптимизация. Не смотря на то что адресное пространство копируется, для системы это всё равно отдельная сущность.

Пример с запуском 1С:
1. Мы открываем терминал.
2. Через него запускаем 1С (`/opt/1c/.../1cv8c`).
3. Терминал вызывает `fork()`, создавая свою копию, а затем эта копия вызывает `exec()` для загрузки программы 1С. Родителем процесса 1С будет процесс терминала.

Если же процесс запускается напрямую при старте системы через systemd, тогда родителем будет systemd (PID 1), потому что systemd создаёт службы и демоны.

В Linux init (systemd) — это «первый родитель всех процессов», а пользовательские процессы получают родителя в зависимости от того, кто их запустил: терминал, скрипт, другой процесс.

Поток (thread) в Linux — это по сути «лёгкий процесс».
Linux не имеет потоков как отдельного типа объектов — у него есть `task_struct`, и все «процессы» и «потоки» внутри ядра — это таски. Отличие в том, что у потоков может быть общее:
- адресное пространство (т.е. общая память);
- дескрипторы файлов;
- сигналы.

То есть в ядре и процесс, и поток — это просто «task» (обычно говорят LWP — Light Weight Process). Потоки создаются системным вызовом `clone()`, где флагами указывается, какие ресурсы будут общими (`CLONE_VM`, `CLONE_FS`, `CLONE_FILES`, `CLONE_SIGHAND`).

Например:

- `fork()` → новый процесс (своё адресное пространство).
- `clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, ...)` → новый поток в рамках того же процесса.
- `pthread_create()` внутри glibc под капотом как раз вызывает `clone()` с нужными флагами.

Зачем ОС разводит процессы и потоки?

- Процессы дают изоляцию: один процесс не может просто так лазить в память другого (без IPC или shm). Это основа безопасности.
- Потоки удобны для параллельной работы внутри одного приложения: они делят память и данные, но ядро планирует их независимо, как отдельные сущности.

Итого:

- в Linux процесс — это task с уникальным адресным пространством;
- поток — это тоже task, но делящий память и ресурсы с другими.

Процесс A (PID 100)
├─ Поток 1 (TID 100)
├─ Поток 2 (TID 101)
└─ Поток 3 (TID 102)

Процесс B (PID 200)
└─ Поток 1 (TID 200)