Öppna systemstandarder som styr programvaruverktygens struktur och gränssnitt. Typer av operativsystem


Virtuella maskinsystem

Multitasking i operativsystem

Multitasking är en egenskap hos operativsystemet eller programmeringsmiljön för att ge möjlighet till parallell (eller pseudoparallell) bearbetning av flera processer. Sann multitasking av ett operativsystem är endast möjligt i distribuerade datorsystem.

Det finns två typer av multitasking:

Process multitasking (baserat på processer - samtidigt kör program). Här är ett program den minsta kod som operativsystemets schemaläggare kan hantera. Bättre känd för de flesta användare (arbetar i en textredigerare och lyssnar på musik).

Trådmultitasking (trådbaserad). Den minsta enheten med hanterad kod är en tråd (ett program kan utföra två eller flera uppgifter samtidigt).

SVM (VM och dess tidiga version CP / CMS) är det första systemet där virtuell maskinteknik implementerades. Virtualisering i CBM var konsekvent och komplett, i synnerhet kunde en annan kopia av CBM-systemet köras på en virtuell maskin och så vidare. Dessutom var lanseringen av CBM på en virtuell CBM-maskin den rekommenderade metoden för att skapa en ny version av systemet för installation. I synnerhet innebar detta att någon riktig datorenhet kunde representeras av en eller annan metod som en virtuell enhet på en virtuell maskin. Hittills har ingen annan implementering av virtuella maskiner den här egenskapen.

Ett öppet system är ett system som består av komponenter som interagerar med varandra genom standardgränssnitt. "Denna definition, som ges av en av författarna till den ovannämnda manualen, Jean-Michel Cornu, betonar systemaspekten (strukturen för ett öppet system ).

"En omfattande och harmoniserad uppsättning internationella standarder för informationsteknik och funktionella standardprofiler som anger gränssnitt, tjänster och stödformat för att säkerställa interoperabilitet och mobilitet för applikationer, data och personal." Denna definition, som ges av experter från IEEE, betonar den aspekt av miljön som ett öppet system tillhandahåller för dess användning (extern beskrivning av ett öppet system).

Förmodligen har en ganska fullständig och allmänt accepterad definition av öppna system ännu inte dykt upp. Det som har sagts ovan är dock redan tillräckligt för att kunna överväga de allmänna egenskaperna hos öppna system och för att klargöra kärnan i problemen som är associerade med dem.

De allmänna egenskaperna hos öppna system bildas vanligtvis enligt följande:

töjbarhet / skalbarhet,

mobilitet (portabilitet) - portabilitet,

interoperabilitet (förmåga att interagera med andra system) - interoperabilitet,

användarvänlighet, inkl. - enkel hantering - körbarhet.

De stora skillnaderna i RTOS-specifikationer och det enorma antalet befintliga mikrokontroller lyfter fram problemet med standardisering i realtidssystem.

Den tidigaste och mest utbredda RTOS-standarden är POSIX (IEEE bärbart operativsystemgränssnitt för datormiljöer, IEEE 1003.1). Den ursprungliga versionen av POSIX-standarden dök upp 1990 och var avsedd för UNIX-system, vars första versioner dök upp på 70-talet under förra seklet. POSIX-specifikationen definierar en standardmekanism för interaktion mellan en applikation och ett operativsystem och innehåller för närvarande en uppsättning med mer än 30 standarder. För RTOS är sju av dem viktigast (1003.1a, 1003.1b, 1003.1c, 1003.1d, 1003.1j, 1003.21, 1003.2h), men bara de tre första har fått omfattande stöd i kommersiella operativsystem.

Trots de tydligt föråldrade bestämmelserna i POSIX-standarden och den stora efterfrågan på standardiseringsuppdateringar för RTOS har inga märkbara framsteg gjorts i denna riktning.

POSIX-standarden skapades som ett standardtjänstgränssnitt för operativsystem. Denna standard gör det möjligt att skapa bärbara applikationer. Därefter utökades denna standard med funktioner i realtidsläget.

POSIX-specifikationen definierar en standardmekanism för kommunikation mellan en applikation och ett operativsystem. Det bör noteras att POSIX-standarden är nära besläktad med Unix-operativsystemet; ändå försöker utvecklarna av många RTOS att upprätthålla överensstämmelse med denna standard.

POSIX-överensstämmelse med operativsystemet och maskinvaruplattformen måste certifieras genom att köra testsviter på dem. Men om operativsystemet inte är Unix-liknande blir det svårt att uppfylla detta krav. Testpaket finns bara för POSIX 1003.1a. Eftersom POSIX-strukturen är en samling av valfria funktioner kan OS-leverantörer bara implementera en del av standardgränssnittet och fortfarande prata om POSIX-kompatibla system.

Även om POSIX-standarden växte ut ur Unix, berör den de underliggande abstraktionerna av operativsystem, och realtidstillägg är tillämpliga på alla RTOS.

Ett antal utländska organisationer och industriföretag under ledning IEEEsedan 1990 har det skett en aktiv utveckling av konsekventa versioner av öppna systemgränssnittsstandarder POSIX(Portabla operativsystemsgränssnitt). Mycket arbete har gjorts för att revidera, utöka och omorganisera ett tjugotal grundspecifikationer POSIX1990-1998 IEEE 1003.Förbättrad systematisering och struktur av standarder, förbättrad användarvänlighet för användarna. Som ett resultat, beredd ett omfattande utkast till en grundläggande internationell standard i fyra stora delarISO 9945: 1-4: 2003 (DEEE 1003.1 - 2003), över tre tusen sidor. Denna standard är en gemensam utveckling av IEEE och The Open Group och är både en IEEE-standard, en ISO-standard och en Open Group Technical-standard.

Syftet med dokumentet ärstandardisering i mjukvaruutvecklingsäkerställa portabilitet av program på källkodsnivå. Den definierar de viktigaste operativsystem och miljögränssnitt, skalgränssnitt och vanliga verktygsprogram.

Tre separata huvudvolymer inkluderar:grundläggande definitioner; systemgränssnitt; kontrollkommandon och serviceprogram (verktyg). Dessutom finns det en stor fjärde volym av allmänna skäl för de valda POSIX-systembesluten. Integritet, modulering av deras konstruktion och parametrerbarhet är viktiga egenskaper hos de utvecklade mjukvarugränssnitten.

Öppna systemstandarder - POSIX reglerar en uppsättning grundläggande systemtjänster för att tillhandahålla enhetliga applikationsprogramgränssnitt som anges för C-språket, kommandospråket och en uppsättning verktygsprogram. Primärt mål -göra program portabla över olika källspråk. Varje programgränssnitt har en uppringare och en uppringd part, och POSIX-standarderna är främst inriktade på att formalisera uppringaren. Applikationsmobilitet måste säkerställas genom användning av ett stort antal standardiserade systemgränssnitstjänster och förmågan att dynamiskt bestämma målplattformens egenskaper och justera applikationsgränssnitten för dem.

Vid formning begreppet POSIX-standarder ställer in följande uppgifter:

  • - för att underlätta och automatisera portering av färdig applikationskod till andra plattformar;
  • - att bidra till att i förväg definiera och förena gränssnitt för programvarukomponenter vid utformning av programvara, och inte bara i samband med implementeringen;
  • - spara, om möjligt, och ta hänsyn till alla de viktigaste, tidigare skapade, ärvda och använda programvaruverktyg och komponenter;
  • - att bestämma det minsta möjliga gränssnitt för komponenter och programvarukomplex för att påskynda skapandet och utbyggnaden av programvaruprodukter, samt för analys, godkännande och godkännande av dokument;
  • - att utveckla standarder för att säkerställa kommunikationsnät, distribuerad databehandling och informationsskydd,
  • - att rekommendera att användningen av objektkod för program i enkla system begränsas.

Utvecklare nya versioner av standarder grupp POSIXtog noga hänsyn till deras förhistoria och förekomsten av många arv, skapade och utvecklade komponenter och programvarukomplex som uppfyller tidigare versioner av dessa standarder. Under utvecklingen av standarder observerades principen om bakåtkompatibilitet - nya gränssnitt lades till så att de inte kom i konflikt med gamla. Detta har dock inte implementerats helt, och vissa gränssnitt i återanvändbara program måste justeras när de används i nya programverktyg.

Syftet med denna version av standarden är att minimera ytterligare arbete för applikationsutvecklare. Eftersom varje historisk implementering av en standard oundvikligen kan ändras, hur små som helst, måste applikationerna oundvikligen ändras för att bli konsekventa. Konceptuellt beskriver denna standard en uppsättning grundläggande tjänster som krävs för effektiv applikationsutveckling. Dessa tjänster nås genom att definiera ett gränssnitt med C-programmeringsspråket, en kommandospråkprocessor och standardtjänstprogram (verktyg) som skapar standardsemantik och syntax för gränssnitten. Denna standard är avsedd för alla som, beroende på vilken typ av aktiviteter de har, är associerade med operativsystem baserade på UNIX.

Målet var också att främja applikationsmobilitet över hela operativsystemet. UNIX,utveckla en tydlig, konsekvent och entydig standard för gränssnittsspecifikationen för ett bärbart operativsystem baserat på systemdokumentationen UNIX.Denna standard kodar alla befintliga typiska operativsystemformuleringar. UNIX.Standarden utformades så att ett program skrivet och översatt för körning på en viss OS-version också kan översättas till en annan OS-version. Denna standard garanterar dock inte att den använda koden (objektkoden) ger ett lämpligt resultat på ett annat operativsystem än det som programmet översattes för, även med identisk hårdvara.

Utvecklarna av denna standard har försökt uppnå en bredast möjlig tillämpning av denna standard på hela utbudet av befintliga och potentiella operativsystem. Det fanns dock enighet om vilken uppsättning funktioner, typer, definitioner (definitioner) och begrepp som är avsedda att bilda gränssnittet som är gemensamt för de flesta av de historiskt etablerade typerna av SS-implementering. Tidigare standarder och deras modifieringar har avslöjat många områden av inkonsekvens, vilket till stor del elimineras i den här versionen. Den reviderade utgåvan av standarden syftar till att minimera antalet ändringar av implementeringar som överensstämmer med tidigare versioner av godkända standarder som krävs för att anpassa dem till denna standard.

Ny version internationella standarderPOSIXutgör de fyra delarna av standarderna som kommenteras nedan ISO 9945: 1-4: 2003 -DEN. Bärbara gränssnitt för operativsystem. Del 1. Grundläggande definitioner. Del 2. Systemgränssnitt. Del 3. Kommandon för lednings- och serviceprogram. Del 4. Motivering.

StandardISO 9945-1: 2003 -innehåller grundläggande konceptuella definitioner och detaljerade förklaringar av metoder för att implementera gränssnitt för att säkerställa bärbarhet av komponenter och programvarukomplex, en innehållsförteckning som är gemensam för alla volymer, inklusive serviceavtal och definitioner av C-rubriker. I ett stort avsnitt - koncept - beskrivs: baskataloger; filhierarki; nätverk; tidsmätning och synkronisering; processer för återanvändning av komponenter; köpolicy och semaforer. Ytterligare markerad avsnitt .

  • - en beskrivning av syntax och organisering av data i filer för systemgränssnitt och interaktion med terminaler;
  • - egenskaper hos bärbara komponenter, kodningsspråk, filbeskrivning;
  • - lokala variabler, definitioner och grammatik;
  • - beskrivning av miljövariabler, internationalisering av variabler;
  • - kontextoberoende syntax för att representera egenskaper hos variabla komponenter, definiera reguljära uttryck, generera krav, grund och grammatik för uttryck, utökade uttryck;
  • - struktur för kataloger, filer och enheter, typer av terminaler;
  • - grundläggande gränssnitt för terminaltyper, parametrar för möjliga enheter;
  • - konvention och riktlinjer för syntaxen för verktygsargument;
  • - innehåll av prototypfunktioner, beskrivning av symboliska konstanter, makron, förprocessorer, definitionstyper, ingångsformat.

StandardISO 9945-2: 2003 -Systemgränssnitt - klargör och detaljerar: begreppet portabilitet och principerna för dess tillhandahållande genom att förena gränssnitt för applikationsprogram med operativsystem, servicefunktioner inriktade på C-programmeringsspråket, funktionella problem, inklusive rörlighet, felhantering och eliminering av fel. Han innehåller tre stora sektioner:

  • - introduktion;
  • - innehållet i grundläggande information,
  • - systemgränssnitt.

Dokumentet är avsett för utvecklare av applikationer där det är nödvändigt att säkerställa mobilitet för program och data, för utvecklare och användare av operativsystem, liksom för köpare av datorutrustning och programvara. Huvudmålet för standarden är att förena applikationsgränssnitt och anslutningar med operativsystemets kärnmiljö genom att formalisera beskrivningar av interna och externa gränssnitt i det grundläggande C-programmeringsspråket. En beskrivning av syntaxen och semantiken för relationer som används vid utformningen av bärbara applikationer presenteras konceptuellt. Föreningen är fokuserad på UNIX-versioner, liksom på andra operativsystem som är gränssnittskompatibla med UNIX-versioner. Utvecklarna av standarden försökte tillhandahålla alla potentiella funktioner för interaktion av applikationsprogram med OS-kärnan på olika hårdvaruplattformar, i autonoma nätverk och distribuerade informationssystem. För detta presenteras innehållet i 1123-gränssnitt, inklusive: beskrivning, typisk struktur, möjliga fel, exempel och skäl för var och en.

De språkorienterade tjänsterna och gränssnitten för C-språket i standarden består av två delar: en kärna som interagerar med kärnan i ett operativsystem på C-språket och en förlängning för interaktion med applikationsprogram implementerade på olika språk. Den inledande, styva inriktningen av POSIX-standarderna på UNIX OS har därefter förändrats och utvidgats till alla driftsmiljöer som ger implementeringen av konceptet med öppna system. Det noteras att det är tillrådligt att tillämpa konceptet och uppsättningen standarder för interaktion med den yttre miljön, som är utvecklingen av den grundläggande referensmodellen för sammankoppling av öppna system (OSI) - ISO 7498.

I den tredje delen standard-ISO 9945-3: 2003 -Skal och verktyg - ger en specifik representation av operativsystemskommandon och verktyg som ger enhetlig interaktion med mobilapplikationer, definitioner för standardkällan för kodnivån för kommandotolk ("shell") och standardverktyg för applikationsprogram. Standarden innehåller fyra sektioner :

  • - introduktion;
  • - beskrivning av kontrollspråket;
  • - servicepaket för miljön,
  • - serviceprogram - verktyg.

Syftet med denna del av standarden är att konkretisera applikationsprogramgränssnitt på kommandonivå för kommandospråkstolk och en komplett uppsättning serviceprogram - verktyg. Den specificerar gränssnitt för operativsystem på nivå med programtexter och koder. Shell-standardiserade kommandospråk är ett verktyg för att förbereda små mobilrutiner och snabbt felsöka dem interaktivt. I en avancerad miljö är det möjligt att kedja kommandon med filtrering av mellanresultat. Varje verktyg innehåller: en beskrivning av strukturen; Ansökan; operander; inmatningsfiler; miljö. Valfria verktyg utökar möjligheterna för användare av mobila applikationer att kommunicera med den externa miljön. När det gäller C-språket (standard ISO 9899: 1990)beskriver språkoberoende gränssnittstjänster för bärbara applikationer och kommunikation med den externa miljön när presentationsgränssnitt presenteras på olika språk på hög nivå.

Kommandon och verktyg innehåller specifika mekanismer på operatörsnivå för att utföra operationer: jämföra, skriva ut och visa filer, redigera filer, utvärdera uttryck, sortera data, beställa signaler och få tillgång till information om miljön. Du kan urskilja:

  • - verktyg för programinteraktionsmiljön;
  • - kontrollverktyg som stöder programrörlighet och anslutning av externa användare med asynkrona terminaler;
  • - verktyg för interaktion av mjukvarukomplex;
  • - språkoberoende systemtjänster för applikationsprogram på flera programmeringsspråk på hög nivå.

Fjärde delen standard-ISO 9945-4: 2003 -Motivering - innehåller en grupp av fem stora applikationer

  • - underbyggande av grundläggande definitioner - bilaga A;
  • - Motiv för systemgränssnitt - Bilaga B;
  • - motivering av kontrollkommandon och serviceverktyg - bilaga C;
  • - Portabilitetsöverväganden - Bilaga D;
  • - Hänsyn till delprofiler - Bilaga E.

De listade bilagorna förklarar i detalj de grundläggande bestämmelserna i POSIX-standarderna, underbyggar de beslut som formaliserats i dem. Omfattande förklaringar som inte passade bra i resten av dokumentet innehåller historiska kommentarer till standardtexten, förklaringar till varför vissa egenskaper, skyltar och komponenter ingick i standarden eller avvisades av utvecklarna. Parallellt med förberedelserna och implementeringen av gruppens nya fyra standarder POSIXde godkända baslinjerna nedan gäller och gäller internationella standarder, fördjupa vissa funktioner och underlätta skapandet av mobila applikationer.

I standard-ISO 14252: 1996 -Guide POSIXÖppen systemmiljö (OSE) - en ideologi och modell för att skapa mobil programvara beskrivs, som beskriver användarna en modell av POSIX-standarder, samt de jure, de facto-standarder och specifikationer som krävs för att skapa bärbara applikationer som interagerar med dem. Modellen återspeglar principerna för att konstruera gränssnitt för applikationsprogram med en plattform - ett operativsystem genom vilket interaktion med komponenter i den externa miljön genomförs. Man tror att applikationsprogram inte direkt interagerar med den externa miljön utan bara är anslutna till det via operativsystemet. Applikationsutveckling är tänkt att vara korsläge, det vill säga utvecklingsplattformen - instrumental kanske inte sammanfaller med plattformen för exekvering (applikation) av program (objekt - mål). Resultatet av att kompilera ett program på en instrumentplattform kan överföras för utförande till målplattformen.

Det finns två definierande gränssnitt mellan de tre grundläggande komponenterna: mellan applikationsprogram och plattformen - operativsystemet (API) och mellan plattformen och den externa miljön (EEI). Vanliga funktioner definieras - plattformstjänster för dessa interaktioner.

Den externa miljön innefattar komponenter i interaktion mellan människor och användare med användare, komponenter i informationsinteraktion med externa datorenheter och med komponenter som säkerställer kommunikation med den externa miljön. Dessa gränssnitt och tjänster formaliseras mer detaljerat av motsvarande POSIX-standarder.

Standarden innehåller också allmänna principer och riktlinjer för bildande och beskrivning av komplexa, enhetliga profiler och för att säkerställa enhetligheten i deras gränssnitt med den yttre miljön;

  • - stödja direkt interaktion av applikationsprogram med användare, reglering av gränssnitt med virtuella terminaler och grafiska system;
  • - tillhandahållande av administrativ hantering av filsystem och olika, inklusive distribuerade, databaser;
  • - reglering av telekommunikation och datautbyte på de övre nivåerna av OSI-referensmodellen,
  • - säkerställa certifiering och säkerhet för användning av informationsteknik, program och data i enlighet med OSI och kryptografistandarder.

StandardISO 13210: 1998 -Testmetoder för att bedöma överensstämmelse med standarder POSIX -innehåller en allmän testmetod för att verifiera att applikationsprogrammeringsgränssnitt överensstämmer med standarder POSIX.Principerna för bildande av testuppsättningar presenteras och en metod för utveckling av ett testsystem föreslås för övervakning av skapade applikationer för efterlevnad ISO 9945.Testmetoden belyser: förberedelse av tester; testförfaranden; utförande av handlingar som intygar att standarder följs. Tre huvudnivåer av testkomplexitet identifieras och presenteras kort: uttömmande; ganska komplett; utvärderande - för att fastställa allmänna kvalitetsegenskaper. Det betonas att de föreslagna metoderna inte garanterar absolut överensstämmelse med standarder, eftersom uttömmande testning är omöjligt av tekniska och ekonomiska skäl. Rekommendationer ges för:

  • - Klassificering av testfall och påståenden.
  • - Utarbetande av beskrivningar av deterministiska tester och tester för kontroll av strukturella strukturer.
  • - kodning och presentation av testresultat i samband med de ursprungliga testerna,
  • - bildande av ett intyg om överensstämmelse för applikationsprogrammets gränssnitt med standarder POSIX.

POSIX-standarderna hänvisar till många standarder ISO, ANSIoch de facto. Resultatet är en komplett uppsättning standarder som stöder design mobilprogram i olika klasser av öppna system, ökar. I specifika tillämpade utvecklingar behövs bara en viss, relativt liten del av dem, vilket är lämpligt att separera i motsvarande problemorienterad profil.

Skapandet av operativsystem för mikrodatorer initierades av OS SR. / M. Den utvecklades 1974 och har sedan dess installerats på många 8-bitars maskiner. Inom ramen för detta operativsystem skapades en betydande mängd programvara, inklusive översättare från språken BASIC, Pascal, C, Fortran, Cobol, Lisp, Ada och många andra, text (ordbehandlare är den mest använda typen av applikationsprogram. De gör att du kan förbereda dokument mycket snabbare och bekvämare än att använda en skrivmaskin. Ordbehandlare låter dig använda olika teckensnitt i tecken, stycken i fritt format, automatiskt slå in ord till en ny rad, låta dig göra fotnoter, inkluderar ritningar, automatiskt nummersidor och fotnoter, etc.) och tabellprocessorer, databashanteringssystem.

MSX-standard

Denna standard definierade inte bara operativsystemet, utan också egenskaperna hos hårdvaran för skoldatorer. Enligt MSX-standarden måste maskinen ha minst 16 K RAM, 32 K skrivskyddat minne med en inbyggd BASIC-tolk, en färggrafisk skärm med en upplösning på 256 x 192 pixlar och 16 färger, en tre-kanals ljudgenerator för 8 oktaver, en parallellport för anslutning av en skrivare och en styrenhet för styrning av en extern lagringsenhet ansluten externt.

Operativsystemet för en sådan maskin bör ha följande egenskaper: det erforderliga minnet är högst 16 K, kompatibilitet med CP. / M på systemanropsnivå, kompatibilitet med DOS när det gäller filformat på externa enheter baserat på diskett diskar, stöd för översättare av BASIC, C-språk, Fortran och Lisp. Detta operativsystem, kallat MSX-DOS, tog således hänsyn till behovet av att stödja den omfattande mjukvaran som utvecklats för CP / M, och fokuserade samtidigt på den nya vid den tidpunkten utvecklingen relaterad till DOS, grafiska paket (Database Management System (DBMS) - låter dig hantera stora informationsmatriser - databaser), symboliska avlusare och andra problemorienterade program.

Systemets framgång berodde till stor del på dess extrema enkelhet och kompaktitet, förmågan att snabbt anpassa sig till olika PC-konfigurationer. Den första versionen av systemet tog bara 4K, vilket var mycket viktigt med tanke på det begränsade minnet på en dator vid den tiden.

Operativsystem av DOS-typ

OS som DOS blev dominerande med tillkomsten av 16-bitars datorer med 16-bitars mikroprocessorer som 8088 och 8086. När det gäller livslängd kan inget mikrodatoroperativsystem ens komma nära DOS. Sedan starten 1981 har DOS spridit sig så mycket att det har förtjänat rätten att betraktas som världens mest populära operativsystem. Trots några av dess brister och det faktum att det mesta är baserat på utvecklingen på 70-talet, fortsätter DOS att existera och spridas till denna dag. På gott och ont, kommer det sannolikt att dominera operativsystemmarknaden under en nära framtid. En enorm pool av programvara har utvecklats för DOS nu. Det finns översättare (Translator - ett program som automatiskt omvandlar ett program i ett programmeringsspråk till en sekvens av instruktioner. Typer av översättare - en kompilator, en tolk) för nästan alla populära högnivåspråk, inklusive BASIC, Pascal, Fortran, C , Modula-2, Lisp, Logo, APL, Fort, Ada, Cobol, PL-1, Prolog, Smalltok, etc. och för de flesta språk finns det flera översättaralternativ. Det finns verktyg för att utveckla program i maskinkod - monterare, symboliska avlusare osv. Dessa verktyg åtföljs av redaktörer, länkar och andra servicesystem som är nödvändiga för att utveckla komplexa program. Förutom systemprogramvaran för DOS har många applikationsprogram skapats.

Disk OS (DOS)

DOS-operativsystemet består av följande delar:

Det grundläggande in- / utmatningssystemet (BIOS) som finns i skrivskyddat minne (skrivskyddat minne, ROM) på en dator. Denna del av operativsystemet är "inbyggt" i datorn. Syftet är att utföra de mest enkla och universella OS-tjänsterna relaterade till implementeringen av input-output. Det grundläggande I / O-systemet innehåller också ett datorfunktionstest som kontrollerar datorns minne och enheter när den slås på. Dessutom innehåller det underliggande I / O-systemet ett program för att åberopa OS-laddaren.

OS-laddaren är ett mycket kort program som finns i den första sektorn i varje DOS-diskett. Funktionen för detta program är att läsa ytterligare två OS-moduler i minnet, som slutför DOS-startprocessen.

På en hårddisk (hårddisk) består OS-laddaren av två delar. Detta beror på att hundra hårddiskar kan delas in i flera partitioner (logiska enheter). Den första delen av startladdaren ligger på hårddiskens första sektor, den väljer från vilken av partitionerna på hårddisken som ska fortsätta. Den andra delen av startladdaren ligger i den första sektorn i detta avsnitt, den läser in i DOS-modulens minne och överför den till kontroll.

Diskfiler 10.SYS och MSDOS.SYS (de kan nämnas annorlunda, till exempel IBMB.COM och IBMDOS.COM för PC DO; URBIOS.SYS och DRDOS.SYS för DR DOS - namnen varierar beroende på OS-version) . De laddas in i minnet av OS-laddaren och förblir permanent i datorns minne. 10.SYS-filen är till det grundläggande I / O-systemet i ROM. MSDOS.SYS-filen implementerar de grundläggande DOS-tjänsterna på hög nivå.

DOS-skalet behandlar kommandon som har angetts av användaren. Kommandoprocessorn finns i COMMAND.COM-skivfilen på den skiva som operativsystemet startar från. Vissa användarkommandon, t.ex. Type, Dir eller Cop, körs av själva skalet. Sådana kommandon kallas interna kommandon. För att utföra andra (externa) användarkommandon söker kommandoprocessorn på skivorna efter ett program med rätt namn och, om den hittar det, laddar det in i minnet och överför kontrollen över det. När programmet är klart tar kommandoprocessorn bort programmet från minnet och visar ett meddelande om att det är redo att utföra kommandot (DOS-prompt).

DOS externa kommandon är program som kommer med operativsystemet som separata filer. Dessa program utför underhållsaktiviteter, till exempel formatering av disketter, kontrollskivor etc.

Enhetsdrivrutiner är speciella program som kompletterar DOS I / O-systemet och tillhandahåller service för nya eller icke-standardiserade användningar av befintliga enheter. Med hjälp av förare är det till exempel möjligt att arbeta med en "ramdisk" dvs. ett datorminne som du kan arbeta med på samma sätt som med en disk. Drivrutiner laddas in i datorns minne när operativsystemet startas, deras namn anges i en speciell fil CONFIG.SYS. Detta schema gör det lättare att lägga till nya enheter gör att du kan göra detta utan att påverka DOS-systemfilerna

DOS-versioner

På bara några år har MS DOS-systemet gått från en enkel bootloader till ett universellt etablerat operativsystem för persondatorer baserat på Intel 8086-mikroprocessorer. MS DOS stöder datanätverk och grafiska användargränssnitt, alla typer av lagringsenheter, fungerar som basen för tusentals applikationsprogram. MS DOS-systemet, som har mer än 10 miljoner registrerade användare, "stänger" ständigt användare från sina konkurrenter. Föregångaren till MS-DOS var operativsystemet 86-DOS, skrivet i mitten av 1980-talet. Tim Peterson för Seattle Computer Products. Vid den tiden var det mest populära systemet för mikrodatorer baserat på Intel 8080 och Zilog Z-80 operativsystemet CP / M-80 från Digital Research. Detta system gav åtkomst till en mängd olika program (ordbehandlare, databasadministratörer, etc.). För att underlätta processen för överföring av applikationsprogram från 8-bitars CP / M-80-systemet till den nya 16-bitarsmiljön i 86-DOS-systemet byggdes det senare ursprungligen så att det simulerar alla funktioner och typer av operationer för CP / M-80. Som ett resultat är strukturerna för filkontrollblock, programsegmentprefix och körbara filer i 86-DOS nästan identiska med de för CP / M-80. De program som fanns i CP / M-80 kunde enkelt konverteras (genom att bearbeta källprogramfilerna med en speciell översättare) och sedan köras i 86-DOS-systemet antingen omedelbart eller genom att utföra enkel manuell redigering. Eftersom 86-DOS marknadsfördes som det egna operativsystemet för Seattle Computer Researchs familj av S-100 mikrodatorer baserade på Intel 8086, har detta tillvägagångssätt generellt sett haft liten inverkan på situationen i persondatorvärlden. Andra Intel 8086-baserade mikrodatorleverantörer, uppenbarligen tvingade att använda konkurrerande operativsystem, väntade ivrigt på lanseringen av Digital Researchs CP / M-86-system. I oktober 1980 uppmanade IBM-kampanjen mikrodatorprogramvara att börja söka efter ett operativsystem för en ny familj av persondatorer. Microsoft kunde inte erbjuda sitt eget operativsystem annat än fristående Microsoft-versioner av BACIC, men betalade Seattle Computer Products för att sälja Petersons 86-DOS-system. I gengäld licensierades Seattle Computer Products för att använda och sälja programmeringsspråk och alla versioner av operativsystemet för 8086-mikroprocessorn som utvecklats av Microsoft. I juli 1981 förvärvade Microsoft alla rättigheter till 86-DOS-systemet, omdesignade det avsevärt och gav det namnet MS DOS. När de första IBM-datorerna dök upp hösten 1981 föreslog IBM operativsystemet MS DOS för dem, PC DOS 1.0. Dessutom valde IBM CP / M-86 (Digital Research) och P-system (Softech) för PC-mikrodatorer som alternativa operativsystem. Båda dessa system hade dock ett antal nackdelar: de hade låg prestanda för IBM PC, höga kostnader och brist på tillgängliga programmeringsspråk. Slutligen tippade skalorna till förmån för PC DOS-systemet efter att IBM-företaget, med sin hjälp, implementerade alla programvaruapplikationer för IBM PC, liksom de verktyg som fungerar under deras kontroll. Därför fokuserade mjukvaruutvecklare från början på PC DOS, och CP / M-86 och P-systemsystemen intog ingen betydande plats på mjukvarumarknaden för IBM PC.

Institutionen för teknik och teknik

"Utveckling av operativsystem"


Introduktion

1. Utveckling av gränssnittet

2. Vägledande principer

3. Paradigmer

1) Användargränssnittsparadigmer

2) Användarparadigmer

3) dataparadigmer

4. Systemanropsgränssnitt

5. Implementering.

6. Systemstruktur

7. System med flera nivåer

8. Exokerneller

9. System för klientserver

10. Utbyggbara system

11. Kärntrådar

12. Mekanism och politik

13. Orthogonalitet

14. Bindningstid

15. Implementering av systemet från topp till botten och botten till toppen

16. Användbara metoder

17. Döljutrustning

18. Indirektitet

19. Återanvänd

20. Lönsamhet

21. Brute force-metod

22. Kontroll av fel

23. Prestanda

24. Cachning

25. Tips

26. Använda lokalitet

27. Optimera det allmänna fallet

28. Projektledning

29. Den mytiska manmånaden

30. Erfarenhetens roll

31. Trender och OS-design

32. OS med ett stort adressutrymme

34. Parallella och distribuerade system

35. Multimedia

Slutsats


Introduktion

Det finns många muntliga legender bland operativsystemutvecklare om vad som är bra och vad som är dåligt, men förvånansvärt få av dessa berättelser skrivs ner. Den viktigaste boken kan kallas Fred Brooks klassiska verk, där författaren delar sin erfarenhet av att designa och implementera operativsystemet IBM OS / 360. Materialet, som släpptes för 20-årsjubileet för publikationen, har reviderats och flera nya kapitel har inkluderats i bokens innehåll. Förmodligen den enda boken om operativsystem som seriöst diskuterar design.

De tre klassikerna om operativsystemsdesign är som Brooks böcker, dessa tre artiklar har framgångsrikt överlevt tiden sedan de skrevs. De flesta av de frågor som diskuteras i dem har behållit sin relevans idag.

Detta kapitel bygger på innehållet i dessa källor och bygger också på författarens personliga erfarenhet av utformningen av tre system: Amoeba, MINIX och Globe. Eftersom det inte finns någon enighet bland utvecklare av operativsystem om hur man bäst utformar operativsystem, kommer detta kapitel att vara mer personligt, mer spekulativt och definitivt mer kontroversiellt än de tidigare kapitlen.

Typen av designproblemet

Operativsystemutveckling är mer ett ingenjörsprojekt än en exakt vetenskap. På detta område är det mycket svårare att sätta tydliga mål och uppnå dem. Låt oss först överväga frågan om hur problemet ställs.

För att en operativsystemsdesign ska lyckas måste utvecklare ha en tydlig uppfattning om vad de vill ha. I avsaknad av ett mål är det mycket svårt att fatta efterföljande beslut. För att göra denna fråga tydligare är det bra att titta på två programmeringsspråk, PL / I och C ++. PL / I-språket utvecklades av IBM på 60-talet, eftersom det var outhärdligt att stödja FORTRAN och COBOL samtidigt som man lyssnade bakom forskarnas rygg att Algol är bättre än FORTRAN och COBOL tillsammans. Därför bildades en kommitté för att skapa ett nytt språk som tillgodoser alla programmerares behov: PL / I. Detta språk hade några av funktionerna i FORTRAN, några av funktionerna i COBOL-språket och några av funktionerna i Algol-språket. Projektet misslyckades eftersom det saknade ett enda koncept. Projektet var bara en uppsättning egenskaper som kom i konflikt med varandra och PL / I-språket var för besvärligt och besvärligt för att kompilera program effektivt.

Låt oss nu titta på C ++ - språket. Den designades av bara en person (Dennis Ritchie) för ett enda syfte (systemprogrammering). Hans framgång var kolossal, och detta berodde inte minst på det faktum att Ritchie visste vad han ville och vad han inte ville. Som ett resultat är språket fortfarande utbrett årtionden efter starten. Att ha en tydlig förståelse för dina mål är avgörande.

Vad vill operativsystemutvecklare ha? Uppenbarligen varierar svaret från system till system och kommer att vara annorlunda för inbäddade system och serversystem.

För universella operativsystem är följande fyra punkter grundläggande:

1. Definition av abstraktioner.

2. Tillhandahålla primitiva operationer.

3. Tillhandahålla isolering.

4. Utrustningshantering.

Var och en av dessa punkter kommer att diskuteras nedan.

Den viktigaste men förmodligen den svåraste uppgiften för operativsystemet är att bestämma de rätta abstraktionerna. Några av dem, såsom processer och filer, har använts så länge att det kan verka uppenbart. Andra, som trådar för utförande, är nyare och därför mindre väletablerade koncept. Till exempel, om en flertrådad process med en av dess trådar blockerad av tangentbordets ingång klonas, bör tråden i den nya processen också vänta på tangentbordets inmatning? Andra abstraktioner är relaterade till timing, signaler, minnesmodell, I / O-modellering och andra områden.

Varje abstraktion kan implementeras som specifika datastrukturer. Användare kan skapa processer, filer, semaforer etc. Hantera dessa datastrukturer med primitiva operationer. Till exempel kan användare läsa och skriva filer. Primitiva operationer implementeras som systemanrop. Ur användarens synpunkt bildas operativsystemets hjärta av abstraktioner, och operationer på dem är möjliga med systemanrop.

Eftersom flera användare kan logga in på samma dator samtidigt måste operativsystemet tillhandahålla mekanismer för att separera dem från varandra. En användare bör inte störa en annans arbete. Begreppet processer används ofta för att gruppera resurser för att skydda dem. Normalt är filer och andra datastrukturer också skyddade. Ett viktigt designmål för ett operativsystem är att säkerställa att varje användare bara kan göra vad han eller hon får göra på data som de har åtkomsträttigheter till. Men användarna måste också dela data och resurser, så isoleringen måste vara selektiv och användarkontrollerad. Allt detta komplicerar operativsystemets design avsevärt.

Nära relaterat till denna fråga är behovet av att isolera fel. Om någon del av systemet misslyckas (oftast är det en av användarprocesserna), bör den misslyckade processen inte störa hela operativsystemet. Operativsystemsenheten måste säkerställa att de olika delarna av operativsystemet är pålitligt isolerade från varandra.

Slutligen måste operativsystemet hantera hårdvaran. I synnerhet måste den ta hand om alla IC-enheter på låg nivå, såsom avbrytare och bussstyrenheter. Det måste också ge en ram för enhetsdrivrutiner för att styra stora I / O-enheter som diskar, skrivare och skärmar.

Varför är det så svårt att utforma ett operativsystem?

Moores lag säger att datorhårdvara ökar prestandan med en faktor 100 vart tionde år. Tyvärr har ingen formulerat något liknande för programvara. Ingen säger att operativsystem generellt förbättras med åren. Det kan faktiskt hävdas att vissa av dem är ännu värre i vissa aspekter (som tillförlitlighet) än till exempel operativsystemet UNIX version 7 från 1970-talet.

Varför? Tröghet och önskan att upprätthålla bakåtkompatibilitet är vanligtvis de främsta orsakerna, även om utvecklarnas underlåtenhet att följa principerna för bra design också bidrar. Men denna fråga bör diskuteras mer detaljerat. Operativsystem skiljer sig i grunden från de små programvaruapplikationer som säljs i datorbutiker för $ 49. Tänk på åtta aspekter som gör utvecklingen av operativsystemet mycket svårare än att skriva ett applikationsprogram.

För det första har operativsystem blivit extremt besvärliga program. Ingen ensam kan skriva ett operativsystem efter att ha sittat vid en persondator i flera månader. Alla moderna UNIX-versioner överstiger 1 miljon källor. Operativsystemet Windows 2000 består av 29 miljoner kodrader. Inte en enda person på jorden kan förstå en miljon rader, än mindre 29 miljoner. Om du skapar en produkt som ingen utvecklare ens kan hoppas att förstå till fullo är det inte förvånande att resultaten ofta är långt ifrån optimala.

Operativsystem är inte de mest komplexa systemen i världen. Till exempel är hangarfartyg mycket mer komplexa system, men de är lättare att separera i separata delsystem. Flygplanstågs toalettdesigners bör inte vara oroliga för radarsystemet. Dessa två delsystem interagerar knappast. I operativsystemet interagerar filsystemet ofta med minnessystemet på oväntade och oförutsägbara sätt. För det andra måste operativsystem hantera samtidighet. Det finns många användare i systemet samtidigt som arbetar med många I / O-enheter. Att hantera flera samtidigt körande processer är betydligt mer komplicerat än att hantera en sekventiell aktivitet. Bland de många problem som uppstår i det här fallet är det tillräckligt att nämna åtminstone tävlingar och blockeringar.

För det tredje måste operativsystem ta hänsyn till förekomsten av potentiellt fientliga användare - användare som vill störa operativsystemets funktion eller utföra förbjudna åtgärder, som att stjäla andras filer. Operativsystemet måste vidta åtgärder för att förhindra sådana åtgärder från skadliga användare. Ordbehandlare och fotoredigerare stöter inte på liknande problem. För det fjärde, trots att användare inte litar på varandra, behöver många ibland dela information eller resurser med en viss grupp användare. Operativsystemet måste tillhandahålla denna kapacitet, men på ett sådant sätt att en angripare inte kan använda dessa egenskaper för sina egna syften. Applikationsprogram har inte heller dessa problem.

För det femte brukar operativsystem leva ganska länge. UNIX-operativsystemet har använts i ett kvarts sekel; Windows har använts i över tio år och visar inga tecken på att dö. Följaktligen måste operativsystemdesigners tänka på hur hårdvara och applikationer kan förändras i en avlägsen framtid och hur de ska förbereda sig för detta. System som återspeglar en specifik världsbild tenderar att snabbt försvinna från scenen.

För det sjätte har operativsystemdesigners inte riktigt en klar uppfattning om hur deras system kommer att användas, så de måste ge en hel del mångsidighet. System som UNIX eller Windows 2000 var inte utformade för att användas för e-post eller för att köra en webbläsare under deras kontroll, men många moderna datorer används främst för detta ändamål. Det är svårt att föreställa sig en marin fartygsdesigner som inte vet vad han designar: ett fiskefartyg, ett passagerarfartyg eller ett krigsfartyg.

För det sjunde kräver moderna operativsystem i allmänhet bärbarhet, vilket innebär att de kan köras på flera plattformar. De måste också stödja hundratals eller till och med tusentals I / O-enheter som är designade helt oberoende av varandra. Exempelvis måste ett operativsystem köras på både stora endian- och big endian-datorer. Det andra exemplet sågs konsekvent på MS-DOS-system när användare försökte installera ett ljudkort och ett modem med samma I / O-portar eller avbryta förfrågningslinjer. Problem som konflikter mellan olika delar av hårdvaran hanteras främst av operativsystem.

Slutligen åttonde, när man utvecklar operativsystem, beaktas ofta behovet av kompatibilitet med den tidigare versionen av operativsystemet. Ett system kan ha många begränsningar för ordlängder, filnamn och så vidare, som nu anses vara föråldrade av designers, men som är svåra att bli av med. Detta påminner om återutrustning av en bilanläggning för produktion av nya modeller med villkoret att bibehålla den nuvarande kapaciteten för produktion av gamla modeller.


Gränssnittsutveckling

Så det bör nu vara tydligt att det inte är en lätt uppgift att skriva ett modernt operativsystem. Men var börjar detta arbete? Det är kanske bäst att först tänka på gränssnitten från operativsystemet. Operativsystemet tillhandahåller en uppsättning tjänster, de flesta datatyperna (till exempel filer) och många operationer med dem (till exempel läs). Tillsammans utgör de gränssnittet för användarna av systemet. Observera att i detta sammanhang är användare av operativsystemet programmerare som skriver program som använder systemanrop, inte personer som kör applikationsprogram.

Förutom det huvudsakliga systemanropsgränssnittet har de flesta operativsystem ytterligare gränssnitt. Till exempel kan vissa programmerare behöva skriva en drivrutin för att lägga till den i operativsystemet. Dessa drivrutiner ger specifik funktionalitet och kan hänvisa till specifika systemsamtal. Funktioner och samtal definierar ett gränssnitt som skiljer sig väsentligt från det som används av applikationsprogrammerare. Alla dessa gränssnitt måste utformas noggrant om systemdesignerna ska lyckas.

Riktlinjer

Finns det principer som du kan utforma gränssnitt med? Vi hoppas att sådana principer finns. Om du uttrycker dem med några ord är det enkelhet, fullständighet och möjligheten till effektivt genomförande.

Princip 1. Enkelhet.

Ett enkelt gränssnitt är lättare att förstå och implementera utan fel. Alla systemdesigners bör komma ihåg detta berömda citat från den franska flygaren och författaren Antoine de Saint-Exupery:

Perfektion uppnås inte när det inte finns något mer att lägga till, utan när det inte finns något mer att subtrahera. Denna princip säger att mindre är bättre än mer, åtminstone för operativsystem. Med andra ord kan denna princip uttryckas med följande förkortning, som föreslår för programmeraren, vars tänkande förmåga är i tvivel, att inte komplicera systemet: KISS (Keep It Simple, Stupid).

Princip 2. Fullständighet.

Naturligtvis måste gränssnittet ge användarna möjlighet att göra vad de vill, det vill säga gränssnittet måste vara komplett. I detta avseende kommer ett annat citat att tänka på, den här gången är det en fras som talas av Albert Einstein: Allt ska vara så enkelt som möjligt, men inte enklare.

Med andra ord måste operativsystemet göra vad som krävs av det, men inte mer. Om användaren behöver lagra data måste operativsystemet tillhandahålla någon mekanism för detta. Om användare behöver kommunicera med varandra måste operativsystemet tillhandahålla en kommunikationsmekanism etc. I sitt Turing Award-tal kombinerade Fernando Corbato, en av utvecklarna av CTSS- och MULTICS-system, begreppen enkelhet och fullständighet och sa:

För det första är det viktigt att betona vikten av enkelhet och elegans, eftersom komplexitet leder till en massa motsägelser och, som vi har sett, fel. Jag skulle definiera elegans som att uppnå en viss funktionalitet med ett minimum av mekanism och maximal klarhet. Om en funktion (eller systemanrop) inte kan implementeras effektivt borde den förmodligen inte implementeras. Programmeraren måste intuitivt förstå kostnaden för att implementera och använda varje systemanrop. Till exempel tycker UNIX-programmerare att 1-söksystemanropet är billigare än det lästa systemanropet eftersom det första systemanropet helt enkelt ändrar innehållet i en pekare som är lagrad i minnet, medan det andra systemanropet utför disk-I / O. Om programmerarens intuition misslyckas skapar programmeraren ineffektiva program.

Paradigmer

När målen är uppställda kan designen börja. Du kan till exempel börja med följande: fundera på hur systemet kommer att presenteras för användarna. En av de viktigaste frågorna är att alla systemets funktioner är väl samordnade med varandra och har det som ofta kallas arkitektonisk konsistens. Det är viktigt att skilja mellan två typer av operativsystemanvändare. Å ena sidan finns det användare som interagerar med applikationsprogram; å andra sidan finns det programmerare som skriver dessa applikationsprogram. De förstnämnda handlar mestadels om det grafiska användargränssnittet, medan det senare mest handlar om systemgränssnittet för systemet. Om målet är att ha ett enda grafiskt användargränssnitt som fyller hela systemet, till exempel på ett Macintosh-system, bör utvecklingen börja härifrån. Om målet är att ge stöd för olika möjliga grafiska användargränssnitt, som i UNIX, bör systemanropsgränssnittet utvecklas först. Att starta en systemutveckling från ett grafiskt användargränssnitt är i grunden en top-down design. Frågan är vilka funktioner detta gränssnitt kommer att ha, hur användaren kommer att interagera med dem och hur systemet ska utformas för att stödja dem. Till exempel, om de flesta program visar ikoner på skärmen och sedan väntar på att användaren ska klicka på dem, föreslår detta att man använder en händelsestyrd modell för det grafiska användargränssnittet och eventuellt operativsystemet. Å andra sidan, om skärmen mestadels är fylld med textrutor, är förmodligen en modell där processer avläst tecken från tangentbordet är bäst.

Implementering främst av systemanropsgränssnittet är en nedifrån och upp-design. Frågorna här är vilka funktioner programmerare behöver. I verkligheten finns det inte många specialfunktioner som krävs för att stödja GUI. Till exempel är UNIX-fönstersystemet X Windows helt enkelt ett stort C-program som får åtkomst till tangentbordet, musen och skärmen med läs- och skrivsystemanropen. X Windows-fönstersystemet utvecklades mycket senare än UNIX-operativsystemet, men krävde inte mycket ändringar av operativsystemet för att få det att fungera. Detta bekräftar att UNIX-systemet är tillräckligt komplett.

Användargränssnittsparadigmer.

För både GUI-nivågränssnittet och systemanropsgränssnittet är den viktigaste aspekten att det finns ett bra paradigm (ibland kallat metafor eller modellrepresentation) för att ge en visuell visualisering av gränssnittet. Detta paradigm används i gränssnittet för att säkerställa enhetlighet av idiomer som att klicka och dubbelklicka, dra osv. Ofta gäller ytterligare krav för program, som att ha en menyrad med Arkiv, Redigera och så vidare, som alla innehåller bekanta menyalternativ. Således kan användare som är bekanta med ett program enkelt behärska ett annat program.

Men WIMP-användargränssnittet är inte det enda möjliga. Vissa fickdatorer använder ett elektroniskt pennagränssnitt. Enheter som är utformade för multimedia kan använda ett videobandspelargränssnitt. Och naturligtvis används ett helt annat paradigm i datorns röststyrning. Valet av paradigm är naturligtvis viktigt, men ännu viktigare är det faktum att man använder ett enda paradigm som förenar hela användargränssnittet.

Oavsett vilket paradigm som väljs är det viktigt att alla applikationer använder det. Därför bör systemdesigners förse applikationsutvecklare med bibliotek och verktyg för att komma åt procedurer som ger ett konsekvent användargränssnitt. Att utforma användargränssnittet är en mycket viktig uppgift, men det är inte ämnet för den här boken, så vi återgår till att diskutera ämnet för operativsystemgränssnittet.

Exekveringsparadigmer.

Arkitektonisk konsistens är viktig på användarnivå, men det är lika viktigt på systemanropsgränssnittsnivå. Här är det ofta bra att skilja mellan exekveringsparadigmet och dataparadigmet, så vi tittar på båda och börjar med det första.

Två paradigmer har blivit utbredda: algoritmiska och händelsestyrda. Det algoritmiska paradigmet bygger på tanken att ett program startas för att utföra någon funktion, känd i förväg eller ges som parametrar. Denna funktion kan vara att kompilera ett program, göra ett uttalande eller styra ett flygplan till San Francisco. Den underliggande logiken är hårdkodad i programmets kod, där programmet gör enstaka systemanrop för att få användarinmatning, få åtkomst till systemtjänster och så vidare.

Ett annat exekveringsparadigm är event management paradigm (Listing 12.1b). Här initialiseras programmet, till exempel, visar ett fönster och väntar sedan på att operativsystemet informerar det om den första händelsen. Denna händelse kan vara en tangenttryckning eller musrörelse. Detta schema är användbart för program som aktivt interagerar med användaren.

Varje paradigm genererar sin egen programmeringsstil. I det algoritmiska paradigmet är algoritmer centrala och operativsystemet ses som en tjänsteleverantör. I händelsehanteringsparadigmet tillhandahåller operativsystemet också tjänster, men dess primära roll är att samordna användaraktivitet och generera händelser som konsumeras av processer.

Dataparadigmer.

Körningsparadigmet är inte det enda paradigm som exporteras av operativsystemet. Dataparadigmet är lika viktigt. Nyckelfrågan här är hur systemstrukturerna och enheterna visas för programmeraren. I tidiga batchsystem utformade för att köra FORTRAN-program modellerades allt som logiskt tejp. Läsbara kortlekar behandlades som inmatningstejp, stansade kortdäck behandlades som utmatningsband. Skrivarutmatningen behandlades också som utmatningstejp. Filer på disken ansågs också som band. Slumpmässig åtkomst till filen var endast möjlig genom att spola tillbaka motsvarande band och läsa igen.

Det första kortet var en manual för operatören. Han var tvungen att få ut rullen nummer 781 ur skåpet och installera den på enheten 8. Det andra kortet var ett kommando till operativsystemet att starta programmet som bara sammanställts från FORTRAN och mappa INPUT (vilket betyder stanskortläsare) till logiskt band 1, skivfil MYDATA till logiskt band 2, skrivare UTGÅNG till logiskt band 3, PUNCH till logiskt band 4 och fysisk bandstation TAPE08 till logiskt band 5.

FORTRAN-syntaksen låter dig läsa och skriva logiska band. När du läser från logiskt band 1 får programmet inmatning från stansade kort. Genom att skriva till logiskt band 3 kan programmet mata ut resultaten till en skrivare. Med hänvisning till logiskt tejp 5 kan programmet läsa och skriva magnetband 781, etc. Observera att idén med magnetband bara var ett paradigm (modell) för att kombinera en hålkortläsare, skrivare, hålslagare, diskfiler och tejp inspelare. I detta exempel var endast logiskt band 5 ett fysiskt band. Allt annat var bara vanliga swap-filer. Det var ett primitivt paradigm, men det var ett steg i rätt riktning.

Sedan kom operativsystemet UNIX, som gick mycket längre i den här riktningen och använde modellen "allt är filer". Med detta paradigm behandlas alla I / O-enheter som filer som kan öppnas och manipuleras som vanliga filer. C-uttalanden öppnar en riktig diskfil och en användares terminal. Efterföljande operatörer kan använda filbeskrivarna fd1 och fd2 för att läsa från och skriva till dessa filer. Från och med den här tiden är det ingen skillnad mellan filåtkomst och terminalåtkomst, förutom att man inte kan flytta pekaren i filen när man tar åt sig terminalen.

UNIX-operativsystemet sammanfogar inte bara filer och I / O-enheter utan tillåter också att andra processer kan nås via rör som om de vore filer. Dessutom, om kartläggning av filer till minnesadressutrymme stöds, kan en process komma åt sitt virtuella minne som om det vore en fil. Slutligen, i UNIX-versioner som stöder / proc-filsystemet, tillåter en C-sträng en process att (försöka) läsa och skriva processminne 501 med filbeskrivare fd3, vilket kan vara användbart, till exempel vid felsökning av ett program.

Operativsystemet Windows 2000 går ännu längre med den här modellen och representerar allt i systemet som objekt. Med ett handtag till en fil, process, semafor, postlåda eller annat kärnobjekt kan en process utföra olika åtgärder med det objektet. Detta paradigm är ännu mer allmänt än det som används i UNIX, och mycket mer allmänt än i FORTRAN.

Förenande paradigm förekommer också i andra sammanhang. En av dem bör noteras: World Wide Web (Web). Paradigmet som används på webben är att hela cyberspace är fyllt med dokument, var och en med sin egen URL. Genom att komma åt lämplig URL (genom att skriva eller klicka på en länk) får du detta dokument. I själva verket är många "dokument" inte dokument alls utan genereras av ett program eller skalskript när en begäran kommer in. Till exempel, när en användare frågar efter en webbutik för en lista med CD-skivor för en viss artist skapas dokumentet i farten av programmet. Det existerade verkligen inte innan begäran mottogs.

Så vi tittade på fyra paradigmer, nämligen: allt är band, filer, objekt eller dokument. I alla fyra fall är utmaningen att förena data, enheter eller andra resurser för att göra det lättare att arbeta med. Varje operativsystem måste ha ett liknande enhetligt dataparadigm.


Gränssnitt för systemanrop

Baserat på Corbatos minimala mekanismprincip bör operativsystemet tillhandahålla så få systemanrop som möjligt (det minsta som krävs), och varje systemanrop bör vara så enkelt som möjligt (men inte enklare). Det enande dataparadigmet kan spela en viktig roll i detta. Till exempel, om filer, processer, I / O-enheter, etc. ser ut som filer eller objekt, kan de alla läsas med bara ett läst systemanrop. Annars måste du ha olika systemanrop som read_file, read_proc, read_tty, etc.

I vissa fall kan flera varianter av systemanrop krävas, men som regel är det i praktiken bättre att ha ett systemanrop som hanterar det allmänna fallet, med olika biblioteksprocedurer som döljer detta för programmerarna. Exempelvis tillhandahåller UNIX-operativsystemet exec-systemanropet för att ersätta det virtuella adressutrymmet för en process. Det vanligaste användningsfallet ser ut så här:

exec (namn.argp.envp);

Detta systemanrop laddar den körbara sökvägen och skickar den argumenten som argp pekar på och listan över miljövariabler som envp pekar på.

Dessa rutiner placerar bara argumenten i en matris och anropar sedan exec-systemanropet, vilket gör allt arbete. Ett sådant schema är bäst i båda avseenden: tack vare ett enda enkel systemanrop förblir operativsystemet enkelt, samtidigt kan programmeraren komma åt exec-systemanropet på olika sätt.

Naturligtvis är det enkelt att försöka använda ett enda systemanrop för alla tillfällen. För att skapa en process på UNIX krävs två systemanrop: gaffel följt av exec. Det första samtalet har inga parametrar; det andra samtalet har tre parametrar. Som jämförelse har Win32 API-anropet för att skapa en process, CreateProcess, 10 parametrar, varav en är en pekare till en struktur med ytterligare 18 parametrar. Frågan borde ha ställts för länge sedan: "Kommer det att finnas en katastrof om vi utelämnar något av detta?" Det sanningsenliga svaret borde ha varit: "I vissa fall måste programmeraren göra mer arbete för att uppnå en viss effekt, men vi får ett enklare, mer kompakt och pålitligt operativsystem." Naturligtvis kan den som erbjuder versionen med dessa 10 + 18-alternativ ha lagt till, "Men användare älskar alla dessa funktioner." Man kan argumentera för detta: "De gillar ännu fler system som använder lite minne och aldrig går sönder." Avvägningen av mer funktionalitet genom att använda mer minne är åtminstone synlig för blotta ögat och kan uppskattas (eftersom minneskostnaden är känd). Det är dock svårt att uppskatta antalet ytterligare störningar per år som kommer att bli resultatet av införandet av en ny funktion. Dessutom är det inte känt om användare skulle ha gjort samma val om de visste detta dolda pris i förväg. Denna effekt kan sammanfattas av den första lagen i Tanenbaum-programvaran:

När programstorleken ökar ökar också antalet fel det innehåller.

När nya funktioner läggs till i programmet läggs nya procedurer till det och därmed nya fel. Programmerare som tror att lägga till nya funktioner i programmet inte kommer att lägga till nya buggar är antingen nya för programmering eller tror att en älvmor övervakar dem.

Enkelhet är inte den enda principen som ska följas vid utformning av systemanrop. Du bör också komma ihåg frasen som B. Lampson sa 1984: Dölj inte makten.

Om hårdvaran har ett mycket effektivt sätt att åstadkomma något, bör programmerare ges lätt tillgång till den möjligheten, snarare än att begrava den i någon abstraktion. Syftet med abstraktioner är att dölja oönskade egenskaper, inte användbara egenskaper. Antag till exempel att hårdvaran har ett speciellt sätt att flytta stora delar av bilder över skärmen (det vill säga i videominnet) i hög hastighet. I det här fallet är det motiverat att införa ett nytt systemanrop som ger tillgång till denna mekanism, eftersom det är bättre än att läsa data från video-RAM i vanligt minne och sedan skriva tillbaka dessa data till video-RAM. Det nya samtalet ska bara flytta bitarna i videominnet. Om det här nya systemanropet är snabbt kommer det att tillåta användare att skapa effektivare program. Om systemanropet är långsamt kommer ingen att använda det.

Systemets utformning väcker också frågan om att använda anslutningsorienterade eller icke-anslutna samtal. Standardanropen för UNIX- och Win32-system för att läsa filer är anslutningsorienterade. Först öppnar du filen, sedan läser du den och slutligen stänger du filen. Vissa fjärrfilprotokoll är också anslutningsorienterade. För att till exempel använda FTP loggar en användare först in på en fjärrmaskin, läser filerna och loggar sedan av.

Å andra sidan kräver vissa fjärråtkomstprotokoll inte anslutningar. Till exempel kräver NFS inga anslutningar, som du såg i kapitel 10. Varje NFS-samtal är oberoende, så filer öppnas inte innan de läses eller skrivs, naturligtvis behöver inte filer stängas efter att de har lästs eller skrivits . World Wide Web kräver inte heller några anslutningar: för att läsa en webbsida begär du det helt enkelt. Ingen förkonfiguration krävs (en TCP-anslutning krävs fortfarande, men det är ett lägre protokolllager. HTTP-protokollet som används för att komma åt själva webbsidan kräver inte anslutningar).

Vilken mekanism som ska väljas - anslutningsorienterad eller anslutningsfri - avgör om mekanismen kommer att kräva ytterligare arbete (till exempel att öppna filer), eller om detta arbete flyttas till applikationsprogrammets axlar med hjälp av mekanismen. I det senare fallet finns det en betydande effektivitetsförstärkning om programmet får åtkomst till samma fil många gånger. För ett enda I / O-system, där kostnaden för att förbereda I / O (att öppna en fil) är låg, är det troligen bäst att använda det vanliga sättet (öppna först och sedan använda). För fjärrfilsystem är båda möjliga.

En annan fråga som uppstår när man utformar ett systemanropsgränssnitt är dess öppenhet. Listan över systemanrop som definieras av POSIX-standarden är lätt att hitta. Dessa systemanrop stöds av alla UNIX-system, liksom ett litet antal andra samtal, men en fullständig lista publiceras alltid. Däremot publicerade Microsoft aldrig en lista över Windows 2000-systemanrop, utan publicerar Win32 API-funktioner samt samtal till andra gränssnitt. dessa listor innehåller ett stort antal bibliotekssamtal (över 13 000 i Windows 2000), men endast ett litet antal av dem är riktiga systemsamtal. Argumentet för att öppna systemanrop är att programmerare känner till kostnaden för att använda funktioner. Funktioner som körs i användarutrymme är snabbare än de som kräver byte till kärnläge. Den slutna karaktären hos systemanrop har också sina fördelar genom att den ger flexibilitet i genomförandet av biblioteksprocedurer. Det vill säga, utvecklarna av operativsystemet kan ändra de faktiska systemanropen, samtidigt som applikationsprogrammens hälsa bibehålls.

Genomförande

Vi har diskuterat användargränssnittet och systemets samtalsgränssnitt. Nu är det dags att diskutera implementeringen av operativsystemet. I de kommande åtta avsnitten kommer vi att utforska några allmänna begreppsmässiga frågor relaterade till implementeringsstrategier. Därefter tittar vi på några exempel på tekniker på låg nivå som ofta är användbara.

Systemstruktur

Förmodligen det första beslutet som systemdesigners måste fatta är beslutet om vad systemet ska struktureras. Vi undersökte de grundläggande alternativen i avsnittet Operativsystemstruktur i kapitel 1, men vi kommer också att täcka dem här. En monolitisk ostrukturerad enhet är faktiskt en dålig idé, endast lämplig för ett litet operativsystem för, till exempel, ett kylskåp, men ändå är dess användning kontroversiell.

System med flera nivåer

Ett förnuftigt tillvägagångssätt som har etablerats genom åren är att skapa nivåindelade system. Systemet THE, utvecklat av E. Dijkstroy, var det första flernivåsystemet. UNIX-operativsystemen (se figur 10.2) och Windows 2000 har också en lagerstruktur, men nivåerna är mer ett sätt att beskriva systemet än den riktiga riktlinje som användes för att bygga det.

När man skapar ett nytt system måste utvecklare först välja nivåerna mycket noggrant och definiera funktionerna för varje nivå. Det nedre lagret ska alltid försöka dölja de mest obehagliga funktionerna i hårdvaran, som HAL-lagret gör. Nästa nivå kommer sannolikt att hantera avbrott, kontextväxlar och MMU, så ovanför den nivån är koden mestadels maskinoberoende. På ännu högre nivåer beror allt på utvecklarnas smak och preferenser. Ett alternativ är att Layer 3 hanterar trådhantering, inklusive schemaläggning och synkronisering av trådar, så att vi från och med Layer 4 får rätt trådar som normalt planeras och synkroniseras med hjälp av en standardmekanism (t.ex. mutexes).

På nivå 4 hittar vi enhetsdrivrutiner, som alla fungerar som en separat tråd, med sitt eget tillstånd, programräknare, register etc., eventuellt (men inte nödvändigtvis) i kärnans adressutrymme. En sådan anordning kan kraftigt förenkla I / O-strukturen, för när ett avbrott inträffar kan det översättas till ett upplåsningssystemanrop på mutex och ett samtal till schemaläggaren för att (potentiellt) starta en ny redo-tråd som blockerades av mutex . Detta tillvägagångssätt används på MINIX-systemet, men på UNIX-, Linux- och Windows 2000-operativsystem implementeras avbrottshanterare som en oberoende del av systemet och inte trådar, som själva kan styras av schemaläggaren, pausas etc. Eftersom den största svårigheten för alla operativsystem ligger i inmatningen - i slutändan, är det värt att överväga att göra det mer bearbetbart och mer inkapslat.

Över nivå 4 kommer vi sannolikt att hitta virtuellt minne, ett eller flera filsystem och systemanropshanterare. Om det virtuella minnet finns på en lägre nivå än filsystem kan blockcachen sparas, vilket gör att den virtuella minneshanteraren dynamiskt kan bestämma hur fysiskt minne ska allokeras mellan användarsidor och kärnsidor, inklusive cache. Så här fungerar operativsystemet Windows 2000.

Exokerneller

Medan skiktade strukturer har många anhängare bland systemutvecklare finns det också ett annat läger som tar exakt motsatt synvinkel. Deras ståndpunkt bygger på ett tvärgående argument. Detta koncept är att om något måste göras av själva användarprogrammet, så är det ineffektivt att göra det också på en lägre nivå.

Låt oss titta på tillämpningen av denna princip för fjärråtkomst till filer. Om systemet tar hand om att filerna inte skadas, bör det läsa kontrollsumman för varje skriven fil och lagra den med filen. När en fil skickas över nätverket skickas dess kontrollsumma också tillsammans med filen, som verifieras efter mottagande av filen av den mottagande parten. Om kontrollsummorna inte matchar skickas filen igen.

Kontrollsumverifiering är en mer exakt metod än att använda ett tillförlitligt nätverk. Det tillåter, förutom fel som uppstår vid överföring av en fil över nätverket, också att fånga upp läs- eller skrivfel till disk, minnesfel, programvarufel i routrar etc. Vidarebefordringsargumentet säger att det samtidigt inte längre är nödvändigt att använda ett tillförlitligt nätverk, eftersom det vid slutpunkten (mottagningsprocessen) finns tillräckligt med information för att verifiera den mottagna filens riktighet på egen hand. Det enda skälet till att använda ett tillförlitligt nätverksprotokoll i detta fall är att förbättra effektiviteten i nätverksfelshantering på en lägre nivå.

Genomgångsargumentet kan utökas till nästan gränserna för hela operativsystemet. Han menar att operativsystemet inte ska göra vad användarprogrammet kan göra själv. Till exempel, varför behöver vi ett filsystem? Varför inte låta användaren bara läsa och skriva delar av disken på ett säkert sätt? Naturligtvis tycker de flesta användare om att arbeta med filer, men argumentet från början till slut är att filsystemet ska vara ett biblioteksförfarande som kan kopplas till alla program som behöver filer. Detta tillvägagångssätt gör det möjligt för olika program att använda olika filsystem. Detta resonemang leder till slutsatsen att operativsystemet endast bör säkerställa en säker fördelning av resurser (till exempel processortid eller diskar) bland konkurrerande användare. En exokernel är ett operativsystem byggt med ett pass-through-argument.

System för klientserver

Avvägningen mellan ett operativsystem som gör allt och ett operativsystem som inte gör något är ett operativsystem som gör något. Resultatet av denna design är en mikrokärnadesign där det mesta av operativsystemet är serverprocesser på användarnivå (se figur 1.23). En sådan anordning har störst modularitet och flexibilitet jämfört med andra system. Gränsen för flexibilitet är att varje enhetsdrivrutin också ska köras som en användarprocess, helt skyddad från kärnan och andra drivrutiner. Att ta bort drivrutiner från kärnan eliminerar den största källan till instabilitet i något operativsystem - buggy tredjepartsdrivrutiner.

Naturligtvis måste enhetsdrivrutiner komma åt maskinvaruregisterna för enheter, så det behövs någon mekanism för att ge denna åtkomst. Om hårdvaran tillåter det kan varje drivrutin endast beviljas åtkomst till de I / O-enheter som behövs. Till exempel, om I / O-enhetsregister mappas till ett minnesadressutrymme, kan varje drivrutinsprocess ha en minnessida som mappar motsvarande enhetsregister, men inte andra sidor. Om I / O-portutrymmet är delvis skyddat kan varje förare få åtkomst till den del av detta utrymme som den behöver.

Även om hårdvarusupport inte är tillgängligt kan denna idé fortfarande utnyttjas. Detta kräver ett nytt systemanrop som endast är tillgängligt för drivrutinsprocesser. Detta systemanrop använder en lista över (port, värde) par för att fungera. Kärnan kontrollerar först om processen äger alla portarna i listan. I så fall kopieras de lämpliga värdena till portarna för att initiera I / O-enheten. Ett liknande systemanrop kan användas för att läsa I / O-portar på ett säkert sätt.

Denna metod skyddar kärndatastrukturerna från att undersökas och skadas av enhetsdrivrutiner, vilket är (för det mesta) bra. Det är möjligt att skapa en liknande uppsättning samtal för att tillåta enhetsdrivrutiner att läsa och skriva kärntabeller, men endast under kärnans kontroll och godkännande.

Huvudproblemet med detta tillvägagångssätt, och problemet med mikrokerner i allmänhet, är prestandaförsämring orsakad av ytterligare kontextomkopplare. Men praktiskt taget allt arbete med att skapa mikrokerneler gjordes för många år sedan, då centrala bearbetningsenheter var betydligt långsammare. Idag finns det inte många applikationer som använder varje droppe processorkraft som inte kan acceptera den minsta prestandaförlusten. När allt kommer omkring, när en textredigerare eller webbläsare körs, är CPU inaktiv i cirka 90% av tiden. Om ett mikrokernelbaserat operativsystem förvandlar ett 900 MHz-system till ett robust 800 MHz-system kommer få användare att klaga. De flesta användare var bara nöjda för bara några år sedan när de köpte sin tidigare dator med den då fantastiska 100 MHz-processorn.

Utbyggbara system

I de klient- / serversystem som diskuterats ovan var tanken att flytta så mycket utanför kärnan som möjligt. Det motsatta tillvägagångssättet är att placera fler moduler i kärnan, men på ett skyddat sätt. Nyckelordet här är naturligtvis skyddat. Det viktigaste av dessa är sandlådor och digitalt signerade program, eftersom det är opraktiskt att tillämpa tolkning i kärnan.

Självklart är själva det utdragbara systemet inte en metod för att strukturera operativsystemet. Men genom att börja med ett minimalt system, som huvudsakligen består av en säkerhetsmekanism, och gradvis lägga till säkra moduler till kärnan tills önskad funktionalitet uppnås kan du skapa ett minimalt system för en viss applikation. Med detta tillvägagångssätt kan ett nytt operativsystem skräddarsys för varje ny applikation genom att endast inkludera de element som är nödvändiga för den givna applikationen. Ett exempel på ett sådant system är Paramecium.

Kärntrådar

En annan fråga som är relevant för detta ämne, oavsett valet av strukturmodell, är systemflöden. Ibland är det praktiskt att låta kärntrådar existera separat från användarprocesser. Dessa trådar kan köras i bakgrunden, skriva smutsiga sidor på hårddisken, byta processer osv. Faktum är att själva kärnan kan bestå helt av sådana trådar. När en användare gör ett systemanrop körs inte användartråden i kärnläge utan blockerar och skickar kontroll till kärntråden, vilket tar kontrollen för att utföra arbetet.

Förutom kärntrådar som körs i bakgrunden, kör de flesta systemen också många demonprocesser i bakgrunden. Även om de inte är en del av operativsystemet, utför de ofta "systemfunktioner". Detta kan vara att ta emot och skicka e-post, såväl som att betjäna olika förfrågningar från fjärranvändare, till exempel FTP eller webbsidor.

Mekanism och politik

En annan princip som hjälper till att uppnå arkitektonisk konsistens, tillsammans med principerna om minimalism och struktur, är att skilja mekanismen från politiken. Om du sätter in motorn i operativsystemet och lämnar policyn till användarprocesser kan systemet förbli oförändrat även om det finns ett behov av att ändra policyn. Även om policymodulen ska finnas i kärnan, bör den isoleras från motorn så mycket som möjligt så att ändringar av policymodulen inte påverkar motormodulen.

För att klargöra gränsen mellan mekanism och politik, överväga två verkliga exempel. Som ett första exempel, ta ett stort företag som har en löneavdelning som ansvarar för att betala anställdas löner. Han har datorer, programvara, formulär, avtal med banker och andra mekanismer som krävs för att faktiskt betala löner. Men politik - att fatta beslut om vem som får vad och hur mycket - är helt separat och befogenhet för ledningen. Löneavdelningen utför helt enkelt det arbete som tilldelats det.

För ett andra exempel, överväg en restaurang. Den har en mekanism för att betjäna kunder, inklusive bord, rätter, servitörer, ett fullt utrustat kök, avtal med företag som säljer varor på kreditkort etc. Policyen, det vill säga vad som kommer att finnas på menyn, bestäms av kocken. Om kocken bestämmer att tofu är ute och de stora biffarna är kvar kan den nya policyn genomföras med den befintliga mekanismen.

Låt oss nu titta på några exempel från operativsystemområdet. Låt oss först ta en titt på schemaläggning av trådar. Kärnan kan innehålla en prioritetsplanerare med k-prioritetsnivåer. Denna mekanism är en matris indexerad av en prioritetsnivå. Varje element i matrisen representerar rubriken i listan med färdiga strömmar med en given prioritetsnivå. Schemaläggaren slingrar helt enkelt genom matrisen, börjar med högsta prioritet och väljer den första matchande tråden. Politiken handlar om att sätta prioriteringar. Systemet kan ha olika användarklasser, till exempel kan varje användare ha sin egen prioritet. Systemet kan också tillåta processer att ställa in relativa prioriteringar för sina trådar. Prioriteringar kan öka efter att en I / O-operation har slutförts eller minska när en process har förbrukat sin tilldelade kvantitet. Det finns många andra politiska tillvägagångssätt som också kan tillämpas, men den grundläggande principen är åtskillnaden mellan att fastställa politik och genomföra den.

Det andra exemplet är personsökning. Mekanismen inkluderar hantering av MMU, underhåll av en lista över ockuperade och lediga sidor och ett program för att flytta sidor från disk till minne och vice versa. I detta fall är policyn att bestämma vad man ska göra när ett sidbrytande inträffar. Policy kan vara lokal eller global, baserad på LRU, FIFO eller någon annan algoritm, men den kan (och borde) vara helt frikopplad från mekaniken för den faktiska personsökningen.

Det tredje exemplet är möjligheten att ladda moduler i kärnan. Mekanismen definierar hur de installeras i kärnan, hur de associeras, vilka samtal de kan komma åt och vilka samtal de kan tillhandahålla själva. Policyn definierar listan över användare som får ladda modulen i kärnan, samt listan över moduler som varje användare får ladda. Kanske är det bara superanvändaren som kan ladda moduler, men behörighet att ladda moduler kan också ges till alla användare om modulen är digitalt signerad av lämplig myndighet.

Orthogonality

God systemorganisation inkluderar diskreta koncept som kan blandas oberoende. Till exempel har C ++ primitiva datatyper som inkluderar heltal, teckennummer och flytande nummer. Det finns mekanismer för att kombinera datatyper, inklusive matriser, strukturer och fackföreningar. Dessa begrepp är oberoende av varandra, vilket gör att du kan skapa hela matriser, karaktärsarrangemang, strukturer av strukturer, strukturer som består av arrays, etc.

När en ny datatyp väl definierats, såsom en rad heltal, kan den faktiskt användas som medlem i en struktur eller union som om den vore en primitiv datatyp. Förmågan att självständigt kombinera separata begrepp kallas ortogonalitet. Denna egenskap är en direkt följd av principernas enkelhet och fullständighet.

Orthonalitet förekommer också i operativsystem i olika former. Ett exempel är Linux-klonsystemanropet, vilket skapar en ny tråd. Det här samtalet tar som en parameter en bitmapp som anger lägen som oberoende delning eller kopiering av adressutrymme, arbetskatalog, filbeskrivare och signaler. Om allt kopieras får vi en ny process, som med gaffelsystemet. Om ingenting kopieras betyder det att en ny tråd skapas i den aktuella processen. Mellanformulär kommer emellertid sannolikt också att skapas som inte är möjliga på traditionella UNIX-system. Separationen av egenskaper och deras ortogonalitet möjliggör en mer detaljerad kontroll.

En annan användning av ortogonalitet är att separera begreppen process och tråd i Windows 2000. En process är en behållare för resurser, inget mer och inget mindre. En ström är ett planeringsobjekt. När en process tar hand om från en annan process spelar det ingen roll hur många trådar den har. När schemaläggaren väljer en tråd spelar det ingen roll vilken process den tillhör. Dessa begrepp är ortogonala.

Vårt sista exempel på ortogonalitet kommer från UNIX-operativsystemet. Processen skapas här i två steg: använda gaffel- och exec-systemanrop. Skapandet av ett nytt adressutrymme och dess fyllning med en ny minnesbild är i detta fall åtskilda, vilket gör att du kan utföra vissa åtgärder mellan dessa steg. I operativsystemet Windows 2000 kan dessa två steg inte separeras, det vill säga begreppen att skapa ett nytt adressutrymme och fylla det med en ny minnesbild är inte ortogonala i detta system. Sekvensen för klon och exec-syscalls i Linux är ännu mer ortogonal eftersom det tillåter ännu mer detaljerad kontroll över dessa åtgärder. Den allmänna regeln kan formuleras enligt följande: att ha ett litet antal ortogonala element, som kan kombineras på olika sätt, gör att du kan skapa ett litet, enkelt och elegant system.

De flesta långlivade datastrukturer som används av operativsystemet har ett namn eller en identifierare som de kan hänvisa till. Tydliga exempel inkluderar inloggningar, filnamn, enhetsnamn, process-ID och så vidare. Hur dessa namn skapas och används är en viktig fråga vid systemdesign och implementering.

Namnen som människor skapar för deras användning är ASCII- eller Unicode-teckensträngar och tenderar att vara hierarkiska. Så hierarkin syns tydligt i exemplet med filvägar, till exempel består sökvägen / usr / ast / books / mos2 / chap-12 av namnen på kataloger, vars sökning ska börja i rotkatalogen. URL: er är också hierarkiska. Till exempel pekar URL: n www.cs.vu.nl/~ast/ på en specifik maskin (www) för en specifik fakultet (cs) vid ett specifikt universitet (vu) i ett visst land (nl). Delen av URL: en efter snedstrecket anger en specifik fil på den angivna maskinen, i det här fallet är standard www / index.html i ast-användarens hemkatalog. Observera att URL: er (såväl som DNS-adresser i allmänhet, inklusive e-postadresser) skrivs bakåt och börjar längst ner i trädet, i motsats till filnamn som börjar längst upp i trädet. Du kan titta på det annorlunda om du placerar trädet horisontellt. I det här fallet, i ett fall, kommer trädet att börja från vänster och växa till höger, och i det andra fallet, tvärtom, det kommer att börja från höger och växa till vänster.

Två-nivå namngivning används ofta: extern och intern. Till exempel kan filer alltid nås med namn, vilket är en teckensträng. Dessutom finns det nästan alltid ett internt namn som används av systemet. På UNIX är det riktiga namnet på en fil dess i-nodnummer. ASCII-namnet används inte alls. I själva verket är det här namnet inte ens unikt, eftersom flera länkar kan peka på filen. Och i Windows 2000 används filindexet i MFT som det interna namnet. En katalogs uppgift är att konvertera externa namn till interna namn.

I många fall (som exempel på filnamnet ovan) är det interna filnamnet ett unikt heltal som fungerar som ett index i kärntabellen. Andra exempel på indexnamn är UNIX-filbeskrivare och Windows 2000-objektbeskrivare. Observera att inget av dessa exempel har externa representationer. De är endast avsedda för internt bruk av systemet och körprocesser. I allmänhet är det en bra idé att använda tabellindex för tillfälliga namn som inte kvarstår över en systemstart.


Bindningstid

Som vi har sett använder operativsystem olika typer av namn för att hänvisa till objekt. Ibland är omvandlingen av ett namn till ett objekt fixad och ibland inte. I det senare fallet kan det vara viktigt när namnet är associerat med objektet. Generellt sett är tidig bindning enklare men inte flexibel, medan sen bindning är mer komplex men ofta mer flexibel.

För att klargöra begreppet bindningstid, låt oss titta på några verkliga exempel. Ett exempel på tidig bindning är att vissa högskolor tillåter föräldrar att registrera sitt barn på college från födseln och betala barnets undervisning i förväg. När en student går in på college 18 år senare är hans undervisning redan helt betald, oavsett den aktuella studieavgiften.

I branschen beställer tidig bindning i förväg delar. Däremot kräver just-in-time produkttillverkning att leverantörer kan tillhandahålla de nödvändiga delarna på plats, utan att de har beställts i förväg. Detta är ett exempel på sen bindning.

Programmeringsspråk stöder ofta olika typer av bindning för variabler. Kompilatorn binder globala variabler till en specifik virtuell adress. Detta är ett exempel på tidig bindning. Lokala variabler för en procedur tilldelas virtuella adresser (på stacken) under utförandet av proceduren. Detta är ett exempel på mellanlänkning. Variabler lagrade på högen (minne för vilka allokeras med malloc i C-program eller nya i Java-program) tilldelas en virtuell adress endast under den tid de faktiskt används. Detta är ett exempel på sen bindning.

För de flesta datastrukturer i operativsystem är tidig bindning vanligare, men sen bindning används ibland för flexibilitet. Minnesallokering är relaterad till denna fråga. I tidiga multitasking-system, på grund av bristen på hårdvaruadresskonfiguration, måste programmet laddas till en specifik minnesadress och konfigureras för att fungera på den här adressen. Om detta program någonsin byttes ut till hårddisken måste det laddas in senare i samma minnesadresser, annars kunde det inte fungera. Däremot är sidans virtuellt minne ett exempel på sen bindning. Den faktiska fysiska adressen som motsvarar en given virtuell adress är okänd tills sidan fysiskt flyttas till minnet.

Ett annat exempel på sen bindning är placeringen av ett fönster i ett grafiskt användargränssnitt. Till skillnad från äldre grafiksystem, där programmeraren var tvungen att ange absoluta skärmkoordinater för alla bilder, använder programvaran i moderna grafiska användargränssnitt koordinater relativt det ursprungliga fönstret. Sådana koordinater är okända tills detta fönster visas och kan till och med ändras över tiden. Statiska och dynamiska strukturer

Operativsystemdesigners tvingas ständigt välja mellan statiska och dynamiska datastrukturer. Statiska strukturer är alltid lättare att förstå, lättare att programmera och snabbare att använda. Dynamiska strukturer är mer flexibla. Ett uppenbart exempel är en processtabell. Tidiga system tilldelade helt enkelt en fast uppsättning strukturer för varje process. Om procestabellen bestod av 256 poster, kunde inte mer än 256 processer existera när som helst. Ett försök att skapa den 257: e processen misslyckades på grund av platsbrist i tabellen. Liknande överväganden gällde för den öppna filtabellen (både för varje användare individuellt och för systemet som helhet) och för olika andra kärntabeller.

En alternativ strategi är att skapa en procestabell som en länkad lista över minibord, med början på ett enskilt minibord. När denna tabell är full tilldelas minne i den globala poolen för nästa tabell, som är länkad till den första tabellen. På detta sätt kommer inte processtabellen att rinna över förrän kärnan tar slut på minnet.

En alternativ metod är att använda en tabell med fast storlek, men fördela en ny tabell med fast storlek, till exempel dubbelt så stor, när den är full. I det här fallet kopieras de aktuella posterna från den gamla tabellen till den nya och minnet som upptogs av den gamla tabellen returneras till poolen. Således är tabellen alltid sammanhängande och inte ansluten. Nackdelen med detta tillvägagångssätt är att någon form av minneshantering krävs och tabellens adress kommer att vara variabel istället för konstant.

Detsamma gäller kärnstaplar. När en tråd byter till kärnläge, eller när en kärnlägetråd startas, behöver den tråden en stapel i kärnans adressutrymme. För användartrådar kan stapeln initieras för att växa nedåt från toppen av det virtuella adressutrymmet. Dessutom kan dess storlek utelämnas i förväg. För kärntrådar måste stackstorleken anges i förväg, eftersom stacken tar upp en del av kärnans virtuella adressutrymme. Dessutom kan det finnas flera stackar. Frågan är: hur mycket minne ska tilldelas för varje stack? Fördelarna och nackdelarna med de olika tillvägagångssätten är ungefär desamma som i fallet med processtabellen.

Problemet med att välja mellan statisk eller dynamisk minnesallokering uppstår också för processchemaläggning. I vissa system, särskilt realtidsystem, kan schemaläggning göras statiskt i förväg. Till exempel vet flygbolagen vid vilken tidpunkt deras flyg kommer att starta, några veckor innan de faktiskt åker. På samma sätt vet multimediasystem i förväg när vissa video-, ljud- och andra processer ska startas. För allmänna ändamålssystem fungerar dessa överväganden inte och planeringen måste vara dynamisk.

Frågan om att välja mellan statisk eller dynamisk minnesallokering uppstår också när man utformar kärnstrukturer. Det är mycket lättare om kärnan är byggd som ett enda binärt program laddat i minnet för drift. En konsekvens av detta tillvägagångssätt är dock att för varje ny I / O-enhet som ska installeras måste kärnan byggas om tillsammans med den nya enhetsdrivrutinen. Tidigare versioner av UNIX-operativsystemet fungerade på det här sättet, vilket var bra eftersom nya I / O-enheter sällan lades till minidatorer. De flesta operativsystem idag låter dig dynamiskt lägga till program i kärnan, med alla ytterligare komplikationer som detta tillvägagångssätt innebär.

Top-down och bottom-up systemimplementering

Även om det är bäst att utforma systemet uppifrån och ner, kan i teorin implementeringen av systemet göras från topp till botten eller från botten till toppen. När de implementeras från topp till botten börjar konstruktörer med systemanropshanterare och tittar sedan på vilka mekanismer och datastrukturer som krävs för att stödja dem. Därefter skrivs dessa procedurer och hela processen upprepas tills hårdvarunivån har uppnåtts.

Nackdelen med detta tillvägagångssätt är att det är svårt att testa någonting så länge som endast toppnivåförfaranden finns tillgängliga. Av denna anledning tycker många utvecklare att det är mer praktiskt att bygga ett system från botten upp. Med detta tillvägagångssätt skriver du först ett program som döljer maskinvaran på lägre nivå. Avbrottshantering och en klockförare behövs också tidigt i designfasen.

Sedan kan du implementera multitasking tillsammans med en enkel schemaläggare (till exempel starta processer i cirkulär ordning). Redan vid denna tidpunkt kan systemet testas för att kontrollera om det hanterar flera processer korrekt. Om allt fungerar bra kan du börja detaljerad design av de olika tabeller och datastrukturer som krävs av systemet, särskilt de som hanterar processer och trådar, samt minne. I / O och filsystemet kan skjutas upp genom att initialt endast implementera primitiva tangentbordets in- och utdata till skärmen för testning och felsökning. I vissa fall bör du skydda viktiga datastrukturer på låg nivå genom att endast tillåta åtkomst med speciella åtkomstprocedurer - som ett resultat får vi objektorienterad programmering, oavsett vilket programmeringsspråk som faktiskt används. När de lägre nivåerna har skapats kan de testas grundligt. Således skapas systemet från botten upp, liknande hur höga byggnader är byggda.

Om det finns ett stort team av programmerare som arbetar med ett projekt, är ett alternativt tillvägagångssätt att först skapa en detaljerad design av hela systemet och sedan fördela uppgifterna för att skriva enskilda moduler mellan olika grupper av programmerare. Varje grupp testar självständigt sitt eget arbete. När alla moduler är klara kombineras de och testas tillsammans. Nackdelen med detta tillvägagångssätt är att om ingenting initialt fungerar, är det mycket svårt att avgöra vilken modul som skapades korrekt och vilken som innehåller fel, och vilken grupp av programmerare missförstod vad de kunde förvänta sig från andra moduler. Denna metod används dock ofta när det finns ett stort antal programmerare för att få ut det mesta av systemdesignarbetets parallellitet.

Användbara metoder.

Så vi har just diskuterat några av de abstrakta idéerna som används i design och konstruktion av operativsystem. Vi kommer nu att titta på några specifika tekniker som är användbara vid implementering av system. Naturligtvis finns det många andra metoder också, men volymen i denna bok tillåter oss inte att täcka alla metoder.

Döljer hårdvara

Ett stort antal utrustning är mycket obekvämt att använda. Det bör döljas tidigt i implementeringen av systemet (såvida det inte erbjuder specialfunktioner). Några av detaljerna på den lägsta nivån kan döljas av ett lager som HAL. Det finns dock detaljer som inte kan döljas på detta sätt.

Avbrytande av hanteringen bör tas upp tidigt i systemdesignen. Närvaron av avbrott gör programmeringen obehaglig, men operativsystem måste hantera avbrott. Ett tillvägagångssätt är att omedelbart förvandla dem till något annat. Till exempel kan varje avbrott förvandlas till en ny tråd som visas. I det här fallet kommer högre nivåer inte längre att hantera avbrott, utan med trådar.

Den andra metoden är att konvertera varje avbrott till en upplåsning på mutex som motsvarande förare förväntar sig. Då är den enda effekten av avbrottet att en av trådarna kommer att gå i ett klart tillstånd.

Den tredje metoden är att konvertera avbrottet till ett meddelande till någon tråd. Programmet på låg nivå genererar helt enkelt ett meddelande som innehåller information om var avbrottet kom ifrån, köar det och åberopar schemaläggaren, som antagligen kommer att starta en process som sannolikt väntar på meddelandet. Alla dessa metoder, och andra liknande dem, försöker konvertera avbrott till trådsynkroniseringsoperationer. Hanteringen av varje avbrott med lämplig tråd i lämpligt sammanhang är lättare att hantera än att skapa en avbrottshanterare som fungerar i ett godtyckligt sammanhang, det vill säga i det där avbrottet inträffade. Naturligtvis måste avbrottshantering vara effektiv, men djupt inne i operativsystemet måste allt vara effektivt.

De flesta operativsystem är utformade för att köras på en mängd olika plattformar. Dessa plattformar kan skilja sig åt i CPU, MMU, maskinordlängd, RAM-storlek och andra parametrar som är svåra att maskera med HAL-nivån eller motsvarande. Det är dock önskvärt att ha en enda uppsättning källfiler som används för att generera alla versioner. Annars måste varje fel du hittar fixas många gånger i många källfiler. Det finns dock en risk att källfilerna skiljer sig mer och mer från varandra.

Vissa maskinvaruskillnader, som mängden RAM, kan hanteras genom att formatera denna parameter som en variabel och definiera dess värde vid starttid. Till exempel kan en variabel som innehåller mängden RAM användas av ett program som tillhandahåller minne till processer, liksom för att bestämma storleken på cacheblocket, sidtabeller etc. Till och med storleken på statiska tabeller, såsom processen kan ställas in vid start, beroende på den totala mängden RAM.

Andra skillnader, såsom skillnader i centrala processorenheter, kan dock inte döljas av en enda binär kod som bestämmer processortypen vid start. Ett sätt att lösa detta problem är att använda en enda källan villkorlig översättning. Källfilerna använder specifika kompileringsflaggor för olika konfigurationer för att producera olika binärer från en enda källkod beroende på CPU, ordlängd, MMU, etc. Tänk dig ett operativsystem som måste köras på datorer med Pentium-processorer och UltraSPARC som kräver olika initialiseringsprogram. Beroende på värdet på den konstanta CPU: n, definierad i config.h-rubrikfilen, kommer antingen en typ av initialisering att utföras. Eftersom det bara finns en version av programmet i en binär fil, förloras ingen effektivitet.

Anta som ett andra exempel att vi vill ha datatypen Register, som måste vara 32 bitar på en Pentium-dator och 64 bitar på en UltraSPARC-dator. Du kan göra detta med den villkorliga koden i Listing 12.3,6 (förutsatt att kompilatorn behandlar int som ett 32-bitars heltal och länge som 64-bitars). När denna definition ges (eventuellt i en rubrikfil som ingår i alla andra källfiler) kan programmeraren helt enkelt deklarera variabler av typen Register och vara säker på att dessa variabler är av rätt storlek.

Naturligtvis måste config.h-rubrikfilen definieras korrekt. För en Pentium-processor kan det se ut så här:

För att kompilera operativsystemet för UltraSPARC-processorn måste du använda en annan config.h-fil som innehåller rätt värden för UltraSPARC-processorn, kanske något liknande

Vissa läsare kanske undrar varför CPU- och WORD_LENGTH-variablerna styrs av olika makron. I definitionen av registerkonstanten kan du förgrena programmet genom att ställa in dess värde beroende på CPU-konstantens värde, det vill säga ställa in registerkonstanten till 32 bitar för Pentium-processorn och 64 bitar för UltraSPARC-processorn. Men denna idé är inte särskilt bra. Vad händer om vi senare migrerar till en 64-bitars Intel Itanium-processor? För att göra detta måste vi lägga till ett tredje villkorligt uttalande för Itanium-processorn. Så här gjordes, allt du behöver göra är att lägga till en rad i Itanium config.h-filen.

Detta exempel illustrerar ortogonalitetsprincipen som diskuterats tidigare. De delar av systemet som beror på typen av CPU ska kompileras med hjälp av villkorlig kompilering, beroende på värdet på den konstanta CPU: n, och för de delar av systemet som beror på ordstorleken bör makrot WORD_LENGTH användas. Samma överväganden gäller för många andra parametrar.

Indirektion

Ibland sägs det att det inte finns något sådant problem i cybernetik som inte kan lösas på en annan nivå av indirektion. Även om detta är en bestämd överdrift finns det viss sanning i frasen. Låt oss titta på några exempel. I system baserade på Pentium-processorn, när en tangent trycks in, genererar hårdvaran ett avbrott och placerar inte ett ASCII-tecken i enhetsregistret utan en skanningskod för nyckeln. När nyckeln senare släpps genereras dessutom ett andra avbrott, även med nyckelnumret. Denna indirektion ger operativsystemet möjligheten att använda nyckelnumret som ett index i tabellen för att få ett ASCII-tecken från dess värde. Denna metod gör det lättare att hantera olika tangentbord i olika länder. Närvaron av information om att både trycka på och släppa tangenter gör att du kan använda valfri tangent som ett register, eftersom operativsystemet vet i vilken ordning tangenterna trycktes och släpptes.

Indirekt används också vid datautdata. Program kan visa ASCII-tecken, men dessa tecken kan tolkas som index i en tabell som innehåller det för närvarande visade teckensnittet. Tabellelementet innehåller bitmappen för symbolen. Denna form av indirektion skiljer tecken från teckensnittet.

Ett annat exempel på indirektion är användningen av större enhetsnummer i UNIX. Kärnan innehåller två tabeller, en för blockenheter och en för karaktärsenheter, indexerad av huvudenhetsnumret. När en process öppnar en speciell fil, till exempel / dev / hd0, extraherar systemet information om enhetstyp (block eller tecken) och större och mindre enhetsnummer från i-noden och använder dem som index för att hitta rätt drivrutin i förarbordet. Denna typ av indirektion gör det lättare att konfigurera om systemet eftersom program handlar om symboliska enhetsnamn snarare än faktiska drivarnamn.

Ett annat exempel på indirektion sker i meddelandesystem där destinationen inte är en process utan en postlåda. På detta sätt uppnås avsevärd flexibilitet (till exempel kan en sekreterare ta emot e-post från sin chef).

På sätt och vis är användningen av makron, till exempel, också en form av indirektion, eftersom en programmerare kan skriva ett program utan att veta det verkliga värdet på tabellen. Det anses vara god praxis att ge symboliska namn till alla konstanter (ibland utom –1, 0 och 1) och placera dem i rubriker med lämpliga kommentarer.


Återanvänd

Det är ofta möjligt att återanvända samma program i ett något annat sammanhang. Detta minskar binärfilernas storlek och innebär också att programmet bara behöver felsökas en gång. Antag till exempel att du använder bitmappar för att hålla reda på lediga block på disken. Diskblock kan hanteras med hjälp av tilldelnings- och fria rutiner.

Dessa procedurer ska åtminstone fungera med alla skivor. Men vi kan gå längre med detta resonemang. Samma procedurer kan användas för att hantera minnesblock, cache-block för filsystem och i-noder. De kan faktiskt användas för att allokera och släppa resurser, som kan numreras linjärt.

Återinträde

Reentrancy betyder en egenskap hos ett program som gör det möjligt för flera processer att köra programmet samtidigt. På multiprocessorer finns det alltid en risk att en av processorerna börjar köra en procedur som redan exekveras av en annan processor. I det här fallet kommer två (eller flera) trådar på olika centrala processorer att köra samma program samtidigt. Om det finns områden i detta program som denna situation är oönskad bör tillgång till dessa (kritiska) områden skyddas med mutexes.

Detta problem finns dock även på uniprocessorsystem. I synnerhet kör de flesta operativsystem med avbrott aktiverat. Om avbrott avaktiveras kommer många av signalerna från I / O-enheterna att gå förlorade och systemet blir opålitligt. Medan operativsystemet kör en viss procedur kan ett avbrott inträffa, och det är möjligt att avbrottshanteraren också kommer att börja köra samma procedur. Om datastrukturerna vid avbrottet vid avbrottet var i inkonsekvent tillstånd (det vill säga det avbrutna förfarandet började ändra dessa data, men hade inte tid att slutföra), kommer avbrottshanteraren antingen att fungera fel eller kommer att inte kunna arbeta alls.

Denna situation kan till exempel uppstå om den avbrytbara proceduren är schemaläggaren själv. Anta att en process har använt sin kvantitet och operativsystemet har flyttat den till slutet av kön. Under arbetet med listan inträffar ett avbrott, vilket resulterar i att en annan process går i klartillstånd och startar schemaläggaren. Om kön är i inkonsekvent tillstånd vid den här tiden kommer operativsystemet sannolikt inte att kunna fortsätta. Därför, även i ett uniprocessorsystem, är det bäst att göra det mesta av systemet återinträngande, skydda kritiska datastrukturer med mutexer och i vissa fall inaktivera avbrott helt.

Brute force-metod

Användningen av enkla lösningar som kallas brute force har fått en negativ konnotation genom åren, men enkelhet är ofta en fördel. Varje operativsystem har många procedurer som sällan anropas eller som fungerar på så liten mängd data att det inte är någon mening att optimera dem. Till exempel måste systemet ofta söka efter ett element i en tabell eller matris. Brute-force-metoden är i det här fallet att lämna bordet som det är utan att beställa elementen på något sätt och söka det linjärt från början till slut. Om antalet element i tabellen är litet (till exempel inte mer än 100) blir vinsten från att sortera tabellen eller använda cachning liten, men programmet blir mycket mer komplext och därför sannolikheten att innehålla fel i den kommer att öka dramatiskt. Naturligtvis, för funktioner som finns i kritiska delar av systemet, till exempel i ett förfarande som handlar om kontextväxling, bör du vidta alla åtgärder för att påskynda dem, kanske till och med skriva dem (Gud förbjuder!) I samlare. Men det mesta av systemet är inte i det kritiska området. Således adresseras många systemanrop sällan. Om gaffelsystemsamtalet utförs var 10: e sekund, och det tar 10 ms, även om det omöjliga lyckas - en optimering som tar gaffelsystemets samtal för att ta 0 ms - är den totala förstärkningen bara 0,1%. Om koden efter optimering blir större och innehåller fler fel, är det i detta fall bättre att inte göra optimering.

Kontrollerar först om det finns fel

Många systemanrop kan misslyckas av många anledningar: filen som ska öppnas kan ägas av någon annan; skapandet av processen kan misslyckas eftersom procestabellen är full; signalen kunde inte skickas eftersom mottagningsprocessen inte existerar. Operativsystemet måste noggrant kontrollera om det finns många fel innan ett systemanrop görs.

Många systemanrop kräver resursförvärv, såsom processtabellposter, i-nodtabellposter eller filbeskrivare. Innan du tar tag i resurser är det bra att kontrollera om det här systemanropet kan göras. Detta innebär att hela kontrollen ska göras i början av proceduren som gör systemanropet. Varje kontroll ska se ut så här:

Om systemanropet klarar alla tester blir det klart att det kommer att lyckas. Vid denna tidpunkt kan resurser tilldelas honom.

Om kontrollerna är blandade med samtal till resurser, om nästa test misslyckas, måste systemanropet returnera alla mottagna resurser. Om programmet inte är skrivet helt korrekt och någon resurs inte returneras fryser inte operativsystemet omedelbart. Till exempel kan ett av elementen i procestabellen vara permanent (tills omstart) inte tillgänglig. Men med tiden kan denna situation upprepas flera gånger, medan mängden otillgängliga resurser kommer att ackumuleras. Slutligen kan de flesta av posterna i procestabellen bli oåtkomliga, vilket får systemet att frysa, och det är troligtvis extremt svårt att fixa detta fel eftersom det inte blir lätt att reproducera.

Många system lider av dessa "sjukdomar" i form av minnesläckor. Ganska ofta ringer program malloc för att få minne, men glöm att ringa gratis senare för att frigöra det. Allt systemminne försvinner gradvis tills systemet fryser.

Engler och hans kollegor har föreslagit en intressant metod för att kontrollera efter sådana fel vid sammanställningen. Författarna till den här boken har funnit att programmerare känner till många villkor som de måste följa när de skriver ett program och som inte kontrolleras av kompilatorn. Så om du till exempel har låst en mutex, måste alla körvägar för detta program, med början från denna punkt, innehålla upplåsning av mutex och får inte innehålla ett andra lås av samma mutex. Författarna till boken har utvecklat en metod som gör det möjligt för programmeraren att instruera kompilatorn att automatiskt tillämpa sådana regler. Programmeraren kan också ange i programmet att minnet som tilldelats processen ska frigöras oavsett vilken gren programmet fortsätter på, och också instruera kompilatorn att övervaka uppfyllandet av många andra villkor.

Prestanda

Allt annat lika är ett snabbt operativsystem bättre än ett långsamt. Ett snabbt men opålitligt operativsystem är dock värre än ett pålitligt men långsamt. Eftersom komplexa optimeringstekniker ofta leder till nya fel i systemet, bör optimering inte överanvändas. Ändå finns det områden där prestanda är kritisk och optimering är värt ansträngningen. I följande avsnitt tittar vi på några allmänna tekniker som kan användas för att förbättra prestanda där det behövs.

Cachning

En välkänd teknik för att förbättra prestanda är cachning. Den kan användas när det sannolikt förutses att samma resultat kommer att krävas många gånger. Den allmänna metoden är att göra allt arbete första gången och sedan lagra resultatet i cachen. Vid efterföljande försök kommer cachen att kontrolleras först. Om resultatet ligger i cachen behöver du bara hämta det därifrån. Annars måste du göra allt arbetet först.

Vi har redan sett användningen av cachen i filsystemet, där den lagrar ett antal nyligen använda diskblock, vilket undviker åtkomst till disken när du läser ett block. Cachning kan dock också användas för andra ändamål. Till exempel är bearbetning av filvägar överraskande CPU-intensiv. Med tanke på UNIX-exemplet igen, för att hitta filen / usr / ast / mbox, måste du göra följande diskåtkomst:

För att helt enkelt bestämma i-nodnumret för filen du letar efter måste du komma åt disken minst sex gånger. Om filstorleken är mindre än blockstorleken (till exempel 1024 byte) krävs det åtta skivåtkomst för att läsa innehållet i filen.

På vissa operativsystem optimeras filsökningshantering genom cachningspar (sökväg, i-nod).

När filsystemet behöver hitta en fil längs en sökväg, letar sökvägshanteraren först i cachen och letar efter det längsta underlag som matchar den sökväg som behandlas. Om sökvägen / usr / ast / grant / stw behandlas svarar cachen att i-noden i katalogen / usr / ast är 26, så sökningen kan startas därifrån och antalet diskåtkomster kan minskas med fyra. Nackdelen med cachningsvägar är att mappningen av ett filnamn till dess i-nodnummer inte är konstant. Tänk dig att filen / usr / ast / mbox raderas och dess i-nod används för en annan fil som kan ägas av en annan användare. Filen / usr / ast / mbox skapas sedan om, men den här gången får den i-nod nummer 106. Om ingen speciell åtgärd vidtas kommer cacheposten att peka på fel i-nodnummer. Därför, när du tar bort en fil eller katalog, bör du ta bort posten som motsvarar den här filen från cachen, och om du tar bort en katalog, bör du också ta bort alla poster för filerna och underkatalogerna i den här katalogen *.

Inte bara diskblock och filvägar kan cachas. I-noder kan också cachas. Om tillfälliga trådar används för att hantera avbrott, kräver var och en en stack och ytterligare mekanism. Dessa tidigare använda strömmar kan också cachas eftersom det är lättare att uppdatera en redan använd ström än att skapa en ny (med hjälp av en cache undviks behovet av att allokera minne för en ny process). Caching kan användas för nästan allt som är svårt att reproducera.

Tips

Cache-objekt måste alltid vara korrekta. Cache-sökningar kan misslyckas, men om ett objekt hittas är det garanterat korrekt, så det hittade objektet kan användas utan extra krångel. I vissa system är det bekvämt att ha en ledtabell. Tips är förslag på lösningar, men de garanteras inte att de är korrekta. Processen som går åt den här tabellen måste kontrollera att själva resultatet är korrekt.

Ett välkänt exempel på tips är webbadresser som finns på webbsidor. När en användare klickar på en länk garanteras de inte att motsvarande webbsida är där webbadressen pekar. Det kan faktiskt visa sig att sidan i fråga har raderats för flera år sedan. Således är informationen på webbsidan bara en ledtråd.

Tips används också när du arbetar med fjärrfiler. Informationen i verktygstipsen berättar något om den raderade filen, till exempel dess plats. Det är dock möjligt att den här filen redan har raderats eller flyttats till en annan plats, så det är alltid nödvändigt att kontrollera om ledtråden är korrekta.

Använd lokalitet

Processer och program fungerar inte slumpmässigt. De verkar till stor del vara lokala både i tid och rum, och denna information kan användas på olika sätt för att förbättra prestanda. Ett välkänt exempel på rumslig lokalitet är det faktum att processer inte hoppar slumpmässigt inom deras adressutrymme. Vanligtvis använder de ett relativt litet antal sidor på en fast tid. Sidor som aktivt används av en process kan betraktas som processens arbetsuppsättning. Och operativsystemet kan säkerställa att denna arbetsuppsättning finns i minnet när processen får kontroll, vilket minskar antalet sidavbrott.

Lokalitetsprincipen gäller också för filer. När en process väljer en viss arbetskatalog, kommer många av dess efterföljande filåtgärder sannolikt att hänvisa till filer som finns i den katalogen. Prestanda kan förbättras genom att placera alla katalogfiler och deras i-noder nära varandra på disken. Detta är principen bakom Berkeley Fast File System.

Ett annat område där lokalitet spelar en viktig roll är schemaläggning av trådar i multiprocessorer. Som visas i kapitel 8 är en teknik för att schemalägga trådar att försöka starta varje tråd på den senaste CPU den kördes på, i hopp om att några av dess block fortfarande finns i cachen.

Optimera det allmänna fallet

Det är ofta bra att skilja mellan det vanligaste fallet och det minst troliga fallet och behandla dem på olika sätt. Vanligtvis hanteras olika ärenden av helt olika program. Det är viktigt att det vanliga fallet går snabbt. Från en algoritm för ett sällsynt fall räcker det för att uppnå korrekt funktion.

Som ett första exempel, överväg att komma in i en kritisk region. I de flesta fall kommer processen att lyckas komma in i det kritiska området, särskilt om processerna inte spenderar mycket tid inom detta område. Operativsystemet Windows 2000 drar nytta av detta genom att tillhandahålla Win32 API-anropet EnterCriticalSection, vilket är en atomfunktion som validerar en flagga i användarläge (med ett TSL-processorkommando eller motsvarande). Om testet lyckas går processen helt enkelt in i en kritisk region som inte kräver att kärnan anropas. Om testet är negativt utför biblioteksproceduren en nedåtgående operation på semaforen för att blockera processen. I det normala fallet krävs alltså inte ett samtal till kärnan.

Som ett andra exempel, överväg att ställa in ett larm (med UNIX-toner). Om ingen väckarklocka har startats just nu skapas en post som placeras i timer-kön. Men om ett alarm redan har ställts in bör det hittas och tas bort från tidskön. Eftersom larmsystemanropet inte anger om ett larm redan har ställts in, måste systemet anta det värsta, det vill säga att det redan har startats. Men i de flesta fall väcks inte larmet, och eftersom det är dyrt att radera ett befintligt larm, bör du skilja mellan de två.

Ett sätt att åstadkomma detta är att lagra lite i procestabellen som anger om larmet har startat. Om biten rensas följer programmet en enkel sökväg (det lägger bara till en ny timer-kö utan någon kontroll). Om biten är inställd måste timerkön kontrolleras.

Projektledning

Många programmerare är eviga optimister. De tror att du bara behöver sätta dig ner vid tangentbordet och börja skriva för att kunna skriva ett program. Kort därefter visas ett helt färdigt felsökat program. Det är omöjligt att skriva mycket stora program på detta sätt. I följande avsnitt kommer vi kort att diskutera frågorna om att hantera stora programvaruprojekt, särskilt hantera stora systemprojekt.

Mytisk månadsmånad

I sin klassiska bok diskuterar OS / 360-utvecklaren Fred Brooks varför det är så svårt att bygga ett stort operativsystem. När de flesta programmerare möter sitt uttalande om att specialister som arbetar med stora projekt endast kan producera 1000 rader med finjusterad kod per år, undrar de om professor Brooks har flög från rymden, från planeten Bug. När allt kommer omkring kommer de flesta ihåg att bygga ett program på 1000 rader på bara en natt. Hur kan denna volym källkod vara den årliga normen för alla programmerare vars IQ överstiger 50?

Brooks konstaterar att stora projekt som använder hundratals programmerare skiljer sig i grunden från små projekt och att de resultat som uppnås när man arbetar med ett litet projekt inte kan överföras till ett stort projekt. I ett stort projekt ägnas mycket tid åt att planera hur man delar upp arbetet i separata moduler. I det här fallet måste du beskriva modulernas arbete och gränssnitten till dem i detalj och försöka föreställa dig hur dessa moduler samverkar och innan själva programmeringen börjar. Sedan skapas och felsökas modulerna individuellt. Slutligen sätts modulerna ihop och hela systemet testas. I det här fallet vill systemet som är monterat från modulerna som arbetar separat inte fungera, och efter montering och start kollapsar det omedelbart. Brooks uppskattar antalet jobb enligt följande:

1/3 planering;

1/6 kodning;

1/4 modul testning;

1/4 systemtestning.

Med andra ord är det faktiskt att skriva ett program den enklaste delen av ett projekt. Den svåraste delen är att bestämma vilka moduler som ska vara, samt att få dessa moduler att kommunicera korrekt med varandra. I ett litet program skrivet av en programmerare är planering bara den enklaste delen.

Med bokens titel uppmärksammar Brooks läsarens uppmärksamhet på hans eget påstående att människor och tid inte är utbytbara. Det finns ingen sådan enhet som en månads månad i programmeringen. Om 15 personer är inblandade i projektet och det tar dem två år att slutföra arbetet, följer inte att 360 personer kommer att slutföra detta arbete på en månad och knappt 60 personer kommer att slutföra detta på sex månader.

Det finns tre skäl till detta fenomen. För det första kan arbetet inte delas helt. Innan planeringen är klar och det bestäms vilka moduler som behövs och vilka gränssnitt som kommer att bli, kan ingen programmering ens börja. I ett tvåårigt projekt kan planering ensam ta åtta månader.

För det andra, för att fullt ut kunna använda ett stort antal programmerare, bör arbetet delas in i ett stort antal moduler för att ge alla arbete. Eftersom potentiellt varje modul samverkar med varje modul, växer antalet modulmodulpar som övervägs i proportion till kvadraten av antalet moduler, det vill säga kvadraten för antalet programmerare. Därför går stora projekt med en ökning av antalet programmerare mycket snabbt ur kontroll. Noggranna mätningar av 63 mjukvaruprojekt bekräftade att beroendet av projektets genomförandetid på antalet programmerare inte är så enkelt som begreppet månatliga månader kan föreslå.

För det tredje är felsökningsprocessen till stor del sekventiell. Att sitta tio personer istället för en felsökare för att lösa ett problem hjälper dig inte att hitta ett fel i ditt program tio gånger snabbare. I själva verket kommer tio debuggare antagligen att springa långsammare än en, eftersom de kommer att spendera mycket tid på att prata med varandra.

Brooks sammanfattar sin erfarenhet av stora projekt genom att formulera följande lag (Brooks 'Law): Att lägga till ytterligare arbetskraft i ett mjukvaruprojekt i ett sent skede ökar ledtiderna för projektet.

Nackdelen med att introducera nya människor till projektet är att de behöver utbildas, moduler måste delas om för att matcha antalet programmerare, många möten krävs för att samordna arbetet för enskilda grupper och programmerare etc. Abdel-Hamid och Madnick fick en experimentell bekräftelse av denna lag. En lite oseriös version av Brooks lag låter så här: Om du samlar nio kvinnor i arbete i ett rum kommer de inte att föda på en månad.


Erfarenhetens roll

Erfarna utvecklare är kritiska när de utformar ett operativsystem. Brooks påpekar att de flesta misstag inte görs i programmeringen utan i designfasen. Programmerare har rätt att göra vad de får veta. Men om det de fick höra var fel, kan ingen testprogramvara fånga felspecifikationsfelet.

Brooks lösning är att överge den klassiska utvecklingsmodellen. Principen är att först skriva huvudprogrammodulen, som helt enkelt kallar toppnivåprocedurer. I början är dessa procedurer stubbar. Från och med den första dagen kommer systemet att sändas och lanseras, även om det inte kommer att göra någonting. Moduler installeras gradvis i systemet. Resultatet av att använda den här metoden är att monteringen av systemet kontrolleras ständigt, så fel i projektet upptäcks mycket tidigare. Således börjar processen att lära av sina egna misstag också mycket tidigare.

Ofullständig kunskap är en farlig sak. I sin bok beskriver Brooks ett fenomen som han kallar den andra systemeffekten. Ofta är den första produkten som skapas av utvecklingsteamet minimal eftersom de fruktar att det inte fungerar alls. Därför är de försiktiga med att lägga in många funktioner i den första versionen av programvaran. Om projektet lyckas skapar de nästa version av programvaran. Inspirerad av sin egen framgång, för andra gången, inkluderar utvecklarna i systemet alla skramlar och prydnadssaker som medvetet inte inkluderades i den första utgåvan. Som ett resultat sväller systemet och dess prestanda minskar. Från detta misslyckande är utvecklingsgruppen nykter och när den tredje versionen släpps är den igen försiktig.

Denna observation syns tydligt i exemplet på ett par CTSS-MULTICS-system. Operativsystemet CTSS var det första tidsdelningssystemet för allmänt ändamål, och dess framgång var enorm, trots systemets minimala funktionalitet. Skaparna av operativsystemet MULTICS, efterföljaren till CTSS, var för ambitiösa och betalade för det. Idéerna i sig var inte dåliga, men för många nya funktioner lades till, vilket påverkade systemets låga prestanda, som led av denna sjukdom i många år och inte fick kommersiell framgång. Det tredje i denna linje var UNIX-operativsystemet, vars utvecklare tog mycket större omsorg och som ett resultat uppnådde betydligt större framgång.

Operativsystems designtrender

Att förutsäga är alltid svårt, särskilt framtiden. Till exempel föreslog Charles Duell, dåvarande chef för US Patent Office 1899, att USA: s president McKinley skulle avveckla patentkontoret (liksom Charles Duells arbetsplats!), Eftersom, som han skrev, ”allt som kunde uppfinnas var redan uppfunnen. " Men bara några år senare dök Thomas Edison upp vid patentkontorets tröskel med applikationer för elektriska lampor, en fonograf och en filmprojektor. Låt oss byta batterier i vår kristallkula och försöka gissa vad som kommer att hända med operativsystem inom en snar framtid.

Operativsystem med stort adressutrymme

Eftersom 32-bitars maskiner byts ut mot 64-bitars maskiner blir en större förändring i utformningen av operativsystem möjlig. 32-bitars adressutrymmet är inte riktigt så stort. Om du försöker dela upp 232 byte mellan alla jordens invånare, kommer var och en att få mindre än en byte. Samtidigt är 264 ungefär lika med 2 × 1019. Dessutom kan varje invånare på planeten tilldelas ett 3 GB-fragment i ett 64-bitars adressutrymme.

Vad kan du göra med ett 2x1019 byte-adressutrymme? Till att börja med kan vi överge konceptet med ett filsystem. Istället kan alla filer lagras permanent i minnet (virtuellt). Det har trots allt tillräckligt med utrymme för över en miljard fullängdsfilmer, komprimerade till 4 GB. En annan möjlighet är att använda permanenta föremål. Objekt kan skapas i adressutrymmet och lagras i det tills alla referenser till objektet tas bort, varefter själva objektet raderas automatiskt. Sådana objekt kommer att förbli i adressutrymmet även efter att datorn stängs av och startas om. För att fylla hela 64-bitars adressutrymmet måste du skapa objekt på 100 MB / s i 5000 år. Naturligtvis kommer det att krävas en hel del diskar för att lagra denna mängd data, men för första gången i historien blev diskernas fysiska kapacitet, inte adressutrymmet, den begränsande faktorn.

Med ett stort antal objekt i adressutrymmet blir det intressant att låta flera processer köras samtidigt i samma adressutrymme för att göra det enklare att dela objekt. Användningen av ett sådant schema kommer naturligtvis att leda till framväxten av operativsystem som skiljer sig mycket från de som finns just nu. Några tankar om detta koncept finns i.

En annan systemaspekt som måste omprövas med införandet av 64-bitars adresser är virtuellt minne. Med 264 byte virtuellt adressutrymme och 8 000 sidor har vi 251 sidor. Normala sidtabeller av den här storleken skulle vara svåra att arbeta med, så en annan lösning skulle krävas. Det är möjligt att använda inverterade sidtabeller, men andra idéer har också föreslagits. Under alla omständigheter skapar 64-bitars operativsystem ett stort nytt forskningsområde.

Moderna operativsystem är utformade för fristående datorer. Nätverk har utvecklats på senare tid och de nås huvudsakligen med speciella program och protokoll som webbläsare, FTP eller telnet. I framtiden kan nätverk vara ryggraden i alla operativsystem. En fristående dator som inte är ansluten till nätverket kommer att vara lika sällsynt som en offline-telefon. Och troligtvis blir anslutningar med en bandbredd på tiotals och hundratals megabit per sekund normen.

Operativsystem måste ändras för att tillgodose detta paradigmskifte. Skillnaden mellan lokal data och fjärrdata kan suddas ut, eftersom nästan ingen bryr sig om var data faktiskt lagras. Datorn kan fungera med alla data som med lokala data. I ett NFS-system är detta redan på ett visst sätt sant, men det ser ut som att denna trend kommer att fortsätta och expandera, och inom detta område kommer att uppnås en högre grad av integration.

Tillgång till Internet, som för närvarande kräver speciella program (webbläsare), kan också integreras helt i operativsystemet. Webbsidor kan bli det vanliga sättet att lagra information, och dessa sidor kan innehålla ett mycket brett spektrum av icke-textdata, inklusive ljud, video, program etc., som alla operativsystemet kommer att hantera som dess stamdata.


Parallella och distribuerade system

Ett annat framväxande område är parallella och distribuerade system. Moderna operativsystem för multiprocessorer och multicomputersystem är helt enkelt vanliga operativsystem med en processor med mindre ändringar i schemaläggningsdesignen för att ge något bättre samtidighetsstöd. I framtiden kanske vi kommer att ha operativsystem som ger samtidighet en central plats. Den potentiella användningen av multiprocessorkretsar för stationära datorer kommer att vara ett allvarligt ytterligare incitament. Som ett resultat kan det finnas många applikationer speciellt utformade för att köras på multiprocessorer, och behovet av bättre stöd för detta arbete från operativsystemet.

Multidatorsystem kommer sannolikt att dominera vetenskapliga och tekniska superdatorer de närmaste åren, men deras operativsystem är fortfarande extremt primitiva. Det tar mycket arbete att sätta processer, lastbalansera och kommunicera.

Moderna distribuerade system byggs ofta som mellanprogram eftersom befintliga operativsystem inte tillhandahåller alla funktioner som distribuerade applikationer behöver. Kanske, när man utformar framtida operativsystem, kommer distribuerade system att beaktas, så alla nödvändiga funktioner kommer att finnas i operativsystemet från början.


Multimedia

Multimediasystem har blivit en stigande stjärna i datavärlden. Det kommer inte att överraska någon om datorer, stereor, tv-apparater och telefoner kombineras till en enda enhet som levererar högkvalitativt ljud och video och är ansluten till ett höghastighetsnätverk för snabb nedladdning av de filer du behöver. Operativsystemen för dessa enheter, eller till och med för fristående ljud- och videoenheter, bör skilja sig väsentligt från moderna operativsystem. I synnerhet kommer realtidsgarantier att krävas och de kommer att ligga till grund för systemdesignen. Dessutom kommer användarna att bli mycket frustrerade om deras TV-operativsystem måste startas om varje timme, så programvaran kommer att ställa högre krav på kvalitet och motståndskraft. Dessutom är storleken på multimediafiler vanligtvis mycket stor, så filsystemet måste kunna arbeta effektivt med dem.

Vad ska optimeras?

Den allmänna regeln är att den första versionen av systemet ska vara så enkel som möjligt. Endast de delar av systemet som sannolikt kommer att vara ett problem bör optimeras, så deras optimering är oundviklig. Ett sådant exempel är att ha en blockcache för filsystemet. När operativsystemet har felsökts till ett användbart tillstånd, bör noggranna mätningar göras för att förstå var tiden verkligen spenderas. Baserat på dessa siffror bör du optimera inom de områden där det kommer att vara mest användbart.

Här är en sann historia om hur optimering har gjort mer skada än nytta. En av författarens studenter (vi kommer inte att namnge studenten här) skrev mkfs-programmet för MINIX-systemet. Detta program skapar ett tomt filsystem på den nyligen formaterade skivan. Studenten tillbringade cirka 6 månader på optimeringen av detta program. När han försökte köra det här programmet visade det sig att det inte fungerade, varefter det tog ytterligare 6 månader att felsöka det. På hårddisken körs detta program vanligtvis bara en gång under systeminstallationen. Den körs också bara en gång för varje diskett efter formatering. Varje programstart tar cirka 2 sekunder. Även om arbetet med den ooptimerade versionen tog 1 minut, skulle det vara slöseri med resurser att spendera så mycket tid på att optimera ett så sällan använt program.

En slogan som tillämpas på prestandaoptimering kan låta så här: det bästa är det godas fiende.

Med detta menar vi att så snart en acceptabel prestandanivå uppnås är det tydligen inte längre värt ansträngningen och komplexiteten i programmet att försöka pressa ut de sista procenten. Om schemaläggningsalgoritmen är tillräckligt bra och ger 90% CPU-användning, kanske det räcker. Att utveckla en mycket mer komplex algoritm som är 5% bättre än den befintliga är inte alltid en bra idé. På samma sätt, om personsökningshastigheten är tillräckligt låg, det vill säga att personsökning inte representerar en flaskhals, är det vanligtvis ingen anledning att gå överbord för att få optimal prestanda. Att undvika systemfel verkar vara mycket viktigare än att uppnå optimal prestanda, särskilt om en algoritm som är optimal på en nivå av datorns arbetsbelastning kan visa sig vara suboptimal på en annan nivå.

Litteratur:

Yu.V. Kuznetsov "Teori om operativsystem".







2021 gtavrl.ru.