02. Процессы и задания в прикладном программном интерфейсе
Лекция №2. Процессы и задания в прикладном программном интерфейсе
Курс: Системное программирование (2026–2027)
Учебная программа: 2025, регистрационный № УП-46/2025Пп/уч
Специальность переподготовки: 9-09-0612-02 «Программное обеспечение информационных систем»
Квалификация: Инженер-программист
Тема по программе: Тема 2. Процессы и задания в прикладном программном интерфейсе (4 академических часа)
Формируемые компетенции: СП-23, СП-24
Введение и цели занятия
📌 Слайд 1: Тема 2. Процессы и задания в прикладном программном интерфейсе
Если на первой лекции мы изучили шлюз взаимодействия между прикладным кодом и ядром (системный вызов и API), то на втором занятии мы переходим к фундаментальным объектам операционной системы — процессам и потокам.
Процесс является центральной единицей изоляции и владения ресурсами в любой многозадачной операционной системе. Понимание того, как ОС конструирует виртуальное адресное пространство, как планировщик распределяет кванты времени процессора, как создаются и синхронизируются параллельные потоки, критически важно для проектирования надежных серверных, клиентских и системных сервисов.
📌 Слайд 2: План лекции
Учебные вопросы лекции:
- Концепция процесса и виртуализация системных ресурсов (память, процессор).
- Анатомия виртуального адресного пространства процесса.
- Граф состояний процесса и механизм переключения контекста (Context Switch).
- Системные интерфейсы создания процессов: модель POSIX (
fork/exec) против модели WinAPI (CreateProcess). - Управление группами процессов и заданиями: cgroups в Linux и Job Objects в Windows.
- Многопоточность: разграничение ресурсов процесса и потока, архитектурные модели потоков (1:1, N:1, M:N).
- Асинхронное выполнение системных вызовов (IOCP,
io_uring) и концепции удаленного запуска.
1. Понятие процесса и виртуализация системных ресурсов
📌 Слайд 3: Понятие процесса и виртуализация системных ресурсов
В операционных системах необходимо четко разделять понятия «программа» и «процесс»:
- Программа (Program): пассивный объект, файл на дисковом накопителе (исполняемый образ в формате ELF или PE), содержащий скомпилированный машинный код, константы и метаданные.
- Процесс (Process): активная сущность, экземпляр выполняющейся программы в оперативной памяти, обладающий собственным изолированным адресным пространством и набором системных ресурсов.
Фундаментальный принцип современных ОС — виртуализация ресурсов:
- Виртуализация процессора (Time-Sharing / Псевдопараллелизм): операционная система создает для процесса иллюзию того, что он монопольно владеет центральным процессором. Планировщик ядра периодически переключает процессор между десятками и сотнями потоков, выделяя каждому поток квант времени (Time Slice, обычно от 1 до 20 мс).
- Виртуализация памяти (Virtual Memory): каждому процессу предоставляется собственное сплошное виртуальное адресное пространство (например, 128 ТБ в 64-битной архитектуре x86-64). Физическая память делится на страницы размером 4 КБ. Процесс работает только с виртуальными адресами, которые аппаратура MMU транслирует в физические кадры памяти (Page Frames). Процесс физически изолирован и не может обратиться к адресам другого процесса.
📌 Слайд 4: Анатомия адресного пространства процесса
Адресное пространство процесса состоит из логических секций (сегментов), обладающих различными атрибутами защиты:
- Секция кода (
.text): содержит машинные инструкции исполняемой программы. Доступна только для чтения и выполнения (Read-Only + Execute). Повторная запись в эту область блокируется аппаратурой (защита от внедрения шеллкода). - Секция инициализированных данных (
.data): глобальные и статические переменные, значения которых заданы программистом до старта (Read/Write). - Секция неинициализированных данных (
.bss): глобальные и статические переменные, не имеющие явной инициализации. В исполняемом файле хранится лишь размер этой области; при создании процесса ядро заполняет ее физическими страницами, содержащими нули. - Куча (Heap): область динамического выделения памяти во время работы программы (
malloc,free,new,delete,HeapAlloc). Растет от младших адресов к старшим. - Область разделяемой памяти и отображения файлов (Memory Mapping / Shared Libraries): страницы, выделяемые вызовом
mmapв Linux илиMapViewOfFileв Windows. Здесь размещаются исполняемые файлы динамических библиотек (libc.so,kernel32.dll) и разделяемые сегменты IPC. - Стек вызовов (User Stack): выделяется операционной системой под аргументы функций, локальные переменные и адреса возврата. Растет от старших адресов к младшим навстречу куче.
- Область ядра (Kernel Space): отображение кода и структур данных ядра. Доступно только при CPL = 0 (Ring 0).

📌 Слайд 5: Управляющие структуры процесса в ядре
Ядро ОС отслеживает состояние каждого процесса с помощью специализированного дескриптора — Блока управления процессом (Process Control Block, PCB).
1. Ядро Linux: структура task_struct
В ядре Linux каждый исполняемый контекст (и процесс, и поток) представляется структурой struct task_struct (определена в заголовке <linux/sched.h>). Дескриптор содержит:
- Идентификаторы:
pid(идентификатор потока/процесса),tgid(Thread Group ID — соответствует общепринятому понятию PID процесса),real_parent(родительский процесс); - Состояние планировщика:
state(Ready, Running, Stopped, Zombie); - Указатель на структуру управления памятью
struct mm_struct *mm(каталог таблиц страницpgd, список областей виртуальной памятиvma); - Таблицу открытых файлов
struct files_struct *files(массив указателей наstruct file); - Структуры обработки сигналов
signal,sighand; - Контекст безопасности:
cred(UID, GID, capabilities).
2. Ядро Windows: структуры EPROCESS, KPROCESS и PEB
В операционной системе Windows процесс описывается тремя ключевыми блоками:
KPROCESS(Kernel Process Block): низкоуровневая структура ядра. Содержит базовый приоритет, маску привязки к процессорам (Affinity), указатель на таблицу страниц (Directory Table Base, значение дляCR3).EPROCESS(Executive Process Block): высокоуровневая надстройка ядра надKPROCESS. Содержит PID, имя исполняемого файла, маркер доступа (Access Token), таблицу дескрипторов (Handle Table), квоты на ресурсы.PEB(Process Environment Block): структура пользовательского режима (Ring 3). Доступна процессу напрямую без системных вызовов (адрес доступен через регистрGSна x64 илиFSна x86). Содержит список загруженных DLL-модулей, аргументы командной строки, переменные окружения и кучу по умолчанию.
2. Жизненный цикл процесса и механизм переключения контекста
📌 Слайд 6: Жизненный цикл и состояния процесса
С момента порождения до полного уничтожения процесс проходит через последовательность состояний:
- Создание (New / Created): процесс формируется. Ядро выделяет память под структуры PCB/EPROCESS, инициализирует дескрипторы и настраивает начальное адресное пространство.
- Готовность (Ready): процесс полностью готов к исполнению и находится в очереди диспетчера планировщика (Run Queue). Он ожидает, когда планировщик предоставит ему свободное процессорное ядро.
- Выполнение (Running): инструкции процесса непосредственно исполняются на физическом ядре процессора.
- Ожидание / Блокировка (Blocked / Waiting): процесс добровольно уступает процессор, так как ожидает завершения внешней операции (чтение сектора с диска, сетевой пакет, освобождение мьютекса или завершение таймера
sleep). Процесс исключается из очереди планирования и не расходует такты CPU. - Завершение (Terminated / Zombie): процесс прекратил выполнение инструкций (вызов
exit()или аварийный сигнал). Большая часть ресурсов (память, открытые файлы) освобождается, но запись о процессе в таблице ядра сохраняется до тех пор, пока родительский процесс не заберет код возврата.

📌 Слайд 7: Переключение контекста процесса (Context Switch)
Переключение контекста (Context Switch) — это процедура остановки одного процесса и загрузки сохраненного состояния другого процесса.
Процедура выполняется планировщиком ядра по прерыванию таймера (квант времени исчерпан) либо при блокировке текущего потока:
- Сохранение регистров общего назначения (RAX, RBX, RCX, RDX, RSI, RDI, RBP, R8–R15) в структуру стека ядра текущего процесса.
- Сохранение указателя текущей инструкции (
RIP), регистра флагов (RFLAGS) и указателя стека (RSP). - Сохранение регистров расширенных инструкций (FPU, SSE, AVX через инструкции
fxsave/xsave). - Смена адресного пространства: запись адреса корневой таблицы страниц нового процесса в регистр управления процессора
CR3(mov cr3, new_cr3). - Загрузка сохраненного состояния регистров нового процесса.
- Выполнение инструкции возврата (
sysretqилиiretq) для продолжения исполнения нового процесса в Ring 3.
Накладные расходы переключения контекста:
- Прямые расходы: выполнение 500–1500 машинных инструкций сохранения и восстановления регистров.
- Косвенные расходы (Cache Pollution):
- При перезаписи регистра
CR3процессор аппаратно инвалидирует буфер ассоциативной трансляции адресов (TLB, Translation Lookaside Buffer). Первые тысячи обращений к памяти нового процесса будут вызывать долгие многоуровневые промахи TLB. Для минимизации этого эффекта в современных процессорах применяется технология PCID (Process-Context Identifiers). - Холодный кэш процессора: кэш-память L1/L2/L3 заполнена данными предыдущего процесса, что приводит к задержкам выборки строк кэша из оперативной памяти.
- При перезаписи регистра
3. Системные интерфейсы создания процессов: Linux против Windows
📌 Слайд 8: Модель создания процессов в Linux: fork() и execve()
В операционной системе Linux создание процессов основано на двух ортогональных операциях: размножении (fork) и замещении исполняемого образа (execve).
1. Системный вызов fork()
Вызов fork() создает почти точную копию вызывающего родительского процесса:
- Дочерний процесс получает собственный уникальный
PID. - Дочерний процесс наследует копию всех файловых дескрипторов родителя (с общими файловыми позициями смещения
offset). fork()вызывается один раз, но возвращает управление дважды:- В родительском процессе вызов возвращает
PIDсозданного потомка (положительное целое число). - В дочернем процессе вызов возвращает
0. - При ошибке нехватки ресурсов возвращается
-1.
- В родительском процессе вызов возвращает
2. Механизм Copy-On-Write (Копирование при записи, COW)
Если бы fork() физически копировал все гигабайты памяти процесса в новые страницы, вызов был бы крайне медленным. Вместо этого ядро Linux дублирует только структуры таблиц страниц.
Страницы памяти помечаются как Read-Only и разделяются обоими процессами:
- Пока процессы только читают память, физического дублирования не происходит.
- В момент, когда один из процессов пытается записать данные в страницу, аппаратура MMU генерирует исключение Page Fault (
#PF). - Обработчик ядра перехватывает
#PF, выделяет отдельный физический фрейм памяти, копирует туда 4 КБ данных, настраивает таблицу страниц на новый адрес, включает флаг записи (R/W) и возобновляет работу процесса.

📌 Слайд 9: Замещение образа программы execve() и сбор статуса waitpid()
После ветвления дочерний процесс обычно вызывает функцию семейства exec:
1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <sys/wait.h>
5
6int main(void) {
7 pid_t pid = fork();
8
9 if (pid < 0) {
10 perror("Ошибка fork");
11 return 1;
12 }
13
14 if (pid == 0) {
15 // Дочерний процесс: замещаем свой образ утилитой /bin/ls
16 char *args[] = {"ls", "-l", "/tmp", NULL};
17 char *env[] = {NULL};
18
19 execve("/bin/ls", args, env);
20 // Если execve вернул управление, произошла ошибка
21 perror("Ошибка execve");
22 exit(127);
23 } else {
24 // Родительский процесс: ждем завершения потомка
25 int status;
26 waitpid(pid, &status, 0);
27
28 if (WIFEXITED(status)) {
29 printf("Потомок PID %d завершился с кодом %d\n",
30 pid, WEXITSTATUS(status));
31 } else if (WIFSIGNALED(status)) {
32 printf("Потомок PID %d убит сигналом %d\n",
33 pid, WTERMSIG(status));
34 }
35 }
36 return 0;
37}
Проблема зомби и сирот (Zombies & Orphans):
- Процесс-зомби (Zombie / Defunct): процесс завершился (
exit()), но родитель еще не вызвалwaitpid(). Память освобождена, но запись в таблице ядра занимает память и расходует дескриптор PID. При накоплении зомби система исчерпывает пул доступных PID и не может создавать новые процессы. - Процесс-сирота (Orphan): родитель завершился раньше своего потомка. Ядро Linux автоматически переподчиняет такого потомка процессу
init(PID 1 / systemd).initпериодически вызываетwait(), гарантированно утилизируя завершившихся сирот.
📌 Слайд 10: Создание процессов в Windows: CreateProcessW()
В отличие от Unix-модели, в операционной системе Windows создание процесса выполняется за один вызов функции CreateProcessW(). Ядро Windows не имеет нативного механизма fork() (за исключением специальных интерфейсов WSL):
1#include <windows.h>
2#include <stdio.h>
3
4int main(void) {
5 STARTUPINFOW si;
6 PROCESS_INFORMATION pi;
7
8 ZeroMemory(&si, sizeof(si));
9 si.cb = sizeof(si);
10 ZeroMemory(&pi, sizeof(pi));
11
12 WCHAR cmdLine[] = L"notepad.exe C:\\example.txt";
13
14 BOOL success = CreateProcessW(
15 NULL, // Имя исполняемого модуля (если NULL, берется из cmdLine)
16 cmdLine, // Командная строка с аргументами
17 NULL, // Атрибуты безопасности процесса
18 NULL, // Атрибуты безопасности первичного потока
19 FALSE, // Флаг наследования дескрипторов
20 0, // Флаги создания (например, CREATE_SUSPENDED)
21 NULL, // Блок переменных окружения (NULL = окружение родителя)
22 NULL, // Текущий рабочий каталог (NULL = каталог родителя)
23 &si, // Указатель на структуру STARTUPINFO
24 &pi // Указатель на структуру PROCESS_INFORMATION
25 );
26
27 if (!success) {
28 printf("CreateProcessW завершился с ошибкой: %lu\n", GetLastError());
29 return 1;
30 }
31
32 printf("Создан процесс PID: %lu, Первичный поток TID: %lu\n",
33 pi.dwProcessId, pi.dwThreadId);
34
35 // Ожидание завершения блокнота (бесконечный таймаут)
36 WaitForSingleObject(pi.hProcess, INFINITE);
37
38 DWORD exitCode;
39 GetExitCodeProcess(pi.hProcess, &exitCode);
40 printf("Процесс завершился с кодом: %lu\n", exitCode);
41
42 // Обязательное закрытие дескрипторов процесса и потока
43 CloseHandle(pi.hThread);
44 CloseHandle(pi.hProcess);
45 return 0;
46}
Ключевая структура PROCESS_INFORMATION возвращает родительской программе сразу два дескриптора ядра: hProcess (дескриптор процесса) и hThread (дескриптор первичного потока). Если родитель не закроет их через CloseHandle(), структуры ядра будут удерживаться в памяти даже после физического выхода процесса.
4. Управление группами процессов и заданиями
📌 Слайд 11: Иерархия и группы процессов в Linux
Операционная система организует процессы в иерархические структуры:
- Идентификатор группы процессов (Process Group ID, PGID): объединяет процессы, решающие общую задачу (например, конвейер команд оболочки
cat file | grep pattern | wc -l). Сигнал, отправленный отрицательному значению PID (например,kill(-pgid, SIGINT)), доставляется всем процессам группы одновременно. - Сессия (Session ID, SID): коллекция групп процессов, привязанных к одному управляющему терминалу (tty/pty). Лидер сессии создает ее системным вызовом
setsid().
Контрольные группы Linux (Control Groups / cgroups v2)
Для системного управления вычислительными ресурсами ядро Linux предоставляет подсистему cgroups:
- Ограничение оперативной памяти (
memory.max): принудительное завершение через OOM-Killer при превышении квоты. - Ограничение процессорного времени (
cpu.max): задание доли времени CPU в микросекундах. - Ограничение дискового ввода-вывода (
io.max): лимиты по числу IOPS и байт в секунду. Механизм cgroups в связке с пространствами имен (Namespaces: PID, NET, MNT, IPC, UTS) составляет технологический базис современных систем изоляции и контейнеризации (Docker, Podman, Kubernetes).
📌 Слайд 12: Управление группами через Job Objects в Windows
В Windows эквивалентом групп процессов и квот ресурсов выступают Объекты-задания (Job Objects).
Объект-задание позволяет управлять совокупностью связанных процессов как единым целым:
- Создание задания:
HANDLE hJob = CreateJobObjectW(NULL, L"MyWorkerPool"); - Настройка жестких лимитов через
SetInformationJobObject:JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE— если дескриптор задания закрывается, операционная система автоматически и надежно уничтожает все дочерние процессы в задании (защита от процессов-зомби и утечек фоновых демонов).JOB_OBJECT_LIMIT_PROCESS_MEMORY— лимит частной памяти на каждый процесс.JOB_OBJECT_LIMIT_JOB_MEMORY— лимит суммарной памяти всех процессов группы.JOB_OBJECT_LIMIT_ACTIVE_PROCESS— ограничение на максимальное число одновременно запущенных процессов.
- Добавление процесса в задание:
AssignProcessToJobObject(hJob, pi.hProcess); - Мгновенное прекращение работы всех процессов группы:
TerminateJobObject(hJob, exitCode);
5. Многопоточность и модели потоков
📌 Слайд 13: Разграничение ресурсов между процессом и потоком
Поток исполнения (Thread) — это наименьшая базовая единица распределения процессорного времени, которой оперирует планировщик операционной системы.
Процесс выступает в роли контейнера ресурсов, а потоки внутри процесса исполняют код:
| Ресурс | Общий для всех потоков процесса | Индивидуальный для каждого потока |
|---|---|---|
| Виртуальное адресное пространство | ✅ | ❌ |
Секция кода (.text) и глобальные данные (.data, .bss) | ✅ | ❌ |
| Куча (Heap) | ✅ | ❌ |
| Таблица открытых дескрипторов файлов / Handles | ✅ | ❌ |
| Стек вызовов (User Call Stack) | ❌ | ✅ |
| Регистры процессора (RIP, RSP, RAX…) | ❌ | ✅ |
| Локальное хранилище потока (Thread-Local Storage, TLS) | ❌ | ✅ |
| Приоритет и маска привязки (Affinity Mask) | ❌ | ✅ |
Поскольку все потоки одного процесса разделяют единое адресное пространство, обмен данными между ними происходит мгновенно через запись в общие переменные. Однако это порождает фундаментальную проблему системного программирования — состояние гонки (Race Condition) при одновременной модификации общих структур данных.

📌 Слайд 14: Модели многопоточности: 1:1, N:1 и M:N
В теории операционных систем выделяют три архитектурные модели связи пользовательских потоков (User Threads) и ядерных контекстов (Kernel Threads):
- Модель 1:1 (Ядерные потоки / Kernel-Level Threads):
- На каждый пользовательский поток создается соответствующий поток ядра.
- Применяется в Linux (библиотека NPTL на базе вызова
clone()) и в Windows Threads. - Достоинства: истинный параллелизм на многоядерных процессорах; если один поток блокируется на операции ввода-вывода, остальные потоки продолжают вычисления.
- Недостатки: создание потока и переключение контекста требуют системного вызова и участия ядра.
- Модель N:1 (Пользовательские потоки / Green Threads / Fibers):
- Множество пользовательских потоков отображаются на один поток ядра. Планирование выполняет библиотека в User Space.
- Достоинства: ультрабыстрое переключение контекста без входа в ядро (несколько наносекунд); малый расход памяти.
- Недостатки: невозможно задействовать более одного физического ядра ЦПУ; если один пользовательский поток выполнит блокирующий системный вызов (
read), ядро заблокирует весь процесс целиком.
- Модель M:N (Гибридная модель):
- $M$ легковесных задач рантайма отображаются на $N$ системных потоков ядра ($M \gg N$).
- Реализуется в рантаймах языков Go (Goroutines), Rust (Tokio) и Erlang (BEAM).

📌 Слайд 15: Системные API управления потоками: POSIX Threads и WinAPI
1. Интерфейс POSIX Threads (pthreads) в Linux
Стандарт pthread объявляется в <pthread.h>. Для компиляции требуется флаг компилятора -pthread:
1#include <pthread.h>
2#include <stdio.h>
3
4void* thread_func(void* arg) {
5 long id = (long)arg;
6 printf("Фоновый поток %ld исполняется\n", id);
7 return (void*)(id * 10);
8}
9
10int main(void) {
11 pthread_t thread;
12 // Создание потока
13 pthread_create(&thread, NULL, thread_func, (void*)1);
14
15 void* result;
16 // Ожидание завершения потока (join)
17 pthread_join(thread, &result);
18 printf("Поток завершился с результатом: %ld\n", (long)result);
19 return 0;
20}
Системный базис в Linux: Под капотом библиотеки glibc вызов
pthread_create()обращается к универсальному системному вызовуclone()с флагамиCLONE_VM(разделение памяти),CLONE_FS(файловая система),CLONE_FILES(таблица дескрипторов),CLONE_SIGHAND(обработчики сигналов) иCLONE_THREAD(включение в общую группу потоков TGID). В Linux концептуально потоки и процессы описываются одинаковыми структурамиtask_struct, отличаясь лишь степенью разделения ресурсов.
2. Управление потоками в Windows API
Создание системного потока осуществляется функцией CreateThread():
HANDLE CreateThread(..., LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, ...);- Поток завершается выходом из функции потока либо вызовом
ExitThread(). - Родительский поток ожидает завершения через
WaitForSingleObject(hThread, INFINITE). - Внимание: в программах на C/C++, использующих стандартную библиотеку времени исполнения CRT, вместо
CreateThreadследует вызывать функцию_beginthreadex()(из<process.h>), чтобы корректно инициализировать потоко-локальные структуры CRT (буферы errno, строковые манипуляторы).
6. Асинхронное выполнение системных вызовов и удаленный запуск
📌 Слайд 16: Синхронный против асинхронного ввода-вывода
При синхронном вызове (read, write, ReadFile) поток переводится ядром в состояние ожидания (Blocked) до момента фактического физического чтения данных с устройства. Если программа обслуживает десятки тысяч сетевых соединений, поддержание десятков тысяч синхронных потоков истощает память (каждый стек требует от 1 до 8 МБ) и перегружает планировщик ядра.
Асинхронный ввод-вывод (Asynchronous I/O) разделяет инициализацию операции и получение результата:
- Поток инициирует операцию системным вызовом и немедленно возвращает управление в пользовательский код.
- Контроллер устройства через DMA выполняет чтение или запись в фоновом режиме без участия процессора.
- По завершении оборудование генерирует аппаратное прерывание, а ядро уведомляет приложение о готовности данных.
📌 Слайд 17: Высокопроизводительные механизмы: IOCP и io_uring
1. Windows: Порты завершения ввода-вывода (I/O Completion Ports, IOCP)
Считается наиболее масштабируемой моделью асинхронного ввода-вывода в Windows:
- Создается порт завершения:
CreateIoCompletionPort(). - Файловые дескрипторы или сокеты, открытые с флагом
FILE_FLAG_OVERLAPPED, привязываются к порту. - Асинхронное чтение инициируется через
ReadFile(..., &overlapped). - Пул рабочих потоков вызывает функцию
GetQueuedCompletionStatus(), засыпая на очереди ядра. Ядро пробуждает ровно столько потоков, сколько ядер процессора доступно, минимизируя лишние переключения контекста.
2. Linux: Интерфейс очередей io_uring
Эволюция асинхронных средств Linux шла через мультиплексирование select -> poll -> epoll. Начиная с ядра Linux 5.1, был внедрен революционный интерфейс io_uring:
- Взаимодействие прикладного процесса и ядра организуется через две кольцевые очереди (Ring Buffers) в разделяемой памяти:
- Submission Queue (SQ): очередь запросов на ввод-вывод, куда приложение без единого системного вызова записывает команды.
- Completion Queue (CQ): очередь завершенных операций, откуда приложение считывает результаты.
- Поддерживается режим опроса без прерываний (Kernel Polling,
IORING_SETUP_SQPOLL), устраняющий накладные расходы на системные вызовы.
📌 Слайд 18: Концепция удаленного запуска приложений
Системный удаленный запуск программ предполагает инициализацию процесса на удаленном узле вычислительной сети с передачей аргументов, окружения и контролем потоков ввода-вывода (stdin/stdout/stderr):
- Удаленный вызов процедур (Remote Procedure Call, RPC):
- Клиентская программа вызывает процедуру локально; клиентский стаб (stub) сериализует аргументы в сетевое сообщение (Marshalling) и отправляет по сети на сервер.
- Серверный скелетон (skeleton) десериализует параметры, вызывает локальную функцию и возвращает результат.
- Примеры: MS-RPC (базис многих служб Windows), gRPC, ONC RPC.
- Сетевые терминальные протоколы:
- SSH (Secure Shell): стандарт удаленного исполнения в Linux. Демон
sshdпорождает псевдотерминал (pty), вызываетfork()иexecve()целевой команды, мультиплексируя шифрованные потоки данных. - WinRM (Windows Remote Management): протокол на базе стандарта WS-Management, применяемый для удаленного администрирования серверов и выполнения скриптов PowerShell.
- SSH (Secure Shell): стандарт удаленного исполнения в Linux. Демон
📌 Слайд 19: Резюме лекции
Основные итоги:
- Процесс — это базовый контейнер системных ресурсов и изолированное виртуальное адресное пространство.
- Адресное пространство процесса логически разбито на сегменты с различными атрибутами защиты:
.text(код),.data/.bss(статические данные), куча (Heap) и стек (Stack). - Граф состояний процесса включает состояния Создания, Готовности, Исполнения, Ожидания и Завершения. Смена процесса на ядре ЦПУ сопровождается дорогостоящим переключением контекста и сбросом кэша TLB.
- В Linux процессы создаются разделением на
fork()с оптимизацией Copy-On-Write и замещением черезexecve(). - В Windows создание процесса атомарно осуществляется функцией
CreateProcessW(). - Для группового управления ресурсами применяются cgroups в Linux и Job Objects в Windows.
- Потоки внутри процесса разделяют общую память и дескрипторы, но имеют собственные регистры, стеки и TLS. Современные ОС используют модель потоков 1:1.
- Масштабируемый ввод-вывод реализуется асинхронными архитектурами без блокировки потоков (IOCP в Windows и
io_uringв Linux).
📌 Слайд 20: Вопросы для самопроверки
- Чем отличается виртуализация процессора от виртуализации оперативной памяти?
- В какую сторону по адресам растут стек вызовов и куча в классической модели процесса?
- Что происходит со страницами физической памяти при вызове
fork()благодаря механизму Copy-On-Write? - В чем разница между процессом-зомби и процессом-сиротой? Как ядро предотвращает утечку ресурсов?
- Какие структуры данных возвращает функция
CreateProcessW()в Windows и почему оба полученных дескриптора нужно закрывать? - С какой целью системный инженер объединяет процессы в Job Object в среде Windows?
- Почему переключение между потоками одного процесса выполняется быстрее, чем между потоками разных процессов?
- Каковы достоинства и недостатки модели многопоточности 1:1 по сравнению с моделью N:1?
- За счет чего механизм
io_uringв Linux обеспечивает сверхвысокую производительность ввода-вывода?
📌 Слайд 21: Литература и рекомендуемые ресурсы
Литература по курсу:
- Современные операционные системы (4-е изд.) — Таненбаум Э., Бос Х. СПб.: Питер, 2021. 1119 с.
- Устройство и функционирование OC Windows. Практикум к курсу «Операционные системы» — Коньков К. А. М.: Бином, 2008. 208 с.
- Операционные системы (2-е изд.) — Гордеев А. В. СПб.: Питер, 2009. 415 с.
- Системное программирование: методические указания по выполнению лабораторных работ — Бизюк А. Н., Соколова А. С. Витебск: УО «ВГТУ», 2024.
- Справочник Windows API: Процессы и потоки — https://learn.microsoft.com/en-us/windows/win32/procthread/
- Руководство Linux Man-Pages: fork(2), execve(2), clone(2) — https://man7.org/linux/man-pages/