Hyrja në sistemet operative
Përshëndetje, Habr! Dua të paraqes para jush një seri artikujsh me përkthime nga një letërsi interesante sipas mendimit tim - OSTEP. Ky material shqyrton mjaft thellë funksionimin e sistemeve operativë të ngjashme me UNIX, domethënë - punën me proceset, planifikuesit e ndryshëm, memorien dhe komponentë të tjerë të ngjashëm që përbëjnë një sistem operativ modern. Origjinali i të gjithë materialeve mund ta shihni këtu . Ju lutem, merrni parasysh se përkthimi është realizuar në mënyrë jo profesionale (mjaft lirshëm), por shpresoj se kuptimi i përgjithshëm e kam ruajtur.
Për laboratorët në këtë lëndë mund të gjeni këtu:
Pjesët e tjera:
Dhe gjithashtu mund të shikoni kanalin tim në =)
Alarm! ka një laboratorium për këtë ligjëratë! shiko
API-i i Procesit
Le të shqyrtojmë një shembull të krijimit të një procesi në sistemin UNIX. Ai ndodh përmes dy thirrjeve sistemike fork() dhe exec().
Thirrja fork()

Le të shqyrtojmë programin që kryen thirrjen fork(). Rezultati i ekzekutimit të tij do të jetë si vijon.

Në radhë të parë ne hyjmë në funksionin main() dhe kryejmë daljen e një vargu në ekran. Vargu përmban identifikuesin e procesit që në origjinal quhet PID ose identifikuesi i procesit. Ky identifikues përdoret në UNIX për t'u referuar një procesi. Komanda e ardhshme do të ekzekutojë thirrjen fork(). Në këtë moment krijohet një kopje pothuajse e saktë e procesit. Për sistemin operativ, duket se janë nisur si të ishin 2 kopje të të njëjtës programi, të cilat përfundimisht do të dalin nga funksioni fork(). Procesi i ri i krijuar (në lidhje me procesin prind) nuk do të ekzekutojë më, duke filluar nga funksioni main(). Duhet mbajtur mend se procesi i ri nuk është një kopje e saktë e procesit prind, veçanërisht pasi ai ka hapësirën e tij të adresave, regjistrat e tij, treguesin për instruktionet e ekzekutueshme dhe gjëra të ngjashme. Kështu, vlera e kthyer nga thirrësja e funksionit fork() do të jetë e ndryshme. Saktësisht, procesi prind do të marrë si kthim vlerën PID të procesit fëmijë, ndërsa fëmija do të marrë një vlerë të barabartë me 0. Me këto kode të kthimit më pas është e mundur të ndahen proceset dhe të bëjnë që secili prej tyre të kryejë punën e vet. Në këtë rast, ekzekutimi i këtij programi nuk është i përcaktuar me saktësi. Pas ndarjes në 2 procese, sistemi operativ fillon t'i ndjekë ato, ashtu si dhe t'i planifikojë aktivitetet e tyre. Në rastin e ekzekutimit në një procesor me një bërthamë, puna do të vazhdojë me një nga proceset, në këtë rast - prindin, dhe më pas kontrolli do të kalojë te procesi fëmijë. Në një përsëritje të mëtejshme, situata mund të zhvillohet ndryshe.
Thirrja wait()

Le të shqyrtojmë programin e mëposhtëm. Në këtë program, falë thirrjes wait() procesi prind gjithmonë do të presë për përfundimin e punës së procesit fëmijë. Në këtë rast, ne do të marrim një dalje të përcaktuar me saktësi në ekran

Thirrja exec()

Le tĂ« shqyrtojmĂ« thirrjen exec(). Ky thirrje sistemike Ă«shtĂ« e dobishme kur duam tĂ« nisemi njĂ« program krejtĂ«sisht tjetĂ«r. KĂ«tu do tĂ« thĂ«rrasim execvp() pĂ«r tĂ« nisur programin wc, i cili Ă«shtĂ« njĂ« program pĂ«r numĂ«rimin e fjalĂ«ve. ĂfarĂ« ndodh kur thirret exec()? KĂ«saj thirrjeje i jepet si argument emri i skedarit ekzekutiv dhe disa parametra. Pas kĂ«saj ngarkohet kodi dhe tĂ« dhĂ«nat statike nga ky skedar ekzekutiv dhe mbi-shkruhet seksioni i vet me kod. PjesĂ«t e tjera tĂ« memories, si steka dhe kupa, inicializohen pĂ«rsĂ«ri. Pas kĂ«saj, OS thjesht ekzekuton programin, duke i dhĂ«nĂ« njĂ« grup argumentesh. KĂ«shtu, ne nuk kemi krijuar njĂ« proces tĂ« ri, thjesht kemi transformuar programin aktual po funksionon nĂ« njĂ« program tjetĂ«r qĂ« po funksionon. Pas thirrjes exec() nĂ« fĂ«mijĂ« krijohet pĂ«rshtypja se programi fillestar nuk Ă«shtĂ« nisur fare.
Ky komplimin i nisi është krejtësisht normal për shell-in Unix dhe i lejon atij të ekzekutojë kod pas thirrjes fork(), por para thirrjes exec(). Një shembull i tillë kodi mund të jetë përshtatja e mjedisit të shell-it për nevojat e programit që do të nisë, para nisjes së tij të drejtpërdrejtë.
Shell â thjesht Ă«shtĂ« njĂ« program pĂ«rdoruesi. Ai tregon rreshtin e promovimit dhe pret derisa tĂ« shkruani diçka atje. NĂ« shumicĂ«n e rasteve, nĂ«se shkruani emrin e programit atje, shell do ta gjejĂ« vendndodhjen e tij, do tĂ« thĂ«rrasĂ« metodĂ«n fork(), dhe pastaj pĂ«r tĂ« krijuar njĂ« proces tĂ« ri do tĂ« thĂ«rrasĂ« ndonjĂ« nga llojet e exec() dhe do tĂ« presĂ« pĂ«r ekzekutimin e tij me thirrjen wait(). Kur procesi fĂ«mijĂ« pĂ«rfundon, shell do tĂ« kthehet nga thirrja wait() dhe do tĂ« tregojĂ« pĂ«rsĂ«ri rreshtin e promovimit dhe do tĂ« presĂ« pĂ«r futjen e komandĂ«s tjetĂ«r.
Ndarja e fork() & exec() i lejon shell-it të bëjë gjëra si:
wc file > new_file.
NĂ« kĂ«tĂ« shembull, dalja e programit wc Ă«shtĂ« ridrejtuar nĂ« skedarin. MĂ«nyra se si shell e arrin kĂ«tĂ« Ă«shtĂ« mjaft e thjeshtĂ« â kur krijon procesin e fĂ«mijĂ«s para thirrjes exec(), shell mbyll rrjedhĂ«n standarde tĂ« daljes dhe hap skedarin new_file, kĂ«shtu qĂ« tĂ« gjitha daljet nga programi qĂ« do tĂ« ekzekutohet mĂ« vonĂ« wc do tĂ« ridrejtohen nĂ« skedar nĂ« vend tĂ« ekranit.
Unix pipe janë zbatuar në mënyrë të ngjashme, me ndryshimin se ato përdorin thirrjen pipe(). Në këtë rast, rrjedha e daljes së procesit do të lidhet me një radhë pipe, e cila ndodhet në bërthamë, me rrjedhën e hyrjes së një procesi tjetër.
Burimi: habr.com
