Käyttöjärjestelmät: Three Easy Pieces. Osa 3: Prosessin sovellusliittymä (käännös)

Johdatus käyttöjärjestelmiin

Hei Habr! Haluan tuoda huomionne artikkelisarjan käännöksiä yhdestä mielestäni mielenkiintoisesta kirjallisuudesta - OSTEP. Tämä materiaali käsittelee melko syvällisesti unix-tyyppisten käyttöjärjestelmien työtä, nimittäin työtä prosessien, erilaisten aikataulujen, muistin ja muiden vastaavien komponenttien kanssa, jotka muodostavat nykyaikaisen käyttöjärjestelmän. Kaikkien materiaalien alkuperäiset näet täältä täällä. Huomaa, että käännös on tehty epäammattimaisesti (melko vapaasti), mutta toivottavasti säilytin yleisen merkityksen.

Aiheen laboratoriotyöt löytyvät täältä:

Muut osat:

Voit myös katsoa kanavaani osoitteessa sähke =)

Hälytys! Tätä luentoa varten on laboratorio! Katso github

Prosessin API

Katsotaanpa esimerkkiä prosessin luomisesta UNIX-järjestelmässä. Se tapahtuu kahden järjestelmäkutsun kautta haarukka() и exec().

Call fork ()

Käyttöjärjestelmät: Three Easy Pieces. Osa 3: Prosessin sovellusliittymä (käännös)

Harkitse ohjelmaa, joka tekee fork()-kutsun. Sen suorittamisen tulos on seuraava.

Käyttöjärjestelmät: Three Easy Pieces. Osa 3: Prosessin sovellusliittymä (käännös)

Ensinnäkin syötetään main()-funktio ja tulostetaan merkkijono näytölle. Rivi sisältää prosessin tunnisteen, jota alkuperäisessä kutsutaan PID tai prosessin tunniste. Tätä tunnistetta käytetään UNIXissa viittaamaan prosessiin. Seuraava komento kutsuu fork(). Tässä vaiheessa prosessista luodaan melkein tarkka kopio. Käyttöjärjestelmässä näyttää siltä, ​​että järjestelmässä on käynnissä 2 kopiota samasta ohjelmasta, mikä puolestaan ​​​​poistuu fork()-funktiosta. Äskettäin luotua aliprosessia (sen luoneen pääprosessin suhteen) ei enää suoriteta main()-funktiosta alkaen. On syytä muistaa, että lapsiprosessi ei ole tarkka kopio pääprosessista, sillä sillä on erityisesti oma osoiteavaruutensa, omat rekisterinsä, oma osoitin suoritettaviin käskyihin ja vastaavaa. Siten fork()-funktion kutsujalle palautettava arvo on erilainen. Erityisesti emoprosessi saa palautuksena aliprosessin PID-arvon ja lapsi arvon, joka on yhtä suuri kuin 0. Näiden palautuskoodien avulla voit sitten erottaa prosessit ja pakottaa jokaisen tekemään oman työnsä. . Tämän ohjelman suorittamista ei kuitenkaan ole tarkasti määritelty. Kahteen prosessiin jakamisen jälkeen käyttöjärjestelmä alkaa seurata niitä ja suunnitella heidän työtään. Jos suoritetaan yksiytiminen prosessori, yksi prosesseista, tässä tapauksessa pääprosesseista, jatkaa toimintaansa, ja sitten aliprosessi saa ohjauksen. Uudelleenkäynnistettäessä tilanne voi olla toinen.

Soita odota ()

Käyttöjärjestelmät: Three Easy Pieces. Osa 3: Prosessin sovellusliittymä (käännös)

Harkitse seuraavaa ohjelmaa. Tässä ohjelmassa puhelun läsnäolon vuoksi odota() Pääprosessi odottaa aina, että alatason prosessi on valmis. Tässä tapauksessa saamme näytölle tiukasti määritellyn tekstitulosteen

Käyttöjärjestelmät: Three Easy Pieces. Osa 3: Prosessin sovellusliittymä (käännös)

exec()-kutsu

Käyttöjärjestelmät: Three Easy Pieces. Osa 3: Prosessin sovellusliittymä (käännös)

Harkitse haastetta exec(). Tämä järjestelmäkutsu on hyödyllinen, kun haluamme ajaa täysin erilaista ohjelmaa. Täällä soitetaan execvp() wc-ohjelman suorittamiseen, joka on sananlaskentaohjelma. Mitä tapahtuu, kun exec():tä kutsutaan? Tämä kutsu välitetään argumentteina suoritettavan tiedoston nimi ja jotkin parametrit. Tämän jälkeen koodi ja staattiset tiedot tästä suoritettavasta tiedostosta ladataan ja sen oma segmentti koodilla korvataan. Loput muistialueet, kuten pino ja pino, alustetaan uudelleen. Tämän jälkeen käyttöjärjestelmä yksinkertaisesti suorittaa ohjelman ja välittää sille joukon argumentteja. Emme siis luoneet uutta prosessia, vaan muutimme vain käynnissä olevan ohjelman toiseksi käynnissä olevaksi ohjelmaksi. Kun exec()-kutsu on suoritettu jälkeläisessä, näyttää siltä, ​​​​että alkuperäinen ohjelma ei toiminut ollenkaan.

Tämä käynnistyksen komplikaatio on täysin normaalia Unix-kuorelle ja sallii sen suorittaa koodin kutsun jälkeen haarukka(), mutta ennen puhelua exec(). Esimerkki tällaisesta koodista olisi shell-ympäristön säätäminen käynnistettävän ohjelman tarpeisiin ennen sen käynnistämistä.

Kuori - pelkkä käyttäjäohjelma. Hän näyttää sinulle kutsurivin ja odottaa, että kirjoitat siihen jotain. Useimmissa tapauksissa, jos kirjoitat siihen ohjelman nimen, komentotulkki löytää sen sijainnin, kutsuu fork()-metodia ja kutsuu sitten jonkin tyyppistä exec()-komentoa luodakseen uuden prosessin ja odottaa sen valmistumista. odota() soita. Kun lapsiprosessi poistuu, komentotulkki palaa wait()-kutsusta ja tulostaa kehotteen uudelleen ja odottaa seuraavan komennon antamista.

Fork() & exec() -jako mahdollistaa esimerkiksi seuraavat asiat:
wc-tiedosto > uusi_tiedosto.

Tässä esimerkissä wc-ohjelman tuloste ohjataan tiedostoon. Tapa, jolla kuori saavuttaa tämän, on melko yksinkertainen - luomalla lapsiprosessi ennen kutsumista exec(), kuori sulkee vakiotulosteen ja avaa tiedoston uusi_tiedosto, siis kaikki jatkossa käynnissä olevan ohjelman tulos wc uudelleenohjataan tiedostoon näytön sijaan.

Unix putki toteutetaan samalla tavalla, sillä erolla, että ne käyttävät pipe()-kutsua. Tässä tapauksessa prosessin lähtövirta yhdistetään ytimessä sijaitsevaan putkijonoon, johon toisen prosessin tulovirta kytketään.

Lähde: will.com

Lisää kommentti