Исходники лежат тут
Немного усложним загрузчик : теперь будет поддерживать не один , а 2 процесса .
Первый процесс назовем условно p1 , второй - p2 .
Процесс p1 посылает процессу p2 пароль .
Если пароль правильный , процесс p2 выводит на экран сообщение - "Yes".
В данном проекте используется принцип cooperative multitasking : если один процесс
попадает в цикл , он пожирает все ресурсы , всё остальное при этом останавливается .
Есть альтернатива - reemptive multitasking, - при этом ядро может принудительно переключать
и управлять процессами . 2-й вариант реализовать конечно будет посложнее .
В нашем загрузчике ядра-то собственно и нет - в данном контексте оно и не нужно .
В проекте используется единое адрессное пространство . Процеес p1 грузится по адресу
0x100000 - или 1 мегабайт - самое начало т.н. extended memory . Процесс p2 грузится по адресу
0x200000 . Адресное пространство между этими процессами не используется .
При линковке используется принцип relocation : линкеру явно указывается на прикручивание
процессов p1 и p2 к соответствующим адресам :
$(V)$(LD) -e start -Ttext 0x100000 -Tdata 0x110000 -o $@ $^
Вообще , если глянуть в makefile , можно увидеть , что kernel.img лепится из 3-х слинкованных кусков :
boot
p1
p2
В свою очередь , boot линкуется из start.S + kernel.c
Структура образа kernel.img :
start.S + kernel.c) - загрузчик : хранится в первом секторе
Со 2-го по 33-й секторы хранится образ первого процесса p1
С 34 по 65 сектора хранится образ 2-го процесса p2
Формат - ELF
Порядок загрузки :
биос читает первый сектор , грузит его в память и передает
ему управление .
Выполняется код в start.S , устанавливается защищенный режим ,
инициализируется стек , запускается си-шный код bootmain() из kernel.c.
Загружаются в память образы 2-х процессов , и управление передается
в первый процесс p1 .
Обоим процессам необходим свой стек : для процесса p1 он будет начинаться с адреса 0x200000 ,
для процесса p2 - с адреса 0x300000 .
Взаимодействуют процессы следующим образом :
процесс p1 печатает пароль по одному символу в "pipe buffer" по адресу памяти PIPEBUF.
После этого он ждет , когда p2 его прочитает , и печатает дальше.
Функция _yield передает контроль от одного процесса к другому.
ID-шник процесса - PROCESSID_P
Понятно , что никакой протекции здесь нет :
процесс p1 преспокойно может прочитать пароль из адрессного пространства процесса p2 ,
может поменять его , может вообще поменять код у p2 , да и много чего еще .
В каталоге obj при компиляции генерятся файлы c расширением .sym - в них прописываются
статические адреса функций - например p1.sym :
00100000 T putch
00100025 T start
00100054 T _yield
00100064 t _yield2
00110000 A __bss_start
00110000 A _edata
00110000 A _end
Также там генерятся дизассемблированные файлы с расширением .asm .
PS: Загрузчик у меня работает только под бошем
|
Филипп | Волшебные у Вас публикации! Мне доставляет нестерпимое наслаждение
знакомиться с ними. Есть несколько вопросов:
А как передать управление процессу p1?
Смысл строки __asm __volatile("movl $0x1FFFFC, %esp; ret");
P.S. К сожалению у меня завелся только первый проект с hello word.
С уважением Филипп
2012-06-15 15:39:59 | Яковлев Сергей | Регистр esp используется для инициализации стека - мы отводим первому процессу в его же адресном
пространстве кусок памяти под собственные нужды.
Ядро, собираемое в этой статье, сделано с помощью хакерских технологий - тех же самых, с помощью
которых Линус собирал свои первые ядра. Я помню, что когда писал эту статью, то не смог его загрузить
ни в одном стандартном загрузчике.
2012-06-16 18:10:07 | Филипп | Здравствуйте, Сергей!
Можете ли Вы пояснить, как Вы передаете управление первому процессу p1?
В Вашем коде я вижу только чтение двух процессов с диска. 2012-06-20 11:43:21 | Филипп | Откуда Вы знаете на каких секторах хранится процессы p1 и p2?
Вы заливали на диск командой cat kernel.img > devsdc ?
2012-06-20 12:15:05 | |
|