Inleiding tot besturingssystemen
Hallo, Habr! Ik wil jullie een serie vertaalde artikelen voorstellen van een literatuur die ik interessant vind - OSTEP. In dit materiaal wordt diep ingegaan op de werking van Unix-achtige besturingssystemen, met name - hoe processen werken, verschillende planners, geheugen en andere soortgelijke componenten die een modern besturingssysteem vormen. Het origineel van al het materiaal kunt u hier bekijken . Houd er rekening mee dat de vertaling niet professioneel is uitgevoerd (vrij vrij), maar ik hoop dat ik de algemene betekenis heb behouden.
Laboratoriumopdrachten voor dit onderwerp kun je hier vinden:
Andere delen:
En je kunt ook mijn kanaal bezoeken op =)
Alarm! er is een lab aan deze lezing! kijk
Process API
Laten we een voorbeeld bekijken van het creƫren van een proces in een UNIX-systeem. Dit gebeurt via twee systeemaanroepen. fork() en exec().
De aanroep fork()

Laten we het programma bekijken dat de aanroep fork() uitvoert. Het resultaat van de uitvoering zal als volgt zijn.

Allereerst gaan we de functie main() binnen en voeren we een string-weergave uit. De string bevat de procesidentificator, die in het origineel wordt genoemd PID of process identifier. Deze identificator wordt in UNIX gebruikt om naar het proces te verwijzen. De volgende opdracht zal de aanroep fork() uitvoeren. Op dat moment wordt er een bijna exacte kopie van het proces gemaakt. Voor het besturingssysteem lijkt het alsof er twee kopieƫn van hetzelfde programma actief zijn die op hun beurt uit de uitvoering van de functie fork() komen. Het opnieuw gemaakte kindproces (ten opzichte van het ouderproces dat het heeft gecreƫerd) zal niet verder worden uitgevoerd vanaf de functie main(). Het is belangrijk om te onthouden dat het kindproces geen exacte kopie is van het ouderproces; het heeft zijn eigen adresruimte, eigen registers, een eigen indicator voor uitvoeringsinstructies, enzovoort. Het resultaat dat door de aanroeper van de functie fork() wordt teruggegeven, zal dus verschillend zijn. Het ouderproces ontvangt bijvoorbeeld de PID-waarde van het kindproces, terwijl het kind de waarde 0 ontvangt. Aan de hand van deze retourcodes kunnen we de processen later scheiden en elk zijn eigen taak laten uitvoeren. Daarbij is de uitvoering van dit programma niet strikt gedefinieerd. Na de splitsing in twee processen houdt het besturingssysteem toezicht op hen en plant ook hun werk. In het geval van uitvoering op een enkele kern processor, gaat een van de processen verder, in dit geval het ouderproces, en daarna krijgt het kindproces de controle. Bij herstart kan de situatie anders uitvallen.
De aanroep wait()

Laten we het volgende programma bekijken. In dit programma wacht het ouderproces altijd op de voltooiing van het kindproces dankzij de aanwezigheid van de aanroep wait() We zullen dan een strikt gedefinieerde uitvoer van tekst op het scherm krijgen.

De aanroep exec()

Laten we de aanroep bekijken exec(). Deze systeemaanroep is nuttig wanneer we een geheel ander programma willen starten. Hier zullen we aanroepen execvp() Voor het starten van het wc-programma, dat een programma is voor het tellen van woorden. Wat gebeurt er bij het aanroepen van exec()? Bij deze aanroep worden de naam van het uitvoerbare bestand en enkele parameters als argumenten meegegeven. Vervolgens worden de code en statische gegevens uit dit uitvoerbare bestand geladen en wordt het eigen segment met code overschreven. Andere geheugenruimten, zoals de stack en heap, worden opnieuw geĆÆnitialiseerd. Daarna voert het besturingssysteem eenvoudig het programma uit, waarbij het een set argumenten doorgeeft. Op deze manier hebben we geen nieuw proces aangemaakt, we hebben gewoon het momenteel draaiende programma omgevormd tot een ander draaiend programma. Na het uitvoeren van de aanroep exec() heeft de afgeleide het gevoel dat het oorspronkelijke programma in feite nooit is gestart.
Deze complicatie bij het starten is volkomen normaal voor de Unix shell en stelt deze shell in staat om code uit te voeren na de aanroep fork(), maar vóór de aanroep exec(). Een voorbeeld van dergelijke code kan zijn het aanpassen van de shell-omgeving aan de eisen van het programma, voordat het feitelijk wordt gestart.
Shell ā is simpelweg een gebruiksprogramma. Het toont je een prompt en wacht tot je iets typt. In de meeste gevallen, als je daar de naam van een programma typt, zal de shell de locatie ervan vinden, de fork()-methode aanroepen en vervolgens om een nieuw proces te creĆ«ren een van de exec()-typen aanroepen en wachten op de uitvoering met de aanroep wait(). Wanneer het afgeleide proces is beĆ«indigd, keert de shell terug uit de wait() aanroep en toont opnieuw de prompt, waarbij het wacht op de invoer van het volgende commando.
De split tussen fork() & exec() stelt de shell in staat om de volgende dingen te doen, bijvoorbeeld:
wc file > new_file.
In dit voorbeeld wordt de uitvoer van het programma wc omgeleid naar een bestand. De manier waarop de shell dit bereikt is vrij eenvoudig ā bij het creĆ«ren van het kindproces sluit de shell de standaard uitvoer en opent het bestand exec()new_file , zodat alle uitvoer van het verder draaiende programmawordt omgeleid naar het bestand in plaats van naar het scherm. wc Unix-pipes
zijn op een vergelijkbare manier geĆÆmplementeerd, met het verschil dat ze de aanroep pipe() gebruiken. In dit geval wordt de uitvoerstroom van het proces verbonden met een pipe-queue, die in de kernel ligt, waarvan de invoerstroom wordt verbonden met een ander proces. š„Operating Systems: Three Easy Pieces. Deel 3: Process API (vertaling) | ProHoster
Bron: habr.com
