Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
 iakovlev.org 
      Languages 
      Kernels 
      Packages 
      Books 
      Tests 
      OS 
      Forum 
      Математика 
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...2334 
 Trees...1465 
 William Gropp...1422 
 Ethreal 3...1401 
 Ethreal 4...1383 
 C++ Patterns 3...1380 
 Максвелл 3...1368 
 Rodriguez 6...1366 
 Httpd-> История Ap...1366 
 Robert Love 5...1365 
 Go Web ...1365 
 Максвелл 5...1364 
 OS ->Intel Manual 1...1362 
 Ext4 FS...1362 
 K&R 1...1361 
 Kamran Husain...1360 
 Rubni-Corbet -> Глав...1358 
 Perl OOP...1356 
 Erlang...1354 
 Стивенс 9...1353 
 
  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). При этом извлекается селекторный адрес и процессор переключает задачи.

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

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

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

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