
От user space до kernel space: как системное и прикладное ПО работают вместе
с помощью нейросети
Вы когда-нибудь задумывались, как компьютерное «железо», которое оперирует нулями и единицами, стабильно и одновременно выполняет множество сложных задач? Причем в результате мы получаем не странные комбинации символов, а информацию в понятном формате — будь то веб-страница, фотография, комплексный отчет или целый игровой мир.
Никита Косырев, инженер-программист группы системного ПО процессорного кластера YADRO, рассказал, как это становится возможным на программном уровне. В этой статье он подробно объясняет, как взаимодействуют разные классы ПО, что для этого требуется от процессора и когда разница между системным и прикладным ПО не так очевидна, как кажется на первый взгляд.
- что разрешает процессор прикладному и системному ПО
- что такое MMU и зачем нужен этот блок
- что такое bash и какому виду ПО он относится
- как перейти между user space и kernel space
- почему в кофемашине нет отдельного прикладного ПО
В своей первой статье я описывал, что такое системное и прикладное ПО. Если совсем коротко, то прикладное ПО решает задачи пользователя — это офисный софт, браузеры, мессенджеры, игры
Как центральный процессор разделяет системное и прикладное ПО
Работа прикладной программы с системным слоем, ядром ОС, всегда строго регламентирована и задействует специальные возможности центрального процессора (ЦП). Для поддержки полноценных операционных систем вроде Linux, macOS или Windows центральный процессор должен обладать двумя функциональными возможностями: разделять режимы работы по уровню привилегий и блокировать управление памятью.
Разделение режимов работы
Прикладной софт всегда работает в пользовательском режиме ЦП, где доступен только ограниченный набор инструкций архитектуры (unprivileged ISA). Ядро операционной системы работает в привилегированном режиме, или режиме супервизора, где доступны как непривилегированные, так и привилегированные инструкции (unprivileged и privileged ISA).
Блок управления памятью
Блок управления памятью (Memory Management Unit, MMU) нужен для трансляции виртуального адреса текущего активного процесса (прикладной программы) в физический адрес на шине памяти с учетом атрибутов доступа.

Как это устроено? Для каждого процесса ядро ОС создает в оперативной памяти отдельную многоуровневую страничную таблицу. Она хранит соответствия между виртуальными и физическими страницами памяти, а также атрибуты доступа: валидность, права на чтение, запись и исполнение, принадлежность к пространству пользователя или ядра
- управляющие регистры (ячейки внутренней памяти процессора), в которых задается режим работы MMU и базовый адрес страничной таблицы текущего активного процесса;
- блок TLB (Translation Lookaside Buffer, буфер ассоциативной трансляции) — кеш для ускорения трансляции адресов;
- обходчик страничных таблиц (page walker) — аппаратный блок для обхода страничной таблицы процесса в оперативной памяти и обновления TLB без участия остальных блоков процессора, активируется в ситуации TLB-промаха, то есть если кеш вдруг оказался бесполезен.
Всё это позволяет операционной системе гибко управлять памятью. В частности, MMU дает возможность:
- создавать для каждого процесса собственное изолированное виртуальное адресное пространство пользователя — тот самый user space);
- создавать виртуальное адресное пространство ядра ОС (kernel space) и защищать его с помощью атрибутов от несанкционированного доступа со стороны прикладного софта;
- по необходимости отображать общие физические страницы памяти на виртуальные страницы разных процессов — так обеспечивается возможность межпроцессного взаимодействия (Inter-process communication, IPC);
- при нехватке оперативной памяти выполнять «откачку» (swapping) редко используемых страниц виртуальной памяти на накопитель и восстанавливать их обратно по мере необходимости.
Переход между user space и kernel space
Ядро ОС может в любой момент переключать работу процессора из режима супервизора в пользовательский, а также настраивать режим работы MMU с помощью специальных привилегированных инструкций архитектуры. А вот обратный переход — из пользовательского в режим супервизора — возможен только при наступлении одного из трех событий:
- выполнение специальной непривилегированной инструкции запроса системного вызова;
- исключительная ситуация при исполнении программной инструкции — неизвестный опкод инструкции, доступ к невалидному региону виртуальной памяти процесса
и т. д. ; - получение асинхронного сигнала прерывания от периферийного устройства.
При наступлении любого из этих событий процессор аппаратно переключается в привилегированный режим, а управление передается на строго определенные адреса — в обработчики пространства ядра. В разных архитектурах процессора для разных событий заданы либо свои векторы (адреса) обработки, либо используется единый обработчик для всех типов событий.
Как работают системные вызовы
Чтобы прикладная программа могла безопасно запрашивать услуги у ядра ОС, оно предоставляет специальный интерфейс — системные вызовы (system calls). Реализация системных вызовов зависит от архитектуры процессора и определяется правилами ABI (Application Binary Interface, двоичный интерфейс приложения). В упрощенном виде работа системного вызова выглядит так:
- Подготовка аргументов. Прикладная программа в пользовательском режиме записывает номер нужного системного вызова и его аргументы в строго определенные регистры общего назначения согласно правилам ABI.
- Инициирование системного вызова. Выполняется специальная инструкция системного вызова — например, svc для процессоров архитектуры ARM или ecall для RISC-V.
- Переключение режима. Процессор переключается в режим супервизора и запускает специальную процедуру-обработчик в пространстве ядра ОС.
- Сохранение контекста. Процедура-обработчик сохраняет текущее состояние регистров процессора (контекст потока) в системный стек. Затем считывает номер вызова и перенаправляет запрос конкретной внутренней функции ядра вместе с аргументами.
- Выполнение обработки вызова. Ядро ОС обрабатывает системный вызов. На этом этапе оно передает управление своим подсистемам или напрямую обращается к драйверам устройств.
- Формирование результата. После обработки управление возвращается в исходную процедуру-обработчик. Она восстанавливает сохраненный контекст потока из стека и записывает результат (или код статуса) системного вызова в специальный регистр общего назначения.
- Возврат в user space. Ядро ОС выполняет привилегированную инструкцию возврата — например, eret для ARM, sret для RISC-V. Процессор переключается обратно в пользовательский режим, а прикладная программа продолжает свою работу, считывая результат из регистра.
Чтобы прикладные программы было проще писать и переносить между платформами, архитектурно-зависимые особенности системных вызовов (правила ABI) обычно скрывают внутри стандартной библиотеки языка C (glibc, musl). Она предоставляет прикладным программам унифицированные функции-обертки, реализующие API (Application Programming Interface, программный интерфейс приложения) к ядру ОС. Кроме того, в состав стандартной библиотеки языка C входят:
- динамический загрузчик (ld.so в Linux), отыскивающий и отображающий файлы разделяемых библиотек в виртуальное адресное пространство процесса;
- код инициализации прикладной программы в пользовательском пространстве, работающий перед началом выполнения прикладной логики — функции main, а также код завершения процесса, отрабатывающий после возврата из функции main ().
По этой причине стандартную библиотеку языка C и относят к классу системного ПО, хотя ее код исполняется в пользовательском режиме работы ЦП. Главная задача библиотеки — предоставлять помощь прикладным программам при их инициализации, доступе к услугам ядра ОС и завершении работы. Напрашивается важный вывод.
Граничный случай системного ПО: оболочка bash
Командная оболочка bash — отличная иллюстрация для тезиса выше. Это системное ПО, которое работает исключительно в пользовательском режиме работы процессора. Она запускается ядром ОС как обычный процесс и не имеет прямого доступа к оборудованию, но выполняет важнейшие системные функции.

Почему bash относится именно к классу системного ПО? На то есть три ключевые причины:
Управление системными ресурсами и процессами. Оболочка bash служит интерфейсом для управления операционной системой. С ее помощью пользователь или скрипты (последовательности команд) запускают, останавливают и контролируют процессы. Bash организует перенаправление потоков ввода/вывода данных и связывает разные программы через конвейеры.
Автоматизация администрирования через shell-скрипты. Скрипты на bash используются для настройки операционной системы, автоматизации резервного копирования, обновления пакетов, управления учетными записями пользователей и мониторинга состояния сервера. Это базовые задачи администрирования, необходимые для поддержания работоспособности всей системы.
Отсутствие прикладной ценности для пользователя. Прикладное ПО решает конечные задачи пользователя, не связанные напрямую с ОС. Оболочка bash сама по себе не обрабатывает фотографии и не воспроизводит аудиофайлы. Она предназначена для запуска и управления работой соответствующих прикладных программ — например, через передачу параметров командной строки.
Помимо bash и упомянутой выше glibc, к «непривилегированному системному ПО» относятся:
- системы инициализации пользовательского пространства (systemd, SysV init);
- фоновые системные службы (udevd, rsyslogd, auditd);
- дисплейные серверы, координирующие работу графического интерфейса (Xorg, Weston);
- инструменты виртуализации и контейнеризации (runc/containerd, QEMU);
- утилиты настройки аппаратного обеспечения (ALSA utils, ip/ifconfig, hdparm, lspci);
- пакетные менеджеры — инструменты для автоматической установки, обновления, настройки и удаления программного обеспечения и библиотек (apt/dpkg, dnf/rpm).
Встраиваемое системное ПО: прошивка кофемашины
Граница между прикладным и системным ПО становится еще более размыта в современных бытовых устройствах. Возьмем для примера прошивку (firmware) кофемашины. На первый взгляд, ее нельзя отнести к системному программному обеспечению, ведь она решает конечные задачи человека как прикладной софт. Что может быть более «конечным» и «прикладным», чем чашка горячего эспрессо утром?

Однако с точки зрения компьютерной инженерии прошивка встраиваемых систем (embedded systems) — это чистокровное системное ПО. И вот почему.
Взаимодействие с физическими объектами, а не с логической абстракцией
Прикладные программы работают с абстрактными сущностями: текстом, пикселями, финансовыми транзакциями или базами данных. Они изолированы от аппаратной части операционной системой.
Для прошивки кофемашины абстракции кофе как кулинарного объекта не существует. Программа оперирует исключительно физическими ресурсами и параметрами:
- Считывание и оцифровка напряжения на аналого-цифровом преобразователе (АЦП) при опросе терморезистора.
- Расчет скважности ШИМ-сигнала для управления мощностью ТЭН (электронагревателя).
- Подсчет импульсов от датчика протока воды (флоуметра) для точного дозирования жидкости.
- Запись логической единицы в регистры портов ввода-вывода (GPIO) для открытия симисторов и реле.
Здесь прослеживается фундаментальный признак системного ПО: перевод метрик физических устройств в логические данные программы и управление аппаратными ресурсами в реальном времени.
Слияние уровней абстракции в монолитную программу
Из-за строго однозадачного характера работы и малого объема вычислений в классических кофемашинах применяются недорогие микроконтроллеры с единственным привилегированным режимом работы ЦП. Поэтому системный и прикладной уровни здесь вынужденно объединены в одну монолитную программу, выполняемую на «голом железе».
Такая программа сама берет на себя базовые функции операционной системы: инициализацию периферии, распределение памяти и обработку аппаратных прерываний от кнопок и датчиков. Ее системные функции тотально доминируют над прикладной логикой. Без их реализации устройство останется бесполезной грудой пластика и меди.
Стоит отметить, что в наиболее продвинутых, «умных» кофемашинах с сенсорными экранами разделение уровней возвращается: там может работать FreeRTOS или ядро Linux как системный слой, поверх которого запускается графическое прикладное приложение.
Несмотря на хитрые случаи в ракурсе разделения прикладного и системного ПО, именно строгость этого разделения во многом обеспечивает стабильную и бесшовную работу современных цифровых устройств, к которой мы все привыкли.



