Rambler's
Top100

Разработка операционных систем. Выпуск 10

Автор: lonesome [TSH/Digital Daemons]
Дата: 7.06.2003
Раздел: Разработка ОС

ПРЕДЫДУЩИЙ ВЫПУСК      СЛЕДУЮЩИЙ ВЫПУСК

Разработка операционных систем

Выпуск 10 от [SUBSCRIBE issue_date]

Сегодня в номере:


Intro

В первых строках этого номера хотелось бы поздравить вас с выходом юбилейного десятого выпуска рассылки. Часть долгого пути уже пройдена (тем, кто к нам присоединился только сейчас, настоятельно рекомендую посмотреть архив рассылки и узнать, чем мы занимались до этого выпуска).

Мы уже вплотную приближаемся к написанию нашей "самой простейшей из всех возможных" системы и пришло время поговорить о ее архитектуре. Вот краткий перечень того, что в ней будет, и чего в ней не окажется :
Поддержка клавиатуры и монитора :)
Многозадачность Обойдемся без нее
Разделение системы и приложений Зачем?
Поддержка различных внешних устройств Смотри первый пункт
Поддержка накопителей на магнитных дисках Слишком сложно
Поддержка RAM-дисков Да
Файловая система Для рамдиска понадобится
Системные вызовы Без них никак
Виртуальная память Пока незачем

Вот такая вот получается хм.. система. У нее есть одно очень большое и никем не превзойденное преимущество - при наличии соответствующих знаний (а они у нас уже есть) ее можно написать за несколько часов. А это ровно в 24 раза меньше, чем обещанные мною несколько дней :)

Что нам осталось реализовать? (tty-устройство было создано в предыдущем выпуске):

Inline Assembler в GCC

При разработке операционной системы, нам придется (раз уж мы перешли на Си) использовать встроенный ассемблер (Inline Assembler) компилятора GCC. Он имеет формат AT&T (в противоположность обычному формату Intel). Это означает, что:

Вот несколько примеров соответствия синтаксиса AT&T синтаксису Intel:
AT&T Intel
movl $0, %eax mov eax, 0
movl $variable, %ebx mov ebx, variable
movw variable, %ebx mov ebx, word [variable]
movb $0, (,%eax,) mov [eax], byte 0
movl %eax, $111(%ebx, %edx, 2) mov [111 + eax + edx*2], eax

Код на ассемблере можно вставить в исходник Си с помощью команды asm(..):
asm("movw %ax, %bx");

Несколько инструкций можно разделить обычным символом новой строки ('\n'):
asm("xorl %eax, %eax \n incl %eax");

GCC constraints или расширенные возможности Inline Assembler'а

По сути дела, constraints - это параметры вызова Inline Assembler'а. В основном они нам понадобятся для того, чтобы связать Inline Assembler с кодом на Си (чтобы использовать одни и те же константы и пр.). Рассмотрим примеры:
asm("lidt 0(,%0,)"::"a"(IDT_REG)); - это означает: поместить значение IDT_REG в регистр EAX (который указывается символом 'a', и выполнить команду lidt 0(,%eax,)

asm("cpuid":"=a"(result_eax),
"=b"(result_ebx), "=c"(result_ecx), "=d"(result_edx):"a"(source_eax)
- это означает: поместить содержимое переменной source_eax в регистр EAX, выполнить команду CPUID и поместить содержимое регистров EAX, EBX, ECX, EDX в переменные result_eax, result_ebx, result_ecx, result_edx соответственно

Как видите, если перед параметром указывается префикс '=', то после выполнения инструкции происходит присвоение указанным переменным

Теперь, с учетом параметров, в общем виде команда asm(..) выглядит так:
asm("ИНСТРУКЦИЯ":"ВЫХОДНЫЕ_ПАРАМЕТРЫ":"ВХОДНЫЕ_ПАРАМЕТРЫ");

Сейчас мы не будем подробно рассматривать различные типы параметров, разберемся в них по ходу работы. Интересующихся же отсылаю к документации на GCC.

Обработка прерываний в системе

Для пущего удобства указанного в заголовке действа мы напишем несколько функций, которые позволят нам устанавливать обработчики прерываний, а также запрещать и разрешать обработку прерываний:

/* 
  intslib.c 
*/

#define IDT_TABLE 0x100000
#define IDT_REG 0x100800
#define SYS_CODE_SELECTOR 0x8

//Функция i_install() устанавливает в качестве обработчика vector функцию func. 
//Тип шлюза (прерывания [0x8e] или ловушки [0x8f]) указывается параметром type.

//Фактически, эта функция создает (или изменяет) соответствующий дескриптор в таблице IDT

void i_install(unsigned char vector, void (*func)(), unsigned char type) 
{
  char * idt_table=IDT_TABLE;
  
  unsigned char i;
  unsigned char b[8];

  b[0]=  (unsigned int)func & 0x000000FF;
  b[1]=( (unsigned int)func & 0x0000FF00) >> 8;
  b[2]=SYS_CODE_SELECTOR;
  b[3]=0;
  b[4]=0;
  b[5]=type;
  b[6]=( (unsigned int)func & 0x00FF0000) >> 16;
  b[7]=( (unsigned int)func & 0xFF000000) >> 24;

  
  
  for(i=0;i<8;i++){
    *(idt_table+vector*8+i)=b[i];
  }
  
  
}

//Функция i_setup() загружает регистр IDTR
void i_setup()
{
  unsigned short *table_limit = IDT_REG;
  unsigned int *table_address = IDT_REG+2; 

  *table_limit = 256*8 - 1;
  *table_address = IDT_TABLE;

  asm("lidt 0(,%0,)"::"a"(IDT_REG));
  asm("sti");
}


//Включение обработки прерываний
void i_enable()
{
  asm("sti");
}

//Отключение обработки прерываний
void i_disable()
{
  asm("cli");
}


Теперь эти четыре функции будут предоставлять остальной части ядра удобный высокоуровневый интерфейс для установки обработчиков прерываний

Работа с портами ввода/вывода

Для этого дела мы тоже напишем парочку функций, которые будут использовать команды IN (чтение из порта) и OUT (запись в порт).

/*
  ioports.c - функции работы с портами ввода/вывода
*/

__inline__ void outportb(unsigned short port, unsigned char value)
{
  asm("outb %b0,%w1":: "a"(value), "d"(port));
}

__inline__ char inportb(unsigned short port)
{
  char value;

  asm("inb %w1, %b0": "=a"(value): "d"(port));

  return value;
}

Outro

На сегодня все, уважаемые подписчики.
Как всегда, мой почтовый ящик открыт для вас:
lonesome@lowlevel.ru
Также вы можете задавать интересующие вас вопросы в форуме lowlevel.ru
Предыдущие выпуски рассылки вы можете найти по этому адресу:
http://subscribe.ru/archive/comp.soft.prog.osdev
Всего наилучшего!
Lonesome


ПРЕДЫДУЩИЙ ВЫПУСК      СЛЕДУЮЩИЙ ВЫПУСК

Rambler's Top100