Operating Systems: Three Easy Pieces. Part 3: Process API (oversættelse)

Introduktion til operativsystemer

Hej, Habr! Jeg vil gerne præsentere for dig en serie af artikeloversættelser af en interessant litteratur — OSTEP. Dette materiale undersøger grundigt, hvordan unix-lignende operativsystemer fungerer, nemlig hvordan man arbejder med processer, forskellige scheduler, hukommelse og andre lignende komponenter, der udgør et moderne OS. Originalen af alt materiale kan du se her her. Vær opmærksom på, at oversættelsen er lavet uprofessionelt (temmelig frit), men jeg håber, at jeg har bevaret den overordnede mening.

Laboratoriearbejder om dette emne kan findes her:

Andre dele:

Og du kan også tjekke min kanal på telegram =)

Alarm! der er en lab til denne forelæsning! se github

Process API

Lad os se et eksempel på oprettelse af en proces i et UNIX-system. Det sker via to systemopkald. fork() og exec().

Opkaldet til fork()

Operating Systems: Three Easy Pieces. Part 3: Process API (oversættelse)

Lad os se på et program, der udfører opkaldet til fork(). Resultatet af dets udførelse vil være som følger.

Operating Systems: Three Easy Pieces. Part 3: Process API (oversættelse)

Først går vi ind i funktionen main() og udfører output af en streng til skærmen. Strengen indeholder identifikatoren for processen, som på originalsproget kaldes PID eller process identifier. Denne identifikator bruges i UNIX til at referere til processen. Den næste kommando vil udføre opkaldet til fork(). På dette tidspunkt oprettes en næsten nøjagtig kopi af processen. For operativsystemet ser det ud som om, der kører to kopier af det samme program, som hver især vil forlade udførelsen af funktionen fork(). Den nyoprettede procesbarn (i forhold til den skabe procesforælder) vil ikke længere udføres fra funktionen main(). Det skal huskes, at procesbarnet ikke er en nøjagtig kopi af procesforælderen; det har blandt andet sit eget adresseområde, egne registre, sin egen peger til de udførende instruktioner osv. Dermed vil værdien, der returneres til kaldende funktion fork(), være forskellig. Specifikt vil procesforælderen få PIDs værdi for børneprocessen som returværdi, mens barnet vil modtage værdien 0. Baseret på disse returkoder kan processerne senere adskilles og tvinges til at udføre deres arbejde. Udførelsen af dette program er dog ikke strengt defineret. Efter opdelingen i to processer begynder operativsystemet at overvåge dem og planlægge deres arbejde. I tilfælde af udførelse på en enkeltkerneprocessor vil en af processerne, i dette tilfælde - forælderen, fortsætte arbejdet, hvorefter kontrol vil gå til procesbarnet. Ved genstart af programmet kan situationen være anderledes.

Opkaldet til wait()

Operating Systems: Three Easy Pieces. Part 3: Process API (oversættelse)

Lad os se på det næste program. I dette program venter procesforælderen altid på, at procesbarnet afslutter. I dette tilfælde får vi en strengt defineret tekstoutput til skærmen. wait() Opkaldet til exec()

Operating Systems: Three Easy Pieces. Part 3: Process API (oversættelse)

Lad os se på opkaldet til

Operating Systems: Three Easy Pieces. Part 3: Process API (oversættelse)

. Dette systemopkald er nyttigt, når vi vil starte et helt andet program. Her vil vi kalde exec()execvp() execvp() for at køre programmet wc, som er et program til tælling af ord. Hvad sker der, når exec() kaldes? Denne funktion modtager navnet på den eksekverbare fil og nogle parametre som argumenter. Herefter indlæses koden og de statiske data fra denne eksekverbare fil, og segmentet med koden overskrives. De øvrige hukommelsesområder, såsom stakken og bunken, geninitialiseres. Derefter udfører OS blot programmet og overfører et sæt argumenter til det. På denne måde oprettede vi ikke en ny proces; vi transformerede blot det nuværende kørende program til et andet kørende program. Efter eksekveringen af exec() har efterkommeren indtrykket af, at det oprindelige program i det hele taget ikke blev startet.

Denne komplicering af opstarten er helt normal for Unix-shell, og tillader denne shell at udføre kode efter kaldet fork(), men før kaldet exec(). Et eksempel på sådan kode kan være at tilpasse shell-miljøet til behovene for det program, der skal startes, inden det faktisk starter.

Shell — er blot et brugerprogram. Det viser dig en kommandoprompt og venter, indtil du skriver noget ind. I de fleste tilfælde, hvis du skriver navnet på et program der, vil shell finde dens placering, kalde metoden fork(), og derefter for at oprette en ny proces kalde en af typerne af exec() og vente på dens udførelse via kaldet wait(). Når efterkommerprocessen afsluttes, returnerer shell fra wait() kaldet og viser igen kommandoprompten og venter på næste kommando.

Opdelingen af fork() & exec() tillader shell at gøre følgende ting, for eksempel:
wc file > new_file.

I dette eksempel er output fra programmet wc videresendt til filen. Måden, hvorpå shell opnår dette, er ret enkel — når den skaber efterfølgerprocessen før kaldet exec(), lukker shell standard outputstrømmen og åbner filen new_file, således at al output fra det efterfølgende kørte program wc vil blive videresendt til filen i stedet for skærmen.

Unix rør er implementeret på lignende måde, med den undtagelse at de bruger kaldet pipe(). I dette tilfælde vil outputstrømmen fra processen blive tilsluttet en rørkø i kernen, som også vil blive tilsluttet inputstrømmen fra en anden proces.

Kilde: habr.com

Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster