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...939 
 Максвелл 3...870 
 Go Web ...823 
 William Gropp...803 
 Ethreal 3...787 
 Gary V.Vaughan-> Libtool...772 
 Ethreal 4...771 
 Rodriguez 6...763 
 Ext4 FS...755 
 Steve Pate 1...754 
 Clickhouse...753 
 Ethreal 1...742 
 Secure Programming for Li...731 
 C++ Patterns 3...716 
 Ulrich Drepper...696 
 Assembler...694 
 DevFS...661 
 Стивенс 9...649 
 MySQL & PosgreSQL...631 
 
  01.01.2024 : 3621733 посещений 

iakovlev.org

bkerndev - boot

 Bran's Kernel Development
 A tutorial on writing kernels
 Version 1.0 (Feb 6th, 2005)
 By: Brandon Friesen
 

Разработка ядра - нетривиальная задача . Это как проверка на умение создать софт и интерфейс для управления железом . Ядро - это центральная часть операционных систем - центр управления ресурсами компьютера .

Одним из важнейших ресурсов является процессор - на нем выделяется время для операций , прерываний , задач и т.д. Через него реализуется многозадачность , которая подразумевает наличие шедулятора (sheduler) , например шедулятор под названием 'Round Robin' - простейший вариант , когда имеется список процессов . Более сложные шедуляторы имеют приоритеты , от которых зависит время выполнения процесса .

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

Следующим важнейшим ресурсом являются прерывания - Interrupt Requests (IRQs), которые представляют из себя специальные сигналы , например от клавиатуры или харда , посланные процессору для того , чтобы тот приготовился от них читать данные .

Следующим ресурсом являются шины данных - Direct Memory Access (DMA) channels. Они управляют потоками данных памяти . DMA позволяют переводить данные без использования ЦПУ

Еще одним ресурсом являются порты ввода-вывода . Устройство может быть прочитано с помощью таких портов .

В данном руководстве будет показано , как создать простейшее ядро , которое может быть загружено с помощью GRUB . Будут проинициализированы Global Descriptor Table (GDT) , Interrupt Descriptor Table (IDT) . Будут созданы процедуры , обслуживающие прерывания - Interrupt Service Routines (ISRs) . Будет проинициализирована клавиатура .

Автор все тестировал на следующей конфигурации : NASM + DJGPP + Windows . Я переписал загрузчик на GNU ASM под 3-й федорой . Вам может пригодиться утилита intel2gas , которая перводит формат INTEL ASM в гну-шный формат AT .

Автор предлагает иметь в наличие второй компьютер для тестирования . Существуют более оптимальные варианты отладки - например , я отлаживался на VMWARE 5.0 .

После того как произошла первичная загрузка , загрузчик обычно передает управление стартовой функции ядра (main()) . Эта часть , равно как и инициализация стека , загрузка GDT, IDT , как правило делается на ассемблере . Все остальное пишется на си .

В ассемблерном файле создается стек размером 8 КБ . Он используется си-шными функциями для передачи параметров . Он также используется для хранения локальных переменных внутри этих функций . Глобальные переменные хранятся в секторе данных . В самом начале ассемблерного файла также необходимо проинициализировать глобальные физические адреса для линковочного скрипта .


 .code32
 .globl start
 start: 
 	mov $(Stack + 8192), %esp    
 	jmp stublet
 
 # Эта часть должна быть выравнена на  4 byte
 .align 4
 mboot: 
     # Multiboot macros 
 .set  MULTIBOOT_PAGE_ALIGN, 1<<0
 .set  MULTIBOOT_MEMORY_INFO, 1<<1
 .set  MULTIBOOT_AOUT_KLUDGE, 1<<16
 .set  MULTIBOOT_HEADER_MAGIC, 0x1BADB002
 .set  MULTIBOOT_HEADER_FLAGS , MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO 
 	| MULTIBOOT_AOUT_KLUDGE
 .set  MULTIBOOT_CHECKSUM  , -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
 .extern code, bss, end
 
     # GRUB Multiboot header
 .long MULTIBOOT_HEADER_MAGIC
 .long MULTIBOOT_HEADER_FLAGS
 .long MULTIBOOT_CHECKSUM
 
     # глобальные физические адреса для линковочного скрипта
 .long mboot
 .long code
 .long bss
 .long end
 .long start
 
 stublet: 
 .extern main
     call main
 right:    
     jmp right
 .....
 .section .bss
 .lcomm Stack, 8192               # Резервируем 8 КБ под стек
        
 

Линкер - инструмент , который берет скомпилированные обьектные файла и получает из них собственно ядро - бинарный файл kernel.img . В таком бинарном файле всегда существуют 3 секции :


 	TEXT
 	DATA
 	BSS
 
TEXT - хранится код . DATA - инициализированные данные . BSS - неинициализированные данные . BSS - это виртуальная секция : в самом бинарнике ее нет , она создается в момент загрузки ядра .

Для линковки линкер использует специальный скрипт . Скрипт имеет 3 главных ключевых слова :

OUTPUT_FORMAT - тип файла - бинарный

ENTRY - указывает обьектный файл , который в списке линковки идет первым .

phys - это не ключевое слово , это просто переменная , которая указывает , по какому адресу памяти будет загружено наше ядро - в данном случае 1 МБ

SECTIONS - определяет секции , которые выравниваются по 4 КБ - размер интеловской страницы по умолчанию


 OUTPUT_FORMAT("binary")
 ENTRY(start)
 phys = 0x00100000;
 SECTIONS
 {
   .text phys : AT(phys) {
     code = .;
     *(.text)
     . = ALIGN(4096);
   }
   .data : AT(phys + (data - code))
   {
     data = .;
     *(.data)
     . = ALIGN(4096);
   }
   .bss : AT(phys + (bss - code))
   {
     bss = .;
     *(.bss)
     . = ALIGN(4096);
   }
   end = .;
 }
 
 
Makefile для получения ядра :

 CFLAGS  = -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions 
            -nostdinc -fno-builtin -I./include -c 
 LDFLAGS = -T link.ld
 OBJS    = obj/start.o obj/main.o  obj/scrn.o obj/gdt.o obj/idt.o obj/isrs.o obj/irq.o 
           obj/timer.o obj/kb.o
 CC      = gcc -pipe
 
 #default: clean all
 
 all: kernel.img
 
 obj/start.o: boot/start.s
 	as -o obj/start.o boot/start.s 
 obj/main.o: kernel/main.c
 	${CC} -o obj/main.o -c kernel/main.c ${CFLAGS}
 obj/scrn.o: kernel/scrn.c
 	${CC} -o obj/scrn.o -c kernel/scrn.c ${CFLAGS}
 obj/gdt.o: kernel/gdt.c
 	${CC} -o obj/gdt.o -c kernel/gdt.c ${CFLAGS}
 obj/idt.o: kernel/idt.c
 	${CC} -o obj/idt.o -c kernel/idt.c ${CFLAGS}
 obj/isrs.o: kernel/isrs.c
 	${CC} -o obj/isrs.o -c kernel/isrs.c ${CFLAGS}
 obj/irq.o: kernel/irq.c
 	${CC} -o obj/irq.o -c kernel/irq.c ${CFLAGS}
 obj/timer.o: kernel/timer.c
 	${CC} -o obj/timer.o -c kernel/timer.c ${CFLAGS}
 obj/kb.o: kernel/kb.c
 	${CC} -o obj/kb.o -c kernel/kb.c ${CFLAGS}
 	
 kernel.img: ${OBJS}
 	ld ${LDFLAGS} -o build/kernel.img ${OBJS}
 
 clean:
 	rm -f -v obj/*.o *.img
 	rm  build/kernel.img
 

Архив исходников лежит тут (GNU ASM)

Архив исходников лежит тут (NASM)

Функция main() - стартовая точка ядра , она находится в main.c . Имеется хидер - system.h - в котором прописаны прототипы глобальных функций . Функция main() ничего не возвращает , она оканчивается как правило бесконечным циклом .


 #include < system.h>
 void *memcpy(void *dest, const void *src, size_t count)
 {
     const char *sp = (const char *)src;
     char *dp = (char *)dest;
     for(; count != 0; count--) *dp++ = *sp++;
     return dest;
 }
 void *memset(void *dest, char val, size_t count)
 {
     char *temp = (char *)dest;
     for( ; count != 0; count--) *temp++ = val;
     return dest;
 }
 unsigned short *memsetw(unsigned short *dest, unsigned short val, size_t count)
 {
     unsigned short *temp = (unsigned short *)dest;
     for( ; count != 0; count--) *temp++ = val;
     return dest;
 }
 size_t strlen(const char *str)
 {
     size_t retval;
     for(retval = 0; *str != '\0'; str++) retval++;
     return retval;
 }
 unsigned char inportb (unsigned short _port)
 {
     unsigned char rv;
     __asm__ __volatile__ ("inb %1, %0" : "=a" (rv) : "dN" (_port));
     return rv;
 }
 void outportb (unsigned short _port, unsigned char _data)
 {
     __asm__ __volatile__ ("outb %1, %0" : : "dN" (_port), "a" (_data));
 }
 void main()
 {
 
     gdt_install();
     idt_install();
     isrs_install();
     irq_install();
     init_video();
     timer_install();
     	
     keyboard_install();
 
     __asm__ __volatile__ ("sti");
 
     ........
     	
     for (;;);
     
 }
 

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

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

 Автор  Комментарий к данной статье
Anton
  На вашем сайте я узнал ряд интересных подробностей о программирование 
в защищенном режиме. Спасибо.
2005-12-20 17:59:43
Антон ( ино�
  Very Good
Спасибо што подсказали, что есть порт LD под Windu!!! 
Не знал. Очень пригодится. Исходники ld можно где-нибудь отыскать (на си)? 
ant-str@yandex.ru
2006-01-26 02:47:41
Яковлев Се�
  Если имеется ввиду линуксовый линкер ,то он входит в состав пакета binutils
binutils версии 2.15 например весит 11 метров в архиве
Там кроме ld входят также :
ar
as
gprof
objdump
и т.д.
2006-01-26 18:57:37
Антон
  Thanx
2006-04-11 03:16:14