Internet Info, s.r.o. Lupa Měšec Podnikatel Root Zdroják DigiZone Slunečnice Vitalia TopDrive KupDnes Navrcholu NovýTarif Dobrý web Weblogy Woko Jagg Computer.cz SK: MojeLinky

Hlavní navigace

Programujeme OS: hello world

Operační systém je softwarová záležitost, bez které by dnes nemohla existovat široká škála zařízení, je to věc, která probudí hardware k životu a plní nejrůznější funkce – všechny jsou si ale v jádru principiálně podobné. Někteří z vás, stejně jako já, rádi nakouknou, jak daná věc funguje a chtějí se naučit něco nového.

Tweetni to Twitter Jaggni to! Jagg Del.icio.us Delicious

Tento seriál se bude zabývat, jak už asi tušíte, vývojem vlastního operačního systému. Nejdůležitější součástí je zcela určitě kernel (jádro), které se stará o klíčové prvky jakožto správa procesů, přidělování prostředků, obsluha periférií. Další částí je obvykle už software či knihovny, které mají zjednodušit a urychlit vývoj uživatelských aplikací.

My se budeme bavit především o jádru, postupně mu budeme přidávat nové schopnosti a posléze si předvedeme i spuštění kódu v uživatelském prostoru. Protože je architektura procesorů zvaná x86 nejrozšířenější, bude náš systém určen právě pro ni, je ale docela možné, že později nakousneme další – ku příkladu ARM, jež se s dobou, počtem mobilních a úsporných zařízení rozšiřuje nezastavitelným tempem.

Doma zajisté nějaký ten stroj s procesorem x86 máte, se softwarovým vybavením to už může být jinak, proto uvedu balíky, které k vývoji budeme potřebovat a jsou běžně dostupné pro platformy (GNU/Linux, BSD, Mac OS X, Windows)

  • binutils – pro slinkování objektů (.o)
  • gcc – pro kompilaci zdrojových kódů jazyka C (.c)
  • nasm – pro překlad jazyka symbolických adres (.s)
  • make – pro pohodlnější správu kódu
  • qemu – pro jednoduché testování našeho OS
  • cdrtools – pro vytvoření obrazu (*.iso)

Jak jste si pravděpodobně všimli, budeme psát pomocí Assembleru, tedy jazyka symbolických adres, bez kterého se naše jádro neobejde, a také jazyka C, který nabízí komfort vysokoúrovňových jazyků. Pro emulaci reálného PC použijeme Qemu, nebude ale problém použít ani virtualizační nástroje jako jsou VirtualBox, VMWare, apod.

Povídání už asi bylo dost, pojďme tedy začít pracovat. Nejdříve je nutné navrhnout topologii našeho jádra, tedy jak a kde ho uložíme do paměti RAM.

Protože i386, pro níž budeme psát, má některé adresy použité pro komunikaci s perifériemi jako je grafický adaptér, pc-speaker, sériový, paralelní port, atd. vyhrazené prostřednictvím BIOSu, budeme nuceni naše jádro uložit tam, kde ničemu nebude překážet – pro naše účely bude vhodná adresa 0×100000 (hexadecimální), ve známých jednotkách je to 1MB. Na této adrese bude začínat binární kód jádra, tedy kopie binárky, kterou na konci zkompilujeme. Otázka zní – Jak ho tam dostat prakticky? Udělá to za nás přece zavaděč (bootloader), my použijeme GRUB.

Bohužel danou adresu mu musíme sdělit nějakým způsobem, řešení spočívá v linkovacím skriptu.

Právě v něm si definujeme onu adresu, plní však i další funkci – dokáže programu „ld“ říct, jak má výstupní binárka ve známém formátu ELF vypadat, tedy přesněji jak mají být rozděleny sekce, jež tento formát využívá. Dále nám přijde vhod pro nastavení funkce, kterou bude kód jádra začínat. Mimochodem binární formát ELF se používá v mnoha unixových systémech, GNU/Linux nevyjímaje.

Takto může vypadat linkovací skript, pojmenujeme ho „link.ld“

/* Linkovací skript pro korektní vytvoření
binárky jádra - je uložena ve formátu ELF a jednotlivé části jsou
rozděleny do tzv. sekcí
 * binární kód začíná na adrese 0x100000, tedy 1MB, poté následuje
sekce .data kde najdeme především statické řetězce.
 * Dále vidíme sekci .bss, do které přicházejí nestatické hodnoty
proměnných,
 * Nakonec je zde .end, což nám říká, že na tomto místě skončilo jádro
 */

/* Náše jádro začíná procedurou start, definovanou ve zdrojovém kódu,
viz. start.s */
ENTRY(start)

SECTIONS
{
    .text 0x100000 :
    {
        code = .; _code = .; __code = .;
        *(.text)
        . = ALIGN(4096);
    }
    .data :
    {
        data = .; _data = .; __data = .;
        *(.data)
        *(.rodata)
        . = ALIGN(4096);
    }
    .bss :
    {
        bss = .; _bss = .; __bss = .;
        *(.bss)
        . = ALIGN(4096);
    }

    end = .; _end = .; __end = .;
}

Jak vidíme z kódu, ALIGN nám pomáhá „zarovnat“ každou ze sekcí do bloků o velikosti 4kB, což je užitečné v případě mapovaní do stránek a prací s virtualní pamětí.

Nyní přejdeme ke psaní samotného zdrojového kódu – použili jsme zaváděč GRUB, ten má schopnost číst hlavičku zvanou multiboot, představme si ji jako datovou strukturu, do které uložíme potřebné informace k tomu, aby mohlo být naše jádro úspěšně zavedeno.

Obsahuje unikátní identifikátor označený jako MAGIC – je tvořen konstantou, podle které zaváděč zjistí, že se jedná právě o multiboot hlavičku. Dále obsahuje FLAGS – uchovává bitové masky, podle nichž si vyžádáme specifické nastavení zaváděče. Další nedílnou součástí je CHECKSUM – zaručuje správnost integrity dat. Nakonec jsou uvedeny adresy odkazující na samotný multiboot, sekce .text, .data, .bss, .end a adresa na proceduru start, kde kód vlastního jádra teprve začíná.

Takto vypadá zdrojový kód k uložení multibootu do binárky a následně procedura start, která je spuštěna samotným zaváděčem – soubor start.s

; Zdrojový kód v jazyku symbolických
adres

; Budeme pracovat s 32bitovými instrukcemi
[BITS 32]

; Zpřístupníme jednotlivé ukazatele na sekce binárky kernelu, viz.
linkovací skript link.ld
EXTERN code, bss, end
; Zpřístupníme funkci main () definovanou v souboru main.c
EXTERN main
; Vytvoříme globální procedury pro multiboot a start
GLOBAL mboot, start

; Zde začíná multiboot hlavička, která má především zaváděči říct, kde v
pamětí se jádro nachází
mboot:
    ; Nastavíme potřebné multiboot makra
    MULTIBOOT_PAGE_ALIGN    equ 1<<0    ; Chceme aby naše jádro bylo
přiřazeno na adresu stránky
    MULTIBOOT_MEMORY_INFO    equ 1<<1    ; Poskytne jádru informace o
paměti
    MULTIBOOT_HEADER_MAGIC    equ 0x1BADB002    ; Nastavíme předdefinovanou
hodnotu, podle které najde zaváděč multiboot hlavičku

    MULTIBOOT_HEADER_FLAGS    equ MULTIBOOT_PAGE_ALIGN |
MULTIBOOT_MEMORY_INFO    ; Přiřadíme nastavené parametry
    MULTIBOOT_CHECKSUM    equ -(MULTIBOOT_HEADER_MAGIC +
MULTIBOOT_HEADER_FLAGS)    ; Vypočteme kontrolní součet pro ověření
integrity hlavičky zaváděčem

    ; Nastavené makra zde použijeme k zapsání do binárky jádra pomocí
instrukce "dd"
    ; POZOR: sled následujících instrukcí je důležitý, nesmí být změněn
    dd MULTIBOOT_HEADER_MAGIC
    dd MULTIBOOT_HEADER_FLAGS
    dd MULTIBOOT_CHECKSUM

    ; Nyní do binárky uložíme jednotlivé adresy
    dd mboot    ; adresa multiboot hlavičky
    dd code        ; adresa binárního kódu - sekce .text
    dd bss        ; adresa sekce .bss
    dd end        ; adresa konce binárky jádra, .end
    dd start    ; adresa, která řekne procesoru, kde s vykonáváním kódu má
začít

; Poté co si zaváděč "přečte" multiboot hlavičku, zjistí,
; že má procesor skočit na níže definovanou proceduru start
start:
    push ebx        ; Ze 32bitového registru ebx získáme ukazatel na
strukturu
dat získanou ze zaváděče

    call main        ; Konečně zavoláme funkci main () definovanou v
jazyku C -
soubor main.c

    jmp $             ; Po vykonání předchozí funkce skončíme na
nekonečné smyčce
                ; Toto opatření zabrání vykonávání kódu, který by mohl
následovat ..
                ; V této chvíli je vhodné ukončit činnost procesoru

Nyní přijde hlavní funkce kernelu main (), tu už definujeme pomocí vysokoúrovňového jazyka C – soubor main.c

/* main.c - jazyk
C */

#define    VGA_BASE    0xb8000
#define    VGA_RES_X    80
#define    VGA_RES_Y    25

/* vyčistí obrazovku */
void cls ()
{
    /* definice ukazatele VGA paměti */
    unsigned short *vid_mem = (unsigned short *) VGA_BASE;

    unsigned i;
    /* cyklus projde všechny pixely */
    for (i = 0; i < VGA_RES_X * VGA_RES_Y; i ++)
        vid_mem[i] = 0;    /* vloží do buňky nulový znak */
}

/* ukázková funkce pro výpis textu na obrazovku */
void print (char *str)
{
    /* definice ukazatele VGA paměti */
    unsigned short *vid_mem = (unsigned short *) VGA_BASE;

    unsigned i;
    for (i = 0; str[i]; i ++)
        vid_mem[i] = str[i] | 0x0f << 8;    /* vložení znaku |
nastavení
barev fontu a pozadí */
}

/* Zdrojový kód funkce main () - zavolá se pomocí kódu ve start.s (call
main)
 * Její parametr s_mboot je ukazatel na datovou strukturu poskytovanou
zaváděčem (push ebx)
 * zahrnuje několik užitečných informací jako informace o paměti, jméno
zaváděče
 * podrobnosti na
http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#multiboot_002eh
*/
int main (void *s_mboot)
{
    cls ();

    print ("Ahoj svete !");

    return 0;
}

Funkce main () pochopitelně v tomto stavu nedělá mnoho – přesněji nejdříve vyčistí obrazovku od toho, co tam bylo předtím pomocí funkce cls (). Ve finále vytiskneme text „Ahoj svete!“ na obrazovku, a hle! Funkce print () je zatím velmi primitivní, takže si neporadí ani s odstavci, atd – na ukázku ale vyhovuje.

Nyní stačí kernel přeložit do spustitelné podoby a otestovat buďto na reálném železe, nebo třeba v nástroji zvaném Qemu.

V poslední části se budeme věnovat Makefile scriptu, který nám kompilaci a testování značně zjednoduší.

Ukázkový soubor Makefile:

# Zde jsou definovány parametry pro kompilaci jádra
CC  =gcc
CFLAGS  =-c -m32 -nostdlib -nostartfiles -nodefaultlibs

AS  =nasm
ASFLAGS =-f elf

LD  =ld
LDFLAGS =-m elf_i386 -T link.ld

SOURCES =start.o main.o

KERNEL  =kernel.bin

all: $(SOURCES) kernel

clean:
    -rm -f *.o $(KERNEL)

.s.o:
    $(AS) $(ASFLAGS) $<

.c.o:
    $(CC) $(CFLAGS) $<

kernel:
    $(LD) $(LDFLAGS) -o $(KERNEL) $(SOURCES)

Celé jádro přeložíme pouhým napsaním příkazu „make“, pokud ho budeme chtít otestovat v praxi, je nutné nainstalovat GRUB a nastavit ho. Jednodušší je si stáhnout archív s kompletními kódy, přibaleným GRUBem a schopností tvořit CD obraz ISO pomocí „make image“ a také rovnou spouštět náš systém – „make qemu“. Archív najdete zde.

TIB2012

       

Věřím, že se vám první článek o tvorbě operačního systému líbil a že vám může pomoci s vývojem vlastního. Pokud se vám zdál příliš obtížný, doporučuji číst dokumentaci k i386 a pro zájemce doporučím web OSDev.org kde lze najít spoustu návodů a tutoriálů. Vývoj operačního systému vyžaduje spoustu nervů a času, ale přináší neocenitelné vědomosti.

Kdo ví – zrovna ten váš se jednou může rozšířit, ať už na desktopy, servery, routery, mobilní zařízení nebo něco úplně jiného.

Tomáš Jędrzejek

Autor je studentem VŠB FEI a pracuje na několika projektech jako např. operační systém ZeX/OS nebo hra Tuxánci.

Školení: SQL pro začátečníky

Kdo nezná jazyk SQL jako kdyby nebyl. Tak lze stručně charakterizovat dnešní význam SQL v IT. Pokud se chcete naučit tento jazyk, tak navštivte naše školení SQL. Školení je určené začátečníkům, a proto se začíná od skutečných základů.

Kromě samotného SQL se účastíci školení seznámí i se základy PostgreSQL, což je databáze, která se díky své shodě se standardem ANSI SQL a komfortem, který poskutuje svým uživatelům, zvlášť hodí pro výuku SQL.

Podrobnější informace a přihláška

Ohodnoťte jako ve škole:
Průměrná známka 2,66

Přehled názorů

Díky
Václav Švejcar 21. 7. 2009 00:40
Nový
└ 
don't code no lame shit
use_a_finished_os 21. 7. 2009 12:17
Nový
 
├ 
Re: don't code no lame shit
xwinus 21. 7. 2009 13:44
Nový
 
├ 
Re: don't code no lame shit
vtech 21. 7. 2009 23:01
Nový
 
└ 
Re: don't code no lame shit
Harvie .cz 22. 7. 2009 01:33
Nový
Pane bože, už zase "píšeme OS" :-)
Biktop 21. 7. 2009 00:52
Nový
├ 
Re: Pane bože, už zase "píšeme OS" :-)
Adam Štrauch 21. 7. 2009 01:05
Nový
│
└ 
Re: Pane bože, už zase "píšeme OS" :-)
mcx 21. 7. 2009 09:29
Nový
│
 
└ 
Re: Pane bože, už zase "píšeme OS" :-)
Tomáš Jędrzejek 21. 7. 2009 09:40
Nový
│
 
 
└ 
Re: Pane bože, už zase "píšeme OS" :-)
kazatel 23. 7. 2009 13:43
Nový
└ 
Re: Pane bože, už zase "píšeme OS" :-)
Father Hurley 21. 7. 2009 02:16
Nový
Re: Programujeme OS: hello world
dc 21. 7. 2009 01:57
Nový
├ 
Re: Programujeme OS: hello world
Tomáš Jędrzejek 21. 7. 2009 09:04
Nový
│
└ 
Re: Programujeme OS: hello world
dc 21. 7. 2009 12:40
Nový
│
 
└ 
Re: Programujeme OS: hello world
povinná 21. 7. 2009 18:51
Nový
│
 
 
└ 
Re: Programujeme OS: hello world
vtech 21. 7. 2009 23:03
Nový
│
 
 
 
└ 
Re: Programujeme OS: hello world
SB 23. 7. 2009 15:50
Nový
└ 
Re: Programujeme OS: hello world
Sten 21. 7. 2009 14:26
Nový
make qemu?
Plague 21. 7. 2009 02:02
Nový
└ 
Re: make qemu?
Tomáš Jędrzejek 21. 7. 2009 09:14
Nový
architektura ARM
HuB 21. 7. 2009 02:20
Nový
├ 
Re: architektura ARM
PavelP 21. 7. 2009 06:46
Nový
└ 
Re: architektura ARM
koduy 26. 7. 2009 12:22
Nový
pěkný článek
Alblaho 21. 7. 2009 08:25
Nový
Komentáře zdrojáků
oto 21. 7. 2009 08:47
Nový
└ 
Re: Komentáře zdrojáků
Michal Zima 21. 7. 2009 10:28
Nový
Vice teorie
hx 21. 7. 2009 09:23
Nový
├ 
Re: Vice teorie
Tomáš Jędrzejek 21. 7. 2009 09:47
Nový
└ 
Re: Vice teorie
BLEK. 21. 7. 2009 20:17
Nový
NASM vs Gnu AS
CruX 21. 7. 2009 09:45
Nový
├ 
Re: NASM vs Gnu AS
Tomáš Jędrzejek 21. 7. 2009 09:59
Nový
│
├ 
Re: NASM vs Gnu AS
CruX 21. 7. 2009 10:19
Nový
│
├ 
Re: NASM vs Gnu AS
vtech 21. 7. 2009 12:05
Nový
│
├ 
Re: NASM vs Gnu AS
BLEK. 21. 7. 2009 20:13
Nový
│
└ 
Re: NASM vs Gnu AS
Michal Ludvig 23. 7. 2009 03:06
Nový
└ 
Re: NASM vs Gnu AS
Sten 21. 7. 2009 14:34
Nový
dalsi linky k vyvoji os
dalsi linky 21. 7. 2009 09:51
Nový
Plácám
petr@ DwAnDeR.cz 21. 7. 2009 10:41
Nový
Re: Programujeme OS: hello world
yossarian 21. 7. 2009 11:37
Nový
└ 
Re: Programujeme OS: hello world
Fluf 21. 7. 2009 11:48
Nový
 
└ 
Re: Programujeme OS: hello world
P. 21. 7. 2009 15:04
Nový
Misto serialu otisknout Minix
mhi 21. 7. 2009 13:11
Nový
├ 
Re: Misto serialu otisknout Minix
Tomáš Jędrzejek 21. 7. 2009 13:18
Nový
│
└ 
Re: Misto serialu otisknout Minix
mhi 21. 7. 2009 20:28
Nový
└ 
Re: Misto serialu otisknout Minix
BLEK. 21. 7. 2009 20:23
Nový
 
└ 
Re: Misto serialu otisknout Minix
Biktop 21. 7. 2009 22:41
Nový
Jen tak dál
Drake 21. 7. 2009 14:19
Nový
pochvala
online 21. 7. 2009 14:22
Nový
64bit
Jardík 21. 7. 2009 14:26
Nový
└ 
Re: 64bit
Tomáš Jędrzejek 21. 7. 2009 14:56
Nový
Sialene, ale vyzva
pepa 21. 7. 2009 15:17
Nový
Díky!
Karel Rys 21. 7. 2009 17:06
Nový
linkovací script
Medved 21. 7. 2009 17:08
Nový
└ 
Re: linkovací script
Jirka P 22. 7. 2009 02:32
Nový
A taky jeden OS ode me :)
^Benny 21. 7. 2009 20:30
Nový
Minimalistic kernel
misisko misko 21. 7. 2009 22:13
Nový
└ 
Re: Minimalistic kernel
Biktop 21. 7. 2009 22:52
Nový
 
└ 
Re: Minimalistic kernel
Tomáš Jędrzejek 21. 7. 2009 23:03
Nový
 
 
└ 
Re: Minimalistic kernel
Biktop 21. 7. 2009 23:49
Nový
 
 
 
└ 
Re: Minimalistic kernel
Matej Kosik 22. 7. 2009 12:16
Nový
autor ma pedagogicky talent
backup 22. 7. 2009 10:02
Nový
├ 
Re: autor ma pedagogicky talent
Jakub Jermář 22. 7. 2009 10:27
Nový
├ 
Re: autor ma pedagogicky talent
Karell 22. 7. 2009 10:52
Nový
├ 
Re: autor ma pedagogicky talent
I/O 22. 7. 2009 12:48
Nový
│
└ 
Re: autor ma pedagogicky talent
backup 22. 7. 2009 13:03
Nový
└ 
Re: autor ma pedagogicky talent
Lenka 22. 7. 2009 19:07
Nový
Kernel panic
Slavek 22. 7. 2009 23:41
Nový
└ 
Root nový systém a Re: Kernel panic
kernel panic na Rootu. 22. 7. 2009 23:48
Nový
 
└ 
Re: Root nový systém a Re: Kernel panic
Slavek 22. 7. 2009 23:55
Nový
knihovny
Shorty 26. 7. 2009 21:49
Nový
└ 
Re: knihovny
Biktop 27. 7. 2009 10:19
Nový
funkcia printf
pato1 20. 8. 2009 19:12
Nový
└ 
Re: funkcia printf
pato1 20. 8. 2009 19:51
Nový
       

Tento text je již více než dva měsíce starý. Chcete-li na něj reagovat v diskusi, pravděpodobně vám již nikdo neodpoví. Pro řešení aktuálních problémů doporučujeme využít naše diskusní fórum.

Zasílat nově přidané příspěvky e-mailem