Writing a Kernel in C
by Tim Robinson,
Introduction
Написание собственной ОС может длиться недели и месяцы .
Но начинать можно , не имея даже особо специфических навыков в С-программировании.
The C Programming Language
Первоначально язык C разрабатывался как низкоуровневый язык .
Компиляция С-программы мало отличается от кода , написанного на ассемблере ,
в том смысле , что си-шный код в конечном итоге транслируется во все тот же ассемблер .
Tools
Существуют 2 главных компонента , транслирующих С-код в машинный -
компилятор и линкер . БОльшую работу выполняет компилятор .
Есть компиляторы , которые генерят непосредственно исполняемые файлы ,
но как правило они вызывают линкер неявно.
Линкер обьединяет все обьекты и настраивает ссылочные связи между ними .
Линкер позволяет писать код внутри одного проекта на нескольких языках .
Для написания ОС в максимальной степени подходят GNU gcc и ld linker, потому что:
* | ld поддерживает любой исполняемый формат |
* | gcc поддерживается практически на всех известных процах |
GNU для различных операционных систем :
MS-DOS - DJGPP, Windows - Cygwin.
GCC по умолчанию входит во все линуксовые дистрибутивы .
Различные гну-шные тулзы полезны при разработке ОС :
пакет binutils включает утилиту objdump , позволяющую исследовать внуренности
исполняемых файлов и дизассемблировать их .
Наиболее популярными форматами сейчас являются юниксовый
ELF и виндовый PE , поскольку они поддерживаются большинством линкеров .
ELF попроще , PE помощнее . Оба хорошо документированы .
Есть еще один бинарный формат - COFF , который более специфичен .
В качестве альтернативы можно для ядер использовать flat binary format -
то бишь отсутствие всякого формата . Ld генерит т.н. raw code , примером которого
может служить MS-DOS-кий .COM format.
Недостатком всех основных компиляторов является то , что по умолчанию используется
flat 32-битное адресное пространство .
Поскольку они не используют far-указатели (48-bit seg16:ofs32) ,
это усложняет написание кода .
Watcom кстати до сих пор обещает написать компилятор OpenWatcom , который теоретически
должен поддерживать дальние указатели для 32-bit protected mode.
Есть еще несколько замечаний по поводу работы gcc и ld .
Например , gcc похоже ложит символьные строки , используемые внутри функций ,
выше самих функций . Рассмотрим пример :
int main(void)
{
char *str = "Hello, world", *ch;
unsigned short *vidmem = (unsigned short*) 0xb8000;
unsigned i;
for (ch = str, i = 0; *ch; ch++, i++)
vidmem[i] = (unsigned char) *ch | 0x0700;
for (;;)
;
}
Код должен вывести строку “Hello, world” в видео-память .
После компиляции строка окажется выше функции main , и если это
залинковать с flat binary format и запустить, то ничего путного не выйдет .
Победить это можно так :
* |
Написать функцию , которая будет вызывать main() и затем halt.
В стартовой функции не должно быть символьных строк .
|
* | Использовать опцию gcc
-fwritable-strings. Строки будут выведены в data-сегмент .
|
В обычном мире пользовательских программ роль функции ,
которая вызывает функцию main() , играет crt0 .
Run-Time Library
Основную часть в написании собственной ОС займет переписывание
run-time library, которая еще известна как libc.
RTL уже является частью компилятора .
Каждый вендор предлагает использовать различные RTL внутри одной OS:
Microsoft Visual C++ предлагает различные библиотеки для различных
вариантов debug/multi-threaded/DLL, MS-DOS как известно предлагают
6 различных моделей памяти .
Но для начала надо будет определиться с одним вариантом ран-тайм библиотеки .
Эта библиотека должна будет соответствовать стандарту ISO C .
Если вы напишите нестандартную библиотеку , все ваши программы будут
непортабельны на другие ОС .
Существует большое количество си-шных функций , которые платформенно-независимы .
например , большинство <string.h> и <wchar.h> могут быть просто скопированы .
Другие функции наоборот зависят от версии ядра , например
<stdio.h> , даже printf() . По поводу printf():
большинство си-шных библиотек используют шаблон printf() для генерации аналогичных
fprintf(), sprintf() и т.д. Нужно уметь пользоваться функциями , которые
посылают символьный вывод на абстрактный интерфейс типа stream.
RTL несет ответственность не только за переносимость приложений ,
но и за нормальную работу ядра . Такие вещи , как strcpy() или malloc())
должны быть реализованы максимально просто и эффективно .
Они должны быть доступны любому драйверо-писателю .
OS-Specific Support
С точки зрения ядра си ,вообще говоря, неидеален в плане получения доступа к железу .
Это приводит к тому , что приходится либо делать инлайн ,
либо писать целые куски на чистом ассемблере .
Поддержка инлайна существует для gcc для всех платформ .
Чего нельзя сказать ни о Visual C++ , ни тем более о Borland C++.
Gcc-инлайн будет пожалуй что помощнее- например , в нем можно явно
указать , какие регистры можно модифицировать внутри инлайна .
Ассемблерный код может потребоваться и в user mode для вызова системных функций .
Можно поместить вызовы прерываний прямо в RTL (как в MS-DOS ),
или создать отдельную библиотеку (как в Windows NT ntdll.dll).
C++ in the Kernel
Точка зрения в линуксе на использование плюсов известна .
В то же время некоторые целиком пишут оси на плюсах .
При использовании плюсов прийдется потратить много усилий на
промежуточный фрейм-ворк , помня о том , что плюсы имеют ограничения
в плане переносимости .
Вам прийдется постоянно помнить о дилемме new - delete.
Например , если вам понадобится какой-то глобальный обьект класса ,
нужно будет очень хорошо разобраться , как это делается у вашего
вендора , возможно прийдется изучить изнутри что-то типа crt0.c.
Если вы не дай бог захотите использовать в ядре try/catch ,
возможно вам тогда прийдется изобретать колесо , поскольку это уже
реализовано в вашем нативном компиляторе , с помощью которого
вы будете собирать свое плюсовое ядро .
Использование плюсовых exception handling и virtual classes вряд ли
будет оправдано по причине их тормознутости .
Ядро должно работать максимально быстро , а прелести дизайна имеют к этому мало отношения .
Итого
Написание ОС на языке C , прямо скажем , занятие гораздо более продуктивное ,
нежели на ассемблере . Такой компилятор как gcc - это безусловно выбор номер 1 .
И это правильно .
|
screw | Про виртуальные ф-ии в ядре написан полный бред. 2007-08-02 15:41:45 | |
|
|
|