Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Операційні системи.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
6.41 Mб
Скачать

Планувальники, що виконують диспетчеризацію процесів

В операційній системі диспетчеризація процесів виконується звичайно декількома планувальниками,кожний з яких має свою періодичність викликів і своє певне завдання, що він вирішує.

Довгостроковий планувальник (планувальник завдань) визначає, які процеси повинні бути переміщені в чергу готових процесів.

Короткочасний планувальник (планувальник процесора) – визначає, які процеси повинні бути виконані наступним й яким процесам повинен бути наданий процесор.

Для реалізації режиму поділу часу в систему може бути доданий також планувальник відкачки й підкачування процесів, що визначає, які користувальницькі процеси повинні бути підкачані в память або відткачані на диск. Схема роботи системи, що включає такий планувальник, зображена на рис. 8.6.

Рис. 8.6.  Додавання в систему планувальника відкачки й підкачування процесів.

Особливості планувальників і процесів. Кожен планувальник має свої особливості поводження, як і кожен процес.

Короткочасний планувальник викликається дуже часто, принаймні не рідше, ніж по витікання чергового кванта часу процесора. Тому він повинен бути дуже швидким, максимально ефективно реалізованим. Зрозуміло, що неприпустимо, наприклад, якщо час роботи цього планувальника виявиться порівнянним з розміром самого кванта часу – занадто великі будуть накладні витрати.

Довгостроковий планувальник викликається відносно рідко, тому що система не настільки часто ухвалює рішення щодо переводу процесу в чергу готових процесів. Тому він може бути порівняно повільним, не настільки ефективно реалізованим.

Однак, оскільки основним завданням системи в цілому залишається обслуговування як можна більшого числа процесів, саме довгостроковий планувальник визначає ступінь (коефіцієнт) мультипрограмування – число процесів, що обслуговує система в одиницю часу.

Самі процеси також можуть поводитися по-різному, з погляду їхньої диспетчеризації. Процеси можуть бути:

  • Орієнтованими на ввід-вивід (I/O-bound) – процеси, які витрачають більше часу на ввід-вивід, чим на обчислення. Такі процеси звичайно витрачають багато коротких квантів процесорного часу.

  • Орієнтовані на використання процесора (CPU-bound) – процеси, які витрачають основний час на обчислення. Такі процеси витрачають невелике число довгострокових квантів процесорного часу.

Перемикання контексту

Коли процесор перемикається на інший процес, система повинна зберегти стан старого процесу й завантажити збережений стан для нового процесу. Така дія системи називається перемиканням контексту (context switch).

Перемикання контексту відноситься до накладних витрат (overhead), тому що система не виконує ніяких корисних дій при перемиканні з одного процесу на іншій.

Час перемикання контексту залежить від апаратної підтримки.

Створення процесу - одна з основних операцій над процесами

Процес-батько створює дочірні процеси, які, у свою чергу, створюють інші процеси, тим самим формуючи дерево процесів.

Можливі різні підходи до створення процесів, з погляду можливості спільного використання (поділу) ресурсів процесом-батьком і дочірнім процесом, з погляду можливості їхнього паралельного виконання й з погляду адресації й використання пам'яті.

Поділ ресурсів. Можливі наступні підходи:

  • Процес-батько й дочірні процеси розділяють всі ресурси;

  • Дочірні процеси розділяють підмножина ресурсів процесу-батька;

  • Процес-батько й дочірній процес не мають загальних ресурсів.

Виконання. Можливі наступні підходи:

  • Процес-батько й дочірні процеси виконуються спільно;

  • Процес-батько очікує завершення дочірніх процесів.

Адресація й використання пам'яті. Можливі наступні підходи:

  • Адресний простір дочірнього процесу копіює адресний простір процесу-батька; у дочірнього процесу є програма, завантажена в нього;

  • Дочірній процес виконується в тім же просторі пам'яті, що й процес-батько (полегшений процес).

У системі UNIX сформульовані питання вирішені наступної образом. fork - системний виклик, що створює новий процес. Він клонує пам'ять процесу-батька й створює для дочірнього процесу новий віртуальний адресний простір. Після цього виконується ще один системний виклик - exec (execve) - системний виклик, з метою заміни простору пам'яті процесу новою програмою. Дочірній процес продовжує виконуватися замість процесу батька.

На рис. 8.7 зображено дерево процесів у системі UNIX.

Рис. 8.7.  Дерево процесів у системі UNIX.

При запуску системи створюється кореневий процес root. Він, у свою чергу, створює три дочірніх процеси: init – ініціалізація системи; pagedaemon – процес-демон (процес, що постійно перебуває в системі до її перезапуску), що управляє сторінковою організацією пам'яті; swapper – процес, керуючий відкачкою й підкачуванням. Процес init після ініціалізації системи запускає користувальницькі процеси. Останні, у свою чергу, можуть запускати нові й т.д.

Знищення процесу

Це також одна з основних операцій над процесами. Як правило, процес знищується (завершується), коли він виконує заключний оператор і звертається до ОС для своєї ліквідації за допомогою системного виклику exit. При цьому можлива передача даних від дочірнього процесу процесу-батькові - наприклад, через файли, створені дочірнім процесом. Ресурси процесу звільняються операційною системою.

Лекція 9 Потоки (threads) і багатопоточне виконання програм (multi-threading)

План

  • Історичний огляд багатопоточності

  • Моделі багатопоточного виконання

  • Проблеми, пов'язані з потоками

  • Потоки в POSIX (Pthreads)

  • Потоки в Solaris 2

  • Потоки в Windows 2000/XP

  • Потоки в Linux

  • Потоки в Java й .NET.

Однопотокові й багатопоточні процеси

Послідовний (однопотоковий) процес – це процес, що має тільки один потік керування (control flow), що характеризується зміною його лічильника команд. Потік (thread) – процес особливого роду пар, який запускається з деякого процесу, і виконується у тім же адресному просторі, що й процес-батько. Схема організації однопотокового й багатопоточного процесів зображена на рис. 10.1.

Рис. 10.1.  Однопотоковий і багатопоточний процеси.

Як видно зі схеми, однопотоковий процес використовує, як звичайно, код і дані в основній пам'яті й файли, з якими він працює. Процес також використає певні значення регістрів і стек, на якому виконуються його процедури. Багатопоточний процес організований трохи складніше. Він має кілька паралельних потоків, для кожного з яких ОС створює свій стек і зберігає свої власні значення регістрів. Потоки працюють у загальній основній пам'яті й використають той же адресний простір, що й процес-батько, а також розділяють код процесу й файли.

Багагопоточність має більші переваги:

  • Збільшення швидкості (у порівнянні з використанням звичайних процесів). Багатопоточність заснована на використанні полегшених процесів (lightweight processes), що працюють у загальному просторі віртуальної пам'яті. Завдяки багатопоточності, не виникає більше неефективних ситуацій, типових для класичної системи UNIX, у якій кожна команда shell (навіть команда виводу вмісту поточної директорії ls виконувалася як окремий процес, причому у своєму власному адресному просторі. На противагу полегшеним процесам, звичайні процеси (мають власний адресний простір) часто називають великоваговими (heavyweight).

  • Використання загальних ресурсів. Потоки одного процесу використають загальну пам'ять і файли.

  • Економія. Завдяки багатопоточносіти, досягається значна економія пам'яті, із причин, пояснених вище. Також досягається й економія часу, тому що перемикання контексту на полегшений процес, для якого потрібно тільки перемінити стек і відновити значення регістрів, значно швидше, ніж на звичайний процес.

Використання мультипроцесорних архітектур. Це особливо важливо в цей час, у період широкого використання багатоядерных гібридних і багатопроцессорных систем. Саме багатопоточность програм, заснована на багатоядерности процесора, дає можливість, нарешті, відчути реальні переваги паралельного виконання.

Історія багатопоточности

Один з перших кроків на шляху до широкого використання багатопоточности, очевидно, був зроблений в 1970-і роки радянськими розроблювачами комп'ютерних апаратур і програмістами. МВК "Ельбрус-1", розроблений в 1979 році, підтримував в апаратурах й операційній системі ефективну концепцію процесу, що була близька до сучасного поняття полегшеного процесу. Зокрема, процес в "Ельбрусі" однозначно характеризувався своїм стеком. Інакше кажучи, всі процеси були полегшеними й виконувалися в загальному просторі віртуальної пам'яті - інших процесів в "Ельбрусі" просто не було!

Концепція багатопоточности початку складатися, очевидно, з 1980-х рр. у системі UNIX й її діалектах. Найбільш розвинена багатопоточность була в діалекті UNIX фірми AT&T, на основі якого, була розроблена система Solaris. Все це відбилося й у стандарті POSIX, у який увійшла й багатопоточность, поряд з іншими базовими можливостями UNIX.

Далі, у середині 1990-х рр. була випущена ОС Windows NT, у яку була також включена багатопоточність.

Однак у різних операційних системах API для багатопоточности істотно відрізнялися. Тому багатопоточні програми, навіть написані на мовах високого рівня, виявилися непереносимими з однієї платформи на іншу, що, зрозуміло, створювало великі незручності.

Важливий крок уперед зробили автори мови Java й Java-технології, перша версія реалізації яких була випущена в 1995 р. Саме в Java уперше багатопоточність була реалізована на рівні конструкцій мови й базових бібліотек. Зокрема, в Java уведений клас Thread, щопредставляє потік, і операції над ним у вигляді спеціальних методів і конструкцій мови.

Платформа .NET, що з'явилася в 2000 р., запропонувала свій механізм багатопоточності, що фактично є розвитком ідей Java.

Розходження підходів до багатопоточності в різних ОС і на різних платформах розробки програм зберігається й дотепер, що доводиться постійно враховувати розроблювачам.

Користувальницькі потоки й потоки ядра

Моделі багатопоточности. Реалізація багатопоточности в ОС, як і багатьох інших можливостей, має кілька рівнів абстракції. Найвищий з них – користувальницький рівень. З погляду користувача і його програм, керування потоками реалізовано через бібліотеку потоків користувальницького рівня (user threads). Існує кілька моделей потоків користувальницького рівня, серед яких:

  • POSIX Pthreads – потоки, специфіковані стандартом POSIX і використовувані в POSIX-додатках (розглянуті пізніше в даній лекції);

  • Mac C-threads – користувальницькі потоки в системі MacOS;

  • Solaris threads – користувальницькі потоки в ОС Solaris.

Низькорівневі потоки, у які відображаються користувальницькі потоки, називаються потоками ядра (kernel threads). Вони підтримані й використовуються на рівні ядра операційної системи. Як і підходи до користувальницьких потоків, підходи до архітектури й реалізації системних потоків і до відображення користувальницьких потоків у системні в різних ОС різні. Наприклад, власні моделі потоків ядра зі своєю специфікою реалізовані в наступних ОС:

  • Windows 95/98/NT/2000/XP/2003/2008/7;

  • Solaris;

  • Tru64 UNIX;

  • BeOS;

  • Linux.

Існують різні моделі багатопоточності – способи відображення користувальницьких потоків у потоки ядра. Теоретично можливі (і на практиці реалізовані) наступні моделі багатопоточности:

- Модель багато / один (many-to-one) – відображення декількох користувальницьких потоків у той самий потік ядра. Використається в операційних системах, що не підтримують множинні системні потоки (наприклад, з метою економії пам'яті). Дана модель зображена на рис. 10.2.

Рис. 10.2.  Схема моделі багатопоточности "багато / один".

- Модель один / один (one-to-one) – взаємо-однозначне відображення кожного користувальницького потоку в певний потік ядра. Приклади ОС, що використають дану модель, - Windows 95/98/NT/2000/XP/2003/2008/7; OS/2. Дана модель зображена на рис. 10.3.

Рис. 10.3.  Схема моделі багатопоточности "один / один".

- Модель багато / багато (many-to-many) – модель, що допускає відображення декількох користувальницьких потоків у кілька системних потоків. Така модель дозволяє ОС створювати велику кількість системних потоків. Характерним прикладом ОС, що використає подібну модель, є ОС Solaris, а також Windows NT / 2000 / XP / 2003 / 2008 / 7 з пакетом ThreadFiber. Дана модель зображена на рис. 10.4.

Рис. 10.4.  Схема моделі багатопоточности "багато / багато".

Проблеми багатопоточності

Багатопоточність - досить складна, ще не повністю вивчена й, тим більше, не повністю формалізована область, у якій є багато цікавих проблем.

Семантика системних викликів fork() і exec().Як ми вже відзначали, у класичної ОС UNIX системний виклик fork створює новий "великоваговий" процес зі своїм адресним простором, що значно "дорожче", чим створення потоку. Однак, з метою підтримки сполучності програм знизу нагору, доводиться зберігати цю семантику, а багатопоточність уводити за допомогою нових системних викликів.

Припинення потоків. Важливою проблемою є проблема припинення потоків: наприклад, якщо батьківський потік припиняється, то чи належить при цьому припинятися дочірній потік? Якщо припиняється стандартний процес, що створив кілька потоків, то чи повинні припинятися всі його потоки? Відповіді на ці питання в різних ОС неоднозначні.

Обробка сигналів. Сигнали в UNIX – низькорівневий механізм обробки помилкових ситуацій. Приклади сигналів: SIGSEGV - порушення сегментації (обіг по невірній адресі, найчастіше по нульовому); SIGKILL – сигнал процесу про виконання команди kill його знищення. Користувач може визначити свою процедура-оброблювач сигналу системним викликом signal. Проблема в наступному: як поширюються сигнали в багатопоточних програмах й якому потоці вони повинні оброблятися? У більшості випадків це питання вирішується в такий спосіб: сигнал обробляється потоком, у якому він згенерирований, і впливає на виконання тільки цього потоку. У більше сучасних ОС (наприклад, Windows 2000 і більше пізніх версіях Windows), заснованих на обєктно-орієнтированій методології, концепція сигналу замінена більше високорівневою концепцією виключення (exception). Виключення поширюється по стеку потоку в порядку, зворотному порядку викликів методів, і обробляється першим з тих, у якому система знаходить підходящий оброблювач. Аналогічна схема обробки виключень реалізована в Java і в. NET.

Групи потоків.

У складних завданнях, наприклад, задачах моделювання, при великій кількості різнорідних потоків, виникає потреба в їхньому структуруванні за допомогою концепції групи потоків – сукупності потоків, що мають своє власне ім'я, над потоками якої визначені групові операції. Найбільше вдало, на наш погляд, групи потоків реалізовані в Java (за допомогою класу ThreadGroup ). Слід зазначити також ефективну реалізацію пулів потоків (ThreadPool) в. NET.

Локальні дані потоку (thread-local storage - TLS) – даному, приналежному тільки певному потоку й використовувані тільки цим потоком. Необхідність у таких даних очевидна, тому що багатопоточність - досить важливий метод розпаралелювання рішення великого завдання, при якому кожен потік працює над рішенням дорученої йому частини. Всі сучасні операційні системи й платформи розробки програм підтримують концепцію локальних даних потоку.

Синхронізація потоків. Оскільки потоки, як і процеси можуть використати загальні ресурси й реагувати на загальні події, необхідні засоби їхньої синхронізації.

Тупики (deadlocks) і їхнє запобігання. Як і процеси, потоки можуть взаємно блокувати один одного (тобто може створитися ситуація deadlock ), при їхньому неакуратному програмуванні.

Потоки POSIX (Pthreads)

Як конкретна модель багатопоточності потоки POSIX (нагадаємо, що дана абревіатура розшифровується як Portable Operating Systems Interface of uni kind – стандарти для переносимих ОС типу UNIX). Багатопоточність в POSIX специфікована стандартом IEEE 1003.1c, що описує API для створення й синхронізації потоків. Відзначимо, що POSIX-стандарт API визначає лише необхідне поводження бібліотеки потоків. Реалізація потоків залишається на розсуд авторів конкретної POSIX-сумісної бібліотеки. POSIX-потоки поширені в ОС типу UNIX, а також, а также підтримуються, с метою сумісності програм, в багатьох других ОС, наприклад, Solaris і Windows NT.

Потоки й процеси в Solaris

В ОС Solaris, як уже було відзначено, використається модель потоків багато / багато. Крім того, у системі використоується також полегшений процес (lightweight process) проміжне між концепцією користувальницького потоку й систебагато потоку. Таким чином, в ОС Solaris кожен користувальницький потік відображається у свій полегшений процес, що, у свою чергу, відображається в потік ядра; останній може виконуватися на будь-якому процесорі (або ядрі процесора) комп'ютерної системи. Схема організації потоків в Solaris зображена на рис. 10.5.

Рис. 10.5.  Потоки в Solaris.

На рис. 10.6 зображено схему організації процесу в ОС Solaris

Рис. 10.6.  Процеси в Solaris.

На схемі видно, що кожен процес містить, крім стандартної інформації блоку керування процесом, також список всіх своїх полегшених процесів для керування ними.

Потоки в Windows 2000

Як ми вже відзначали, у системі Windows реалізована модель багатопоточности "один / один". Кожен потік містить:

  • ідентифікатор потоку (thread id);

  • набір регістрів

  • окремі стеки для користувальницьких і системних процедур;

  • область пам'яті для локальних даних потоку (thread-local storage - TLS).

Потоки в Linux

У системі Linux потоки називаються tasks (завданнями),а не threads. Потік створюється системним викликом clone(). Даний системний виклик дозволяє дочірньому завданню використати загальний адресний простір з батьківським завданням (процесом).

Потоки в Java

Як ми вже відзначали, Java - перша платформа для розробки програм, у якій багатопоточность підтримана на рівні мови й базових бібліотек. Потоки в Java можуть бути створені такими способами:

  • Як розширення класу Thread

  • Як класи, що реалізують інтерфейс Runnable, що містить єдиний метод run – тіло потоку що виконується

Потоки в Java управляються JVM. Можливе створення груп потоків й ієрархії таких груп.

Можливі стани потоків в Java зображені на рис. 10.7. Подібно потокам в ОС, потік в Java створюється й перебуває в стані новий, потім – виконуваний ; при виклику методів типу wait, sleep й ін. потік переходить у стан очікування; при завершенні методу run потік завершується.

Рис. 10.7.  Стану потоків в Java