Operacijski sistemi: trije preprosti deli. 3. del: Procesni API (prevod)

Uvod v operacijske sisteme

Hej Habr! Rad bi vam predstavil serijo člankov-prevodov ene po mojem mnenju zanimive literature - OSTEP. To gradivo precej poglobljeno obravnava delo operacijskih sistemov, podobnih unixu, in sicer delo s procesi, različnimi načrtovalci, pomnilnikom in drugimi podobnimi komponentami, ki sestavljajo sodoben OS. Original vseh materialov si lahko ogledate tukaj tukaj. Upoštevajte, da je prevod narejen neprofesionalno (precej svobodno), vendar upam, da sem ohranil splošni pomen.

Laboratorijske naloge na to temo najdete tukaj:

Drugi deli:

Ogledate si lahko tudi moj kanal na telegram =)

Alarm! Za to predavanje obstaja laboratorij! Poglej github

Proces API

Oglejmo si primer ustvarjanja procesa v sistemu UNIX. To se zgodi prek dveh sistemskih klicev vilice () и exec().

Call fork()

Operacijski sistemi: trije preprosti deli. 3. del: Procesni API (prevod)

Razmislite o programu, ki izvede klic fork(). Rezultat njegove izvedbe bo naslednji.

Operacijski sistemi: trije preprosti deli. 3. del: Procesni API (prevod)

Najprej vnesemo funkcijo main() in izpišemo niz na zaslon. Vrstica vsebuje identifikator procesa, ki se v izvirniku imenuje PID ali identifikator procesa. Ta identifikator se v sistemu UNIX uporablja za sklicevanje na proces. Naslednji ukaz bo poklical fork(). Na tej točki se ustvari skoraj natančna kopija procesa. Za operacijski sistem je videti, kot da se v sistemu izvajata 2 kopiji istega programa, ki bosta zapustila funkcijo fork(). Novo ustvarjeni podrejeni proces (glede na nadrejeni proces, ki ga je ustvaril) ne bo več izveden, začenši s funkcijo main(). Ne smemo pozabiti, da podrejeni proces ni natančna kopija nadrejenega procesa, zlasti ima svoj naslovni prostor, lastne registre, svoj kazalec na izvršljiva navodila in podobno. Tako bo vrednost, vrnjena klicatelju funkcije fork(), drugačna. Zlasti nadrejeni proces bo prejel vrednost PID podrejenega procesa kot povratno vrednost, podrejeni proces pa bo prejel vrednost enako 0. Z uporabo teh povratnih kod lahko nato ločite procese in prisilite vsakega od njih, da opravi svoje delo . Vendar pa izvajanje tega programa ni strogo definirano. Po razdelitvi na 2 procesa jih OS začne spremljati in načrtovati njihovo delo. Če se izvaja na enojedrnem procesorju, bo eden od procesov, v tem primeru nadrejeni, nadaljeval z delom, nato pa bo podrejeni proces prejel nadzor. Pri ponovnem zagonu je lahko situacija drugačna.

Čakaj na klic()

Operacijski sistemi: trije preprosti deli. 3. del: Procesni API (prevod)

Razmislite o naslednjem programu. V tem programu zaradi prisotnosti klica počakaj () Nadrejeni proces bo vedno čakal na dokončanje podrejenega procesa. V tem primeru bomo na zaslonu dobili strogo definiran izpis besedila

Operacijski sistemi: trije preprosti deli. 3. del: Procesni API (prevod)

klic exec().

Operacijski sistemi: trije preprosti deli. 3. del: Procesni API (prevod)

Razmislite o izzivu exec(). Ta sistemski klic je uporaben, ko želimo pognati popolnoma drugačen program. Tukaj bomo poklicali execvp() za zagon programa wc, ki je program za štetje besed. Kaj se zgodi, ko se pokliče exec()? Temu klicu se posreduje ime izvršljive datoteke in nekateri parametri kot argumenti. Po tem se koda in statični podatki iz te izvedljive datoteke naložijo, njen lastni segment s kodo pa se prepiše. Preostala področja pomnilnika, kot sta sklad in kopica, se znova inicializirajo. Po tem OS preprosto izvede program in mu posreduje nabor argumentov. Torej nismo ustvarili novega procesa, preprosto smo preoblikovali trenutno delujoč program v drug delujoč program. Po izvedbi klica exec() v potomcu se zdi, kot da se izvirni program sploh ne izvaja.

Ta zagonski zaplet je povsem običajen za lupino Unix in tej lupini omogoča izvajanje kode po klicu vilice (), vendar pred klicem exec(). Primer takšne kode bi bila prilagoditev okolja lupine potrebam programa, ki se zažene, preden se zažene.

Shell - samo uporabniški program. Pokaže ti vrsto vabila in počaka, da vanjo nekaj napišeš. V večini primerov, če tam zapišete ime programa, bo lupina našla njegovo lokacijo, poklicala metodo fork() in nato poklicala neko vrsto exec(), da ustvari nov proces in počaka, da se zaključi z uporabo čakaj() klic. Ko se podrejeni proces zapre, se ukazna lupina vrne iz klica wait(), znova natisne poziv in počaka na vnos naslednjega ukaza.

Razdelitev fork() & exec() omogoča lupini, da naredi naslednje stvari, na primer:
wc datoteka > nova_datoteka.

V tem primeru je izhod programa wc preusmerjen v datoteko. Način, kako lupina to doseže, je precej preprost – z ustvarjanjem podrejenega procesa pred klicem exec(), lupina zapre standardni izhod in odpre datoteko nova_datoteka, torej ves izhod iz nadaljnjega programa, ki se izvaja wc bo preusmerjen v datoteko namesto na zaslon.

Unix cev so implementirani na podoben način, s to razliko, da uporabljajo klic pipe(). V tem primeru bo izhodni tok procesa povezan s cevno čakalno vrsto, ki se nahaja v jedru, na katero bo povezan vhodni tok drugega procesa.

Vir: www.habr.com

Dodaj komentar