Windows.  Virus.  Anteckningsböcker.  Internet.  kontor.  Verktyg.  Förare

Operativsystem 0 till 1 boken publiceras på GitHub och har över 2 000 stjärnor och 100 gafflar. Som namnet antyder, efter att ha läst det, kan du skapa ditt eget operativsystem - och kanske kan få saker i programmerarnas värld vara coolare.

Genom den här boken kommer du att lära dig följande:

  • Lär dig hur du skapar ett operativsystem baserat på hårdvaruteknisk dokumentation. Så här fungerar det i den verkliga världen, du kan inte använda Google för snabba svar.
  • Förstår hur datorkomponenter interagera med varandra, från mjukvara till hårdvara.
  • Lär dig skriva kod själv. Att blint kopiera kod är inte att lära, du kommer faktiskt att lära dig hur man löser problem. Blindkopiering är förresten också farligt.
  • Bemästra de välbekanta verktygen för utveckling på låg nivå.
  • Bekanta dig med assemblerspråk.
  • Ta reda på vad programmen består av och hur operativ system lanserar dem. Liten översikt vi gav detta ämne för nyfikna.
  • Lär dig hur du felsöker ett program direkt på hårdvara med GDB och QEMU.
  • C. Du kan snabbt bemästra det genom att följa.
  • Grundläggande kunskaper i Linux. Det räcker med att studera på vår hemsida.
  • Grundläggande kunskaper i fysik: atomer, elektroner, protoner, neutroner, spänning.

Jag säger genast, stäng inte artikeln med tankarna "Fan, ännu en Popov." Han har bara en slickad Ubuntu, och jag har allt från grunden, inklusive kärnan och applikationer. Så, fortsättning under skärningen.

OS-grupp: Här.
Först ska jag ge dig en skärmdump.

Det finns inga fler av dem, och nu mer i detalj om varför jag skriver det.

Det var en varm aprilkväll, torsdag. Sedan barndomen drömde jag om att skriva ett OS, när jag plötsligt tänkte: "Nu vet jag fördelarna och asm, varför inte förverkliga min dröm?". Jag googlade sidor om detta ämne och hittade en artikel från Habr: "Hur man börjar och inte slutar skriva OS". Tack till dess författare för OSDev Wiki-länken nedan. Jag gick dit och började jobba. Det fanns i en artikel all data om det lägsta operativsystemet. Jag började bygga cross-gcc och binutils och skrev sedan om allt därifrån. Du borde ha sett min glädje när jag såg inskriptionen "Hej, kärnvärlden!" Jag hoppade direkt från stolen och insåg – jag kommer inte att ge upp. Jag skrev "konsol" (inom citattecken, jag hade inte tillgång till ett tangentbord), men sedan bestämde jag mig för att skriva ett fönstersystem. Som ett resultat fungerade det, men jag hade inte tillgång till tangentbordet. Och så bestämde jag mig för att komma på ett namn baserat på X Fönstersystem. Googlade Y Window System - det är det. Som ett resultat kallade han Z Window System 0.1, som ingår i OS365 pre-alpha 0.1. Och ja, ingen såg det förutom jag. Sedan kom jag på hur jag skulle implementera tangentbordsstöd. Skärmdump av den allra första versionen, när det inte fanns något, inte ens ett fönstersystem:

Det flyttade inte ens textmarkören, som du kan se. Sedan skrev jag ett par enkla applikationer baserad på Z. Och här är release 1.0.0 alfa. Det fanns många saker, även systemmenyn. A filhanterare och kalkylatorn fungerade helt enkelt inte.

Jag blev direkt terroriserad av en vän som bara bryr sig om snygghet (Mitrofan, förlåt). Han sa "Svättade ner VBE-läget 1024 * 768 * 32, sköljde ner, sköljde ner! Nåväl, de blev fulla! Tja, jag var redan trött på att lyssna på honom och sköljde ändå ner honom. Mer om implementeringen nedan.

Jag gjorde allt med min bootloader, nämligen GRUB "ohm. Med den kan du ställa in grafikläget utan komplikationer genom att lägga till några magiska rader i Multiboot-huvudet.

Ställ ALIGN, 1<<0 .set MEMINFO, 1<<1 .set GRAPH, 1<<2 .set FLAGS, ALIGN | MEMINFO | GRAPH .set MAGIC, 0x1BADB002 .set CHECKSUM, -(MAGIC + FLAGS) .align 4 .long MAGIC .long FLAGS .long CHECKSUM .long 0, 0, 0, 0, 0 .long 0 # 0 = set graphics mode .long 1024, 768, 32 # Width, height, depth
Och så tar jag framebuffer-adressen och skärmupplösningen från Multiboot-informationsstrukturen och skriver pixlar där. VESA gjorde allt väldigt förvirrat - RGB-färger måste anges i omvänd ordning (inte R G B, utan B G R). Jag förstod inte på flera dagar - varför visas inte pixlarna!? Till slut insåg jag att jag glömde att ändra värdena för 16 färgkonstanter från 0...15 till deras RGB-ekvivalenter. Som ett resultat släppte jag den, samtidigt som jag sköljde ner gradientbakgrunden. Sedan gjorde jag en konsol, 2 applikationer och släppte 1.2. Åh ja, jag glömde nästan - du kan ladda ner OS på

Original: AsmSchool: Gör ett operativsystem
Författare: Mike Saunders
Publiceringsdatum: 15 april 2016
Översättning: A. Panin
Datum för överföring: 16 april 2016

Del 4: Med de färdigheter du har lärt dig från tidigare artiklar i serien kan du börja utveckla ditt eget operativsystem!

Vad är det för?

  • För att förstå hur kompilatorer fungerar.
  • För att förstå instruktionerna för den centrala bearbetningsenheten.
  • För att optimera din kod vad gäller prestanda.

Under loppet av några månader har vi kommit långt, som började med utvecklingen av enkla assemblerspråksprogram för Linux och slutade i den sista artikeln i serien med utvecklingen av fristående kod som körs på en persondator utan operativsystem. Nåväl, nu ska vi försöka samla all information tillsammans och skapa ett riktigt operativsystem. Ja, vi kommer att gå i Linus Torvalds fotspår, men först är det värt att svara på följande frågor: "Vad är ett operativsystem? Vilka av dess funktioner måste vi återskapa?".

I den här artikeln kommer vi bara att fokusera på operativsystemets huvudfunktioner: ladda och köra program. Komplexa operativsystem utför många fler funktioner, som att hantera virtuellt minne och bearbeta nätverkspaket, men de kräver år av kontinuerligt arbete för att implementeras korrekt, så i den här artikeln kommer vi bara att överväga huvudfunktionerna som finns i alla operativsystem. Förra månaden utvecklade vi ett litet program som passade i en 512-byte sektor på en diskett (dess första sektor), och nu kommer vi att modifiera det lite för att lägga till funktionen att ladda ytterligare data från disken.

Utveckling av bootloader

Vi skulle kunna försöka hålla vårt operativsystem binärt så litet som möjligt för att placera det i den första 512-bytesektorn på disketten, den som laddas av BIOS, men i det här fallet kommer vi inte att kunna implementera några intressanta funktioner. Därför kommer vi att använda dessa 512 byte för att placera den binära koden för en enkel systemladdare som kommer att ladda den binära koden för OS-kärnan i RAM-minnet och exekvera den. (Efter det kommer vi att utveckla själva OS-kärnan, som kommer att ladda den binära koden för andra program från disken och även köra den, men vi kommer att prata om detta lite senare.)

Du kan ladda ner källkoden för exemplen som diskuteras i den här artikeln på www.linuxvoice.com/code/lv015/asmschool.zip . Och det här är koden för vår bootloader från en fil som heter boot.asm:

BITS 16 jmp kort start ; Hoppa till etikett skipping disk description nop ; Tillägg före diskbeskrivning %inkludera "bpb.asm" start: mov ax, 07C0h ; Ladda adress mov ds, axe ; Datasegment mov ax, 9000h ; Förbered stack mov ss, ax mov sp, 0FFFFh ; Högen växer ner! cld ; Ställ in riktningsflagga mov si, kern_filnamn anrop load_file jmp 2000h:0000h ; Hoppa till OS-kärnan-binär laddad från filen kern_filename db "MYKERNELBIN" %include "disk.asm" gånger 510-($-$$) db 0 ; Noll utfyllnad av binär kod upp till 510 byte dw 0AA55h; Boot loader binär kod slutmarkeringsbuffert: ; Starta buffert för diskinnehåll

I den här koden är den första CPU-instruktionen jmp-instruktionen, som finns efter BITS-direktivet, som talar om för NASM-montören att 16-bitarsläge används. Som du säkert kommer ihåg från den tidigare artikeln i serien börjar exekveringen av den 512-byte binära koden som laddas av BIOS från disken från början, men vi måste hoppa till etiketten för att hoppa över den speciella datamängden. Förra månaden skrev vi uppenbarligen koden till början av disken (med hjälp av verktyget dd) och lämnade resten av diskutrymmet tomt.

Nu måste vi använda en diskett med ett lämpligt MS-DOS (FAT12) filsystem, och för att fungera korrekt med detta filsystem måste vi lägga till en uppsättning specialdata nära början av sektorn. Denna uppsättning kallas ett "BIOS Parameter Block" (BPB) och innehåller data som disketikett, antal sektorer och så vidare. Det borde inte intressera oss i detta skede, eftersom sådana ämnen kan ägnas åt mer än en serie artiklar, varför vi placerade alla instruktioner och data relaterade till det i en separat källkodsfil som heter bpb.asm .

Baserat på ovanstående är detta direktiv från vår kod extremt viktigt:

%inkludera "bpb.asm"

Detta är ett NASM-direktiv som tillåter innehållet i den angivna källfilen att inkluderas i den aktuella källfilen under montering. Således kommer vi att kunna göra koden för vår systemladdare så kort och begriplig som möjligt genom att flytta alla detaljer om implementeringen av BIOS-parameterblocket till en separat fil. BIOS-parameterblocket måste placeras tre byte efter starten av sektorn, och eftersom jmp-instruktionen bara upptar två byte, måste vi använda nop-instruktionen (dess namn står för "no operation" - det här är en instruktion som inte gör någonting men slösa CPU-cykler ) för att fylla den återstående byten.

Jobbar med stacken

Därefter måste vi använda instruktioner som liknar de som diskuterades i den föregående artikeln för att förbereda registren och stack, såväl som cld-instruktionen (står för "clear direction"), som låter dig ställa in riktningsflaggan för vissa instruktioner, såsom lodsb-instruktionen, som, när den exekveras, kommer att öka värdet i SI-registret snarare än att minska det.

Efter det lägger vi adressen till strängen i SI-registret och anropar vår load_file-funktion. Men tänk efter ett ögonblick – vi har inte utvecklat den här funktionen än! Ja, det är sant, men dess implementering kan hittas i en annan källkodsfil som vi inkluderar kallad disk.asm .

FAT12-filsystemet, som används på disketter som är formaterade i MS-DOS, är ett av de enklaste filsystemen som finns, men det kräver också en hel del kod för att fungera med dess innehåll. Subrutinen load_file är cirka 200 rader lång och kommer inte att visas i den här artikeln, eftersom vi överväger utvecklingen av ett operativsystem, inte en drivrutin för ett specifikt filsystem, därför är det inte särskilt klokt att slösa utrymme på loggsidor på det här sättet. I allmänhet inkluderade vi disk.asm-källkodsfilen nästan innan slutet av den aktuella källfilen och vi kan glömma det. (Om du fortfarande är intresserad av strukturen för FAT12-filsystemet kan du läsa den utmärkta översikten på http://tinyurl.com/fat12spec , och sedan titta på disk.asm-källkodsfilen - koden som finns i den är bra kommenterat.)

Hur som helst laddar subrutinen load_file den binära koden från filen med det namn som anges i SI-registret till segment 2000 med offset 0, varefter vi hoppar till dess början för exekvering. Och det är allt - kärnan i operativsystemet är laddad och systemladdaren har slutfört sin uppgift!

Du kanske har märkt att vår kod använder MYKERNELBIN istället för MYKERNEL.BIN som operativsystemets kärnfilnamn, vilket passar bra med namnschemat 8+3 som används på disketter i DOS. Faktum är att FAT12-filsystemet använder en intern representation av filnamn, och vi sparar utrymme genom att använda ett filnamn som garanterat inte kräver vår load_file-subrutin för att implementera en mekanism för att leta efter ett punkttecken och konvertera filnamnet till den interna representationen av filsystem.

Efter raden med direktivet för anslutning av källkodsfilen disk.asm, finns det två rader utformade för att fylla ut den binära koden för systemladdaren med nollor upp till 512 byte och inkludera slutmärket för dess binära kod (detta diskuterades i den sista artikeln). Slutligen, i slutet av koden finns "buffert"-etiketten, som används av subrutinen load_file. I allmänhet behöver subrutinen load_file ledigt utrymme i RAM-minnet för att utföra några mellanliggande åtgärder i processen att hitta en fil på disken, och vi har tillräckligt med ledigt utrymme efter att ha laddat starthanteraren, så vi placerar bufferten här.

För att montera starthanteraren, använd följande kommando:

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

Nu måste vi skapa en MS-DOS virtuell diskettavbildning och lägga till vår bootloader-binär till sina första 512 byte med hjälp av följande kommandon:

Mkdosfs -C floppy.img 1440 dd conv=notrunc if=boot.bin of=floppy.img

Detta avslutar starthanterarens utvecklingsprocess! Vi har nu en startbar diskettavbildning som låter oss ladda operativsystemets kärnbinär från en fil som heter mykernel.bin och köra den. Därefter väntar vi på en mer intressant del av arbetet - utvecklingen av själva operativsystemets kärna.

operativsystemets kärna

Vi vill att kärnan i vårt operativsystem ska utföra många viktiga uppgifter: visa en hälsning, acceptera input från användaren, avgöra om inmatningen är ett kommando som stöds och köra program från disken efter att användaren anger sina namn. Det här är operativsystemets kärnkod från filen mykernel.asm:

mov ax, 2000h mov ds, ax mov es, ax loop: mov si, prompt call lib_print_string mov si, user_input call lib_input_string cmp byte , 0 je loop cmp word , "ls" je list_files mov ax, si mov cx anrop, 32768 jc load_fail anrop 32768 jmp loop load_fail: mov si, load_fail_msg anrop lib_print_string jmp loop list_files: mov si, file_list anrop lib_get_file_list anrop lib_print_string jmp loop prompt db 13, 10, "MyOS 13, 10, "Mitt OS > 1, 0, 0, 0, 0, 0, 0,1 ", 0 user_input gånger 256 db 0 file_list gånger 1024 db 0 %inkludera "lib.asm"

Innan du tittar på koden, var uppmärksam på den sista raden med direktivet för att inkludera källkodsfilen lib.asm, som också finns i asmschool.zip-arkivet från vår webbplats. Detta är ett bibliotek med användbara underrutiner för att arbeta med skärmen, tangentbordet, linjerna och diskarna som du också kan använda - i det här fallet inkluderar vi denna källkodsfil i slutet av huvudkällkodsfilen för operativsystemets kärna i ordning att göra den senare så kompakt och vacker som möjligt . Se avsnittet "lib.asm biblioteksrutiner" för mer information om alla tillgängliga rutiner.

I de första tre raderna i operativsystemets kärnkod fyller vi segmentregistren med data för att peka på 2000-segmentet där den binära koden laddades in. Detta är viktigt för att säkerställa att instruktioner som lodsb , som ska läsa data från det aktuella segmentet och inte från något annat, fungerar korrekt. Efter det kommer vi inte att utföra några ytterligare operationer på segmenten; vårt operativsystem kommer att köras med 64 KB RAM!

Längre in i koden finns en etikett som motsvarar början av slingan. Först och främst använder vi en av rutinerna från lib.asm-biblioteket, nämligen lib_print_string , för att skriva ut hälsningen. Byte 13 och 10 före hälsningsraden är nyradstecken, på grund av vilka hälsningen inte kommer att visas direkt efter utmatningen av något program, utan alltid på en ny rad.

Efter det använder vi en annan rutin från lib.asm-biblioteket som heter lib_input_string , som tar de tecken som användaren matat in med tangentbordet och lagrar dem i en buffert, vars pekare finns i SI-registret. I vårt fall deklareras bufferten mot slutet av operativsystemets kärnkod enligt följande:

User_input gånger 256 db 0

Denna deklaration tillåter en nollfylld buffert med 256 tecken, som bör vara tillräckligt lång för att hålla kommandon för ett enkelt operativsystem som vårt!

Därefter utför vi validering av användarinmatning. Om den första byten i user_input-bufferten är null, tryckte användaren helt enkelt på Enter utan att ange något kommando; Glöm inte att alla strängar slutar med nolltecken. Så i det här fallet ska vi bara hoppa till början av slingan och skriva ut hälsningen igen. Men om användaren anger något kommando måste vi först kontrollera om han skrev in kommandot ls. Hittills har du bara sett jämförelser av enstaka bytes i våra assemblerprogram, men glöm inte att det också är möjligt att jämföra tvåbytevärden eller maskinord. I denna kod jämför vi det första maskinordet från user_input-bufferten med maskinordet som motsvarar raden ls, och om de är identiska går vi till kodblocket nedan. Inom detta kodblock använder vi en annan rutin från lib.asm-biblioteket för att få en kommaseparerad lista med filer på disken (som ska lagras i file_list-bufferten), skriva ut den listan till skärmen och gå tillbaka till processen användarinmatning.

Utförande av tredjepartsprogram

Om användaren inte anger kommandot ls, antar vi att han skrev in programnamnet från disken, så det är vettigt att försöka ladda det. Vårt lib.asm-bibliotek innehåller en implementering av en användbar subrutin lib_load_file , som analyserar tabellerna i FAT12-filsystemet på en disk: det tar en pekare till början av en rad med ett filnamn med hjälp av AX-registret, samt en offsetvärde för att ladda en binär kod från en programfil med hjälp av CX-registret. Vi använder redan SI-registret för att lagra en pekare till användarinmatningssträngen, så vi kopierar den pekaren till AX-registret och lägger sedan in värdet 32768, som används som en offset för att ladda binären från programfilen, i CX-registret.

Men varför använder vi detta värde som en offset för att ladda binär kod från en programfil? Tja, det är bara ett av minneskartalternativen för vårt operativsystem. Eftersom vi arbetar i ett enda 64KB-segment och vår kärnbinär laddas med offset 0, måste vi använda de första 32KB minnet för kärndata och de återstående 32KB för laddningsbara programdata. Således är offset 32768 mitten av vårt segment och låter oss tillhandahålla en tillräcklig mängd RAM till både operativsystemets kärna och laddade program.

Efter det utför rutinen lib_load_file en mycket viktig operation: om den inte kan hitta en fil med det givna namnet på disken, eller av någon anledning inte kan läsa den från disken, avslutas den helt enkelt och ställer in en speciell bärflagga. Detta är en CPU-statusflagga som ställs in under vissa matematiska operationer och bör inte vara av intresse för oss för tillfället, men vi kan fastställa förekomsten av denna flagga för att fatta snabba beslut. Om subrutinen lib_load_asm anger bärflaggan använder vi instruktionen jc (hopp om bär) för att hoppa till kodblocket som skriver ut felmeddelandet och återgår till början av användarinmatningsslingan.

I samma fall, om överföringsflaggan inte är inställd, kan vi dra slutsatsen att subrutinen lib_load_asm framgångsrikt laddade den binära koden från programfilen till RAM-minnet på adress 32768. Allt vi behöver i detta fall är att initiera exekveringen av den binära koden laddas på denna adress, det vill säga börja köra programmet som specificerats av användaren! Och efter att ret-instruktionen har använts i det här programmet (för att återgå till anropskoden), måste vi bara återgå till användarinmatningsslingan. Sålunda har vi skapat ett operativsystem: det består av de enklaste mekanismerna för att analysera kommandon och ladda program, implementerade inom cirka 40 rader assemblerkod, om än med mycket hjälp från subrutiner från biblioteket lib.asm.

För att sätta ihop operativsystemets kärnkod, använd följande kommando:

Nasm -f bin -o mykernel.bin mykernel.asm

Efter det måste vi på något sätt lägga till filen mykernel.bin till diskettavbildningsfilen. Om du är bekant med tricket med att montera skivbilder med loopback-enheter, kan du komma åt innehållet i diskavbildningen floppy.img med den, men det finns ett enklare sätt, som är att använda GNU Mtools (www.gnu.org) /programvara /mtools). Detta är en uppsättning diskettprogram som använder MS-DOS/FAT12-filsystem, tillgängliga från programvarupaketförråden för alla populära Linux-distributioner, så du behöver bara använda apt-get , yum , pacman eller något annat verktyg som brukade installera programpaket på din distribution.

Efter att du har installerat lämpligt programpaket, för att lägga till filen mykernel.bin till diskavbildningsfilen floppy.img, måste du köra följande kommando:

Mcopy -i floppy.img mykernel.bin::/

Lägg märke till de roliga karaktärerna i slutet av kommandot: kolon, kolon och snedstreck. Nu är vi nästan redo att lansera vårt operativsystem, men vad är poängen med det tills det finns applikationer för det? Låt oss rätta till detta missförstånd genom att utveckla en extremt enkel applikation. Ja, nu kommer du att utveckla en applikation för ditt eget operativsystem - tänk bara hur mycket din auktoritet kommer att stiga i nördarna. Spara följande kod i en fil som heter test.asm:

Org 32768 mov ah, 0Eh mov al, "X" int 10h ret

Denna kod använder helt enkelt BIOS-funktionen för att visa tecknet "X" på skärmen, varefter den återställer kontrollen till koden som kallade den - i vårt fall är denna kod koden för operativsystemet. Organisationsraden som startar applikationens källkod är inte en CPU-instruktion, utan ett NASM assembler-direktiv som säger att den binära koden kommer att laddas in i RAM-minnet vid offset 32768, därför är det nödvändigt att räkna om alla offsets med hänsyn till denna omständighet .

Den här koden måste också monteras, och den resulterande binära filen måste läggas till i diskettavbildsfilen:

Nasm -f bin -o test.bin test.asm mcopy -i floppy.img test.bin::/

Ta nu ett djupt andetag, gör dig redo att bevittna de oöverträffade resultaten av ditt eget arbete och starta upp diskettavbildningen med en PC-emulator som Qemu eller VirtualBox. Till exempel kan följande kommando användas för detta ändamål:

Qemu-system-i386 -fda floppy.img

Voila: boot.img-starthanteraren som vi integrerade i den första sektorn av diskavbildningen laddar operativsystemkärnan mykernel.bin, som visar en hälsning. Skriv kommandot ls för att få namnen på de två filerna på disken (mykernel.bin och test.bin), skriv sedan namnet på den sista filen som ska köras och visa X-tecknet på skärmen.

Det är coolt, eller hur? Nu kan du börja anpassa ditt operativsystems skal, lägga till implementeringar av nya kommandon och lägga till ytterligare programfiler på disken. Om du vill köra det här operativsystemet på en riktig PC bör du hänvisa till avsnittet "Köra starthanteraren på en riktig hårdvaruplattform" från föregående artikel i serien - du behöver exakt samma kommandon. Nästa månad kommer vi att göra vårt operativsystem kraftfullare genom att tillåta nedladdningsbara program att använda systemfunktioner, och på så sätt implementera konceptet med kodseparation för att minska kodduplicering. Mycket av arbetet ligger kvar.

lib.asm biblioteksrutiner

Som nämnts tidigare, tillhandahåller biblioteket lib.asm en stor uppsättning användbara subrutiner för användning inom ditt operativsystems kärnor och individuella program. Vissa av dem använder instruktioner och begrepp som ännu inte har behandlats i artiklarna i denna serie, andra (som rutiner för att arbeta med diskar) är nära besläktade med filsystemens struktur, men om du anser dig vara kompetent i dessa frågor, du kan bekanta dig med deras implementeringar och förstå principen för arbetet. Det är dock viktigare att förstå hur man ringer dem från din egen kod:

  • lib_print_string - Tar en pekare till en nollterminerad sträng via SI-registret och skriver ut den strängen till skärmen.
  • lib_input_string - tar en pekare till en buffert via SI-registret och fyller denna buffert med tecken som användaren matar in med tangentbordet. Efter att användaren tryckt på Enter-tangenten avslutas strängen i bufferten null och kontrollen återgår till det anropande programmets kod.
  • lib_move_cursor - Flyttar markören på skärmen till positionen med koordinaterna passerade genom DH (radnummer) och DL (kolumnnummer) registren.
  • lib_get_cursor_pos - anrop denna subrutin för att få aktuell rad- och kolumnnummer med hjälp av DH- respektive DL-registren.
  • lib_string_uppercase - Tar en pekare till början av en nollterminerad sträng med hjälp av AX-registret och konverterar tecknen i strängen till versaler.
  • lib_string_length - Tar en pekare till början av en noll-terminerad sträng med hjälp av AX-registret och returnerar dess längd med hjälp av AX-registret.
  • lib_string_compare - Tar pekare till början av två nollterminerade strängar via SI- och DI-registren och jämför dessa strängar. Ställer bärflaggan om strängarna är identiska (för att använda en hoppinstruktion beroende på jc carry-flaggan) eller rensa denna flagga om strängarna är olika (för att använda jnc-instruktionen).
  • lib_get_file_list - Tar en pekare till början av en buffert via SI-registret och lägger in en noll-terminerad sträng som innehåller en kommaseparerad lista med filnamn från disken i den bufferten.
  • lib_load_file - Tar en pekare till början av en sträng som innehåller ett filnamn med hjälp av AX-registret och laddar innehållet i filen med den offset som ges av CX-registret. Returnerar antalet byte som kopierats till minnet (det vill säga storleken på filen) med hjälp av BX-registret, eller ställer in bärflaggan om ingen fil med det angivna namnet hittas.

Denna artikelserie ägnas åt lågnivåprogrammering, det vill säga datorarkitektur, operativsystemdesign, assemblerspråksprogrammering och relaterade områden. Hittills är två habrausare sysselsatta med att skriva – och. För många gymnasieelever, studenter och professionella programmerare visar sig dessa ämnen vara mycket svåra att lära sig. Det finns mycket litteratur och kurser som ägnas åt programmering på låg nivå, men det är svårt att få en komplett och heltäckande bild av dem. Det är svårt, efter att ha läst en eller två böcker om assembler och operativsystem, att åtminstone i allmänna termer föreställa sig hur detta komplexa system av järn, kisel och många program - en dator - faktiskt fungerar.

Alla löser problemet med lärande på sitt eget sätt. Någon läser mycket litteratur, någon försöker snabbt gå vidare för att öva och förstå på vägen, någon försöker förklara för vänner allt han studerar. Och vi bestämde oss för att kombinera dessa tillvägagångssätt. Så i den här artikeln kommer vi steg för steg att demonstrera hur man skriver ett enkelt operativsystem. Artiklarna kommer att vara av översiktskaraktär, det vill säga att de inte kommer att innehålla uttömmande teoretisk information, dock kommer vi alltid att försöka tillhandahålla länkar till bra teoretiskt material och svara på alla frågor som dyker upp. Vi har ingen tydlig plan, så många viktiga beslut kommer att tas längs vägen, med hänsyn till din feedback.

Vi kan medvetet leda utvecklingsprocessen in i en återvändsgränd för att tillåta dig och oss själva att fullt ut förstå konsekvenserna av ett felaktigt beslut, samt finslipa vissa tekniska färdigheter på det. Så ta inte våra beslut som de enda sanna och tro oss blint. Vi betonar än en gång att vi förväntar oss att läsarna är aktiva i att diskutera artiklar, vilket i hög grad bör påverka den övergripande processen för att utveckla och skriva efterföljande artiklar. Helst skulle jag vilja att några av läsarna hänger med i utvecklingen av systemet över tid.

Vi kommer att anta att läsaren redan är bekant med grunderna i assembly och C-språk, såväl som elementära begrepp för datorarkitektur. Det vill säga, vi kommer inte att förklara vad ett register eller, säg, RAM är. Saknar du kunskap kan du alltid vända dig till ytterligare litteratur. En kort lista med referenser och länkar till webbplatser med bra artiklar finns i slutet av artikeln. Det är också önskvärt att kunna använda Linux, eftersom alla kompileringsinstruktioner kommer att ges för detta system.

Och nu - närmare saken. I resten av artikeln kommer du och jag att skriva det klassiska programmet "Hello World". Vår Halloweenvärld kommer att vara lite specifik. Den kommer inte att köras från något operativsystem, utan direkt så att säga "på bar metall". Innan vi fortsätter direkt till att skriva koden, låt oss ta reda på exakt hur vi försöker göra detta. Och för detta måste du överväga processen att starta upp datorn.

Så ta din favoritdator och tryck på den största knappen på systemenheten. Vi ser en glad skärmsläckare, systemenheten gnisslar glatt med en högtalare, och efter ett tag laddas operativsystemet. Som du förstår lagras operativsystemet på hårddisken, och här uppstår frågan: hur startade operativsystemet på magiskt sätt i RAM och började köra?

Vet detta: systemet som finns på vilken dator som helst är ansvarigt för detta, och dess namn är nej, inte Windows, pip din tunga - det kallas BIOS. Dess namn står för Basic Input-Output System, det vill säga det grundläggande input-output-systemet. BIOS sitter på en liten mikrokrets på moderkortet och startar direkt efter att du tryckt på den stora ON-knappen. BIOS har tre huvuduppgifter:

  1. Upptäck alla anslutna enheter (processor, tangentbord, bildskärm, RAM, grafikkort, huvud, armar, vingar, ben och svansar...) och testa dem för funktionalitet. POST-programmet (Power On Self Test) ansvarar för detta. Om vital hårdvara inte hittas kan ingen programvara hjälpa, och vid denna tidpunkt kommer systemhögtalaren att gnisa något illavarslande och operativsystemet kommer inte att nå alls. Låt oss inte prata om sorgliga saker, låt oss anta att vi har en fullt fungerande dator, glädjas och gå vidare till att överväga den andra BIOS-funktionen:
  2. Ger operativsystemet en grundläggande uppsättning funktioner för att arbeta med järn. Genom BIOS-funktioner kan du till exempel visa text på skärmen eller läsa data från tangentbordet. Det är därför det kallas det grundläggande input-output-systemet. Vanligtvis kommer operativsystemet åt dessa funktioner genom avbrott.
  3. Startar operativsystemets loader. I det här fallet läses som regel startsektorn - den första sektorn av informationsbäraren (diskett, hårddisk, CD, flashenhet). Medieavfrågningsordningen kan ställas in i BIOS SETUP. Startsektorn innehåller ett program som ibland kallas den primära starthanteraren. Grovt sett är bootloaderns uppgift att starta operativsystemet. Processen att ladda ett operativsystem kan vara mycket specifik och mycket beroende av dess funktioner. Därför skrivs den primära laddaren direkt av OS-utvecklare och skrivs till startsektorn under installationen. När starthanteraren startar är processorn i verkligt läge.
Den tråkiga nyheten: storleken på starthanteraren bör endast vara 512 byte. Varför så få? För att göra detta måste vi bekanta oss med enheten för disketten. Här är en pedagogisk bild:

Bilden visar ytan på diskenheten. En diskett har 2 ytor. På varje yta finns ringformade spår (spår). Varje spår är uppdelat i små bågformade bitar som kallas sektorer. Så historiskt sett har en diskettsektor en storlek på 512 byte. Den allra första sektorn på disken, bootsektorn, läses av BIOS "in i nollminnessegmentet vid offset 0x7C00, och sedan överförs kontrollen till denna adress. Starthanteraren laddar vanligtvis in i minnet inte själva operativsystemet, utan en annan loader-program lagrat på disken, men av någon anledning (mest troligt är detta skälet storleken) som inte passar in i en sektor. Och eftersom vårt operativsystem hittills spelas av en banal helloworld, är vårt huvudmål att få datorn att tro på existensen av vårt operativsystem, även om det är på en sektor, och kör det.

Hur är bootsektorn upplagd? På en PC är det enda kravet för en startsektor att dess sista två byte innehåller värdena 0x55 och 0xAA, signaturen för startsektorn. Så det är redan mer eller mindre klart vad vi behöver göra. Låt oss skriva kod! Ovanstående kod är skriven för yasm assembler.

Section.text use16 org 0x7C00 ; vårt program laddas vid 0x7C00 start: mov ax, cs mov ds, ax ; välj datasegment mov si, meddelande cld ; riktning för strängkommandon mov ah, 0x0E ; BIOS-funktionsnummer mov bh, 0x00 ; videominnessida puts_loop: lodsb ; ladda nästa tecken i al test al, al ; noll-tecken betyder slutet av raden jz puts_loop_exit int 0x10 ; anropa BIOS-funktionen jmp puts_loop puts_loop_exit: jmp $ ; evig loop meddelande: db "Hello World!", 0 finish: gånger 0x1FE-finish+start db 0 db 0x55, 0xAA ; bootsektorsignatur

Detta korta program kräver ett antal viktiga förklaringar. org 0x7C00-raden behövs så att assemblern (som betyder programmet, inte språket) korrekt beräknar adresserna för etiketter och variabler (puts_loop, puts_loop_exit, meddelande). Så vi berättar för honom att programmet kommer att laddas in i minnet vid 0x7C00.
I rader
mov yxa, cs mov ds, yxa
datasegmentet (ds) sätts lika med kodsegmentet (cs), eftersom både data och kod i vårt program lagras i samma segment.

Då visas meddelandet "Hello World!" tecken för tecken i slingan. För detta används funktionen 0x0E av avbrott 0x10. Den har följande alternativ:
AH = 0x0E (funktionsnummer)
BH = videosidnummer (inte bry dig ännu, ange 0)
AL = ASCII teckenkod

På raden "jmp $" hänger programmet. Och med rätta, det finns inget behov för henne att exekvera extra kod. Men för att datorn ska fungera igen måste du starta om.

På raden " gånger 0x1FE-slut+start db 0 " fylls resten av programkoden (förutom de två sista byten) med nollor. Detta görs så att efter kompilering visas signaturen för startsektorn i programmets två sista byte.

Vi har liksom listat ut programkoden, nu ska vi försöka kompilera denna lycka. För att kompilera behöver vi faktiskt en assembler - den ovan nämnda yasm . Det är tillgängligt i de flesta Linux-förråd. Programmet kan sammanställas så här:

$ yasm -f bin -o hello.bin hello.asm

Den resulterande hello.bin-filen måste skrivas till diskettstartsektorn. Detta görs ungefär så här (naturligtvis, istället för fd, måste du ersätta namnet på din enhet).

$ dd if=hej.bin av=/dev/fd

Eftersom inte alla har hårddiskar och disketter kan du använda en virtuell maskin, till exempel qemu eller VirtualBox. För att göra detta måste du göra en diskettavbildning med vår bootloader och sätta in den i den "virtuella diskenheten".
Skapa en diskavbildning och fyll den med nollor:

$ dd if=/dev/zero of=disk.img bs=1024 count=1440

Vi skriver vårt program i början av bilden:
$ dd if=hej.bin of=disk.img conv=notrunc

Kör den resulterande bilden i qemu:
$ qemu -fda disk.img -boot a

Efter lanseringen bör du se ett qemu-fönster med en glad rad "Hello World!". Det är här den första artikeln slutar. Vi kommer gärna att se din feedback och önskemål.

Vad du behöver veta för att skriva ett operativsystem

Att skapa ett operativsystem är en av de svåraste uppgifterna inom programmering, eftersom det kräver omfattande och komplex kunskap om driften av en dator. Vilka? Vi förstår nedan.

Vad är OS

Operativsystemet (OS) är programvaran som arbetar med datorhårdvara och dess resurser och är bryggan mellan hårdvaran och mjukvaran på datorn.

Första generationens datorer hade inga operativsystem. Programmen på de första datorerna inkluderade kod för direkt drift av systemet, kommunikation med kringutrustning och beräkningar, för vilka detta program skrevs. På grund av denna anpassning var även program som var enkla i logik svåra att implementera i mjukvara.

I takt med att datorer har blivit mer mångfaldiga och komplexa har det helt enkelt blivit obekvämt att skriva program som fungerar som både operativsystem och program. Därför, för att göra program lättare att skriva, började datorägare utveckla programvara. Det var så operativsystem kom till.

OS tillhandahåller allt som behövs för driften av användarprogram. Deras utseende innebar att program nu inte behöver kontrollera hela mängden arbete på datorn (detta är ett bra exempel på inkapsling). Nu behövde programmen fungera med operativsystemet, och systemet tog själv hand om resurser och arbete med kringutrustning (tangentbord, skrivare).

Kort om operativsystemets historia

C språk

Som nämnts ovan finns det flera programmeringsspråk på hög nivå för att skriva ett OS. Den mest populära av dem är dock C.

Du kan börja lära dig detta språk härifrån. Den här resursen kommer att introducera dig till de grundläggande begreppen och förbereda dig för mer avancerade uppgifter.

Learn C the Hard Way är titeln på en annan bok. Förutom den vanliga teorin innehåller den många praktiska lösningar. Denna handledning kommer att täcka alla aspekter av språket.

Eller så kan du välja en av dessa böcker:

  • "The C Programming Language" av Kernighan och Ritchie;
  • "C Programming Absolute Beginner's Guide" av Parry och Miller.

OS utveckling

När du har behärskat allt du behöver veta om datavetenskap, assemblerspråk och C, bör du läsa minst en eller två böcker om direkt OS-utveckling. Här är några resurser för det:

Linux från grunden. Här övervägs monteringsprocessen för Linux-operativsystemet (handledningen har översatts till många språk, inklusive ryska). Här, liksom i andra läroböcker, kommer du att förses med alla nödvändiga grundläggande kunskaper. Om du litar på dem kan du prova dig fram med att skapa ett OS. För att göra mjukvarudelen av operativsystemet mer professionell finns det tillägg till handledningen: "

Om du upptäcker ett fel, välj en textbit och tryck på Ctrl + Retur
DELA MED SIG: