Въведение в операционните системи
Здравейте, Хабр! Искам да ви представя серия от статии-преводи на интересна, според мен, литература — OSTEP. В този материал се разглежда дълбоко работата на Unix-подобни операционни системи, а именно — работа с процеси, различни планиратели, памет и други подобни компоненти, които съставят съвременната ОС. Оригиналът на всички материали можете да видите тук . Моля, имайте предвид, че преводът е направен непрофесионално (достатъчно свободно), но се надявам, че общият смисъл е запазен.
Лабораторните работи по този предмет можете да намерите тук:
- оригинал:
- оригинал:
- моя лична адаптация:
Други части:
И можете да посещавате канала ми в =)
Нека разгледаме най-фундаменталната абстракция, която ОС предоставя на потребителите: процес. Определението за процес е доста просто – това е работеща програма. Самата програма е безжизнен обект, който се намира на диска – това е набор от инструкции и възможно някакви статични данни, очакващи момента на стартиране. Именно ОС взема тези байтове и ги стартира, преобразувайки програмата в нещо полезно.
Най-често потребителите искат да стартират повече от една програма едновременно, например можете да стартирате вашия лаптоп браузър, игра, медия плеър, текстов редактор и подобни. Всъщност типичната система може да стартира десетки и стотици процеси едновременно. Този факт прави системата по-лесна за използване, никога не се налага да се притеснявате дали CPU е свободен, просто стартирате програмите.
От тук произтича проблемът: как да се осигури илюзията за множество CPU? Как ОС може да създаде илюзията за практически безкраен брой CPU, дори ако имате само един физически CPU?
ОС създава тази илюзия чрез виртуализация на CPU. Стартивайки един процес, след това го спирайки, стартирайки друг процес и така нататък, ОС може да поддържа илюзията, че съществуват множество виртуални CPU, въпреки че всъщност ще бъдат един или няколко физически процесора. Т техника се нарича разпределение на ресурсите на CPU по време. Тази техника позволява на потребителите да стартират толкова много паралелни процеси, колкото желаят. Цената на това решение е производителността – тъй като ако CPU се делят между няколко процеса, всеки процес ще бъде обработван по-бавно.
За осъществяване на виртуализацията на CPU, а особено за да го направи добре, ОС се нуждае както от нискоуровнева, така и от високоуровнева поддръжка. Ниското ниво на поддръжка се нарича механизми — това са нискоуровневите методи или протоколи, които реализират необходимата част от функционалността. Пример за такава функционалност е контекстното превключване, което дава възможност на ОС да спре едно приложение и да стартира на процесора друго приложение. Такова времево разделение е реализирано в всички съвременни ОС.
На върха на тези механизми се намира определена логика, заложена в ОС, под формата на "политики". Политика — това е определен алгоритъм за вземане на решения от операционната система. Такива политики, например, решават коя програма да се стартира (от списъка с команди) на първо място. Например, тази задача ще реши политика, наречена планировчик (scheduling policy) и при избора на решение ще се ръководи от данни като: история на стартиране (коя програма е била стартирана най-дълго за последната минута), какво натоварване извършва този процес (какви типове програми са били стартирани), метрики за производителността (оптимизирана ли е системата за интерактивно взаимодействие или за пропускна способност) и така нататък.
Абстракция: процес
Абстракцията на работещата програма, изпълнявана от операционната система, е това, което наричаме процес. Както беше споменато по-рано, процесът е просто работеща програма в определен момент от времето. Програмата, с помощта на която можем да получим обобщена информация от различни ресурси на системата, и до които се обръща или които тази програма засяга в процеса на изпълнението си.
За да разберем компонентите на процеса, е нужно да разберем състоянията на системата: какво програмата може да чете или изменя по време на своята работа. Във всеки момент от времето е нужно да се разбира кои елементи на системата са важни за изпълнението на програмата.
Един от очевидните елементи на състоянието на системата, които включва в себе си процесът, е памет. Инструкциите се разполагат в паметта. Данните, които програмата чете или записва, също се разполагат в паметта. Така паметта, която процесът може да адресира (така нареченото адресно пространство), е част от процеса.
Част от състоянието на системата са регистри. Множество инструкции са насочени към промяна на стойността на регистрите или четене на стойността им и по този начин регистрите също стават важна част от работата на процеса.
Следва да се отбележи, че състоянието на машината се формира също и от някои специални регистри. Например, IP — указател на инструкцията — указател на инструкцията, която програмата изпълнява в текущия момент. Има също stack pointer и свързаният с него frame pointer, които се използват за управление на: параметри на функции, локални променливи и адреси за връщане.
Накрая, програмите често се обръщат към ROM (постоянно запомнящо устройство). Тази информация относно “I/O” (вход/изход) трябва да включва списък на файловете, отворени от процеса в момента.
Process API
За да подобрим разбирането за работата на процеса, ще разгледаме примери за системни повиквания, които трябва да бъдат включени в всеки интерфейс на операционната система. Тези API в една или друга форма са достъпни на всяка ОС.
● Create (създаване): Операционната система трябва да съдържа някакъв метод, който да позволява създаването на нови процеси. Когато въвеждате команда в терминала или стартирате приложение чрез двоен клик върху иконата, се изпраща запитване до ОС за създаване на нов процес и последващо стартиране на указаната програма.
● Удаление: След като има интерфейс за създаване на процес, ОС също трябва да предоставя възможност за принудително изтриване на процеса. Повечето програми естествено ще стартират и завършат сами по време на изпълнението си. В противен случай потребителят ще иска да може да ги убие и по този начин интерфейсът за спиране на процеса ще бъде полезен.
● Wait (изчакване): Понякога е полезно да изчакате завършването на процеса, затова се предоставят някои интерфейси, които дават възможност за изчакване.
● Misc Control (разнообразно управление): Освен убиването и изчакването на процеса, съществуват и други разнообразни контролни методи. Например, повечето ОС предоставят възможност за замразяване на процеса (спиране на неговото изпълнение за известно време) и последващо възобновяване (продължаване на изпълнението).
● Status (състояние): Има различни интерфейси за получаване на информация относно статуса на процеса, като продължителността на неговата работа или в какво състояние се намира в момента.

Създаване на процес: детайли
Една от интересните неща е как точно програмите се трансформират в процеси. Особено, как операционната система стартира и активира програмата. Как точно се създава процес.
На първо място, операционната система трябва да зареди кода на програмата и статичните данни в паметта (в адресното пространство на процеса). Програмите обикновено се намират на диск или твърдотелен носител в изпълним формат. Следователно, процесът на зареждане на програмата и статичните данни в паметта изисква от операционната система да прочете тези байтове от диска и да ги позиционира някъде в паметта.
В ранните операционни системи процесът на зареждане е бил изпълняван нетърпеливо (eagerly), т.е. кодът е бил зареждан в паметта изцяло преди програмата да се стартира. Съвременните операционни системи правят това лениво (lazily), т.е. зареждат парчета код или данни само когато те са необходими на програмата по време на нейното изпълнение.
След като кодът и статичните данни са заредени в паметта, операционната система трябва да извърши още няколко действия преди да стартира процеса. Няколко паметни единици трябва да бъдат разпределени за стека. Програмите използват стека за локални променливи, параметри на функции и адреси на връщане.. Операционната система разпределя тази памет и я предоставя на процеса. Стекът може също да бъде разпределен с няколко аргумента, конкретно той запълва параметрите на функцията main(), например масива argc и argv.
Операционната система може също да разпредели няколко единици памет за купа (heap) на програмата. Купата се използва от програмите за явно заявени динамично разпределени данни.. Програмите искат това пространство, извиквайки функцията malloc() и явно го освобождават, извиквайки функцията free(). Купа е необходима за структури от данни като: свързани списъци, хеш таблици, дървета и други. В началото за купата се заделя малко количество памет, но с времето, по време на работата на програмата, купата може да поиска повече памет чрез библиотечния API повик, malloc(). Операционната система е ангажирана в процеса на заделяне на допълнителна памет, за да помогне в удовлетворяването на тези повиквания.
Операционната система също ще изпълнява задачи по инициализация, особено тези, свързани с вход-изход. Например, в системи UNIX всеки процес по подразбиране има 3 отворени файлови дескриптора за стандартния вход, изход и грешки. Тези дескриптори позволяват на програмите да четат вход от терминала, както и да извеждат информация на екрана.
Така, като зарежда код и статични данни в паметта, създавайки и инициализирайки стек, а също така извършвайки друга работа, свързана с изпълнение на задачи по вход-изход, ОС подготвя терена за изпълнение на процеса. Накрая остава последната задача: да стартира програмата чрез нейната точка на вход, наречена функция main(). Преминавайки към изпълнение на функция main(), ОС предава управлението на CPU на новосъздадения процес, по този начин, програмата започва да се изпълнява.
Състояние на процеса
Сега, когато имаме известно разбиране за това какво е процес и как се създава, да изброим състоянията на процеса, в които той може да се намира. В най-простата си форма процесът може да бъде в едно от тези състояния:
● В процес на изпълнение. В състояние на изпълнение процесът се изпълнява на процесора. Това означава, че се извършва изпълнение на инструкции.
● Готово. В състояние на готовност процесът е готов да бъде стартиран, но по някакви причини ОС не го изпълнява в конкретния момент от време.
● Блокиран. В блокирано състояние процесът извършва операции, които не му позволяват да бъде готов за изпълнение, докато не настъпи някакво събитие. Един от обичайните примери е, когато процесът инициализира операция по вход-изход, той става блокиран и по този начин някой друг процес може да използва процесора.

Представете си тези състояния като граф. Както можем да видим на изображението, състоянието на процеса може да варира между RUNNING и READY по преценка на ОС. Когато състоянието на процеса се промени от READY на RUNNING, това означава, че процесът е планиран. Обратно, в обратната посока — свален от планирането. В момента, когато процесът стане BLOCKED, например, започвайки операция IO, ОС ще го държи в това състояние до настъпването на някакво събитие, например завършване на IO. В този момент настъпва преход в състояние READY и възможно е моментално да премине в състояние RUNNING, ако ОС реши така.
Нека разгледаме пример за това как два процеса преминават през тези състояния. Първо, нека предположим, че двата процеса са стартирани, и всеки използва само CPU. В този случай, техните състояния ще изглеждат по следния начин.

В следващия пример, първият процес след известно време работа изисква IO и преминава в състояние BLOCKED, предоставяйки възможност на другия процес да стартира (РИС 1.4). ОС вижда, че процес 0 не използва CPU и стартира процес 1. По време на изпълнението на процес 1 — IO завършва и статусът на процес 0 се променя на READY. Накрая, процес 1 завършва, а след неговото завършване, процес 0 стартира, изпълнява и завършва работата си.

Структура на данните
ОС сама по себе си е програма и, както всяка друга програма, има някои ключови структури от данни, които проследяват разнообразни релевантни части информация. За проследяване на състоянието на всеки процес, ОС ще поддържа някакъв списък на процесите за всички процеси в състояние READY и някаква допълнителна информация за проследяване на процесите, които се изпълняват в текущия момент. Също така, ОС трябва да проследява и блокираните процеси. След завършване на IO, ОС е длъжна да събуди необходимия процес и да го преведе в състояние готовност за стартиране.
Така, например, ОС трябва да запази състоянието на регистрирания процесор. В момента на спиране на процеса, състоянието на регистрите се запазва в адресното пространство на процеса, а при възобновяване на работата му — да възстанови стойностите на регистрите и така да продължи изпълнението на този процес.
Освен за състоянията ready, blocked и running, съществуват и други състояния. Понякога, в момента на създаване, процесът може да има състояние INIT. Накрая, процесът може да бъде поставен в състояние FINAL, когато вече е завършен, но информацията за него все още не е изчистена. В UNIX системите това състояние се нарича зомби-процес. Това състояние е полезно, когато родителският процес иска да научи кода за връщане на потомъка, например, обикновено 0 сигнализира за успешно завършване, а 1 за грешка, но програмистите могат да създават допълнителни кодове за изход, сигнализиращи за различни проблеми. При завършване, родителският процес прави последвваща системна повикваща функция, като wait(), за да изчака завършването на работата на процеса-потомък и да сигнализира на ОС, че може да изчисти всякакви данни, свързани със завършения процес.

Ключови моменти от лекцията:
● Процес е основната абстракция на работеща програма в ОС. Във всеки момент процесът може да бъде описан със своето състояние: съдържанието на паметта в неговото адресно пространство, съдържанието на регистрите на процесора, включително instruction pointer и stack pointer, а също и информация за IO, например отворени файлове, които се четат или записват.
● Process API се състои от извиквания, които програмите могат да правят във връзка с процесите. Обикновено това са извиквания за създаване, изтриване или други.
● Процесът се намира в едно от многото състояния, включително running, ready, blocked. Различни събития, като планиране, изключване от планиране или изчакване, могат да прехвърлят състоянието на процеса от едно в друго.
● Списък на процесите съдържа информация за всички процеси в системата. Всяка запис в него се нарича process control block, което всъщност е структура, съдържаща цялата необходима информация за конкретен процес.
Източник: habr.com
