Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
 iakovlev.org 
 OS
 osjournal 
 Protected Mode 
 Hardware 
 Kernels
  Dark Fiber
  BOS
  QNX
  OS Dev
  Lecture notes
  MINIX
  OS
  Solaris
  История UNIX
  История FreeBSD
  Сетунь
  Эльбрус
NEWS
Последние статьи :
  Тренажёр 16.01   
  Эльбрус 05.12   
  Алгоритмы 12.04   
  Rust 07.11   
  Go 25.12   
  EXT4 10.11   
  FS benchmark 15.09   
  Сетунь 23.07   
  Trees 25.06   
  Apache 03.02   
 
TOP 20
 Linux Kernel 2.6...5170 
 Trees...940 
 Максвелл 3...871 
 Go Web ...823 
 William Gropp...803 
 Ethreal 3...787 
 Gary V.Vaughan-> Libtool...773 
 Ethreal 4...771 
 Rodriguez 6...766 
 Ext4 FS...755 
 Clickhouse...754 
 Steve Pate 1...754 
 Ethreal 1...742 
 Secure Programming for Li...732 
 C++ Patterns 3...716 
 Ulrich Drepper...698 
 Assembler...695 
 DevFS...662 
 Стивенс 9...649 
 MySQL & PosgreSQL...632 
 
  01.01.2024 : 3621733 посещений 

iakovlev.org

JeffOS

Автор - jwthomp@cu-online.com

Для начала вспомним формат дескриптора gdt:


 63 - 56    4-й байт базового адреса 
 55         Granularity Bit
 54         Default Bit
 53         0
 52         Available for Use (free bit)
 51 - 48    старшие биты ограничения
 47         Segment Present Bit
 46 - 45    Descriptor Privilege Level
 44         System Bit
 43         Data/Code Bit
 42         Conforming Bit
 41         Readable bit
 40         Accessed bit
 39 - 32    3-й байт базового адреса
 31 - 24    2-й байт базового адреса
 23 - 16    1-й байт базового адреса
 15 - 8     2-й байт ограничения
 7 - 0      1-й байт ограничения
 
Здесь : базовый адрес - это адрес самой таблицы. Ограничение - число , измеряемое либо в байтах , либо в килобайтных страницах. Если Granularity Bit=0 , то размер сегмента может быть произвольным в килобайтах. Если Granularity Bit=1 , то размер сегмента кратен 4 килобайтам.

default bit - определяет тип кодового сегмента - 32-битный или 16-битный. Если он равен 0 - то 16-битный.

present bit - устанавливается в единицу , если сегмент загружен.

system bit - устанавливается в 0 , если сегмент принадлежит самой ОС.

data/code bit - указывает на кодовый сегмент либо сегмент данных.

readable bit - сегмент на чтение либо выполнение

Первая стадия загрузки ядра : смотрим каталог /boot. mainboot.c -> функция "_start" - в ее начале идет вызов kinit_MemMgmt (memboot.c). Здесь происходит попытка определения количества памяти в системе - val = kmemcountboot();.. Далее происходит инициализация page tables - kinit_pageboot. Пэйджинг - механизм захвата процессом памяти. Сейчас будет создана т.н. "виртуальная память". Каждый раз , когда происходит выделение памяти , процессор смотрит в эту таблицу.

page tables организованы по следующему принципу : вначале идет т.н. page directory , которая разбита на 1024 частей.Каждая такая часть устроена следующим образом :


 31 - 12    базовый адрес
 11 - 9     не используется
 8          0
 7          Page Size Bit
 6          0
 5          Accessed Bit
 4          Page Cache Disable Bit
 3          Page Write Through Bit
 2          User/Supervisor Bit
 1          Read/Write Bit
 0          Page Present Bit
базовый адрес состоит из 2-х частей - индекса и физического адреса самой таблицы. Page Size Bit - указывает на размер страницы (Bit = 0 для 4kb или Bit = 1 для 4MB). accessed bit - устанавливается при доступе к ней. Page Cache Disable Bit и Page Write Bit не используются. User/Supervisor Bit - устанавливает права доступа. Если бит=0 , к странице имеют доступ привилегированные уровни 0, 1, 2 Если бит=1 , к странице имеют доступ привилегированные уровни 0, 1, 2, 3. Read/Write - устанавливает право на запись для пользовательского процесса. Present Bit - представлена ли страница в памяти.

Т.о. page directory ссылается на тысячу таблиц page tables , каждая из которых в свою очередь имеет следующую структуру :


 31 - 12    Page Base Address
 11 - 9     Unused (Free bits)
 8 - 7      0
 6          Dirty Bit
 5          Accessed Bit
 4          Page Cache Disable Bit
 3          Page Write Through Bit
 2          User/Supervisor Bit
 1          Read/Write Bit
 0          Page Present Bit
 
 
Все Kernel pages установлены с правами Supervisor, Read/Write, Page Present .

Функция kinit_pageboot(memboot.c) инициализирует три page tables. Первая - размером 1 метр - это сама page directory. Вторая имеет размер 4 метра и будет использоваться пользовательскими процессами. Третья является указателем на верхнюю память. Первый адрес в page directory будет указывать на нижний адрес в пространстве этих 4 метров. Адрес page directory помещается в регистр cr3. В регистре cr0 устанавливается paging bit. Первая стадия загрузки ядра завершена.

Вторая стадия начинается с фуункции _start_kernel(main.c). Выводим на экран "Main kernel loaded.". Инициализируем память - kinit_page()(mem.c). Вначале проинициализируем битовое поле PMAT размером 2048 байт. Каждый бит этого поля представляет одну страницу физической памяти. Если бит=1 , соответствующая страница памяти выделена.

Функция kinit_tmpuser инициализирует память для пользовательских процессов, эта память начинается с адреса 20000h.

Функция kinit_task()(task.c) инициализирует стартовый процесс ядра. Она вызывает 2 функции : первая - kinit_gdt() - инициализирует GDT. Инициализируются 5 сегментов : код ядра , данные ядра , данные ядра 2 , код пользователя , данные пользователя.

Вторая функция - kinit_ktask() - инициализирует задачу , управляемую ядром. Обнуляется TaskList - сюда включается список всех задач в системе. Выделяем 4 килобайта для kernel task segment. Этот сегмент добавляем в GDT. Этот Task segment имеет следующую структуру :


 struct TSS {
     ushort link;            // set to 0
     ushort unused0;
     ulong esp0;             // set to the end of the task segment page
     ushort ss0;             // set to SEL_KDATA (Kernel Data segment)
     ushort unused1;
     ulong esp1;             // set to 0
     ushort ss1;             // set to 0
     ushort unused2;
     ulong esp2;             // set to 0
     ushort ss2;             // set to 0
     ushort unused3;
     ulong cr3;              // set to the physical address of this tasks page 
                             // tables
     ulong eip;              // set to the entry point to this tasks code
     ulong eflags;           // set to 0x4202
     ulong eax, ecx, edx, ebx, esp, ebp, esi, edi; // set to garbage values
     ushort es;              // set to SEL_KDATA (Kernel data segment)
     ushort unused4;
     ushort cs;              // set to SEL_KCODE (Kernel code segment)
     ushort unused5;
     ushort ss;              // set to SEL_KDATA
     ushort unused6;
     ushort ds;              // set to SEL_KDATA
     ushort unused7;
     ushort fs;              // set to SEL_KDATA
     ushort unused8;
     ushort gs;              // set to SEL_KDATA
     ushort unused9;
     ushort ldt;             // set to 0
     ushort unused10;
     ushort debugtrap;       // set to 0
     ushort iomapbase;       // set to 0
 };
 
Параметры espx и ssx используются для хранения указателя на стек . Параметр cr3 используется для хранения указателя на физический адрес страницы памяти. Понятно , что каждая задача имеет уникальный набор страниц. Параметры eax, ecx, edx, ebx, esp, ebp, esi, edi; используют свои наборы. В данной версии LDT не используется. Поля из этой структуры будут загружаться каждый раз в регистры , когда процессор будет переключаться на данную задачу.Когда процесс становится неактивным , соответственно происходит обратное сохранение.

При создании task segment в GDT нужно проинициализировать указатель на него - 8-байтный дескриптор. Его формат :


 63 - 56    Fourth Byte of Base Address
 55         Granularity Bit
 54 - 53    0
 52         Available for use (free bit)
 51 - 48    Upper Nibble of Size
 47         Present in Memory Bit
 46 - 45    Descriptor Privilege Level
 44         System Built
 43         16/32 Bit
 42         0
 41         Busy Bit
 40         1
 39 - 32    Third Byte of Base Address
 31 - 24    Second Byte of Base Address
 23 - 16    First Byte of Base Address
 15 - 8     Second Byte of Segment Size
 7 - 0      First Byte of Segment Size
Его структура похожа на кодовый сегментный дескриптор , за исключение 16/32 bit, и Busy Bit.

Создаем пользовательский процесс - task_create. Он размещается по адресу 0x20000. Его стек размещается в 0x2107c. Этот процесс имеет привилегии ядра и мало похож на обычный пользовательский процесс. Процесс добавляется в список задач.

После создания 2-х процессов проинициализируем таблицу прерываний - kinit_idt(int.c). Большинство прерываний имеет указатель на заглушку. Таблица загружаетя в idt-регистр и разрешаются прерывания. Врапперы прерываний лежат в jump.S.

Следующий шаг - переключение задач - swtch()(task.c). При этом извлекается селекторный адрес и процессор переключает задачи.

Исходники лежат тут

Оставьте свой комментарий !

Ваше имя:
Комментарий:
Оба поля являются обязательными

 Автор  Комментарий к данной статье