Sistemet Operative: Tre Pjesë të Lehta. Pjesa 3: Process API (përkthim)

Hyrje në Sistemet Operative

Hej Habr! Do të doja të sjell në vëmendjen tuaj një seri artikujsh-përkthimesh të një letërsie interesante për mendimin tim - OSTEP. Ky material diskuton mjaft thellë punën e sistemeve operative të ngjashme me unix, domethënë punën me proceset, planifikuesit e ndryshëm, memorien dhe komponentët e tjerë të ngjashëm që përbëjnë një OS modern. Këtu mund të shihni origjinalin e të gjitha materialeve këtu. Ju lutem vini re se përkthimi është bërë në mënyrë joprofesionale (mjaft lirisht), por shpresoj se kam ruajtur kuptimin e përgjithshëm.

Punimet laboratorike mbi këtë temë mund të gjenden këtu:

Pjesë të tjera:

Ju gjithashtu mund të shikoni kanalin tim në telegram =)

Alarm! Ka një laborator për këtë leksion! Shikoni github

API-ja e procesit

Le të shohim një shembull të krijimit të një procesi në një sistem UNIX. Kjo ndodh përmes dy thirrjeve të sistemit pirun () и exec ().

Call fork()

Sistemet Operative: Tre Pjesë të Lehta. Pjesa 3: Process API (përkthim)

Konsideroni një program që bën një thirrje fork(). Rezultati i ekzekutimit të tij do të jetë si më poshtë.

Sistemet Operative: Tre Pjesë të Lehta. Pjesa 3: Process API (përkthim)

Para së gjithash, ne futim funksionin main() dhe shtypim vargun në ekran. Rreshti përmban identifikuesin e procesit i cili në origjinal quhet PID ose identifikuesin e procesit. Ky identifikues përdoret në UNIX për t'iu referuar një procesi. Komanda tjetër do të thërrasë fork(). Në këtë pikë, krijohet një kopje pothuajse e saktë e procesit. Për sistemin operativ, duket se ka 2 kopje të të njëjtit program që funksionon në sistem, i cili nga ana tjetër do të dalë nga funksioni fork(). Procesi i sapo krijuar fëmijë (në lidhje me procesin prind që e krijoi) nuk do të ekzekutohet më, duke filluar nga funksioni main(). Duhet mbajtur mend se një proces fëmijëror nuk është një kopje e saktë e procesit prind; në veçanti, ai ka hapësirën e vet të adresave, regjistrat e vet, treguesin e tij drejt udhëzimeve të ekzekutueshme dhe të ngjashme. Kështu, vlera që i kthehet thirrësit të funksionit fork() do të jetë e ndryshme. Në veçanti, procesi prind do të marrë vlerën PID të procesit të fëmijës si një kthim dhe fëmija do të marrë një vlerë të barabartë me 0. Duke përdorur këto kode kthimi, më pas mund të ndani proceset dhe të detyroni secilin prej tyre të bëjë punën e vet . Megjithatë, ekzekutimi i këtij programi nuk është i përcaktuar në mënyrë strikte. Pas ndarjes në 2 procese, OS fillon t'i monitorojë ato, si dhe të planifikojë punën e tyre. Nëse ekzekutohet në një procesor me një bërthamë, një nga proceset, në këtë rast prindi, do të vazhdojë të punojë dhe më pas procesi fëmijë do të marrë kontrollin. Kur rifilloni, situata mund të jetë e ndryshme.

Pritja e thirrjes ()

Sistemet Operative: Tre Pjesë të Lehta. Pjesa 3: Process API (përkthim)

Konsideroni programin e mëposhtëm. Në këtë program, për shkak të pranisë së një telefonate prisni () Procesi prind do të presë gjithmonë që procesi i fëmijës të përfundojë. Në këtë rast, ne do të marrim një dalje teksti të përcaktuar rreptësisht në ekran

Sistemet Operative: Tre Pjesë të Lehta. Pjesa 3: Process API (përkthim)

thirrje exec().

Sistemet Operative: Tre Pjesë të Lehta. Pjesa 3: Process API (përkthim)

Merrni parasysh sfidën exec (). Kjo thirrje sistemi është e dobishme kur duam të ekzekutojmë një program krejtësisht të ndryshëm. Këtu do të thërrasim execvp () për të ekzekutuar programin wc i cili është një program për numërimin e fjalëve. Çfarë ndodh kur thirret exec()? Kësaj thirrjeje i kalohet emri i skedarit të ekzekutueshëm dhe disa parametra si argumente. Pas së cilës kodi dhe të dhënat statike nga ky skedar i ekzekutueshëm ngarkohen dhe segmenti i tij me kodin mbishkruhet. Zonat e mbetura të kujtesës, të tilla si grumbulli dhe grumbulli, rifillohen. Pas së cilës OS thjesht ekzekuton programin, duke i kaluar atij një grup argumentesh. Pra, ne nuk krijuam një proces të ri, ne thjesht e transformuam programin aktual në ekzekutim në një program tjetër që ekzekutohet. Pas ekzekutimit të thirrjes exec() në pasardhësin, duket sikur programi origjinal nuk ka funksionuar fare.

Ky ndërlikim i fillimit është krejtësisht normal për një guaskë Unix dhe e lejon atë shell të ekzekutojë kodin pas thirrjes pirun (), por para thirrjes exec (). Një shembull i një kodi të tillë do të ishte përshtatja e mjedisit të guaskës me nevojat e programit që po lansohet, përpara se ta nisni atë.

Predhë - vetëm një program përdoruesi. Ajo ju tregon linjën e ftesës dhe pret që ju të shkruani diçka në të. Në shumicën e rasteve, nëse shkruani emrin e një programi atje, guaska do të gjejë vendndodhjen e saj, do të thërrasë metodën fork() dhe më pas do të thërrasë një lloj exec() për të krijuar një proces të ri dhe do të presë që ai të përfundojë duke përdorur një prisni() telefononi. Kur procesi i fëmijës del, shell do të kthehet nga thirrja e pritjes () dhe do të printojë përsëri promptin dhe do të presë që komanda tjetër të futet.

Ndarja fork() & exec() lejon shell të bëjë gjërat e mëposhtme, për shembull:
skedar wc > skedari_i ri.

Në këtë shembull, dalja e programit wc ridrejtohet në një skedar. Mënyra se si guaska e arrin këtë është mjaft e thjeshtë - duke krijuar një proces fëmijësh përpara se të telefononi exec (), guaska mbyll daljen standarde dhe hap skedarin skedar_i ri, pra, i gjithë prodhimi nga programi i mëtejshëm i ekzekutimit wc do të ridrejtohet në një skedar në vend të një ekrani.

Tub Unix zbatohen në mënyrë të ngjashme, me ndryshimin që përdorin një thirrje pipe(). Në këtë rast, rrjedha e daljes së procesit do të lidhet me një radhë tubacioni të vendosur në kernel, me të cilin do të lidhet rrjedha hyrëse e një procesi tjetër.

Burimi: www.habr.com

Shto një koment