- Leren
- Informatica & ICT
- Informatica
- Besturingssystemen: processen, geheugen, threads en bestandssystemen
- Processen en systeemaanroepen
Processen en systeemaanroepen
GemiddeldLes 1 van 9 in Cursus Besturingssystemen: processen, geheugen, threads en bestandssystemen
Wat je leert
- Beschrijf de belangrijkste taken van het besturingssysteem en de geschiedenis en ontwerpideeën die verklaren waarom het de machine op deze manier deelt en beschermt.
- Beschrijf de belangrijkste taken van een besturingssysteemLeg uit hoe het besturingssysteem ruwe hardware omzet in gebruiksvriendelijkere, gedeelde en beschermde bronnen voor programma's.
- Leg uit hoe multiprogrammering en tijdsverdeling het besturingssysteem hebben gevormdVolg de stappen van batchverwerking naar multiprogrammering en tijdsverdeling, zie waarom elke stap nieuwe bescherming vereiste en vergelijk de belangrijkste manieren om een kernel te organiseren.
- Beschrijf de belangrijkste taken van een besturingssysteem
- Leg uit wat een proces is, wat het besturingssysteem over elk proces vastlegt en hoe processen wisselen tussen de toestand in uitvoering, de gereedtoestand en de geblokkeerde toestand.
- Leg uit wat een proces is en welke gegevens het besturingssysteem erover bijhoudtOnderscheid een programma van een proces, beschrijf de machinetoestand en adresruimte van een proces en leg uit hoe het besturingssysteem een proces aanmaakt en bijhoudt.
- Volg de toestandsveranderingen van een procesGebruik de toestanden in uitvoering, gereed en geblokkeerd en hun overgangen om te volgen hoe processen één CPU delen, en herken de extra toestanden die echte systemen rapporteren.
- Leg uit wat een proces is en welke gegevens het besturingssysteem erover bijhoudt
- Gebruik de Unix-proces-API in C om kindprocessen aan te maken, daarin nieuwe programma's uit te voeren, hun resultaten te verzamelen, ze te beëindigen en deze aanroepen tot een eenvoudige shell te combineren.
- Maak een kindproces met forkRoep fork aan, onderscheid ouderproces en kindproces aan de hand van de retourwaarde en voorspel wat elk proces ziet en afdrukt.
- Voer een ander programma uit met execVervang het programma in een proces met exec, geef argumenten correct door en leg uit waarom Unix het aanmaken van een proces scheidt van het laden van een programma.
- Wacht op kindprocessen, lees de afsluitstatus en beëindig processenBeëindig processen met exit en signalen, haal het resultaat van een kindproces op met wait en waitpid, decodeer zijn status en voorkom dat zombie- en weesprocessen problemen veroorzaken.
- Bouw een eenvoudige shell met fork, exec, wait en omleidingCombineer de procesaanroepen in een shelllus die commando's uitvoert, invoer en uitvoer omleidt, commando's met pipes verbindt en taken op de achtergrond uitvoert.
- Maak een kindproces met fork
- Leg uit hoe de hardware geprivilegieerde kernelcode scheidt van gebruikersprogramma's en hoe programma's die grens gecontroleerd overschrijden via systeemaanroepen, excepties en interrupts.
- Onderscheid gebruikersmodus van kernelmodusLeg uit wat elke CPU-modus toestaat, welke bewerkingen geprivilegieerd zijn en wat er gebeurt wanneer een programma de grenzen van zijn modus overschrijdt.
- Leg stap voor stap uit hoe een systeemaanroep werktVolg een systeemaanroep vanaf de wrapperfunctie in de bibliotheek via de trap naar de kernel en terug, en interpreteer de retourwaarde en fouten.
- Onderscheid traps, excepties en interruptsDeel de gebeurtenissen in die de CPU naar de kernel laten overschakelen, leg uit hoe de afhandelaartabel van de kernel werkt en voorspel wat na elk soort gebeurtenis gebeurt.
- Onderscheid gebruikersmodus van kernelmodus
- Leg uit hoe het besturingssysteem programma's rechtstreeks op volle snelheid op de CPU uitvoert en tegelijk de controle behoudt, en hoe het de CPU van het ene proces naar het andere overschakelt.
- Leg beperkte directe uitvoering uitLeg de twee problemen uit van programma's rechtstreeks op de CPU uitvoeren en hoe gebruikersmodus, traps en de timerinterrupt die oplossen, stap voor stap van opstarten tot procesbeëindiging.
- Volg een contextwisseling en schat de kosten ervanVolg elke stap bij het overschakelen van de CPU van het ene proces naar het andere, onderscheid een contextwisseling van een moduswisseling en schat hoeveel CPU-tijd de wisseling kost.
- Leg beperkte directe uitvoering uit
Processen en systeemaanroepen: uitleg
1Leg uit wat een besturingssysteem doet
Het besturingssysteem geeft gewone programma's bruikbare manieren om te rekenen, gegevens op te slaan en apparaten te gebruiken, terwijl ze de machine veilig delen. Zijn taken verklaren waarom een desktoptoepassing geen eigen schijfstuurprogramma nodig heeft en waarom één programma met een fout niet alles zou moeten stoppen. Volg de ontwikkeling van computers met één taak naar interactieve systemen en vergelijk daarna hoe verschillende kernelontwerpen geprivilegieerd werk verdelen.
1.1 Beschrijf de belangrijkste taken van een besturingssysteem
Een browser, een compiler en een muziekspeler kunnen allemaal voortgang maken op één computer. Elk heeft CPU-tijd, geheugen en toegang tot apparaten nodig. Het besturingssysteem biedt die bronnen via diensten die programma's kunnen gebruiken zonder de hele machine te besturen.
Geef programma's bruikbare bronnen
Virtualisatie zet een fysieke bron om in een handige virtuele bron. CPU-virtualisatie laat veel processen werken alsof elk een eigen processor heeft. Het besturingssysteem voert elk proces een tijdje uit en bewaart zijn toestand tussen de beurten. Geheugenvirtualisatie geeft elk proces zijn eigen adresruimte: de adressen die zijn instructies kunnen gebruiken.
Een abstractie verbergt details achter een bruikbare interface. Een bestand is een abstractie voor opgeslagen bytes. Een programma kan write aanroepen zonder te weten of die bytes naar een SSD of een netwerkschijf gaan. Een systeemaanroep biedt een gecontroleerde manier om een dienst van de kernel aan te vragen. Apparaatspecifiek werk blijft achter de interface.
Deel bronnen en bescherm programma's
Als beheerder van bronnen beslist het besturingssysteem wie CPU-tijd, geheugen en apparaatdiensten krijgt. Het probeert de hardware nuttig te benutten en programma's tegelijk redelijke toegang te geven. Delen vereist ook bescherming: één programma mag het afgeschermde geheugen van een ander niet overschrijven of de timer uitschakelen om de CPU te behouden.
De hardware helpt deze grens af te dwingen. Instructies van toepassingen draaien in de gebruikersmodus met beperkte rechten. Kernelcode draait met de privileges die nodig zijn om de machine te beheren. Een timer kan een programma onderbreken, zelfs als dat programma nooit iets aan het besturingssysteem vraagt.
Volg bij een laptop die een bestand downloadt terwijl hij code compileert drie afzonderlijke taken:
- Het netwerkapparaat meldt binnenkomende gegevens. De kernel stelt die beschikbaar aan het downloadproces.
- Het besturingssysteem verdeelt CPU-tijd tussen de browser, de compiler en andere gereedstaande processen.
- De compiler vraagt bestandsbewerkingen aan om zijn uitvoer op te slaan. Zijn code stuurt de opslagcontroller niet rechtstreeks aan.
Onderscheid de kernel van de interface
De kernel is de geprivilegieerde kern. De shell, de desktop en de meeste systeembibliotheken draaien als gewone programma's, zelfs wanneer ze met het besturingssysteem worden meegeleverd. Een desktopvenster sluiten verwijdert de kernel niet. Een server zonder grafische interface heeft nog steeds procesbeheer, geheugenbescherming en apparaatdiensten nodig.
Op één logische CPU wisselen gebruikerscode en kernelcode elkaar af. Terwijl de compiler gewone gebruikersinstructies uitvoert, wordt de kernel niet ook op die CPU uitgevoerd. Systeemaanroepen, excepties en hardware-interrupts geven de kernel de controle terug.
Kies hoe en kies daarna wie
Een mechanisme biedt een bewerking; beleid bepaalt hoe die wordt gebruikt. De registers van het ene proces opslaan en die van een ander herstellen is een mechanisme. Kiezen welk gereed proces de volgende beurt krijgt is beleid. Door die te scheiden kan het besturingssysteem zijn planningskeuzes veranderen zonder het wisselmechanisme te vervangen.
Bij het ontwerp van een besturingssysteem worden bruikbare abstracties, prestaties, bescherming, betrouwbaarheid, beveiliging en energieverbruik tegen elkaar afgewogen. De extra CPU-tijd en het extra geheugen die aan beheer worden besteed, zijn overhead. In het hele vakgebied keren drie thema's terug: virtualisatie, gelijktijdigheid (activiteiten die tegelijk bezig zijn) en persistentie, waardoor opgeslagen gegevens de levensduur van een programma of een stroomuitval overleven.
Belangrijkste punten
- Virtualisatie geeft programma's handige weergaven van gedeelde hardware.
- Abstracties zoals bestanden verbergen apparaatdetails achter bruikbare bewerkingen.
- De kernel is geprivilegieerd; de shell en de desktop zijn gewone programma's.
- Een mechanisme biedt een bewerking; beleid bepaalt wanneer of voor wie die wordt gebruikt.
- Bescherming en lage overhead zijn beide doelen bij het ontwerp van een besturingssysteem.
1.2 Leg uit hoe multiprogrammering en tijdsverdeling het besturingssysteem hebben gevormd
Besturingssystemen ontwikkelden zich toen computers van het uitvoeren van afzonderlijke taken overgingen naar het bedienen van veel activiteiten tegelijk. Elke verandering loste een praktisch probleem op: ongebruikte hardware, trage interactie of fouten die zich tussen programma's verspreidden.
Van batches naar overlappend werk
Bij batchverwerking worden ingediende taken achter elkaar uitgevoerd zonder interactie met degene die ze heeft ingediend. Vroege ondersteunende software hielp vooral bij het laden van taken en het uitvoeren van veelvoorkomende I/O-bewerkingen. Als de huidige taak op een apparaat moest wachten, kon de CPU ongebruikt blijven.
Multiprogrammering houdt meerdere taken in het geheugen. Wanneer één taak voor I/O blokkeert, kan een andere rekenen. Stel dat een taak telkens 20 ms rekent en 80 ms op schijf-I/O wacht. Als die alleen draait, is de CPU-bezettingsgraad 20 / 100 = 20%. Met twee van zulke taken en geschikte overlap kunnen nuttige berekeningen 40% van de verstreken tijd vullen. De vertraging van het apparaat is niet korter geworden; CPU-tijd die anders ongebruikt bleef, is benut.
De winst hangt ervan af of er werk gereedstaat. Door taken toe te voegen kan de CPU-bezettingsgraad niet boven 100% komen en taken die op hetzelfde apparaat wachten, kunnen nog steeds een knelpunt tegenkomen. Meerdere taken in het geheugen hebben ook bescherming nodig: de ene mag de andere niet overschrijven of de eigen gegevens van het besturingssysteem beschadigen.
Maak interactie snel
Tijdsverdeling geeft processen korte beurten op de CPU. Een terminal of desktop kan reageren terwijl andere programma's blijven werken. Het zou heel anders aanvoelen als een batchtaak eerst moest eindigen voordat een toetsaanslag van een andere gebruiker werd verwerkt.
| Manier van delen | Wat wordt verdeeld | Voorbeeld |
|---|---|---|
| Tijdsverdeling | Gebruik van een bron over opeenvolgende intervallen | CPU-beurten |
| Ruimteverdeling | Capaciteit waarover verschillende gebruikers tegelijk beschikken | Geheugengebieden of schijfruimte |
Gelijktijdigheid betekent dat meerdere activiteiten in dezelfde periode bezig zijn. Hun instructies kunnen op één logische CPU worden afgewisseld. Parallellisme betekent dat instructies daadwerkelijk gelijktijdig op verschillende verwerkingsbronnen worden uitgevoerd. Multiprogrammering vereist geen parallelle hardware.
Plaats diensten binnen of buiten de kernel
| Ontwerp | Waar de meeste diensten draaien | Belangrijkste afweging |
|---|---|---|
| Monolithische kernel | Bestandssystemen, stuurprogramma's en netwerkfuncties draaien in de kernelmodus | Rechtstreekse aanroepen zijn efficiënt; een fout in een stuurprogramma kan de kernel beschadigen |
| Microkernel | Een kleine geprivilegieerde kern ondersteunt dienstprocessen in de gebruikersmodus | Isolatie van diensten helpt fouten te beperken; communicatie kost extra werk |
| Hybride kernel | Combineert elementen van beide benaderingen | De exacte grens tussen diensten hangt af van het ontwerp |
Linux is monolithisch en kan kernelmodules laden. MINIX 3, QNX en seL4 zijn voorbeelden van microkernelontwerpen. Windows NT en XNU van Apple worden doorgaans beschreven als hybride ontwerpen. Een stuurprogramma in de gebruikersmodus kan na een crash vaak opnieuw worden gestart, terwijl een geprivilegieerd stuurprogramma toegang heeft waardoor een fout het hele systeem kan treffen.
Vroege systemen voor personal computers zoals MS-DOS en het klassieke Mac OS misten de procesgeheugenbescherming die tegenwoordig wordt verwacht. Moderne systemen combineren regels van het besturingssysteem met handhaving door de hardware. Korte CPU-beurten alleen zouden niet voorkomen dat een programma het geheugen van een ander programma verandert; delen en bescherming lossen verschillende delen van het probleem op.
Belangrijkste punten
- Multiprogrammering vult I/O-wachttijd met berekeningen van een andere taak.
- Tijdsverdeling ondersteunt interactief gebruik met korte CPU-beurten.
- Gelijktijdig werk kan op één CPU worden afgewisseld; parallel werk wordt op hetzelfde moment uitgevoerd.
- Tijdsverdeling verdeelt beurten; ruimteverdeling verdeelt capaciteit.
- Bij de kernelorganisatie worden rechtstreekse aanroepen tussen diensten afgewogen tegen sterkere isolatie van diensten.
- Meerdere taken in het geheugen vereisen geheugenbescherming en bescherming door privilegeniveaus.
2Beschrijf processen en de toestanden die ze doorlopen
Een programmabestand is slechts het beginpunt. Om het uit te voeren bouwt het besturingssysteem een actief proces op en houdt het dat bij, met geheugen, registers, open descriptoren en een plaats in de processenlijst. Leer die toestand lezen en volg daarna twee processen terwijl CPU-werk overlapt met het wachten op apparaten. Het verschil tussen wachten op een CPU en wachten op een gebeurtenis verklaart welk proces de scheduler kan kiezen.
2.1 Leg uit wat een proces is en welke gegevens het besturingssysteem erover bijhoudt
Een uitvoerbaar bestand op schijf kan herhaaldelijk worden gestart. Elke start vereist een eigen veranderlijke toestand, zodat één instantie onafhankelijk kan pauzeren, hervatten of mislukken. Die actieve instantie is een proces.
Herken de draaiende instantie
Een programma bestaat uit opgeslagen instructies en statische gegevens. Een proces combineert een programma met zijn huidige geheugen, CPU-registers en I/O-toestand. Drie shells kunnen hetzelfde uitvoerbare bestand draaien en tegelijk verschillende variabelen, huidige directory's en geschiedenissen bijhouden.
De programmateller (PC) duidt de volgende instructie aan. De stackpointer geeft de locatie van de actieve stack aan. Algemene registers bevatten tussenwaarden. Wanneer een proces stopt met draaien, moet voldoende toestand worden bewaard om het bij de juiste instructie met de juiste waarden te laten doorgaan.
Lees de indeling van de adresruimte
De adresruimte van een proces is het geheugen dat zijn instructies kunnen adresseren. Een veelgebruikte indeling in leermateriaal plaatst code en statische gegevens bij lagere adressen, een heap daarboven en een stack bij hogere adressen. De heap ondersteunt dynamisch toegewezen objecten; de stack ondersteunt functieaanroepen en lokale toestand. In deze indeling groeit de heap naar hogere adressen en de stack naar lagere adressen. De werkelijke indeling hangt af van het systeem en implementaties van malloc kunnen op meer dan één manier geheugen verkrijgen.
Twee processen kunnen voor een variabele hetzelfde virtuele adres afdrukken en daar toch verschillende waarden bevatten. Virtuele adressen horen bij de geheugenweergave van een proces. Gelijke adresnummers betekenen niet dat schrijfbare opslag wordt gedeeld.
Start een programma
Om een nieuw programmabeeld te starten, richt het besturingssysteem deze onderdelen in:
- De uitvoerbare code en gegevens, waarvan delen op moderne systemen worden geladen wanneer ze nodig zijn.
- Een initiële gebruikersstack en opstartinformatie, waaronder de argumentstrings en argumentvector die de runtime gebruikt om
mainaan te roepen. - Geheugen voor dynamische toewijzing en de initiële registertoestand.
- Een I/O-omgeving, inclusief geërfde bestandsdescriptoren.
- Een ingangspunt waar de opstartcode van het programma begint.
Op Unix zijn bestandsdescriptoren kleine gehele getallen die open I/O-bronnen aanduiden. Volgens de shellconventie is 0 standaardinvoer, 1 standaarduitvoer en 2 standaardfoutuitvoer. Ze zijn vaak met een terminal verbonden. Een proces kan extra descriptoren erven; de drie bekende nummers zijn geen limiet en geen garantie dat elk proces ze altijd open heeft.
Bekijk de gegevens van de kernel
Het procesbesturingsblok (PCB) bevat informatie die de kernel nodig heeft om één proces te beheren: PID, toestand, opgeslagen uitvoeringscontext, geheugengegevens, open descriptoren, ouderproces, eigenaar, huidige directory en gebruiksgegevens. Linux gebruikt task_struct; xv6 gebruikt struct proc. Een processentabel of processenlijst ordent deze gegevens.
Terwijl een proces draait, staan zijn actuele registers in de CPU. Bij binnenkomst in de kernel worden zijn gebruikersregisters op zijn kernelstack opgeslagen; een contextwisseling bewaart ook de uitvoeringscontext van de kernel. Het PCB is geen voortdurend bijgewerkte afspiegeling van elk gebruikersregister.
Een Linux-uitvoer van ps kan bijvoorbeeld 8240 8200 S bash en 8291 8240 R ps bevatten. Het tweede proces heeft PID 8291 en ouder-PID 8240, dus het is een kindproces van de shell. R omvat zowel processen in uitvoering als gereedstaande processen. Een PID identificeert een actief proces binnen zijn PID-naamruimte, maar na het opruimen kan dat nummer opnieuw worden gebruikt.
Belangrijkste punten
- Een proces is een instantie van een programma met een geheugen-, register- en I/O-toestand.
- Identieke virtuele adressen in verschillende processen kunnen verschillende gegevens aanduiden.
- Het PCB bevat de beheergegevens van de kernel, geen voortdurend actuele CPU-registers.
- Bestandsdescriptoren worden geërfd; 0, 1 en 2 hebben afgesproken rollen.
- PID's kunnen opnieuw worden gebruikt nadat een proces is beëindigd en opgeruimd.
2.2 Volg de toestandsveranderingen van een proces
Een proces kan bestaan zonder op dit moment een instructie uit te voeren. Zijn toestand vertelt het besturingssysteem of het een CPU kan gebruiken of eerst op iets anders moet wachten. Dit onderscheid verklaart waarom een drukke computer veel processen kan hebben waarvan er op een bepaald moment maar enkele worden uitgevoerd.
Bepaal wat het proces nodig heeft
| Toestand | Betekenis | Voorbeeld |
|---|---|---|
| In uitvoering | Wordt uitgevoerd op een CPU | Een compiler die instructies genereert |
| Gereed | Kan worden uitgevoerd, wacht op CPU-tijd | Een berekening waarvan de beurt net is geëindigd |
| Geblokkeerd | Wacht op een gebeurtenis voordat het verder kan | Een leesbewerking van de terminal die op invoer wacht |
Een geblokkeerd proces verbruikt geen CPU-tijd zolang het geblokkeerd is. Het apparaat of de gebeurtenis kan nog wel voortgang maken. Een gereed proces heeft al wat het nodig heeft, behalve een CPU.
De basisovergangen zijn gereed → in uitvoering wanneer het proces wordt gekozen, in uitvoering → gereed wanneer het van de CPU wordt gehaald, in uitvoering → geblokkeerd wanneer het moet wachten en geblokkeerd → gereed wanneer de gebeurtenis plaatsvindt. Door voltooiing van de gebeurtenis komt een proces in aanmerking voor uitvoering; het krijgt niet onmiddellijk de CPU. Kiezen tussen processen die in aanmerking komen is de taak van de scheduler.
Volg overlappend werk
Gebruik één logische CPU en negeer wisseloverhead. Proces A rekent 2 ms, wacht 3 ms op schijf-I/O en rekent daarna 1 ms. Proces B heeft 4 ms CPU-tijd nodig. A begint eerst.
| Interval | CPU | Toestand van A | Toestand van B |
|---|---|---|---|
| 0–2 ms | A | In uitvoering | Gereed |
| 2–5 ms | B | Geblokkeerd | In uitvoering |
| 5–6 ms | B | Gereed | In uitvoering |
| 6–7 ms | A | In uitvoering | Klaar |
Op 2 ms start A de I/O en blokkeert. B rekent terwijl de schijf werkt. Op 5 ms is de I/O van A voltooid, maar het besturingssysteem laat B zijn vierde CPU-milliseconde afmaken voordat het A uitvoert. Dit is een beleidskeuze, geen extra toestandsovergang rechtstreeks van geblokkeerd naar in uitvoering.
Het totale CPU-werk is 2 + 1 + 4 = 7 ms en beide zijn na 7 ms klaar. Als A volledig zou draaien voordat B begon, zou de verstreken tijd 2 + 3 + 1 + 4 = 10 ms zijn. De CPU-bezettingsgraad zou 7 / 10 = 70% zijn, omdat geen ander werk de I/O-wachttijd vulde.
Lees toestanden op een echt systeem
Linux ps combineert in uitvoering en gereed als R. Het toont onderbreekbare slaap als S, niet-onderbreekbare slaap, vaak voor I/O, als D, een gestopt proces als T en een beëindigd proces waarvan de status nog niet is opgehaald als Z. Een gestopt proces wacht niet simpelweg op de voltooiing van een gewone I/O-bewerking. Een zombieproces is klaar met uitvoeren en behoudt alleen de gegevens die nodig zijn om zijn resultaat te melden.
Een nieuw proces doorloopt ook de aanmaakfase voordat het gereed is. Op een systeem met vier logische CPU's kunnen hoogstens vier processen met één thread tegelijk worden uitgevoerd. Gereedwachtrijen ordenen werk dat voor uitvoering in aanmerking komt; lijsten met geblokkeerde processen houden bij welke gebeurtenissen die processen nodig hebben. Een contextwisseling bewaart de uitvoeringstoestand van het ene proces en herstelt die van een ander wanneer het besturingssysteem verandert welk proces draait.
Belangrijkste punten
- Gereed betekent wachten op een CPU; geblokkeerd betekent wachten op een gebeurtenis.
- De voltooiing van I/O maakt een geblokkeerd proces gereed.
- Wachttijd op apparaten laten overlappen met nuttig CPU-werk kan de totale verstreken tijd verkorten.
- Linux rapporteert gereed en in uitvoering met dezelfde code,
R. - Een zombieproces is beëindigd, maar zijn beëindigingsstatus is nog niet opgehaald.
- Het aantal logische CPU's beperkt de gelijktijdige uitvoering van processen met één thread.
3Maak en bestuur processen met fork, exec en wait
De Unix-procesinterface scheidt het aanmaken van een kindproces, het vervangen van zijn programma en het ophalen van zijn resultaat. Volg wat elke aanroep verandert en behoudt, waaronder afgeschermde variabelen en gedeelde toestand van geopende bestanden. Combineer de aanroepen daarna tot gedrag van een shell: commando's op de voorgrond, omleiding, pijplijnen en achtergrondtaken. Bij elke stap moet je weten welk proces die uitvoert.
3.1 Maak een kindproces met fork
fork() maakt vanuit het aanroepende ouderproces een kindproces aan. Beide gaan direct na de aanroep verder, zodat één pad in de broncode twee uitvoeringen wordt. De retourwaarde vertelt elk proces welke rol het heeft.
Handel alle drie de resultaten af
pid_t rc = fork();
if (rc < 0) {
perror("fork");
exit(1);
} else if (rc == 0) {
printf("child %ld\n", (long) getpid());
} else {
printf("parent of %ld\n", (long) rc);
}
Gebruik <unistd.h> voor fork en getpid, <stdio.h> voor afdrukken en <stdlib.h> voor exit. Bij succes ontvangt het ouderproces de positieve PID van het kindproces; het kindproces ontvangt 0. Bij een fout ontvangt de aanroeper -1, geeft errno de fout aan en bestaat er geen kindproces.
Als het ouderproces PID 5000 heeft en het kindproces 5001, drukt het ouderproces “parent of 5001” af en het kindproces “child 5001”. Het is niet gegarandeerd welk proces eerst draait. getpid() geeft de eigen PID van de aanroeper terug; getppid() geeft de PID van zijn ouderproces terug. Het kindproces begint main niet opnieuw en herhaalt geen instructies van vóór de fork.
Volg afgeschermd geheugen en gedeelde I/O
Ouderproces en kindproces beginnen met dezelfde gewone geheugeninhoud in afzonderlijke adresruimten. Als x vóór de aanroep 10 is en het kindproces zijn kopie op 20 zet, blijft de kopie van het ouderproces 10. Zelfs het afgedrukte adres van x kan gelijk zijn: die gelijke virtuele adressen horen bij verschillende procesadresruimten.
Kopiëren bij schrijven voorkomt dat elke geheugenpagina direct wordt gekopieerd. Ouderproces en kindproces delen aanvankelijk fysieke pagina's die tegen schrijven zijn beschermd. Wanneer een van beide naar een afgeschermde pagina schrijft, zorgt de kernel voor een eigen kopie voordat hij de schrijfbewerking toestaat. Zo blijft het gedrag van gescheiden geheugen behouden, terwijl de kosten lager zijn als een kindproces snel daarna exec aanroept.
Bestandsdescriptoren gedragen zich anders. Hun gekopieerde vermeldingen verwijzen naar dezelfde onderliggende beschrijving van een geopend bestand (open file description), die de huidige bestandspositie bevat. Opeenvolgende leesbewerkingen via een geërfde descriptor verschuiven die gedeelde positie. De descriptortabel wordt gekopieerd, maar de onderliggende toestand van het geopende bestand in de kernel kan gedeeld blijven.
Tel alleen forks die een proces bereikt
Als elke aanroep slaagt, laat fork(); fork(); vier processen over: beide processen die door de eerste aanroep ontstaan, voeren de tweede uit. Een lus met drie iteraties die door alle processen wordt bereikt, laat processen over, inclusief het oorspronkelijke proces. Een fork in een tak die alleen het ouderproces uitvoert, wordt door minder processen bereikt, zodat herhaalde verdubbeling niet langer het hele programma beschrijft.
Leeg gebufferde tekst bewust
printf kan tekst in een buffer in de gebruikersruimte achterlaten. fork kopieert die buffer samen met de rest van het geheugen. Als uitvoer wordt omgeleid naar een volledig gebufferde stroom, kan tekst die vóór de fork is afgedrukt door beide processen bij normale beëindiging worden weggeschreven.
Bij printf("start\n"); fork(); schrijft een gebruikelijke regelgebufferde terminal de regel met het regelafbrekingsteken vóór de fork weg. Bij omleiding naar een bestand kan de tekst in de buffer blijven staan en tweemaal worden weggeschreven. fflush(stdout) vóór fork() schrijft de wachtende tekst eenmaal weg. De dubbele uitvoer komt door gekopieerde gebufferde gegevens, niet doordat de eerdere printf tweemaal wordt uitgevoerd.
Belangrijkste punten
fork()geeft in het ouderproces een PID van een kindproces terug, in het kindproces 0 en bij een fout -1.- Beide processen gaan na een geslaagde aanroep direct na
fork()verder. - Wijzigingen in gewoon afgeschermd geheugen van het kindproces veranderen de waarden van het ouderproces niet.
- Geërfde descriptoren kunnen één beschrijving van een geopend bestand en één bestandspositie delen.
- Het aantal processen hangt af van welke processen elke fork bereiken.
- Schrijf wachtende uitvoer vóór een fork weg als dubbel wegschrijven ongewenst zou zijn.
3.2 Voer een ander programma uit met exec
Een proces kan zijn identiteit behouden terwijl het van programma verandert. De exec-familie voert die vervanging uit. Door die met fork te combineren kan een ouderproces zijn werk voortzetten terwijl een kindproces een ander programma gaat uitvoeren.
Vervang het beeld, behoud het proces
Een geslaagde exec vervangt de oude code, statische gegevens, heap en stack. De kernel bereidt de argumenten en begintoestand van het nieuwe programma voor en begint daarna met de opstartcode. De oude aanroepplek bestaat niet meer, dus een geslaagde exec keert daar nooit naar terug.
De PID, de relatie met het ouderproces en de huidige directory blijven behouden. Open descriptoren blijven behouden, behalve die welke zijn gemarkeerd om bij exec te sluiten. Signalen waarvoor een afhandelaar was ingesteld, krijgen weer hun standaardactie; genegeerde signalen blijven doorgaans genegeerd. De gebruikers- en groepsidentiteit blijft meestal gelijk, maar de set-user-ID- of set-group-ID-rechten van een uitvoerbaar bestand kunnen de effectieve identiteit veranderen als het systeem dat toestaat.
| Bewerking | Nieuw proces? | Belangrijkste effect |
|---|---|---|
fork() | Ja | Maakt een kindproces met gekopieerde uitvoeringstoestand |
| Geslaagde exec | Nee | Vervangt het programmabeeld van het aanroepende proces |
Bouw een geldige argumentvector
Om wc -l notes.txt uit te voeren kan het kindproces dit gebruiken:
char *args[] = {"wc", "-l", "notes.txt", NULL};
execvp(args[0], args);
perror("execvp");
_exit(127);
De strings zijn afzonderlijke argumenten, geen gecombineerde opdrachtregel. Volgens afspraak geeft args[0] het programma aan. De afsluitende NULL is een pointer die het einde van de array markeert; het is niet de string “NULL”. Zonder geldige afsluiting kan de aanroep voorbij de array lezen.
De regels na execvp handelen een fout af. Als het programma niet kan worden gevonden, geeft exec -1 terug en stelt errno in, bijvoorbeeld op ENOENT. _exit beëindigt het kindproces dan zonder geërfde uitvoerbuffers van C te legen. Na een fout terugkeren naar de shelllus zou onbedoeld een extra uitvoeringspad van de shell creëren.
Lees de naam van de variant
De v in execv betekent dat argumenten als vector binnenkomen; l-varianten ontvangen een lijst. Een p-variant zoekt in PATH naar een programmanaam. Een e-variant geeft een expliciete omgeving door. De onderliggende systeemaanroep van Linux is execve; de bibliotheek biedt de handige varianten.
execvp("wc", args) zoekt in PATH. execv("wc", args) behandelt “wc” als een relatieve padnaam in de huidige directory. Een absolute padnaam zoals /usr/bin/wc kan worden gebruikt waar dat uitvoerbare bestand is geïnstalleerd. Exec ontleedt geen shellsyntaxis: een string > in args is gewoon een argument, tenzij een programma die zelf interpreteert.
Gebruik de tijd vóór vervanging
In het voorbeeld met ouderproces PID 6000 en kindproces PID 6001 gaat het kindproces wc uitvoeren, maar behoudt het PID 6001. Zijn ouderproces kan op die PID wachten. Vóór exec kan het kindproces descriptoren omleiden, een pipe aansluiten of van directory veranderen; het vervangende programma erft de voorbereide omgeving.
Deze scheiding staat centraal in de Unix-shell. Windows CreateProcess combineert het aanmaken van een proces en het laden van een programma en gebruikt parameters om het nieuwe proces te configureren. POSIX biedt ook posix_spawn voor een gecombineerde bewerking.
Belangrijkste punten
- Exec vervangt een programmabeeld zonder een proces aan te maken of zijn PID te veranderen.
- Bij succes keert exec nooit terug; de daaropvolgende opdrachten handelen fouten af.
- Sluit argumentvectoren af met een
NULL-pointer. p-varianten zoeken inPATH; een variant die alleen een padnaam gebruikt doet dat niet.- Door descriptoren vóór exec in te stellen kan een ongewijzigd programma aan omleiding en pipes deelnemen.
3.3 Wacht op kindprocessen, lees de afsluitstatus en beëindig processen
Een ouderproces moet weten of een kindproces klaar is en hoe het is geëindigd. Procesbeëindiging en het ophalen van het resultaat zijn afzonderlijke stappen: het kindproces eindigt en daarna haalt het ouderproces de resterende gegevens bij de kernel op.
Beëindig een proces
Terugkeren uit main of exit(status) aanroepen beëindigt de uitvoering normaal. exit leegt C-uitvoerbuffers en voert geregistreerde beëindigingsafhandelaars uit. _exit(status) beëindigt zonder die opruimactie van de bibliotheek, wat nuttig is na een mislukte exec in een met fork aangemaakt kindproces. Volgens afspraak betekent 0 succes en een waarde anders dan nul een fout.
Een signaal kan een proces ook beëindigen. SIGTERM vraagt om beëindiging en kan worden afgehandeld; SIGKILL kan niet worden opgevangen of genegeerd. SIGSTOP stopt de uitvoering en kan ook niet worden opgevangen of genegeerd; SIGCONT hervat een gestopt proces. Een terminal stuurt normaal SIGINT bij Ctrl-C en SIGTSTP bij Ctrl-Z naar de taak op de voorgrond. Een signaal is een softwaremelding aan een proces, geen hardware-interrupt.
Wacht op één resultaat
wait(&status) wacht op één kindproces en geeft zijn PID terug. waitpid(pid, &status, 0) selecteert een bepaalde positieve PID van een kindproces. De status van een geëindigd kindproces die nog niet is opgehaald, kan direct worden opgehaald. Anders onderbreekt een gewone blokkerende wachtbewerking het ouderproces totdat een passend resultaat beschikbaar is.
Met WNOHANG geeft waitpid 0 terug wanneer passende kindprocessen bestaan, maar geen daarvan een resultaat klaar heeft. Een positief resultaat geeft aan welk kindproces is opgehaald. Een resultaat van -1 is een fout: ECHILD betekent dat er geen passend kindproces meer is; EINTR betekent dat een signaalafhandelaar het wachten heeft onderbroken.
Decodeer de status vóór gebruik
int status;
pid_t r;
do {
r = waitpid(child, &status, 0);
} while (r == -1 && errno == EINTR);
if (r == -1) {
perror("waitpid");
} else if (WIFEXITED(status)) {
printf("exit %d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("signal %d\n", WTERMSIG(status));
}
Dit gebruikt <sys/wait.h>, <errno.h> en <stdio.h>. De opgeslagen status codeert zowel de manier van beëindigen als een resultaat. WEXITSTATUS is zinvol nadat WIFEXITED waar is gebleken; WTERMSIG volgt op WIFSIGNALED.
Alleen de laagste acht bits van een normale afsluitwaarde zijn beschikbaar. exit(300) levert daarom 44 op, terwijl exit(-1) 255 oplevert. Op Linux levert exit(3) doorgaans de ruwe wachtstatus 768 op; dat ruwe gehele getal afdrukken is niet hetzelfde als de afsluitcode decoderen.
Haal zombieprocessen op en herken weesprocessen
Een zombieproces is beëindigd, maar behoudt zijn PID en resultaat totdat een wachtbewerking die ophaalt. Het voert geen code uit, dus SIGKILL sturen kan het niet verwijderen. Een langlopend ouderproces moet de beëindigingsstatus van elk kindproces ophalen. Om alle kindprocessen op te halen blijf je na elke positieve PID wachten, probeer je opnieuw bij EINTR en stop je bij ECHILD; andere fouten meld je.
Een weesproces heeft zijn ouderproces verloren en kan nog draaien. Linux geeft het een passend subreaperproces of het initproces van de naamruimte als nieuw ouderproces, dat later zijn resultaat kan ophalen. Gewone rechten om signalen te sturen hangen af van gebruikersidentiteiten en privileges. Als root draaien geeft rechten, maar betekent niet dat de uitvoering in de kernelmodus plaatsvindt.
Belangrijkste punten
- Elke geslaagde wachtbewerking haalt het resultaat van één kindproces op.
- Decodeer normale beëindiging en beëindiging door een signaal met de bijbehorende statusmacro's.
WNOHANGgeeft 0 terug als er een passend kindproces bestaat maar nog geen resultaat klaar heeft.- Probeer een onderbroken wachtbewerking opnieuw bij
EINTR;ECHILDbetekent dat er geen passend kindproces meer is. - Een zombieproces is geëindigd; een weesproces heeft zijn ouderproces verloren en kan nog draaien.
SIGKILLenSIGSTOPkunnen niet worden opgevangen of genegeerd.exitleegt C-buffers;_exitdoet dat niet.
3.4 Bouw een eenvoudige shell met fork, exec, wait en omleiding
Een shell zet commandotekst om in het aanmaken van processen, het instellen van descriptoren en het ophalen van resultaten. Het is een gewoon gebruikersprogramma dat dezelfde beschermde diensten van het besturingssysteem gebruikt als andere programma's.
Start een extern commando
Voor wc -l notes.txt bouwt de shell de argumentvector en start hij het commando. De ontlede woorden vormen een met een nullpointer afgesloten argumentvector, args:
pid_t child = fork();
if (child == -1) {
perror("fork");
} else if (child == 0) {
execvp(args[0], args);
perror("execvp");
_exit(127);
} else {
int status;
pid_t result;
do {
result = waitpid(child, &status, 0);
} while (result == -1 && errno == EINTR);
if (result == -1) perror("waitpid");
else if (WIFEXITED(status)) {
printf("exit %d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("signal %d\n", WTERMSIG(status));
}
}
Dit gebruikt <unistd.h>, <sys/wait.h>, <errno.h> en <stdio.h>. De omringende shell leest en ontleedt elk commando, voert dit startpad uit en toont daarna de volgende prompt.
Alleen het kindproces vervangt zijn programma; de shell kan het crashen van een commando als beëindigingsresultaat ophalen. Ingebouwde commando's zoals cd moeten de eigen toestand van de shell veranderen: als een kindproces van directory verandert, blijft de directory van de oudershell ongewijzigd.
Leid een descriptor om vóór exec
Voor ls -l > out.txt opent het kindproces de bestemming om te schrijven en maakt het bestand aan of wijzigt de lengte ervan naar nul. Daarna laat het standaarduitvoer naar dat bestand verwijzen:
int fd = open("out.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1) { perror("open"); _exit(1); }
if (dup2(fd, STDOUT_FILENO) == -1) {
perror("dup2"); _exit(1);
}
if (fd != STDOUT_FILENO && close(fd) == -1) {
perror("close"); _exit(1);
}
open heeft <fcntl.h> nodig; descriptoraanroepen en _exit gebruiken <unistd.h>. De rechten voor het aanmaken worden gefilterd door het bestandsaanmaakmasker van het proces. De controle fd != STDOUT_FILENO houdt descriptor 1 open, ook als open zelf 1 teruggaf. Na een geslaagde instelling behoudt exec descriptor 1 en schrijft ls normaal zonder iets van de omleiding te weten.
Een oudere aanpak sluit descriptor 1 voordat het bestand wordt geopend: open geeft de laagste ongebruikte descriptor terug. dup2 geeft de bestemming expliciet aan. Invoeromleiding verbindt op dezelfde manier een leesbaar bestand met descriptor 0.
Verbind twee commando's
Voor ls | wc -l maakt pipe(fds) een leeskant fds[0] en een schrijfkant fds[1]. Het producerende kindproces verbindt de schrijfkant met descriptor 1. Het consumerende kindproces verbindt de leeskant met descriptor 0. Na het dupliceren sluit elk proces de oorspronkelijke pipedescriptoren die het niet nodig heeft. Het ouderproces sluit zijn kopieën en wacht op beide kindprocessen.
Maak beide kindprocessen aan voordat je op een ervan wacht. Een producent kan de pipe vullen en blokkeren; de lezer moet bestaan om de pipe leeg te maken. De lezer bereikt het bestandseinde pas wanneer alle schrijfdescriptoren zijn gesloten en de gebufferde pipegegevens zijn verbruikt. Een ongebruikte schrijfkant die het ouderproces openhoudt, kan wc voor altijd laten wachten.
Houd de prompt beschikbaar
Voor sleep 60 & start de shell het kindproces en toont hij de volgende prompt zonder direct te wachten. Later gebruikt hij niet-blokkerende wachtbewerkingen of reageert hij op SIGCHLD om de status van beëindigde kindprocessen op te halen.
De afsluitstatus bepaalt ook commandocombinaties. make && ./app start het tweede commando alleen na succes; make || echo failed start het tweede alleen na een fout. Deze beslissingen gebruiken het gedecodeerde resultaat, niet de ruwe gecodeerde wachtstatus.
Belangrijkste punten
- Een extern commando draait in een kindproces; een ingebouwd commando dat de toestand verandert draait in de shell.
- Omleiding verandert descriptoren in het kindproces vóór exec.
- Een pipe verbindt de standaarduitvoer van een producent met de standaardinvoer van een consument.
- Start beide kanten voordat je wacht en sluit ongebruikte pipekanten.
- Voor bestandseinde moet elke schrijfkant van de pipe gesloten zijn nadat gebufferde gegevens zijn verbruikt.
- Uitvoering op de achtergrond stelt het wachten uit; de shell moet de beëindigingsstatus van het kindproces later nog ophalen.
4Leg gebruikersmodus, kernelmodus en systeemaanroepen uit
Gebruikersprogramma's hebben diensten van het besturingssysteem nodig zonder de controle over de machine te krijgen. Privilegemodi van de hardware vormen die grens en gecontroleerde toegangspaden laten de kernel op verzoeken en gebeurtenissen reageren. Volg een systeemaanroep via de wrapperfunctie in de bibliotheek en de kernelafhandelaar en onderscheid daarna bewuste traps, instructiefouten en apparaatinterrupts aan de hand van hun oorzaken en mogelijke gevolgen.
4.1 Onderscheid gebruikersmodus van kernelmodus
De kernel moet programma's snel laten rekenen en tegelijk bewerkingen tegenhouden waarmee de controle over de machine kan worden overgenomen. De privilegemodus van de CPU is de hardwaregrens die beide mogelijk maakt.
Scheid berekeningen van geprivilegieerde besturing
In de gebruikersmodus kunnen instructies toegang krijgen tot toegestaan procesgeheugen en gewone berekeningen uitvoeren. Ze kunnen de hardwareconfiguratie die andere programma's beschermt niet vrij veranderen. In de kernelmodus, ook supervisormodus of geprivilegieerde modus genoemd, kan het besturingssysteem de beschermde bewerkingen uitvoeren die nodig zijn om de machine te beheren.
Geprivilegieerde bewerkingen zijn onder meer de timer programmeren, interrupts uitschakelen, interruptafhandelaars instellen, besturingsregisters voor geheugenvertaling veranderen, beschermde apparaten rechtstreeks aansturen en de CPU stilzetten. Als elke toepassing de timer kon uitschakelen, zouden korte CPU-beurten afhangen van de medewerking van die toepassing.
| Platform | Toepassingsmodus | Kernelmodus |
|---|---|---|
| x86-64 op Linux of Windows | Ring 3 | Ring 0 |
| AArch64 | EL0 | EL1 |
AArch64 biedt ook EL2 voor hypervisors en EL3 voor beveiligde firmware. Deze namen duiden privilegeniveaus van de hardware aan, geen verschillende soorten gebruikersaccounts.
Ga binnen op een gecontroleerd adres
Gebruikerscode kan haar eigen privileges niet verhogen en niet zomaar een kerneladres kiezen om code uit te voeren. De overgang vindt plaats via een systeemaanroep, een door een instructie veroorzaakte exceptie of een hardware-interrupt. De CPU draagt de controle over aan een ingangspunt dat door het besturingssysteem is ingesteld. De terugkeercode van de kernel herstelt de opgeslagen gebruikerstoestand en verlaagt het privilegeniveau om de toepassingscode te hervatten.
De kernel beschermt zijn gegevens ook met geheugentoegangsrechten. Een proces kan kerneladressen in zijn virtuele indeling hebben en toch geen rechten hebben om die in de gebruikersmodus te benaderen. Een adres kennen geeft geen toegang tot de bytes op dat adres.
Neem een x86-64 Linux-toepassing die hlt uitvoert, een instructie die is bedoeld om een CPU tot een gebeurtenis stil te zetten. In de gebruikersmodus veroorzaakt de instructie een exceptie. Linux stuurt normaal SIGSEGV, waarvan de standaardactie dat proces beëindigt. De toepassing zet niet de hele computer stil; de beschermingsgrens verandert de poging tot een geprivilegieerde bewerking in een gebeurtenis die de kernel afhandelt.
Houd identiteit en modus gescheiden
Een root- of beheerdersproces is nog steeds een programma in de gebruikersmodus terwijl zijn eigen instructies draaien. Zijn gebruikersidentiteit geeft het ruimere rechten om diensten aan te vragen. Het gaat nog steeds via gecontroleerde paden de kernel binnen en de kernel controleert het verzoek.
Een geprivilegieerd onderhoudsprogramma mag bijvoorbeeld een beschermd bestand openen. Het programma vraagt dat via een systeemaanroep; het wordt geen kernelcode alleen omdat het verzoek is toegestaan. Omgekeerd wordt kernelcode die een read van een gewone gebruiker afhandelt in de kernelmodus uitgevoerd, ook al heeft de aanroeper geen beheerdersidentiteit.
Op één logische CPU is dit een volgorde: gebruikersinstructies, toegang tot de kernel, kernelwerk, terugkeer naar gebruikersinstructies. Het is geen gelijktijdige uitvoering van een programma en een tweede taak van het besturingssysteem op dezelfde CPU. Afzonderlijke logische CPU's kunnen op hetzelfde moment verschillende modi uitvoeren.
Belangrijkste punten
- De gebruikersmodus staat gewone berekeningen toe en beperkt bewerkingen die de machine besturen.
- De toegang tot de kernel verloopt via ingangspunten die het besturingssysteem heeft ingesteld.
- Root is een gebruikersidentiteit; de kernelmodus is een privilegetoestand van de CPU.
- Een verboden gebruikersinstructie veroorzaakt een exceptie zodat de kernel kan reageren.
4.2 Leg stap voor stap uit hoe een systeemaanroep werkt
Een C-programma lijkt vaak een gewone functie aan te roepen wanneer het een dienst van het besturingssysteem nodig heeft. Onder de wrapperfunctie van een systeemaanroep zorgt een gecontroleerde CPU-overgang ervoor dat kernelcode dat verzoek kan controleren en uitvoeren.
Onderscheid een bibliotheekaanroep van een kernelverzoek
De C-bibliotheek implementeert strlen in de gebruikersruimte zonder systeemaanroep. printf maakt gegevens op en kan die bufferen; een latere write draagt bytes via de kernel over. Een dunne wrapperfunctie voor read zet argumenten klaar volgens de systeemaanroepconventie van het platform en voert de toegangsinstructie uit.
POSIX beschrijft interfaces voor Unix-achtige systemen. Linux implementeert veel diensten die grofweg zijn ingedeeld in procesbesturing, bestands- en apparaatbewerkingen, informatie, communicatie en bescherming. Een systeemaanroepnummer selecteert een dienst uit de dispatchtabel van de kernel. Nummers zijn platformspecifiek: Linux x86-64 gebruikt 1 voor write en 39 voor getpid; AArch64 gebruikt 64 voor write.
Volg één schrijfverzoek
Volg voor write(1, "hi\n", 3) op Linux x86-64 het verzoek in volgorde:
- De wrapperfunctie plaatst dienstnummer 1 in
rax, descriptor 1 inrdi, het bufferadres inrsien het aantal 3 inrdx. syscallbewaart het retouradres en de vlaggen in registers, gaat naar de kernelmodus en springt naar het ingestelde ingangspunt. De instructie wisselt niet automatisch van stack.- Ingangscode van de kernel richt de kernelstack van het proces in en slaat de resterende gebruikerstoestand op.
- De kernel kiest via zijn dispatchtabel
write; de implementatie controleert de descriptor, de toegang tot de gebruikersbuffer en het verzoek voordat ze bytes doorgeeft aan de uitvoerbron. - Bij een volledig geslaagde schrijfbewerking is het resultaat 3 bytes. Terugkeercode van de kernel herstelt de gebruikerstoestand en hervat de wrapperfunctie, die terugkeert naar de aanroeper.
Descriptor 1 kan een terminal, bestand of pipe aanduiden. Het nummer alleen geeft niet aan welk uitvoerapparaat wordt gebruikt. Een schrijfbewerking kan ook met succes een kleiner aantal bytes teruggeven; aanroepers die elke byte moeten overdragen houden bij hoeveel daadwerkelijk is geschreven.
Controleer fouten bij de interface
De kernel behandelt gebruikerspointers en andere argumenten als onbetrouwbaar. Een verzoek om uit een ontoegankelijke buffer te kopiëren kan mislukken met EFAULT; gecontroleerd kopiëren voorkomt dat de aanroeper toegang krijgt tot kernelgeheugen.
Op Linux is een kernelfout een negatieve foutcode. De gebruikelijke C-wrapperfunctie rapporteert -1 en stelt errno in. Als open("missing.txt", O_RDONLY) bijvoorbeeld mislukt omdat het bestand niet bestaat, is errno ENOENT. perror("missing.txt") kan “missing.txt: No such file or directory” tonen. Bekijk errno na een gemelde fout, niet als primaire test voor succes: een geslaagde aanroep hoeft een eerdere fout niet te wissen.
Houd rekening met tijd in de kernel
Een aanroep die meteen kan worden voltooid, kan naar hetzelfde proces terugkeren. Een blokkerende leesbewerking van de terminal kan dat proces daarentegen in de geblokkeerde toestand brengen en een ander laten draaien. De invoergebeurtenis maakt de lezer gereed; zodra die wordt ingepland, voltooit hij het terugkeerpad.
Toegang tot de kernel kost meer werk dan een normale functieaanroep: toestand opslaan, privileges veranderen, argumenten controleren en de uitvoering herstellen. Buffering in de bibliotheek kan kleine uitvoerstukken combineren tot minder schrijfbewerkingen. Op Linux voert strace ls het commando ls uit en rapporteert daarbij de systeemaanroepen, argumenten en resultaten.
Belangrijkste punten
- Een bibliotheekfunctie kan al haar werk in de gebruikersruimte doen of systeemaanroepen uitvoeren.
- Een dienstnummer selecteert een kernelbewerking; de aanroeper kiest geen willekeurig kerneladres.
- Kernelcode controleert descriptoren, pointers en andere argumenten.
- Gebruik de retourwaarde om een fout te ontdekken en bekijk daarna
errno. - Een blokkerende systeemaanroep kan een contextwisseling veroorzaken; alleen de kernel binnengaan hoeft dat niet te doen.
- Systeemaanroepnummers en registerconventies hangen af van het besturingssysteem en de architectuur.
4.3 Onderscheid traps, excepties en interrupts
Toegang tot de kernel kan een dienstverzoek, een instructieprobleem of een apparaatgebeurtenis betekenen. Door de oorzaak in te delen kun je voorspellen wat het besturingssysteem zal doen en of dezelfde instructie opnieuw moet worden uitgevoerd.
Vraag wat de gebeurtenis veroorzaakte
| Gebeurtenis | Oorzaak | Tijdstip ten opzichte van de huidige instructie |
|---|---|---|
| Systeemaanroeptrap | Het programma vraagt bewust om een dienst | Synchroon |
| Exceptie of fout | De huidige instructie vereist speciale afhandeling | Synchroon |
| Hardware-interrupt | Een timer of apparaat geeft een signaal aan de CPU | Asynchroon |
Synchroon betekent gekoppeld aan de instructie die wordt uitgevoerd. Het betekent niet dat dezelfde invoer bij elke uitvoering precies dezelfde fouten veroorzaakt. Een laadinstructie kan een fout veroorzaken wanneer de pagina ontbreekt en later slagen wanneer die pagina in het geheugen staat. Asynchroon betekent dat de oorzaak van de gebeurtenis onafhankelijk is van de huidige instructie, zoals een schijf die eerder werk voltooit.
CPU-handleidingen gebruiken deze termen op verschillende manieren. Let op de oorzaak: een bewust dienstverzoek, een door een instructie veroorzaakte gebeurtenis of een hardwaremelding.
Verwijs door naar de juiste afhandelaar
Bij het opstarten installeert het besturingssysteem beschermde adressen van afhandelaars. Een trap- of interrupttabel koppelt soorten gebeurtenissen aan kernelroutines. Op x86-64 dient de interruptdescriptortabel (IDT) voor interrupts en excepties; syscall gebruikt een ingangsadres in een speciaal processorregister. Alleen geprivilegieerde code kan deze bestemmingen instellen.
Bij een interrupt vanuit de gebruikersmodus slaat x86-64-hardware een terugkeerframe op een kernelstack op. Ingangscode slaat extra registers op voordat de afhandeling begint. Het syscall-pad bewaart de initiële terugkeertoestand in registers en vertrouwt op ingangscode om de kernelstack in te richten. Als je alle toegangspaden als identieke hardwareacties behandelt, verberg je een belangrijk verschil in implementatie.
Kies de uitkomst
Een geldige pagina die niet in het geheugen staat, kan worden geladen, waarna de instructie die de fout veroorzaakte opnieuw wordt geprobeerd. Toegang tot geheugen dat het proces niet mag gebruiken, leidt er normaal toe dat Linux SIGSEGV stuurt. Een exceptie is dus niet altijd fataal en ook niet altijd een verzoek om de huidige instructie over te slaan.
Bij een daadwerkelijke x86-64-instructie idiv voor deling van gehele getallen met deler nul veroorzaakt de CPU een deelfoutexceptie. Linux levert normaal SIGFPE af; de bekende melding noemt drijvendekommagetallen, ook bij deze instructie voor gehele getallen. De instructie voor deling van gehele getallen op AArch64 geeft daarentegen nul bij een deler nul. Deling door nul in C is ongedefinieerd gedrag, dus overdraagbare C-code kan niet op een van beide machine-uitkomsten vertrouwen.
Wanneer een leesbewerking van de schijf eindigt, legt de interruptafhandelaar de voltooiing vast en maakt het wachtende proces gereed. Dat proces kan later draaien. Een timerinterrupt geeft het besturingssysteem de kans opnieuw te bepalen welk proces de CPU gebruikt, maar de afhandelaar kan ook naar hetzelfde proces terugkeren.
Onderscheid een signaal van een interrupt
Een hardware-interrupt stelt de CPU op de hoogte; een signaal stelt via het besturingssysteem een proces op de hoogte. Een exceptie kan ertoe leiden dat de kernel een signaal stuurt en één proces kan via kill om een signaal voor een ander proces vragen. Normale voltooiingen van apparaatbewerkingen zijn interrupts en betekenen niet dat er programmafouten zijn.
Kernelcode moet ook gedeelde gegevens beschermen terwijl gebeurtenissen optreden. De kernel kan interrupts op een CPU kort uitschakelen en waar nodig vergrendelingen gebruiken. Lange perioden met uitgeschakelde interrupts vertragen de afhandeling van apparaten; alleen lokale interrupts uitschakelen voorkomt niet dat een andere CPU toegang krijgt tot gedeelde gegevens.
Belangrijkste punten
- Traps en excepties ontstaan door de huidige instructie; hardware-interrupts hebben een externe oorzaak.
- Een herstelbare fout kan worden verholpen en de bijbehorende instructie kan opnieuw worden geprobeerd.
- Beschermde toegangstabellen voorkomen dat gebruikersprogramma's kernelafhandelaars vervangen.
- Een signaal is gericht op een proces; een hardware-interrupt is gericht op afhandeling door de CPU.
- Voltooiing van een apparaatbewerking maakt een geblokkeerd proces gereed en een timerinterrupt hoeft niet tot een proceswisseling te leiden.
5Leg uit hoe het besturingssysteem de CPU deelt met beperkte directe uitvoering
Snelle uitvoering en controle door het besturingssysteem moeten samengaan. Beperkte directe uitvoering laat gewone instructies op de hardware draaien terwijl modi, beschermde ingangspunten en een timer de kernel de controle laten behouden. Volg wat gebeurt wanneer het besturingssysteem naar hetzelfde proces terugkeert en wanneer het een ander proces hervat. De opgeslagen toestand en wisselkosten verklaren hoe korte CPU-beurten interactief werk kunnen ondersteunen.
5.1 Leg beperkte directe uitvoering uit
Elke instructie van een toepassing in een softwaresimulator uitvoeren zou het delen van de CPU duur maken. Een toepassing onbeperkte controle over de machine geven zou delen onveilig maken. Beperkte directe uitvoering (LDE) gebruikt gewone uitvoering op de hardware met een beschermd pad terug naar het besturingssysteem.
Los twee verschillende besturingsproblemen op
Directe uitvoering betekent dat de gewone instructies van een proces op de CPU zelf worden uitgevoerd. Het besturingssysteem onderzoekt en keurt niet elke rekeninstructie goed. Twee problemen blijven over: bewerkingen beperken die de machine kunnen beschadigen en de controle terugkrijgen wanneer een programma niet vrijwillig stopt.
De gebruikersmodus lost het beperkingsprobleem op. Via systeemaanroeptraps kan een programma diensten aanvragen zonder willekeurige geprivilegieerde code te kiezen. Een hardwaretimer lost het besturingsprobleem op: die kan een toepassing onderbreken die helemaal geen aanroepen doet. Samen ondersteunen deze mechanismen CPU-virtualisatie met weinig overhead.
Bereid de uitvoering van toepassingen voor
Bij het opstarten stelt de kernel gebeurtenisafhandelaars in en initialiseert hij de timer. Deze instelbewerkingen zijn geprivilegieerd. Latere gebruikersprogramma's kunnen de afhandelaars niet omleiden of de timer uitschakelen om te voorkomen dat hun beurt eindigt.
Om een proces te starten maakt het besturingssysteem zijn beheervermelding en geheugenbeeld aan, bereidt het opstartargumenten en een gebruikersstack voor en stelt het initiële registerwaarden in. Het zorgt voor een initiële opgeslagen context zodat de normale terugweg van kernel naar gebruiker het programma kan starten. Conceptueel begint het proces door terug te keren uit een trap die het nooit heeft veroorzaakt.
De gedetailleerde indeling van het opgeslagen frame hangt af van de CPU en het besturingssysteem. Het bruikbare ontwerpidee is hergebruik: dezelfde herstelmechanismen kunnen een onderbroken proces hervatten of een nieuw voorbereid proces starten.
Keer vrijwillig terug naar de kernel
Tijdens een systeemaanroep bewaart de hardware de initiële terugkeertoestand en draagt de controle over. Ingangscode van de kernel slaat de extra registers op de kernelstack van het proces op. Na voltooiing van een niet-blokkerend verzoek herstelt de kernel het proces en keert terug naar de gebruikersmodus. Als het verzoek blokkeert, kan eerst een ander gereed proces draaien.
Wanneer main terugkeert, vraagt de beëindigingsverwerking van de runtime uiteindelijk aan de kernel om het proces te beëindigen. Het geheugen kan worden vrijgegeven terwijl de beëindigingsgegevens bewaard blijven totdat een ouderproces die ophaalt.
Keer terug zonder medewerking
Een coöperatief systeem is afhankelijk van programma's die aanroepen doen, de CPU-beurt afstaan of excepties veroorzaken. Een eindeloze lus die geen van die dingen doet, kan een CPU monopoliseren. Het klassieke Mac OS illustreert deze zwakte.
Een niet-coöperatief ontwerp gebruikt een timerinterrupt om toegang tot de kernel af te dwingen. Neem een leermodel met één logische CPU en een periodieke timer die om de 4 ms afgaat. De timer produceert 1.000 / 4 = 250 tikken per seconde. Een gebruikersproces dat vastzit in while (1) { } wordt bij de volgende tik onderbroken. De kernel kan dan een ander gereed proces uitvoeren; de lus hoeft niet eerst een systeemaanroep te bereiken.
De tik is een kans om te handelen, geen verplichting om van proces te wisselen. De scheduler kan het onderbroken proces laten doorgaan. Timerafhandeling en herstel van de opgeslagen toestand zijn mechanismen; de keuze van de scheduler is beleid. De werkelijke responstijd hangt ook af van kernelwerk en van wanneer interrupts kunnen worden afgehandeld, dus een timerperiode op zichzelf garandeert geen responstijd van een toepassing.
Belangrijkste punten
- LDE voert gewone gebruikersinstructies rechtstreeks uit met beperkingen die door de hardware worden afgedwongen.
- De gebruikersmodus beperkt bewerkingen; systeemaanroepen bieden gecontroleerde geprivilegieerde diensten.
- Een timer laat het besturingssysteem de controle terugkrijgen zonder medewerking van gebruikerscode.
- Instellingen bij het opstarten beschermen de adressen van afhandelaars en de timerconfiguratie.
- Een timertik geeft de scheduler een keuze; die vereist geen proceswisseling.
5.2 Volg een contextwisseling en schat de kosten ervan
Naar de kernelmodus gaan verandert niet noodzakelijk het draaiende proces. Een contextwisseling vindt plaats wanneer de kernel de uitvoeringstoestand van het ene proces bewaart en die van een ander hervat. Dit vereist zowel het bewaren van de gebruikerstoestand als een verandering in de eigen uitvoeringscontext van de kernel.
Volg hoe A wordt onderbroken en B wordt hervat
Volg voor een wisseling op x86-64 door de timer de lagen:
- A voert gebruikersinstructies uit. De timer onderbreekt A.
- De hardware gaat naar de kernelmodus en slaat het interruptterugkeerframe van A op de kernelstack van A op. Ingangscode slaat daar de overige gebruikersregisters van A op.
- De scheduler kiest B. De wisselroutine slaat de uitvoeringscontext van de kernel voor A op en herstelt de opgeslagen kernelcontext van B, inclusief zijn kernelstackpointer.
- De kernel schakelt waar nodig naar de adresruimtecontext van B. De kernel draait nu namens B met de kernelstack van B.
- De afsluitcode van de kernel herstelt de gebruikersregisters van B. De terugkeerinstructie herstelt de terugkeertoestand van B en gaat naar de gebruikersmodus. B gaat verder waar het eerder stopte.
De eerste opslag bewaart de onderbroken gebruikersuitvoering van A. De tweede bewaart waar de kernel was tijdens de afhandeling voor A. Het zijn geen twee identieke volledige kopieën van dezelfde registerverzameling. Een PCB of bijbehorende contextstructuur en de kernelstack per proces werken samen om de toestand te bewaren.
Herken de gebeurtenis en de beslissing
Een blokkerende aanroep, procesbeëindiging of een beslissing bij een timerinterrupt kan tot een wisseling leiden. Een apparaatinterrupt kan een ander proces gereed maken, waarna de scheduler dat kan kiezen. Alle wisselmechanismen draaien in de kernel; een toepassing slaat haar eigen registers niet op om zich voor onderbreking beschikbaar te stellen.
| Situatie | Modusovergang? | Ander proces? |
|---|---|---|
| Niet-blokkerende dienst keert terug naar de aanroeper | Gebruiker → kernel → gebruiker | Nee |
| Timer afgehandeld, hetzelfde proces wordt hervat | Gebruiker → kernel → gebruiker | Nee |
| A blokkeert en B wordt hervat | Toegang tot kernel, daarna terugkeerpad van B | Ja |
Een contextwisseling bewaart de uitvoeringstoestand die nodig is om door te gaan. De tijd verstrijkt en de buitenwereld kan veranderen: gedeelde bestandsposities en externe invoer worden niet bevroren terwijl een proces is onderbroken.
Houd rekening met directe en indirecte kosten
Direct werk omvat het uitvoeren van de scheduler, het opslaan en herstellen van registers en het selecteren van een adresruimte. Vertaalcaches moeten mogelijk worden aangepast; vertalingen met labels kunnen voorkomen dat alle oude vermeldingen worden weggegooid. Indirecte kosten volgen wanneer B CPU-caches of gecachte adresvertalingen opnieuw moet vullen die door A zijn verdrongen. De kosten hangen af van hardware en werklast, dus gebruik gemeten of gegeven waarden wanneer je overhead berekent.
Stel dat een wisseling, inclusief de relevante effecten, 5 µs kost en nuttige CPU-beurten 10 ms duren. Zet 10 ms om naar 10.000 µs. Het wisselaandeel is 5 / (10.000 + 5), ongeveer 0,05%. Voor nuttige beurten van 1 ms is dat 5 / 1005, ongeveer 0,50%; bij 0,1 ms is dat 5 / 105, ongeveer 4,8%.
De noemer bevat nuttig werk plus wisseltijd voor één herhaalde cyclus. Een kortere beurt kan meer kansen bieden voor interactief werk, maar besteedt een groter aandeel aan herhaald wisselen. De timer en wisselcode maken korte beurten mogelijk; het planningsbeleid moet die kosten afwegen tegen de reactiesnelheid.
Belangrijkste punten
- Na toegang tot de kernel kan de uitvoering naar hetzelfde proces terugkeren zonder contextwisseling.
- Het opslaan van de gebruikerstoestand bij binnenkomst en van de kernelcontext tijdens een wisseling dient verschillende doelen.
- Om B te hervatten zijn de kernelstack, opgeslagen registers en adresruimtecontext van B nodig.
- Wisselkosten omvatten directe administratie en het later opnieuw vullen van caches of adresvertalingen.
- Het overheadaandeel is de wisselkosten gedeeld door de nuttige beurt plus de wisselkosten.
- Kortere beurten vergroten het wisselaandeel bij vaste kosten per wisseling.
Processen en systeemaanroepen: voorbeelden
1Bekijk wat het besturingssysteem bijdraagt
12 stappen
- 1IntroductieBegin met meerdere programma's
Stel je een laptop voor die een project compileert terwijl hij muziek afspeelt en netwerkgegevens ontvangt. Elke toepassing heeft CPU-tijd, geheugen en I/O nodig, maar geen enkele bestuurt de hele machine.
Volg wie die bronnen levert, hoe meerdere taken voortgang maken en waar de geprivilegieerde diensten draaien. De terugkerende vraag is concreet: welk werk vraagt de toepassing aan en welk werk moet het besturingssysteem beheren?
- 2UitlegVerberg de apparaatdetails
Het besturingssysteem biedt abstracties: bruikbare interfaces die de onderliggende werking verbergen. Een programma opent een bestand en leest bytes zonder commando's voor elk opslagapparaat te implementeren.
Een systeemaanroep vraagt via een gecontroleerde interface een kerneldienst aan. De kernel controleert en voert het beschermde werk uit. De toepassing beslist nog steeds welke gegevens ze wil; ze krijgt geen toestemming om het apparaat rechtstreeks aan te sturen.
- 3VoorbeeldVolg een uitvoerbestand van een compiler
Een compiler produceert bytes voor een uitvoerbaar bestand en vraagt een schrijfbewerking aan. De kernel controleert de uitvoerbron en handelt het opslagwerk achter de bestandsinterface af.
Tegelijk beheert het besturingssysteem het geheugen van de compiler en deelt het CPU-tijd met de muziekspeler. Eén handeling, een bestand opslaan, valt dus onder meerdere taken van het besturingssysteem: abstractie, beheer van bronnen en bescherming van de andere programma's.
- 4KernpuntGeef elk proces een bruikbare machine
Virtualisatie presenteert een fysieke bron via een bruikbare afgeschermde weergave. Een proces kan voortgang maken alsof het een eigen processor heeft, ook al worden zijn instructies afgewisseld met die van andere processen.
Ook zijn geheugenweergave is afgeschermd: het adres dat het gebruikt hoeft in een ander proces niet dezelfde fysieke bytes aan te duiden. CPU- en geheugenvirtualisatie geven programma's handige modellen terwijl het besturingssysteem de werkelijke gedeelde hardware beheert.
- 5UitdagingZoek het geprivilegieerde deel
Op een computer is geen grafische desktop geïnstalleerd, maar hij voert diensten uit, slaat bestanden op en verdeelt CPU-tijd. Is de essentiële kern van het besturingssysteem verdwenen? Leg uit.
---reveal---
Nee. De kernel beheert nog steeds processen, geheugen en apparaatdiensten. Een grafische desktop is een gebruikersprogramma boven op die diensten. Door het ontbreken ervan verandert de interface die iemand ziet, maar blijft het beheer van bronnen door de kernel nodig.
- 6UitlegScheid de bewerking van de keuze
Een mechanisme maakt een handeling mogelijk. De registers van het ene proces opslaan en die van een ander herstellen biedt het mechanisme om te wisselen.
Beleid bepaalt hoe je dat gebruikt, bijvoorbeeld door te beslissen welk gereed proces de volgende CPU-beurt krijgt. Het onderscheid is van belang wanneer een ontwerp verandert: een nieuwe keuzeregel kan dezelfde betrouwbare wisselcode hergebruiken. Efficiënte mechanismen alleen bepalen niet wat eerlijk is.
- 7UitlegBenut anders ongebruikte wachttijd
Een batchsysteem kan de CPU ongebruikt laten terwijl de huidige taak op I/O wacht. Multiprogrammering houdt andere taken beschikbaar en voert er tijdens die wachttijd één uit.
De wachtende taak rekent niet en het apparaat is niet sneller geworden. De winst komt van overlappend onafhankelijk werk: het ene proces wacht terwijl een ander de CPU gebruikt. Meerdere programma's in het geheugen vereisen ook bescherming zodat het ene niet het geheugen van het andere kan overschrijven.
- 8VoorbeeldBereken de winst in bezettingsgraad
Een taak rekent 20 ms en wacht daarna 80 ms op I/O. Als de taak alleen draait, bevat één cyclus van 100 ms slechts 20 ms CPU-werk: de bezettingsgraad is 20%.
Met een tweede dergelijke taak en geschikte overlap kan nog eens 20 ms aan berekeningen anders ongebruikte tijd vullen, wat in dit voorbeeld tot 40% oplevert. De verbetering benut wachttijd productief; taken toevoegen kan de CPU-bezettingsgraad niet boven 100% brengen.
- 9VerbandGeef interactief werk korte beurten
Tijdsverdeling geeft processen korte CPU-beurten zodat interactief werk niet op een volledige batchtaak hoeft te wachten. Dit is gelijktijdigheid, zelfs op één logische CPU: meerdere activiteiten maken voortgang door afwisseling.
Parallellisme vereist daadwerkelijke gelijktijdige uitvoering op afzonderlijke verwerkingsbronnen. Geheugen- en schijfcapaciteit illustreren een andere verdeling, ruimteverdeling, waarbij verschillende gebruikers tegelijk over afzonderlijke delen beschikken.
- 10UitlegKies een grens voor diensten
Een monolithische kernel, zoals Linux, voert de meeste diensten samen met privileges uit. Rechtstreekse aanroepen zijn efficiënt, maar een defect stuurprogramma kan de kerneltoestand beschadigen.
Een microkernel, zoals seL4, laat meer diensten in afzonderlijke gebruikersprocessen draaien. Isolatie kan een storing in een dienst beperken, ten koste van communicatie en modusovergangen. Windows NT en XNU combineren benaderingen. De grens bepaalt welke storingen zich kunnen verspreiden en welke diensten opnieuw kunnen worden gestart.
- 11UitdagingVerbind delen en bescherming
Een systeem met één CPU voert meerdere taken uit en schakelt naar een andere wanneer er één op schijf-I/O wacht. Een voorstel zou elke taak elk geheugenadres laten overschrijven. Leg het voordeel voor de bezettingsgraad en de fout in dit voorstel uit.
---reveal---
Multiprogrammering vult I/O-wachttijd met berekeningen van een andere taak. Onbeperkt schrijven zou één taak een andere of het besturingssysteem laten beschadigen. Overlappend werk verbetert het gebruik van bronnen, maar gedeeld gebruik vereist nog steeds afgedwongen bescherming van geheugen en privileges.
- 12SamenvattingBeoordeel een ontwerp met drie vragen
Vraag bij het ontwerp van een besturingssysteem welke bron wordt gedeeld, welke interface een programma ziet en welke grens ander werk beschermt.
Deze vragen verbinden CPU-virtualisatie met procesbeurten, bestanden met verborgen apparaatdetails en kernelorganisatie met foutisolatie. Mechanismen voeren beschermde bewerkingen uit; beleid bepaalt hoe het gebruik ervan wordt verdeeld. Gelijktijdigheid kan voortkomen uit afwisseling op één CPU.
2Volg een proces tijdens CPU- en I/O-werk
13 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieVolg een actieve instantie
- 2UitlegMaak van een bestand een proces
- 3UitlegBewaar de volgende instructie
- 4VoorbeeldLees een processenoverzicht
- 5UitdagingInterpreteer gelijke adressen
- 6TipBekijk descriptoren als geërfde bronnen
- 7UitlegVraag wat ontbreekt
- 8VoorbeeldBegin de tijdlijn
- 9VoorbeeldVoltooi de tijdlijn
- 10UitdagingVerander de keuze voor een gereed proces
- 11VerbandHerken echte toestandsnamen
- 12UitdagingVerbind procesgegevens met CPU-capaciteit
- 13SamenvattingLees de toestand als geschiktheid voor uitvoering
3Bouw een commando met procesaanroepen
23 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieGeef elke aanroep één taak
- 2UitlegSplits één uitvoering in twee
- 3VoorbeeldVolg een afgeschermde waarde
- 4VoorbeeldTel een vertakking, niet de aanroepen in de broncode
- 5UitdagingTel de paden die afdrukken
- 6TipControleer wat is gekopieerd
- 7UitlegVervang het programma van het kindproces
- 8VoorbeeldGeef afzonderlijke argumentstrings door
- 9UitdagingVoorspel een geslaagde vervanging
- 10KernpuntGebruik de tijd vóór exec
- 11UitlegHaal één resultaat van een kindproces op
- 12VoorbeeldDecodeer normale beëindiging
- 13UitlegLaat geen resultaten ongehaald
- 14UitdagingHandel onderbreking correct af
- 15TipKies een beëindigingsverzoek
- 16UitlegKeer terug naar een prompt op de voorgrond
- 17VoorbeeldLeid uitvoer om vóór vervanging
- 18UitdagingBehoud de uitvoerbestemming
- 19VoorbeeldVerbind de twee kanten van de pipe
- 20KernpuntStart voordat je wacht
- 21VerbandHoud achtergrondwerk bij
- 22UitdagingVolg een lege pijplijn
- 23SamenvattingHoud identiteit, beeld en resultaat bij
4Volg een verzoek over de privilegegrens
15 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieVraag waarom de kernel is binnengegaan
- 2UitlegBeperk wat toepassingen kunnen besturen
- 3VoorbeeldScheid identiteit van privileges
- 4UitdagingVoorspel een verboden instructie
- 5UitlegZoek de werkelijke systeemaanroep
- 6VoorbeeldBereid één schrijfbewerking voor
- 7VoorbeeldGa binnen en controleer
- 8VoorbeeldInterpreteer de retourwaarde
- 9UitdagingVolg een geblokkeerde leesbewerking
- 10UitlegDeel in op basis van de oorzaak
- 11VoorbeeldHerstel of beëindig
- 12TipHoud signalen op procesniveau
- 13UitdagingDeel een voltooiing van een apparaatbewerking in
- 14UitdagingOnderscheid twee soorten toegang
- 15SamenvattingVolg oorzaak, controles en uitkomst
5Behoud controle terwijl programma's rechtstreeks worden uitgevoerd
15 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieCombineer snelheid en controle
- 2UitlegLos twee besturingsproblemen op
- 3VoorbeeldBereid de machine en het proces voor
- 4VerbandBekijk de zwakte van de coöperatieve aanpak
- 5VoorbeeldOnderbreek een eindeloze lus
- 6UitdagingBepaal welk mechanisme ontbreekt
- 7UitlegSla het onderbroken gebruikerspad op
- 8VoorbeeldSla ook het kernelpad op
- 9VoorbeeldKeer terug via de opgeslagen toestand van B
- 10UitdagingHerken een overgang zonder proceswisseling
- 11UitlegTel de kosten na een wisseling
- 12VoorbeeldBouw de noemer voor de volledige cyclus op
- 13UitdagingSchat een veranderde beurtlengte
- 14UitdagingVerbind een geblokkeerde aanroep met hervatting
- 15SamenvattingVolg bij wie het terugkeerpad hoort
Processen en systeemaanroepen: oefenvragen
Set 1: Maak en vervang een proces en haal zijn resultaat op10 Oefenvragen
Vraag 1 van 10
Toon antwoordVerberg antwoord
getpid() aanroepen om zijn eigen identificatie te vinden. Als in beide paden de PID van het kindproces werd teruggegeven, zou het onderscheid in retourwaarden verdwijnen waarmee gedrag voor ouderproces en kindproces wordt gekozen.Vraag 2 van 10
Elke fork slaagt. Hoeveel processen bereiken puts("done") in deze code?
if (fork() == 0) {
fork();
}
puts("done");
Toon antwoordVerberg antwoord
puts: in totaal drie. Als je twee aanroepen in de broncode telt als automatische verdubbeling tot vier, negeer je de vertakking die het oorspronkelijke ouderproces uitsluit.Vraag 3 van 10
Toon antwoordVerberg antwoord
Vraag 4 van 10
Toon antwoordVerberg antwoord
Vraag 5 van 10
/opt/course/bin/checker en PATH bevat /opt/course/bin. Er is geen bestand met de naam checker in de huidige directory. Waarom kan execv("checker", args) het niet vinden?Toon antwoordVerberg antwoord
execv gebruikt het padnaamargument rechtstreeks, dus “checker” verwijst in dit geval naar de huidige directory. execvp zou de opgegeven PATH-omgeving doorzoeken. De argumentvectorvorm betekent niet dat PATH wordt doorzocht; dat gedrag hoort bij de p-variant.Vraag 6 van 10
exit(515) aan. Wat rapporteert WEXITSTATUS na een geslaagde wachtbewerking en een WIFEXITED-test die waar is?Toon antwoordVerberg antwoord
WEXITSTATUS gebruikt. Wie de oorspronkelijke waarde 515 terugverwacht, houdt geen rekening met de resultaatinterface van acht bits; die is niet hetzelfde als de opslag van een willekeurig geheel getal in C.Vraag 7 van 10
waitpid(child, &status, WNOHANG) geeft 0 terug. Wat is er gebeurd?Toon antwoordVerberg antwoord
WNOHANG betekent nul dat het passende kindproces bestaat, maar dat geen gevraagd resultaat beschikbaar is. Het ouderproces keert onmiddellijk terug zonder de beëindigingsstatus op te halen. Het is niet de afsluitcode van het kindproces: de retourwaarde van waitpid identificeert bij succes een kindproces en gebruikt een afzonderlijk statusobject om te melden hoe dat kindproces eindigde.Vraag 8 van 10
SIGKILL blijft de vermelding zichtbaar. Welke handeling maakt de bewaarde resultaatgegevens van dit kindproces vrij?Toon antwoordVerberg antwoord
SIGKILL kan het proces niet opnieuw laten eindigen en er is geen uitvoeringspad in het kindproces meer om een ander signaal af te handelen.Vraag 9 van 10
Toon antwoordVerberg antwoord
Vraag 10 van 10
producer | consumer. De producent eindigt, maar de consument blijft na het lezen van alle beschikbare bytes eindeloos wachten. De shell heeft zijn kopieën van de pipe gesloten; de consument houdt zijn geërfde oorspronkelijke descriptor voor de schrijfkant nog open. Wat verhelpt het vastlopen?Toon antwoordVerberg antwoord
Set 2: Bronnen, processen en bescherming10 Oefenvragen
Sla deze les op om elk antwoord te zien en elke set te oefenen.
Vraag 1 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 3 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Set 3: Volg toegang tot de kernel en CPU-wisselingen10 Oefenvragen
Sla deze les op om elk antwoord te zien en elke set te oefenen.
Vraag 1 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 3 van 10
errno is ENOENT. Wat moet de aanroeper concluderen?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Set 4: Volg procesgedrag en het delen van bronnen10 Oefenvragen
Sla deze les op om elk antwoord te zien en elke set te oefenen.
Vraag 1 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 3 van 10
Een proces met één thread heeft een afgeschermde x gelijk aan 6. Elke fork slaagt. Alle processen bereiken puts. Hoeveel regels bevatten “9”?
pid_t p = fork();
if (p == 0) x += 3;
fork();
if (x == 9) puts("9");
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
execvp aan voor een programma dat niet bestaat. De volgende opdracht drukt een fout af. Waarom kan die opdracht worden uitgevoerd?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
errno EINTR oplevert. Wat moet het ouderproces doen om de resultaten van kindprocessen te blijven ophalen?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
chdir alleen in een met fork aangemaakt kindproces aan om cd te implementeren, wacht op dat kindproces en start daarna een ander commando. Waarom begint het volgende commando in de oude directory?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Set 5: Analyseer interacties tussen processen en kernel10 Oefenvragen
Sla deze les op om elk antwoord te zien en elke set te oefenen.
Vraag 1 van 10
WIFSIGNALED nadat waitpid is geslaagd. Welke macro geeft het nummer van het beëindigende signaal?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 3 van 10
SIGSEGV af. Welke volgorde onderscheidt de twee gebeurtenissen?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
dup2(fd, 0) aan, sluit fd onvoorwaardelijk en voert het commando uit met exec. Waarom kan de standaardinvoer van het commando gesloten zijn?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
Een programma heeft een afgeschermde x gelijk aan 2. Elke fork slaagt. Hoeveel processen drukken “hit” af?
pid_t p = fork();
if (p > 0) {
x = 7;
fork();
}
if (x == 7) puts("hit");
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Kom je niet uit je eigen opgave? Huiswerkhulp
Kernbegrippen
A
abstractie
achtergrondtaak
adresruimte
adresvertaalbuffer
afsluitstatus
argumentvector
B
batchverwerking
beleid
beperkte directe uitvoering
beschermingsring
beschrijving van een geopend bestand
bestandsdescriptor
bestandseinde
besturingssysteem
C
contextwisseling
coöperatieve besturing
CPU-bezettingsgraad
E
errno
errno is een foutindicator van de C-bibliotheek die door veel interfaces bij een fout wordt ingesteld en wordt bekeken nadat de retourwaarde een fout meldt.exceptie
exceptieniveau
exec
F
fork
G
geblokkeerde toestand
gebruikersmodus
gelijktijdigheid
geprivilegieerde instructie
gereedtoestand
H
hardware-interrupt
I
ingebouwd commando
K
kernel
kernelcontext
kernelmodus
kernelstack
kindproces
kopiëren bij schrijven
M
mechanisme
microkernel
moduswisseling
monolithische kernel
multiprogrammering
N
niet-blokkerend wachten
O
omleiding
ophalen van de beëindigingsstatus
ouderproces
overhead
P
paginafout
parallellisme
PATH
execvp naar namen van uitvoerbare bestanden zoeken.pipe
proces
proces-ID
procesbesturingsblok
programma
programmateller
R
ruimteverdeling
S
scheduler
shell
signaal
sluiten bij exec
stackpointer
systeemaanroep
systeemaanroepnummer
T
terugkeer uit een trap
tijdschijf
tijdsverdeling
timerinterrupt
toestand in uitvoering
trap
traptabel
U
uitvoerbuffer
V
virtualisatie
W
wachtstatus
weesproces
wrapperfunctie in een bibliotheek
Z
zombieproces
Over deze les
Door StudeliFout melden
Maak je eigen Studiemateriaal
Maak van je aantekeningen, slides en documenten Leermodules, Oefenvragen, Flashcards en 6 andere tools.