01. Прикладной программный интерфейс

Лекция №1. Прикладной программный интерфейс

Курс: Системное программирование (2026–2027)
Учебная программа: 2025, регистрационный № УП-46/2025Пп/уч
Специальность переподготовки: 9-09-0612-02 «Программное обеспечение информационных систем»
Квалификация: Инженер-программист
Тема по программе: Тема 1. Прикладной программный интерфейс (4 академических часа)
Формируемые компетенции: СП-21, СП-22


Введение и цели занятия

📌 Слайд 1: Тема 1. Прикладной программный интерфейс

Системное программирование формирует фундамент квалификации инженера-программиста. Прикладной разработчик оперирует бизнес-логикой и абстракциями высокоуровневых сред исполнения. Системный разработчик управляет аппаратными ресурсами вычислительной машины через интерфейсы операционной системы.

Цель настоящей лекции — разобрать внутреннее устройство прикладного программного интерфейса (API), принципы разграничения привилегий между пользователем и ядром, аппаратный механизм системных вызовов, а также сравнить нативные парадигмы ОС Linux и Windows.

📌 Слайд 2: План лекции

Учебные вопросы лекции:

  1. Архитектурные уровни вычислительной системы и место API.
  2. Аппаратная защита памяти и кольца привилегий процессора (Ring 0 и Ring 3).
  3. Механизм прерываний и эволюция системного вызова (от int 0x80 к syscall/sysret).
  4. Нативный API операционных систем: сопоставление подходов Linux (POSIX, libc) и Windows (WinAPI, Native API ntdll).
  5. Стратегии обработки и локализации ошибок (errno против GetLastError).
  6. Современный инструментарий разработки, отладки и трассировки системного ПО (GCC, Clang, MSVC, GDB, WinDbg, strace, ProcMon).

1. Архитектурные уровни вычислительной системы и понятие нативного API

📌 Слайд 3: Архитектурные уровни вычислительной системы

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

  1. Аппаратный уровень (Hardware): центральный процессор, оперативная память, постоянные накопители, сетевые адаптеры, контроллеры прерываний и шины обмена данными.
  2. Ядро операционной системы (Operating System Kernel): монопольно управляет оборудованием, изолирует программы друг от друга, распределяет процессорное время и страницы оперативной памяти.
  3. Интерфейс системных вызовов (System Call Interface, SCI): формальная граница между непривилегированным пользовательским кодом и привилегированным ядром.
  4. Стандартные системные библиотеки и платформенный API: библиотеки языка Си (glibc, musl) в Linux и подсистемные динамические библиотеки (kernel32.dll, user32.dll) в Windows.
  5. Прикладное программное обеспечение (Applications): пользовательские процессы, выполняющие прикладные вычисления.

center

📌 Слайд 4: Понятие прикладного программного интерфейса (API)

Прикладной программный интерфейс (Application Programming Interface, API) — это задокументированный контракт взаимодействия между вызывающей программой и поставщиком сервиса (операционной системой, библиотекой или сетевым сервисом). Контракт включает:

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

В системном программировании ключевым является понятие нативного API (Native API). Нативный API взаимодействует с операционной системой на уровне машинного кода целевой архитектуры без промежуточных виртуальных машин, интерпретаторов и эмуляторов.

Различают два родственных понятия:

  • API (Application Programming Interface): исходный программный контракт на уровне исходного текста (заголовочные файлы .h, имена функций, макросы). Код, написанный под строгий стандарт API (например, POSIX.1), переносим между различными ОС путем перекомпиляции.
  • ABI (Application Binary Interface): бинарный контракт на уровне машинного кода. ABI определяет размер и выравнивание типов данных в памяти, схему передачи параметров через регистры процессора, порядок размотки стека и формат исполняемых файлов (ELF в Linux, PE/COFF в Windows). Бинарная совместимость гарантирует, что уже скомпилированный модуль будет исполняться без пересборки.

2. Аппаратная изоляция и кольца защиты процессора

📌 Слайд 5: Кольца защиты процессора (Ring 0 и Ring 3)

Безопасность и отказоустойчивость современных ОС опираются на аппаратную поддержку со стороны центрального процессора. Начиная с архитектуры Intel 80286 и 80386, микропроцессоры x86/x64 поддерживают концепцию иерархических уровней привилегий (кольца защиты, Protection Rings).

Аппаратура поддерживает 4 кольца (от Ring 0 до Ring 3):

  • Ring 0 (Supervisor Mode / Kernel Mode): максимальный уровень привилегий. Код ядра имеет прямой доступ ко всему физическому адресному пространству памяти, портам ввода-вывода и управляющим регистрам ЦПУ.
  • Ring 1 и Ring 2: промежуточные уровни, изначально предназначавшиеся для служб ОС и драйверов. В современных ОС общего назначения (Linux, Windows, macOS, FreeBSD) не используются ради скорости и универсальности межплатформенного портирования.
  • Ring 3 (User Mode): минимальный уровень привилегий. Прикладной код лишен прямого доступа к оборудованию, изолирован в собственном виртуальном адресном пространстве и не может выполнять критические инструкции процессора.

Текущий уровень привилегий процессора определяется битами CPL (Current Privilege Level) в селекторе сегмента кода CS (Code Segment). Значение 00b соответствует Ring 0, а 11b — Ring 3.

📌 Слайд 6: Привилегированные инструкции и барьер изоляции

При попытке кода в режиме Ring 3 выполнить привилегированную инструкцию центральный процессор блокирует выполнение и генерирует исключение общей защиты (General Protection Fault, прерывание вектор #GP, код 13).

К привилегированным инструкциям относятся:

  • прямое управление контроллером прерываний: cli (запрет маскируемых прерываний), sti (разрешение);
  • перевод процессора в состояние энергосберегающего ожидания: hlt;
  • изменение управляющих регистров: mov cr0, reg, mov cr3, reg (загрузка адреса корневой таблицы страниц памяти), mov cr4, reg;
  • чтение и запись модельно-зависимых регистров: rdmsr, wrmsr;
  • модификация таблиц дескрипторов сегментов и прерываний: lgdt, lidt, ltr.

Аппаратная защита оперативной памяти реализуется блоком управления памятью (Memory Management Unit, MMU). Каждая запись в таблицах страниц (Page Table Entry, PTE) содержит служебные биты:

  • U/S (User / Supervisor): при сброшенном бите доступ к странице разрешен только из Ring 0. При попытке чтения или записи из Ring 3 процессор генерирует отказ страницы (Page Fault, #PF, вектор 14).
  • R/W (Read / Write): разрешение модификации данных.
  • NX / XD (No-Execute / Execute-Disable): запрет исполнения кода из страниц данных (защита от атак переполнения стека и кучи).

Таким образом, прикладная программа физически не может самостоятельно обратиться к диску, отправить сетевой пакет или прочитать память соседнего процесса. Единственный легальный путь взаимодействия с внешним миром — запрос к ядру через системный вызов.


3. Механизм прерываний и системные вызовы

📌 Слайд 7: Механизм прерываний в вычислительных системах

Прерывание (Interrupt) — это аппаратный сигнал процессору, требующий немедленного приостановления текущего потока команд и перехода на специальную подпрограмму обработки (Interrupt Service Routine, ISR).

Прерывания делятся на три категории:

  1. Аппаратные прерывания (Hardware Interrupts / Asynchronous): генерируются внешними контроллерами независимо от текущей выполняемой инструкции ЦПУ (нажатие клавиши клавиатуры, поступление пакета в сетевую карту, сигнал системного таймера APIC).
  2. Исключения процессора (Processor Exceptions / Synchronous): генерируются самим ЦПУ при возникновении нестандартных условий в процессе исполнения команды:
    • Faults (Ошибки): обнаруживаются до выполнения инструкции, могут быть исправлены ядром с повторным запуском команды (например, Page Fault при подкачке страницы с диска).
    • Traps (Ловушки): фиксируются сразу после выполнения инструкции (например, точка останова отладчика int 3).
    • Aborts (Аварии): неустранимые аппаратные сбои (Machine Check Exception).
  3. Программные прерывания (Software Interrupts): вызываются кодом преднамеренно через специальную команду процессора (int N). Именно программные прерывания исторически служили механизмом перехода в ядро.

📌 Слайд 8: Таблица дескрипторов прерываний (IDT)

В защищенном и длинном режимах работы процессоров x86/x64 маршрутизация всех прерываний выполняется через Таблицу дескрипторов прерываний (Interrupt Descriptor Table, IDT).

Таблица содержит массив из 256 дескрипторов (шлюзов), адрес которых загружается в регистр IDTR инструкцией lidt:

  • Шлюзы прерываний (Interrupt Gates): при входе в обработчик автоматически сбрасывают флаг IF в регистре RFLAGS, блокируя другие маскируемые прерывания.
  • Шлюзы ловушек (Trap Gates): не модифицируют флаг IF.

Каждый дескриптор в IDT задает:

  • сегмент кода ядра (CS) и смещение точки входа обработчика;
  • уровень привилегий шлюза (DPL, Descriptor Privilege Level). Если DPL шлюза равен 0, инструкция int N, вызванная из Ring 3, приведет к исключению #GP. Для разрешения системных вызовов DPL шлюза выставляется в 3.

📌 Слайд 9: Эволюция системных вызовов: int 0x80 против syscall

Исторически системные вызовы в 32-разрядных ОС реализовывались через программные прерывания:

  • В Linux x86 применялся вектор int 0x80.
  • В Windows x86 применялся вектор int 0x2e.

Недостаток программного прерывания — высокие накладные расходы. При вызове int 0x80 процессор выполнял десятки циклов проверки сегментных дескрипторов в GDT, считывал IDT, проверял права DPL/CPL, сохранял в стек старые значения SS, ESP, EFLAGS, CS, EIP и считывал стек ядра из сегмента состояния задачи (TSS). Переход занимал 100–150 тактов процессора на каждый вызов.

Для устранения этой проблемы производители процессоров разработали специализированные инструкции быстрого системного вызова:

  • Инструкции SYSENTER / SYSEXIT (Intel x86): целевые адреса и селекторы сегментов заранее заносятся операционной системой в регистры MSR (Model-Specific Registers) IA32_SYSENTER_CS, IA32_SYSENTER_ESP, IA32_SYSENTER_EIP.
  • Инструкции SYSCALL / SYSRET (AMD64 / Intel 64): стандарт для всех современных 64-битных ОС. Точка входа в ядро настраивается в регистре IA32_LSTAR (Long System Target Address Register, MSR 0xC0000082).

Инструкция syscall не обращается к таблице IDT и не обращается к оперативной памяти при смене контекста:

  1. Сохраняет адрес следующей пользовательской инструкции в регистр RCX.
  2. Сохраняет регистр флагов RFLAGS в регистр R11.
  3. Маскирует флаги согласно регистру IA32_FMASK (в частности, отключает внешние прерывания).
  4. Загружает в RIP адрес точки входа ядра из регистра IA32_LSTAR.
  5. Устанавливает CPL = 0 (Ring 0).

Накладные расходы снизились до 10–20 тактов, обеспечив высокую производительность системного ввода-вывода.

center


4. Архитектура нативного API: сопоставление Linux и Windows

📌 Слайд 10: Реализация системных вызовов в ОС Linux

В операционной системе Linux системные вызовы представляют собой первичный публичный интерфейс ядра. Ядро гарантирует обратную бинарную совместимость: программа, скомпилированная под ядро Linux 2.6 двадцать лет назад, продолжает функционировать на современном ядре Linux 6.x.

Соглашение о вызовах Linux на архитектуре x86-64 регламентируется стандартом System V AMD64 ABI:

  • Номер системного вызова передается в регистре RAX (например, 0 — read, 1 — write, 2 — open, 3 — close, 60 — exit).
  • Аргументы функции (до 6 параметров) передаются в строгой последовательности через регистры:
    1. Первый аргумент: RDI
    2. Второй аргумент: RSI
    3. Третий аргумент: RDX
    4. Четвертый аргумент: R10 (внимание: компиляторы языка Си используют RCX, но при системном вызове RCX затирается аппаратурой, поэтому ядро использует R10)
    5. Пятый аргумент: R8
    6. Шестой аргумент: R9
  • Результат вызова возвращается в регистре RAX.

Рассмотрим ассемблерную реализацию прямого вызова без использования стандартных библиотек:

 1; Прямой системный вызов write(1, msg, 14) на ассемблере NASM (Linux x86-64)
 2global _start
 3
 4section .data
 5    msg db "Hello, System!", 0x0A
 6    len equ $ - msg
 7
 8section .text
 9_start:
10    mov rax, 1          ; номер sys_write
11    mov rdi, 1          ; дескриптор stdout (1)
12    mov rsi, msg        ; адрес буфера
13    mov rdx, len        ; размер данных в байтах
14    syscall             ; переход в ядро
15
16    mov rax, 60         ; номер sys_exit
17    xor rdi, rdi        ; код возврата 0
18    syscall

В реальных программах на языке Си разработчик не пишет ассемблерный код вручную. Стандартная библиотека GNU C Library (glibc) или альтернативная компактная musl предоставляет тонкие Си-обертки (wrappers), объявляемые в заголовках <unistd.h>, <fcntl.h>, <sys/stat.h>.

📌 Слайд 11: Архитектура Windows: Win32 API и Native API

Архитектура операционной системы Windows принципиально отличается от монолитной структуры Linux. В Windows интерфейс системных вызовов ядра не является публичным контрактом. Microsoft не гарантирует постоянство номеров системных вызовов: таблица SSDT (System Service Descriptor Table) меняется между выпусками ОС и сервис-паками.

Архитектура Windows базируется на концепции подсистем окружения (Environment Subsystems):

  1. Win32 Subsystem (Windows API): официальный стабильный программный интерфейс. Реализуется набором динамических библиотек пользовательского режима:
    • kernel32.dll / KernelBase.dll — базовые службы ОС: процессы, потоки, память, файлы, синхронизация.
    • user32.dll — окна, диалоги, оконные сообщения, ввод с мыши и клавиатуры.
    • gdi32.dll — графический интерфейс устройств (Graphics Device Interface), шрифты, примитивы рисования.
    • advapi32.dll — реестр Windows, службы (Services), учетные записи и токены безопасности.
  2. Native API (ntdll.dll): низкоуровневая прослойка пользовательского режима. Содержит функции с префиксами Nt... и Zw... (NtCreateFile, NtAllocateVirtualMemory). Именно функции ntdll.dll выполняют инструкцию syscall для перехода в режим ядра. Этот слой считается внутренним и не подлежит прямому вызову в обычном прикладном софте.
  3. Ядро Windows (ntoskrnl.exe): исполняет системные службы, взаимодействует с диспетчером объектов (Object Manager), диспетчером памяти и драйверами через уровень абстракции от оборудования (HAL, hal.dll).

center

📌 Слайд 12: Особенности WinAPI: Дескрипторы HANDLE и Unicode

В WinAPI ключевую роль играют две системные концепции:

1. Дескрипторы ресурсов (HANDLE)

В отличие от целочисленных файловых дескрипторов Linux (int fd), в Windows системные ресурсы идентифицируются непрозрачным типом HANDLE. HANDLE представляет собой псевдоуказатель (индекс во внутренней таблице дескрипторов процесса).

Через HANDLE адресуются:

  • файлы и каталоги,
  • процессы и потоки,
  • объекты синхронизации (мьютексы, семафоры, события),
  • сопоставления памяти (file mappings),
  • токены доступа.

После завершения работы объект ядра обязательно закрывается системным вызовом CloseHandle(h). Неосвобожденный дескриптор приводит к утечке ресурсов в ядре.

2. Поддержка кодировок ANSI и Unicode

Функции WinAPI, принимающие строки, существуют в двух вариантах:

  • Вариант A (ANSI): однобайтовые строки char*, работающие через текущую кодовую страницу системы (например, CreateFileA).
  • Вариант W (Wide / Unicode): строки UTF-16LE на базе двухбайтового типа wchar_t* (например, CreateFileW).

Внутреннее ядро Windows всегда работает в Unicode (UTF-16). Любой вызов функции с суффиксом A приводит к скрытому выделению памяти, перекодированию строки в UTF-16, вызову соответствующей функции W и освобождению буфера. Современное системное программирование под Windows ведется исключительно с использованием функций Unicode (W).


5. Стратегии обработки системных ошибок

📌 Слайд 13: Обработка системных ошибок в Linux

Системный вызов ядра Linux при возникновении сбоя возвращает отрицательное число в диапазоне от -4095 до -1. Модуль этого числа соответствует стандартизированному коду ошибки POSIX (например, -2 означает -ENOENT, файл не найден).

Библиотечная обертка glibc при получении отрицательного значения выполняет следующие действия:

  1. Инвертирует знак кода ошибки: val = -res.
  2. Записывает этот код в потоко-локальную переменную errno.
  3. Возвращает прикладной программе маркер ошибки — значение -1 (для целочисленных функций) или NULL (для функций, возвращающих указатели).
 1#include <stdio.h>
 2#include <fcntl.h>
 3#include <unistd.h>
 4#include <errno.h>
 5#include <string.h>
 6
 7int main(void) {
 8    int fd = open("/etc/shadow", O_RDONLY);
 9    if (fd == -1) {
10        // Сохраняем errno сразу, пока последующие вызовы его не затерли
11        int err = errno;
12        fprintf(stderr, "Ошибка открытия: %s (код ошибки: %d)\n", 
13                strerror(err), err);
14        return 1;
15    }
16    close(fd);
17    return 0;
18}

Важно: Переменная errno сохраняет свое значение до следующего ошибочного системного вызова. Успешные вызовы не обнуляют errno. В многопоточных программах errno объявляется как макрос, раскрывающийся в вызов функции (*__errno_location()), возвращающей адрес ячейки в локальном хранилище текущего потока (Thread-Local Storage, TLS). Это исключает гонки потоков при параллельной фиксации ошибок.

📌 Слайд 14: Обработка системных ошибок в Windows

В Windows системный уровень ядра возвращает 32-битный статус NTSTATUS (например, 0xC0000034 — STATUS_OBJECT_NAME_NOT_FOUND). Библиотека подсистемы (KernelBase.dll) преобразует его в 32-битный код ошибки Win32 (ERROR_FILE_NOT_FOUND, код 2) с помощью функции RtlNtStatusToDosError.

Код ошибки сохраняется в структуре TEB (Thread Environment Block) текущего потока в поле LastErrorValue. Прикладной код извлекает ошибку вызовом функции GetLastError(), а текстовое описание ошибки формируется системной функцией FormatMessageW:

 1#include <windows.h>
 2#include <stdio.h>
 3
 4int main(void) {
 5    HANDLE hFile = CreateFileW(
 6        L"C:\\non_existent_file.txt",
 7        GENERIC_READ,
 8        FILE_SHARE_READ,
 9        NULL,
10        OPEN_EXISTING,
11        FILE_ATTRIBUTE_NORMAL,
12        NULL
13    );
14
15    if (hFile == INVALID_HANDLE_VALUE) {
16        DWORD errCode = GetLastError();
17        LPWSTR msgBuffer = NULL;
18
19        FormatMessageW(
20            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
21            NULL,
22            errCode,
23            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
24            (LPWSTR)&msgBuffer,
25            0,
26            NULL
27        );
28
29        wprintf(L"Сбой CreateFileW. Код: %lu, Описание: %s", errCode, msgBuffer);
30        LocalFree(msgBuffer);
31        return 1;
32    }
33
34    CloseHandle(hFile);
35    return 0;
36}

center


6. Современный инструментарий системного разработчика

📌 Слайд 15: Сборочные цепочки и компиляторы

Разработка системно-ориентированного ПО предъявляет строгие требования к сборочному инструментарию:

  • GNU Compiler Collection (GCC): промышленный стандарт в мире UNIX/Linux. Отличается глубокой поддержкой низкоуровневых расширений языка Си (встроенный ассемблер __asm__, атрибуты секций __attribute__((section)), указание выравнивания памяти).
  • Clang / LLVM: модульная компиляторная инфраструктура. Обеспечивает детальный статический анализ, интеграцию с санитайзерами памяти (AddressSanitizer, ThreadSanitizer) и быструю генерацию промежуточного представления (LLVM IR).
  • Microsoft Visual C++ (MSVC): основной инструмент экосистемы Windows. Интегрирован с отладчиком Visual Studio, предоставляет богатые директивы #pragma и intrinsic-функции для прямого доступа к инструкциям процессора без ассемблерных вставок (__readgsqword, _mm_pause, _InterlockedIncrement).

Рекомендуемые флаги компиляции системного кода (GCC/Clang):

  • -Wall -Wextra -Wpedantic — строгий контроль предупреждений;
  • -O2 — базовая оптимизация без искажения последовательности обращений к памяти;
  • -fno-omit-frame-pointer — сохранение базового указателя фрейма стека (RBP), необходимое для точной трассировки стека отладчиками и утилитами профилирования;
  • -g3 — включение расширенной отладочной информации DWARF, включая макроопределения.

📌 Слайд 16: Системные отладчики: GDB и WinDbg

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

  1. GDB (GNU Debugger) под Linux:

    • Позволяет подключаться к исполняемому процессу, анализировать файлы аварийных дампов памяти (Core Dumps).
    • Управление точками останова по инструкциям: b *0x401120, layout asm, layout regs.
    • Просмотр памяти по адресам: x/16xg $rsp (вывод 16 восьмибайтовых слов со стека в шестнадцатеричном виде).
    • Дизассемблирование машинного кода: disassemble /r main.
  2. WinDbg (Windows Debugger):

    • Профессиональный инструмент анализа пользовательского режима и режима ядра Windows.
    • Подключение отладочных символов с сервера символов Microsoft (.sympath srv*https://msdl.microsoft.com/download/symbols).
    • Анализ структур ядра и структур подсистем: !peb (Process Environment Block), !teb (Thread Environment Block), !gle (вывод последней ошибки GetLastError).
    • Анализ аварийных дампов падения (minidump, memory.dmp).

📌 Слайд 17: Инструменты динамической трассировки: strace и ProcMon

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

1. Утилита strace в Linux

Использует системный вызов ядра ptrace. Перехватывает каждый вход в системный вызов и выход из него, расшифровывая аргументы, строковые буферы, битовые маски и коды ошибок:

1# Запуск трассировки команды ls с выводом только файловых операций
2strace -e trace=openat,read,write,close ls -la
3
4# Подключение к уже работающему процессу по его PID с таймстемпами
5strace -tt -p 14205 -o trace_output.log
6
7# Сбор статистики по времени и количеству системных вызовов
8strace -c ./my_program

Пример протокола трассировки:

1openat(AT_FDCWD, "config.json", O_RDONLY) = 3
2read(3, "{\n  \"host\": \"localhost\"\n}\n", 1024) = 28
3close(3)                                = 0
4write(1, "Config loaded\n", 14)         = 14

2. Process Monitor (ProcMon) в Windows

Графическая утилита из состава Microsoft Sysinternals Suite. Работает через специализированный драйвер файловой системы и фильтр системных событий ядра. Позволяет в реальном времени протоколировать:

  • все обращения к файловой системе (CreateFile, ReadFile, CloseFile);
  • операции с реестром Windows (RegOpenKey, RegQueryValue);
  • операции с процессами, потоками и сетевыми сокетами;
  • стек вызовов (Call Stack) вплоть до конкретного модуля и адреса в памяти.

📌 Слайд 18: Резюме лекции

Основные итоги:

  1. Нативный API обеспечивает прямое взаимодействие прикладных программ с ядром вычислительной системы без промежуточных виртуальных сред.
  2. Аппаратные кольца защиты (Ring 0 и Ring 3) в связке с блоком MMU гарантируют изоляцию памяти и предотвращают сбои оборудования из-за ошибок в прикладных процессах.
  3. Эволюция системных вызовов перешла от медленных программных прерываний (int 0x80) к быстрым аппаратным инструкциям (syscall/sysret), настроенным через модельно-зависимые регистры (MSR).
  4. Linux предоставляет стабильный и документированный интерфейс прямых системных вызовов (System V ABI), обернутый библиотекой Си.
  5. Windows скрывает системные вызовы за фасадом стабильных подсистемных DLL (WinAPI), изолируя разработчика от нестабильного Native API ядра.
  6. Обработка системных ошибок в Linux опирается на потоко-локальную переменную errno, а в Windows — на поле LastErrorValue в структуре TEB, опрашиваемое функцией GetLastError().

📌 Слайд 19: Вопросы для самопроверки

  1. В чем заключается принципиальное различие между понятиями API и ABI?
  2. Какие аппаратные механизмы процессора и MMU предотвращают доступ пользовательской программы к адресному пространству ядра?
  3. Почему инструкция syscall выполняется существенно быстрее программного прерывания int 0x80?
  4. Какие регистры процессора используются для передачи номера вызова и аргументов в Linux на архитектуре x86-64?
  5. Почему корпорация Microsoft не рекомендует напрямую вызывать системные функции из ntdll.dll?
  6. Что представляет собой дескриптор HANDLE в Windows и почему его необходимо закрывать?
  7. Каким образом потокобезопасность переменной errno в Linux обеспечивается на многопоточных системах?
  8. Для решения каких задач системный инженер применяет утилиты strace и Process Monitor?

📌 Слайд 20: Литература и рекомендуемые ресурсы

Литература по курсу:

  • Современные операционные системы (4-е изд.) — Таненбаум Э., Бос Х. СПб.: Питер, 2021. 1119 с.
  • Устройство и функционирование OC Windows. Практикум к курсу «Операционные системы» — Коньков К. А. М.: Бином, 2008. 208 с.
  • Операционные системы (2-е изд.) — Гордеев А. В. СПб.: Питер, 2009. 415 с.
  • Системное программирование: методические указания по выполнению лабораторных работ — Бизюк А. Н., Соколова А. С. Витебск: УО «ВГТУ», 2024.
  • Официальная документация Windows API — https://learn.microsoft.com/en-us/windows/win32/api/
  • Справочник системных вызовов Linux (man-pages) — https://man7.org/linux/man-pages/dir_section_2.html
02. Процессы и задания в прикладном … →