Operating Systems: Three Easy Pieces. Part 3: Process API (пераклад)

Увядзенне ў аперацыйныя сістэмы

Прывітанне, Хабр! Жадаю прадставіць вашай увазе серыю артыкулаў-перакладаў адной цікавай на мой погляд літаратуры - OSTEP. У гэтым матэрыяле разглядаецца досыць глыбока праца unix-падобных аперацыйных сістэм, а менавіта - праца з працэсамі, рознымі планавальнікамі, памяццю і іншымі падобнымі кампанентамі, якія складаюць сучасную АС. Арыгінал усіх матэрыялаў вы можаце паглядзець вось тут. Прашу ўлічыць, што пераклад выкананы непрафесійна (дастаткова вольна), але спадзяюся агульны сэнс я захаваў.

Лабараторныя працы па дадзеным прадмеце можна знайсці вось тут:

Іншыя часткі:

А яшчэ можаце зазіраць да мяне на канал у тэлеграм =)

Алярм! да гэтай лекцыі ёсць лаба! глядзі гітхаб

Process API

Разгледзім прыклад стварэння працэсу ў UNIX сістэме. Ён адбываецца праз два сістэмныя выклікі відэлец () и выкананне().

Выклік fork()

Operating Systems: Three Easy Pieces. Part 3: Process API (пераклад)

Разгледзім праграму, якая выконвае выклік fork(). Вынік яе выканання будзе наступны.

Operating Systems: Three Easy Pieces. Part 3: Process API (пераклад)

У першую чаргу мы ўваходзім у функцыю main() і выконваем вывад радка на экран. Радок змяшчае ідэнтыфікатар працэсу які ў арыгінале называецца PID ці process identifier. Гэты ідэнтыфікатар ужываецца ў UNIX для таго каб звяртацца да працэсу. Наступнай камандай будзе выкананы выклік fork(). У гэты момант ствараецца практычна дакладная копія працэсу. Для АС гэта выглядае так, што ў сістэме запушчаныя як быццам бы 2 копіі адной і той жа праграмы, якія ў сваю чаргу выйдуць з выканання функцыі fork(). Ізноў створаныя працэс-нашчадак (у адносінах да які стварыў яго працэсу-бацьку) ужо не будзе выконвацца, пачынальна з функцыі main(). Варта памятаць, што працэс-нашчадак не з'яўляецца дакладнай копіяй працэсу-бацькі, у прыватнасці ў яго ёсць уласная адрасная прастора, уласныя рэгістры, свой паказальнік на выкананыя інструкцыі і таму падобнае. Такім чынам, значэнне, якое вяртаецца выклікальніку функцыі fork() будзе розным. У прыватнасці, працэс-бацька атрымае ў якасці звароту значэнне PID працэсу дзіцяці, а дзіця атрымае значэнне роўнае 0. Па гэтых кодах звароту ў наступным ужо можна падзяляць працэсы і прымушаць кожны з іх выконваць сваю працу. Пры гэтым выкананне гэтай праграмы не вызначана строга. Пасля падзелу на 2 працэсы АС пачынае сачыць за імі, гэтак жа, і планаваць іх працу. У выпадку выканання на аднаядзерным працэсары, працу працягне адзін з працэсаў, у дадзеным выпадку - бацькоўскі, а затым кіраванне атрымае працэс-нашчадак. Пры паўторным запуску сітуацыя можа скласціся інакш.

Выклік wait()

Operating Systems: Three Easy Pieces. Part 3: Process API (пераклад)

Разгледзім наступную праграму. У гэтай праграме за кошт наяўнасці выкліку пачакайце() працэс-бацька заўсёды будзе чакаць завяршэння працы працэсу-нашчадка. У гэтым выпадку мы атрымаем строга вызначаную выснову тэксту на экран.

Operating Systems: Three Easy Pieces. Part 3: Process API (пераклад)

Выклік exec()

Operating Systems: Three Easy Pieces. Part 3: Process API (пераклад)

Разгледзім выклік выкананне(). Гэты сістэмны выклік карысны, калі мы жадаем запусціць зусім іншую праграму. Тут мы будзем выклікаць execvp() для запуску праграмы wc, якая з'яўляецца праграмай падліку слоў. Што ж адбываецца пры выкліку exec()? Гэтаму выкліку перадаюцца ў якасці аргументаў імя выкананага файла і некаторыя параметры. Пасля чаго адбываецца загрузка кода і статычных дадзеных з гэтага выкананага файла і перазаціранне ўласнага сегмента з кодам. Астатнія ўчасткі памяці, такія як стэк і куча пераініцыялізуюцца. Пасля чаго АС проста выконвае праграму, перадаючы ёй набор аргументаў. Такім чынам, мы не стваралі новы працэс, мы проста трансфармавалі бягучую запушчаную праграму ў іншую запушчаную праграму. Пасля выканання выкліку exec() у нашчадку надыходзіць уражанне, што першапачатковая праграма як быццам у прынцыпе не запускалася.

Такое ўскладненне запуску абсалютна нармальна для shell абалонкі Unix, і дазваляе гэтай абалонцы выконваць код пасля выкліку відэлец (), але да выкліку выкананне(). Прыкладам такога кода можа быць падладка акружэння абалонкі пад патрэбы якая запускаецца праграмы, перад яе непасрэдным запускам.

Абалонка - усяго толькі карыстацкая праграма. Яна паказвае вам радок запрашэння і чакае, пакуль вы ў яго што-небудзь напішаце. У большасці выпадкаў калі напісаць туды імя праграмы, shell знойдзе яго месцазнаходжанне, выкліча метад fork(), а затым каб стварыць новы працэс выкліча які-небудзь з тыпаў exec() і дачакаецца яго выкананні з дапамогай выкліку wait(). Калі працэс-нашчадак завершыцца shell вернецца з выкліку wait() і выведзе зноў радок запрашэння і будзе чакаць уводу наступнай каманды.

Падзел fork() & exec() дазваляе shell рабіць наступныя рэчы, напрыклад:
wc file > new_file.

У гэтым прыкладзе выснова праграмы wc перанакіраваны ў файл. Спосаб, якім shell дасягае гэтага досыць просты пры стварэнні працэсу-дзіцяці перад выклікам выкананне(), shell закрывае стандартны паток вываду і адкрывае файл new_file, такім чынам, уся выснова з далей запушчанай праграмы wc будзе перанакіраваны ў файл замест экрана.

Unix pipe рэалізаваны падобнай выявай, з розніцай, што яны выкарыстаюць выклік pipe(). У гэтым выпадку струмень высновы працэсу будзе падлучаны да чаргі pipe, размешчанай у ядры да якой жа будзе далучаны струмень уводу іншага працэсу.

Крыніца: habr.com

Дадаць каментар