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

Пишем операционную систему Триалогия - Программы ring 3: оконные приложения, инструменты оболочки и как они стартуют

В прошлой части речь шла о командах, которые работают внутри самого ядра. Однако они составляют лишь небольшую часть всей системы. Почти всё, с чем пользователь сталкивается каждый день, живёт в ring 3. Терминал, окно настроек, небольшой сетевой пингер, файловые утилиты - именно они определяют повседневную работу. Таких программ уже около десятка. Каждая служит наглядным результатом всех предыдущих слоёв, о которых рассказывали прошлые главы. Без загрузчика ELF, библиотек, оконного менеджера и системы событий ни одна из них не смогла бы запуститься. Сначала полезно посмотреть на картину целиком. Все программы удобно разделить на четыре семейства в зависимости от их назначения. Первую группу образуют оконные приложения. Они создают собственное окно, появляются на рабочем столе и чаще всего стартуют двойным щелчком по значку Icon. В этот набор входят терминал term, settings, менеджер устройств devmgr, файловый менеджер files, а также несколько небольших демонстрационных программ вроде ан
Оглавление

В прошлой части речь шла о командах, которые работают внутри самого ядра. Однако они составляют лишь небольшую часть всей системы. Почти всё, с чем пользователь сталкивается каждый день, живёт в ring 3. Терминал, окно настроек, небольшой сетевой пингер, файловые утилиты - именно они определяют повседневную работу. Таких программ уже около десятка. Каждая служит наглядным результатом всех предыдущих слоёв, о которых рассказывали прошлые главы. Без загрузчика ELF, библиотек, оконного менеджера и системы событий ни одна из них не смогла бы запуститься.

Зоопарк

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

Четыре семейства программ ring 3
Четыре семейства программ ring 3

Первую группу образуют оконные приложения. Они создают собственное окно, появляются на рабочем столе и чаще всего стартуют двойным щелчком по значку Icon. В этот набор входят терминал term, settings, менеджер устройств devmgr, файловый менеджер files, а также несколько небольших демонстрационных программ вроде анимации matrix или часов time.

Во вторую группу входят утилиты оболочки. Они продолжают традиции классической командной строки: ls, cat, copy, rm, mkdir, touch, ps. Здесь же находится и сама оболочка trish. Отдельное окно таким программам не требуется. Весь ввод и вывод проходят через терминал.

Отдельное место занимают сетевые инструменты. К ним относятся ipinfo, ipping, arpshow и dnsshow. Все они обращаются к NetworkManager и получают информацию через него.

Последнюю группу составляют тестовые программы. Среди них можно найти fstest, kcall_test, malloc_test и другие небольшие проверки. Их задача заключается не в повседневной работе, а в подтверждении того, что очередной механизм действительно выполняет свою функцию. Они были написаны специально, что бы протестировать определенный механизм системы. Скорей всего, я их уберу из образа. А пока они там...

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

Две базовые формы

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

Оконная программа против инструмента терминала
Оконная программа против инструмента терминала

Оконное приложение подключает libwm и libterm, открывает окно, после чего входит в цикл обработки событий. Программа получает сообщения от системы, реагирует на нажатия клавиш и клики мыши, рисует новое изображение и передаёт готовый кадр оконному менеджеру через present. Работа продолжается до получения события закрытия окна.

int main() {
w = libterm_open(...); // открыть окно
while (running) {
while (poll_event(&ev)) {
if (ev == CLOSE) running = 0;
if (ev == KEY) handle(ev);
}
draw();
libterm_present(w); // показать изображение
}
}

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

int main(int argc, char** argv) {
data = libvfs_read_file(argv[1]);
write(STDOUT, data, size);
return 0; // готово, без цикла
}

Различия уходят гораздо глубже внешнего вида. Оконное приложение постоянно поддерживает диалог с окружающей средой. Терминальная утилита решает одну конкретную задачу, после чего немедленно завершает выполнение. Несмотря на такую разницу, оба варианта представляют собой обычные ELF-файлы внутри каталога /bin. Загрузчик использует для них один и тот же механизм.

Как они рождаются и стартуют

Теперь можно проследить путь программы от исходного текста на C до запуска внутри системы.

Собрать, положить в образ, запустить
Собрать, положить в образ, запустить

Сборка проходит по одной схеме. Makefile компилирует исходники, подключает необходимые библиотеки - libwm и libterm для оконного приложения либо libvfs для терминальной утилиты. В любом случае компоновщик добавляет небольшую libc и стартовый модуль crt1. На выходе появляется ELF-файл.

Следующий шаг выполняет сценарий copy_kernel.sh. Он переносит готовый исполняемый файл в каталог /bin внутри образа диска. Здесь образ диска не .img файл, а просто структура каталогов и файлов которая отображает 1:1 будущий образ системы. Именно эта структура каталогов определяет, какие программы попадут в систему.

Запуск возможен тремя разными способами. Пользователь может открыть программу двойным щелчком по значку рабочего стола. В таком случае оконный менеджер читает TOML-файл, получает путь к исполняемому файлу и создаёт новый процесс. Другой вариант предлагает оболочка trish: достаточно набрать имя программы, после чего оболочка найдёт её в поисковом пути и выполнит запуск. Существует и третий путь. Одна программа вполне может породить другую. Именно так терминал создаёт процесс оболочки.

Внешне эти сценарии отличаются довольно сильно. Финальная точка у всех одна. Вызов exec_program передаёт управление загрузчику ELF, который загружает исполняемый файл и передаёт выполнение стартовому коду crt1. Значок рабочего стола, команда оболочки и программный запуск служат лишь разными входами в один механизм.

Честное место

Не каждая программа уже достигла одинаковой степени готовности. Особенно хорошо это проявилось после крупных изменений архитектуры. Перенос ядра на кольцо kcall и перемещение оконного менеджера в ring 3 нарушили работу многих компонентов. Каждый случай потребовал отдельного расследования и исправления. Я последовательно проверяю программы, фиксирую найденные проблемы и устраняю их одну за другой. Такое положение вполне естественно для проекта, где нижние уровни ещё продолжают активно меняться.

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

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

Что дальше

На этом описание пользовательского пространства подходит к концу. Мы прошли путь от библиотек до приложений и оболочки. Почти в каждой главе появлялся NetworkManager, поскольку именно через него работают сетевые инструменты. Теперь пришло время заглянуть ещё глубже. Каким образом пакет проходит путь от сетевого кабеля до этого сервиса? Ответ начинается далеко внизу, рядом с аппаратурой. Следующий большой раздел посвящён сетевой подсистеме. Разговор откроют сетевые карты и драйверы для реальных Ethernet-чипов.

Было бы интересно увидеть ваши комментарии и улучшить статьи.

Предыдущая статья · Содержание · Следующая статья

*Система не стоит на месте, поэтому в дальнейшем тексты могут не совпадать с реальным положением