Operacijski sistemi: Tri enostavne dele. Del 3: Procesna API (prevod)

Uvod v operacijske sisteme

Pozdravljeni, Habr! Želim vam predstaviti serijo prevodnih člankov, ki se mi zdijo zanimivi — OSTEP. V tem gradivu se poglobljeno obravnava delovanje unix-podobnih operacijskih sistemov, in sicer obravnava procesov, različnih pojedinškov, pomnilnika in ostalih podobnih komponent, ki sestavljajo sodobni OS. Izvirnik vseh gradiv si lahko ogledate tukaj. tukaj. Prosimo, upoštevajte, da je prevod izveden nepoklicno (dokaj prosto), a upam, da sem ohranil splošen smisel.

Laboratorijske vaje za to temo lahko najdete tukaj:

Druge dele:

Lahko tudi obiščete moj kanal v telegram =)

Alarm! there is a lab for this lecture! see github

Process API

Let's consider an example of process creation in a UNIX system. It occurs through two system calls. fork() in exec().

The fork() call

Operacijski sistemi: Tri enostavne dele. Del 3: Procesna API (prevod)

Let's look at a program that executes a fork() call. The result of its execution will be as follows.

Operacijski sistemi: Tri enostavne dele. Del 3: Procesna API (prevod)

First, we enter the main() function and output a string to the screen. The string contains the process identifier which is originally called PID or process identifier. This identifier is used in UNIX to refer to a process. The next command will be a fork() call. At this moment, a nearly exact copy of the process is created. For the OS, it appears as though there are two copies of the same program running, which will both exit the execution of the fork() function. The newly created child process (in relation to its parent process) will not continue execution starting from the main() function. It should be noted that the child process is not an exact copy of the parent process; in particular, it has its own address space, its own registers, its own instruction pointer, and so on. Therefore, the value returned to the caller of the fork() function will be different. In particular, the parent process will receive the PID of the child process as the return value, while the child will receive a value of 0. Based on these return codes, the processes can be differentiated later, allowing each to perform its own work. However, the execution of this program is not strictly defined. After the separation into two processes, the OS starts monitoring them and planning their execution. In the case of execution on a single-core processor, one of the processes, here the parent, will continue to run, and then the child process will take control. During a subsequent run, the situation may be different.

The wait() call

Operacijski sistemi: Tri enostavne dele. Del 3: Procesna API (prevod)

Let's consider the following program. In this program, due to the presence of the call wait() the parent process will always wait for the child process to complete its work. In this case, we will receive a strictly defined output of text on the screen.

Operacijski sistemi: Tri enostavne dele. Del 3: Procesna API (prevod)

The exec() call

Operacijski sistemi: Tri enostavne dele. Del 3: Procesna API (prevod)

Let's look at the call exec(). This system call is useful when we want to launch a completely different program. Here, we will be calling execvp() Za zagon programa wc, ki je program za štetje besed. Kaj se zgodi, ko pokličemo exec()? Ta klic prejme ime izvajalne datoteke in nekatere parametre kot argumente. Nato se naložijo koda in statični podatki iz te izvajalne datoteke ter prepiše lastni segment s kodo. Ostali deli pomnilnika, kot sta sklad in kup, se ponovno inicializirajo. Nato OS preprosto izvede program in mu posreduje niz argumentov. Tako nismo ustvarili novega procesa, temveč smo preprosto preoblikovali trenutno zagnani program v drugega. Po izvedbi klica exec() ima v podedovancu občutek, da začetni program sploh ni bil zagnan.

Takšna zapletenost zagona je povsem normalna za Unix lupino in omogoča, da ta lupina izvršuje kodo po klicu fork(), toda pred klicem exec(). Primer take kode bi lahko bil prilagajanje okolja lupine glede na potrebe zaganjanega programa, pred njegovo neposredno izvedbo.

Shell — zgolj uporabniški program. Prikazuje vam vrstico ukazov in čaka, da vnesete nekaj. V večini primerov, če tja vpišete ime programa, bo lupina našla njegovo lokacijo, poklicala metodo fork(), nato pa, da ustvari nov proces, poklicala eno od vrst exec() in čakala na njegovo izvajanje s klicem wait(). Ko se podedovani proces zaključi, se lupina vrne iz klica wait() in ponovno izpiše vrstico ukazov ter čaka na vnos naslednji ukaz.

Ločevanje fork() in exec() omogoča lupini, da izvede naslednje stvari, na primer:
wc file > new_file.

V tem primeru je izhod programa wc preusmerjen v datoteko. Način, kako lupina to doseže, je precej preprost: ob ustvarjanju otroškega procesa pred klicem exec(), lupina zapre standardni izhodni tok in odpre datoteko new_file, tako da bo ves izhod iz pozneje zagnanega programa wc preusmerjen v datoteko namesto na zaslon.

Unijski cevovodi so implementirani na podoben način, z razliko, da uporabljajo klic pipe(). V tem primeru bo izhodni tok procesa povezan s cevjo v jedru, na katero bo povezan vstopni tok drugega procesa.

Izvor: habr.com

Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki 🔥 Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki | ProHoster