Vývoj operačného systému/Nízkoúrovňový program

Od momentu čo začínate písať prvý riadok kódu operačného systému si musíte uvedomovať, že ste na všetko sami. Zatiaľ neexistujú žiadne knižnice, frameworky, operačné systémy a tým pádom ani rôzne pomocné funkcie. Tu je zoznam vašich nových a jediných priateľov:

  • Kompilátor
  • BIOS
  • Testovací stroj/Virtualizačný program (a jeho nástroje na odstraňovanie chýb)

Kvôli absencii operačného systému alebo knižníc začnete mať radi momenty, keď sa na obrazovke vypíše chybová hláška. Vy síce nebudete vedieť v čom chyba spočíva, no budete radi že to vie aspoň počítač. Úroveň operačného systému budem nazývať aj ako nízku úroveň, kedže tento kód je naozaj to jediné, čo programátora delí od elektronickej podstaty procesora. V tomto článku popíšem spôsob akým pracuje procesor s inštrukciami a ako urobiť vlastný nízkoúrovňový program.

Výkon procesora

upraviť

Celý princíp práce procesora je vlastne vykonávanie nejakých operácií s parametrami, ktoré sú k operácii priradené. Kód ktorý sa napíše v akejkoľvek forme sa skôr či neskôr preloží do týchto inštrukcií (alebo sa pomocou nich vykoná). Medzi obyčajným programom jazyka C a operačným systémom nie je z hľadiska inštrukcií žiadny rozdiel. Je pravda, že bežný program nemá právo na vykonávanie niektorých inštrukcií, no podstata je jasná: jeden procesor a jedny inštrukcie, ktoré sa vykonávajú v rozdielny čas. Rozdiel medzi programom a operačným systémom potom spočíva v prostredí, v ktorom sa nachádza. Nízkoúrovňové programy pracujú v neobmedzenom prostredí z ktorého vytvárajú prostredia (napríklad pamäťové priestory) pre bežné programy. To, že všetky programy bežia na jednom procesore (vynechávajúc viacjadrové procesory) znamená, že programy nemôžu fungovať paralelne. To čo užívateľ vidí je dôsledok rýchleho striedania kontextu vykonávania procesoru (kontext - obsahy registrov, zásobník, ukazovateľ na inštrukciu a adresový priestor). Hovoríme teda o pseudoparalelizme. Systémy striedania sú tak presne navrhnuté, že zabezpečia aby sa žiadne dáta medzi kontextami nestratili a program túto zmenu vôbec nepocíti. Z toho všetkého vyplýva, že operačný systém má od načítania až po prechod do striedania kontextov celý výkon iba pre seba. Tak isto aj operačný systém, ktorý idete vyvíjať bude mať na procesore celý výkon prístupný pre jeho úlohy.

Operačný systém je načítaný kódom nazývaným BIOS (Basic Input/Output System) alebo ROM-BIOS (Read Only Memory Basic Input/Output System). BIOS je kód, ktorý býva uložený v pamäti ROM (pamäť len na čítanie). Obsah tejto pamäte sa často zvykne pri zapnutí počítača presunúť do pamäte RAM, kedže prístup do pamäť ROM je často pomalý. Tento kód sa následne začne vykonávať a zariadi, aby sa vytvorilo prostredie pre operačný systém, ktorý sa má načítať. Utilita BIOS Setup sa často používa na upravovanie jednotlivých nastavení BIOSu a zvyčajne sa dá spustiť pri zapínaní počítača stisnutím potrebnej klávesy (napr. enter, escape, delete alebo F12). Kód BIOSu nevie nič o operačnom systéme, ktorý ide načítavať. Z nastavení (upraviteľných z utility BIOS Setup) pozná zoznam zariadení, z ktorých má operačný systém načítať, no nič iné o ňom nevie. Preto sa vytvoril štandard načítania prvých 512-tich bajtov z disku a overenie "podpisu" bootloadera (programu načítaného operačným systémom). Bežný operačný systém sa nezmestí do 512-tich bajtov a preto sa tento priestor využíva pre kód, ktorý načítava ďalšie kroky spúšťania operačného systému. Pretože načítať operačný systém je bežne možné zo skoro hocijakého zariadenia na ukladanie dát, často sa prvých 512 bajtov vynecháva.

Poznámka: Prvý sektor disku sa zvykne nazývať rôzne. Typické sú označenia bootloader, boot sector (jeden sektor na diskete zvykol mať 512 bajtov), MBR (Master Boot Record) alebo len "boot code".


S príchodom súborových systémov bolo nutné oboznámiť už bootloader o rozložení súborového systému, a tak sa do vyššej časti pamäti bootloadera uložila tabuľka obsahujúca informácie o partíciach. Postupom času vzniklo množstvo rôznych návrhov rozložení bootloaderu, ale to pôvodné sa stále s menšími obmenami akceptuje. Tu sa nachádza zjednodušená tabuľka rozloženia dnešného MBR sektoru (všetky údaje sú v bajtoch):

Odsadenie od začiatku sektoru Veľkosť Popis
0 446 Kód bootloaderu používaný na načítanie operačného systému. Môže obsahovať aj tabuľky niektorých súborových systémov (napríklad FAT, ak sa jedná o malé zariadenie, napr. disketa)
446 16 Zápis partície #1
462 16 Zápis partície #2
478 16 Zápis partície #3
494 16 Zápis partície #4
510 2 "Podpis" MBR (používa sa na overenie či sa jedná naozaj o správny sektor): 0xAA55

Teda prakticky máte na svoj kód len 446 bajtov. Iné dáta by mohli spôsobiť neželanú manipuláciu alebo zmenu MBR sektoru.

Kód bootloaderu

upraviť

Pri tvorbe bootloaderu je nutné vedieť, čo od svojho bootloaderu očakávate. V mnohých prípadoch sa bootloader naozaj nepoužíva na nič iné, než len na načítanie ďalších sektorov spustenie načítaného kódu. Na načítanie ďalších sektorov nám BIOS poskytuje API (systémové/BIOSové volania použiteľné už z bootsektoru), čiže tým sa trápiť nemusíme. BIOS štandard vyžaduje niekoľko bodov, ktoré pomáhajú zaručiť načítanie operačných systémov na rôznych počítačoch a v rôznych podmienkach:

  • Kód bootloaderu musí byť uložený na adrese 0x7C00. Problémom niekedy je, že jednotlivé BIOSy nemajú presný príkaz týkajúci sa rozloženia tejto adresy na pár segmentu a odsadenia (adresa môže byť načítaná ako 7C:00, 7C0:0 alebo 0000:7C00 hexadecimálne). V podstate to nespôsobuje problémy, len je nutné si tieto kombinácie overiť (alebo nastaviť na potrebnú formu) v prípade priamej práce na tejto adrese.
  • V registri DL musí byť uložený BIOSový kód (alias) pre zariadenie, z ktorého sa bootloader načítal. Toto číslo je potom priamo použiteľné pri volaniach BIOSových funkcií.
  • Adresy 0x500/0x600 alebo 0x7A00 by mali byť prístupné pre použitie na nastavenie zásobníka alebo premiestnenie bootloaderu v prípade viacnásobného načítavania (napr. v prípade operačného systému DOS).

Poznajúc tieto skutočnosti môžeme pokročiť ku ukážke kódu.

Poznámka #1: Kód bootloaderu je v prípade procesorov s inštrukčnou sadou x86 vykonávaný procesorom v pôvodnom reálnom (16-bitovom) móde. To znamená že pri písaní assemblerového kódu musíte na túto skutočnosť svoj kompilátor upozorniť.

Poznámka #2: Uvedené kódy sú písané pre kompilátor NASM (pre architektúry x86, dostupný pre Linux, Windows, Mac OS X a DOS) použitím syntaxe Intel. Je možné že v iných kompilátoroch nebudú fungovať.

BITS 16 ; sme v 16-bitovom móde
start: ; označenie začiatku kódu pre prípadnú rýchlu potrebu vrátenia sa
	cli ; zakáž prerušenia
        hlt ; zastav vykonávanie procesora

times 510-($-$$) db 0 ; vyplň priestor do 510-tich bajtov nulami, podpis bootsektoru musí byť presne na odsadení 510
dw 0xAA55 ; zapíš bajty 0x55 a 0xAA - architektúra x86 používa zoradenie little-endian

Tento kód neurobí doslova nič. Po načítaní sa zakážu prerušenia (aby kód neželane nepreskočil na miesto vykonávať nejakú z procedúr BIOSu). Následne sa procesoru prikáže nepokračovať ďalej vo vykonávaní inštrukcií. Ku koncu je kompilátoru prikázané, aby vyplnil zvyšný priestor nulami, kedže je potrebné aby bol podpis MBR sektoru presne na odsadení 510. Napokon sa na koniec súboru zapíše podpis MBR sektoru (BIOS ho má akceptovať pri načítaní).

Poznámka: Inštrukcia hlt je alternatíva k zacyklenému skoku:

.tu:
jmp .tu

Kód sa dá preložiť kompilátorom NASM používajúc nasledujúce parametre:

nasm -f bin -o boot.o zdrojovySubor.asm

Pri kompilácií špecifikujeme čistý binárny súbor (bez prídavných hlavičiek na spustenie v operačnom systéme) ako formát, súbor "boot.o" ako výtupný súbor a "zdrojovySubor.asm" ako súbor obsahujúci zdrojový kód. Ak sa kód vykompiluje bez chýb, mal by mať výsledný súbor presne 512 bajtov (v operačnom systéme si treba prečítať presné vlastnosti v podrobnostiach).

Zatiaľ nám tento kód nič nevypisuje, takže spustiť si ho môžete len pre overenie, že ste neurobili chybu pri kompilácii. Na overenie funkčnosti si ale môžeme kód spustiť v nástroji na odstraňovanie chýb:

 
Vykonávané sú len dve inštrukcie (CLI a HLT), pričom z inštrukcie HLT sa už procesor nevracia.

Pozri aj

upraviť