Реалізація багатозадачності в Windows Vista

Тип работы:
Курсовая
Предмет:
Программирование


Узнать стоимость

Детальная информация о работе

Выдержка из работы

Відокремлений структурний підрозділ Золочівський коледж

Національного університету «Львіррвська політехніка»

Методична комісія природничо-математичних та комп’ютерних дисциплін

КУРСОВА РОБОТА

з Операційних систем

на тему:

«Реалізація багатозадачності в Windows Vista»

Студента ІІ курсу РПЗ групи

напряму підготовки 6. 30 501 «Програмна інженерія»

спеціальності 5. 5 010 301 Розробка програмного забезпечення

Гринчишина Я.П.

Керівник викладач Повх Тетяна Володимирівна

м. Золочів — 2013 рік

Зміст

багатозадачність пріоритет windows

Вступ

Розділ 1. Теоретична частина

1.1 Суть багатозадачності

1.1.1 Властивості багатозадачного середовища

1.1. 2Типи псевдопаралельної багатозадачності

1.2 Моделювання режиму багатозадачності

1.2.1 Процеси і потоки

1.2.2 Стан процесу

1.3 Реалізяція багатозадачності в Windows Vista

1.3.1 Фундаментальні концепці

1.3.2 Реалізація процесів і потоків в Windows Vista

1.3.3 Планування

Розділ 2. Практична частина

Висновки

Список використаної літератури

Вступ

Windows Vista — це версія Microsoft Windows, із серії графічних операційних систем для персональних комп’ютерів, яка використовується як для дому так і для роботи. До оголошення про її розробку 22 липня 2005, Vista була відома як Longhorn. 8 листопада 2006, розробка Windows Vista була завершена і версія вийшла в продаж.

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

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

Таким чином, метою даної роботи є розгляд реалізації багатозадачності в Windows Vista. Для досягнення поставленої мети необхідно вирішити наступні завдання:

Висвітлити сутність багатозадачності.

Вивчити модель режиму багатозадачності.

Приділити особливу увагу розгляду реалізації багатозадачності в Windows Vista.

Об'єктом дослідження в даній роботі є операційна система Windows Vista. Предметом дослідження — реалізація багатозадачності в Windows Vista.

Розділ 1. Теоретична частина

1.1 Суть багатозадачності

1.1.1 Властивості багатозадачного середовища

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

Істинна багатозадачність операційної системи можлива тільки в розподілених обчислювальних системах.

Примітивні багатозадачні середовища забезпечують чисте «розділення ресурсів», коли за кожною задачею закріплюється певна ділянка пам’яті, і завдання активізується в строго певні інтервали часу.

Більш розвинені багатозадачні системи проводять розподіл ресурсів динамічно, коли завдання стартує в пам’яті або покидає пам’ять залежно від її пріоритету і від стратегії системи. Така багатозадачна система володіє наступними особливостями:

Кожне завдання має свій пріоритет, відповідно до якого отримує час і пам’ять.

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

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

По закінченні відведеного часу завдання може тимчасово викидатися з пам’яті, віддаючи ресурси іншим завданням, а потім через певний системою час, відновлюватися в пам’яті.

Система забезпечує захист пам’яті від несанкціонованого втручання інших завдань.

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

Система розпізнає збої і зависання окремих завдань і припиняє їх.

Система гарантує кожному завданню, що рано чи пізно вона буде активована.

Система обробляє запити реального часу.

Система забезпечує комунікацію між процесами.

1.1.2 Типи псевдопаралельної багатозадачності

Розподіл процесів між наявними ресурсами має назву «планування процесів». Існує два основних типи процедур планування процесів — витісняючий (preemptive) і невитісняючий (non-preemptive).

Невитісняюча багатозадачність (non-preemptive multitasking) — це спосіб планування процесів, при якому активний процес виконується до тих пір, поки він сам, за власною ініціативою, не віддасть керування планувальнику ОС для того, щоб той вибрав з черги інший, готовий до виконання процес.

Витісняюча багатозадачність (preemptive multitasking) — це такий спосіб, при якому рішення про переключення процесора з виконання одного процесу на виконання іншого процесу приймається планувальником ОС, а не самою активною задачею.

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

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

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

1.1.3 Історія розвитку багатозадачних операційних систем

Основною ознакою багатозадачної ОС є здатність поєднувати виконання декількох прикладних програм. Велике значення при цьому має спосіб поєднання, тобто на якому рівні або як конкретно реалізовано поєднання. Якщо однопроцесорна, але багатозадачна, система виділяє кожній прикладній програмі певний квант часу (time slice), через який вона перемикається на виконання наступної програми, то це система з поділом часу (time-sharing system).

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

Потім з’явилися персональні комп’ютери, які стали використовуватися в якості віддалених терміналів. У зв’язку з цим для операційної системи головного процесора (наприклад, IBM-370) відпала необхідність займатися посимвольним введенням-виводом. Тепер акцент в розробці операційних систем був перенесений на управління виконуваними програмними модулями, що належать різним користувачам і одночасно знаходяться в пам’яті головного комп’ютера. З’явилися такі поняття, як черга завдань, черга на обслуговування яким-небудь пристроєм: принтером, плоттером, накопичувачем на магнітному носії, пріоритет завдання, очікуваний час завершення завдання і т. д.

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

Перші операційні системи, реалізовані на персональних комп’ютерах, сильно поступалися в концептуальному плані і по своїм реальним можливостям системам з розділенням часу, давно реалізованим в mainframe-комп'ютерах. У Win16, наприклад, теж існує поняття багатозадачності. Реалізовано воно таким чином: обробивши чергове повідомлення, додаток передає управління операційній системі, яка може передати управління іншому застосуванню. Такий вид багатозадачності, при якому операційна система передає управління від одного застосування іншому не у будь-який момент часу, а тільки коли поточний додаток віддає управління системі, отримав, назву кооперативної багатозадачності (cooperative multitasking).

Якщо при такому підході обробка повідомлення затягується, то користувач побачить реакцію системи лише після завершення обробки поточним додатком поточного повідомлення. Зазвичай при виконанні тривалих операцій програміст змінює форму курсора (пісочний годинник), викликавши API-функцію BeginWaitCursor. Іноді, якщо це передбачив розробник програми, в таких випадках застрягле застосування навіть викликає функцію PeekMessage, повідомляючи системі, що вона може обробити чергове повідомлення, а поточне застосування здатне і почекати. Але головна неприємність при такому підході полягає в тому, що в разі нескінченного циклу, викликаного помилкою в програмі, ОС не має шансів одержати управління і також зависне. Користувачеві доведеться перезавантажувати систему.

У Windows починаючи з Windows 95 реалізований принципово інший вид багатозадачності, в якому операційна система дійсно контролює і керує процесами, потоками і їх перемиканням. Здатність операційної системи перервати виконуваний потік практично в будь-який момент часу і передати управління іншому чекаючому потоку визначається терміном preemptive multitasking — переважна, або витісняюча, багатозадачність. Реалізація її виглядає так: усі існуючі в даний момент потоки, частина з яких може належати одному і тому ж процесу, претендують на процесорний час і, з точки зору користувача повинні виконуватися одночасно. Для створення цієї ілюзії система через певні проміжки часу забирає управління, аналізує свою чергу повідомлень, розподіляє повідомлення по інших чергах в просторі процесів і, якщо вважає за потрібне, перемикає потоки (рис. 1.1.3.1.)

Рис. 1.1.3.1. Перемикання потоків в системі з витісняючою багатозадачністю

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

Якщо комп’ютер має декілька процесорів, то системи Windows NT/2000 можуть дійсно поєднувати виконання декількох додатків. Якщо процесор один, то поєднання залишається ілюзією. Коли закінчується квант часу, відведений поточній програмі, система її перериває, зберігає контекст і віддає управління іншій програмі, яка чекає своєї черги. Величина кванта часу (time slice) залежить від ОС і типу процесора, в Windows NT вона в середньому дорівнює 20 мс. Слід зазначити, що домогтися дійсно одночасного виконання потоків можна тільки на машині з декількома процесорами і лише під управлінням Windows, ядра яких підтримують розподіл потоків між процесорами і процесорного часу між потоками на кожному процесорі. Наприклад, Windows 95 працювала тільки з одним процесором. Навіть якщо у комп’ютера декілька процесорів, під управлінням Windows 95 міг бути задіяний лише один з них, а інші повинні.

1.2 Моделювання режиму багатозадачності

1.2.1 Процеси і потоки

Розрізняють два способи реалізації багатозадачності:

— Створити один процес, що має кілька потоків виконання (threads);

— Створити декілька процесів, кожен з яких має один або декілька потоків виконання.

Багатозадачна (multi-process) система дозволяє двом або більше програмам виконуватися одночасно.

Багатопотокова (multi-threaded) система дозволяє одній програмі виконувати відразу декілька потоків одночасно.

Сучасні операційні системи поєднують в собі обидві ці властивості. Додаток Win32 може складатися з одного або більше процесів. Наприклад, додаток за розрахунком параметрів турбогенератора може складатися з зручної оболонки, написаної на мові C++ (головний процес), і обчислювальних модулів, написаних на мові FORTRAN і запускаються у вигляді окремих (породжених) процесів. При цьому можливий варіант, коли один процес (модуль програми) зайнятий виведенням геометрії розрахункової області, а інший одночасно виробляє розрахунок електромагнітного поля.

Процес — це поняття, що відноситься до операційної системи. Кожного разу, як ви запускаєте додаток, система створює і запускає новий процес. Процес можна грубо ототожнити з ехе-кодом, що виконуються в окремому процесорі.

З кожним процесом система пов’язує такі ресурси, як: віртуальний адресний простір; виконані код і дані; базовий пріоритет; описувачі об'єктів; змінні оточення.

Кожен процес обов’язково створює первинний потік (primary thread) виконання. Він робить це автоматично і, якщо програміст не робить якихось спеціальних зусиль по створенню другого потоку, то первинний потік і породивший його процес зазвичай ототожнюються в свідомості користувача, а, часто і в свідомості програміста. Але останній може створити ще один або декілька потоків, які розміщуються в одному і тому ж адресному просторі, що належить процесові. Коли вони створюються, батьківський процес починає виконуватися не послідовно, а паралельно. Так реалізується потокова багатозадачність. Кажуть, що потоки виконуються в контексті процесу.

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

Всі потоки (threads) одного процесу користуються ресурсами породжувача їх процесу. Крім того, кожному потоку система і / або програміст приписує пріоритет виконання і набір структур мови С, що описують контекст потоку. Система використовує їх для запам’ятовування контексту потоку, коли його виконання уривається. У контекст входять:

Стан регістрів;

Системний стек ядра ОС (kernel stack);

Стек користувача розташований в адресному просторі процесу;

Блок змінних оточення потоку.

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

Перерахуємо найбільш типові випадки, коли слід застосовувати багатопоточність:

Керування вводом в різні документи МШ-інтерфейсу. Введення даних кожного документа додатка організований у вигляді окремого потоку;

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

Розмежування пріоритетів виконання завдань. Потокам, що вимагає високої швидкості реакції, присвоюється високий пріоритет, а іншим потокам більш низький;

Зниження часу реакції на дії користувача по вводу даних при одночасному виконанні фонових обчислень.

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

Контексти потоків займають менший об'єм, ніж контексти процесів і система перемикає їх швидше;

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

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

1.2.2 Стан процесу

Будь-який процес у багатозадачній ОС багаторазово відчуває перехід з одного стану в інший.

Основних станів всього три.

Робота (running) — в цьому стані знаходиться процес, програму якого в даний момент виконує процесор. Працюючий процес іноді зручно називати також поточним процесом.

Готовність (ready) — стан, процес якого може бути переведений в стан роботи, як тільки це визнає потрібним зробити ОС.

Блокування або, що те ж саме, сон (sleeping, waiting) — стан, в якому процес не може продовжувати виконання, поки не відбудеться деяка зовнішня по відношенню до процесу подія.

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

Для станів готовності і сну спільне те, що процес не працює. У чому різниця між цими двома «способами не працювати»?

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

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

Чого він може чекати? Ну, наприклад:

Завершення розпочатої операції синхронного вводу / виводу (тобто, наприклад, процес чекає натискання клавіші Enter або закінчення запису на диск);

Звільнення запитаного у системи ресурсу (наприклад, додаткової області пам’яті або відкритого файлу);

Закінчення заданого інтервалу часу («посплю-но я хвилин десять!») Або досягнення заданого моменту часу («розбудіть мене рівно опівночі!») (В обох випадках процес чекає сигналу від запрограмованого таймера);

Сигналу на продовження дій від іншого, взаємопов'язаного процесу;

Повідомлення від системи про необхідність виконати певні дії (наприклад, перемалювати вміст вікна).

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

Умовно можна сказати, що якщо б в обчислювальну систему раптом було додано ще кілька процесорів, то «готові» процеси могли б відразу перейти в стан «роботи», але «сплячі» продовжили б свій сон.

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

Оскільки ОС бере на себе блокування, «усипляння» процесу, вона повинна забезпечити і його розблокування, «пробудження». Щоб це стало можливим, система повинна для кожного сплячого процесу пам’ятати, «чого він чекає», тобто пам’ятати умови пробудження процесу. Система відслідковує всі події, здатні розблокувати який-небудь процес (у багатьох випадках використовуючи для цього апаратні переривання) і, коли для одного або відразу декількох процесів настає очікувана подія, переводить ці події зі стану сну в стан готовності.

1.2.3 Модель режиму багатозадачності

Режим багатозадачності дозволяє використовувати центральний процесор більш раціонально. При грубій прикидці, якщо для середньостатистичного процесу обчислення займають лише 20% часу його перебування в пам’яті, то при п’яти одночасно знаходяться в пам’яті процесах центральний процесор буде завантажений постійно. Але в цієї моделі закладено абсолютно нереальний оптимізм, оскільки в ній свідомо передбачається, що всі п’ять процесів ніколи не будуть одночасно перебувати в очікуванні закінчення якого-небудь процесу вводу-виводу.

Краще вибудовувати модель на основі імовірнісного погляду на використання центрального процесора. Припустимо, що процес проводить частину свого часу p в очікуванні завершення операцій введення-виведення. При одночасній присутності в пам’яті n процесів ймовірність того, що всі n процеси очікують завершення вводу-виводу (в разі чого процесор простоює), дорівнює рn. Тоді час задіяння процесора обчислюється за формулою:

Час задіяння ЦП = 1 — рn.

Рис. 1.2.3.1. Час задіяння ЦП у вигляді функції від кількості процесів

На рис. 1.2.3.1. показано час задіяння центрального процесора у вигляді функції від аргументу n, який називається ступенем багатозадачності.

Судячи по малюнку, якщо процес витрачає 80% свого часу на очікування завершення вводу-виводу, то для зниження простою процесора до рівня не більше 10% в пам’яті можуть одночасно перебувати принаймні 10 процесів. Коли ви зрозумієте, що до очікування введення-виведення відноситься і очікування інтерактивного процесу користувальницького введення з терміналу, стане зрозуміло, що час очікування завершення введення-виведення, що становить 80% і більше, не така вже й рідкість. Але навіть на серверах процеси, що здійснюють безліч операцій вводу-виводу, часто мають такий же або навіть більший відсоток простою.

Заради справедливості слід зауважити, що розглянута нами імовірнісна модель носить дуже приблизний характер. У ній безумовно передбачається, що всі n процесів є незалежними один від одного, а значить, в системі з п’ятьма процесами в пам’яті цілком припустимо мати три виконуваних і два чекаючих процеси. Але маючи один центральний процесор, ми не може мати відразу три виконуваних процеси, тому процес, який стає готовим до роботи при зайнятій центральному процесорі, змушений чекати своєї черги. Тому процеси не володіють незалежністю. Більш точна модель може бути вибудувана з використанням теорії черг, але зроблений нами акцент на багатозадачність, що дозволяє завантажувати процесор щоб уникнути його простою, як і раніше зберігається.

Припустимо, наприклад, що пам’ять комп’ютера становить 512 Мбайт, операційна система займає 128 Мбайт, а кожна програма користувача також займає до 128 Мбайт. Цей об'єм дозволяє одночасно розмістити в пам’яті три користувацькі програми. При середньому очікуванні введення-виведення в 80% ми маємо завантаженість центрального процесора (якщо ігнорувати витрати на роботу операційної системи), рівну 1 — 0,83, або близько 49%. Збільшення обсягу пам’яті ще на 512 Мбайт дозволить системі перейти від трикратної багатозадачності до семикратної, що підвищить завантаженість центрального процесора до 79%. Іншими словами, додаткові 512 Мбайт пам’яті збільшать його продуктивність на 30%.

Збільшення пам’яті ще на 512 Мбайт підніме рівень продуктивності всього лише з 79 до 91%, тобто додатковий приріст продуктивності складе тільки 12%. Використовуючи цю модель, власники комп’ютерів можуть прийти до висновку, що перше нарощування обсягу пам’яті стане, на відміну від другого, непоганим внеском у підвищення продуктивності процесора.

1.3 Реалізяція багатозадачності в Windows Vista

1.3.1 Фундаментальні концепції

У Windows Vista процеси є контейнерами для програм. Вони містять віртуальний адресний простір, описувачі об'єктів режиму ядра, а також потоки. Як контейнери для потоків вони містять також загальні ресурси (використовувані для виконання потоків), такі як покажчик на структуру квоти, спільно використовуваний об'єкт маркера, а також параметри за замовчуванням (використовувані для ініціалізації потоків), включаючи пріоритет і клас планування.

Потоки — це абстракції ядра для планування процесора в Windows. Кожному потоку привласнюється пріоритет (в залежності від значення пріоритету його процесу). Потоки можуть бути аффінізірованними (affinitized), щоб вони виконувалися тільки на певних процесорах. Це допомагає паралельним програмами (працюючим на декількох процесорах) явним чином розподіляти навантаження. Кожен потік має два окремих стека викликів, один — для виконання в режимі користувача, інший — для режиму ядра.

Процеси створюються з об'єктів сегментів, кожний з яких описує об'єкт пам’яті, заснований на дисковому файлі. При створенні процесу створюючий процес отримує описувач для процесу, який дозволяє йому модифікувати новий процес (за допомогою відображення сегментів, виділення віртуальної пам’яті, запису параметрів і даних оточення, дублювання дескрипторів файлів в свою таблицю описувачів, створення потоків). Це відрізняється від того, як процеси створюються в системах UNIX, і демонструє різницю між UNIX і Windows. UNIX була спроектована для 16-бітних однопроцесорних систем, які застосовували підкачку для спільного використання пам’яті процесами. У таких системах використання процесу як одиниці паралельності та операції fork для створення процесів було просто блискучою ідеєю. Для виконання нового процесу в невеликій кількості пам’яті (при відсутності апаратних засобів віртуальної пам’яті) доводилося процеси з пам’яті завантажувати на диск. Спочатку fork в системі UNIX була реалізована за допомогою простого завантаження батьківського процесу і передачі його фізичної пам’яті дочірньому процесу.

На момент написання системи NT звичайним апаратним середовищем були 32-бітові багатопроцесорні системи з апаратною віртуальною пам’яттю, яка використовувала від 1 до 16 Мбайт фізичної пам’яті. Наявність декількох процесорів дозволяє одночасно виконувати частині програм, тому NT застосовувала процеси як контейнери для спільного використання пам’яті і ресурсів об'єктів, а потоки — як одиницю паралельності (для планування).

Windows може групувати процеси в завдання, однак абстракція «завдання» (job) має не дуже загальний характер. Вона була спеціально створена для групування процесів з метою застосування обмежень до присутніх в них потоках, таких як обмеження використання ресурсів за допомогою спільно використовуваної квоти або застосування маркера обмеженого доступу (restricted token), який не дозволяє потокам звертатися до багатьох системних об'єктів. Найважливішою властивістю завдань (в плані управління ресурсами) є те, що з того моменту, як процес виявився в завданні, всі створені (у цих процесах) потоками процеси будуть також перебувати в цьому завданні. Виходу немає. У повній відповідності зі своєю назвою завдання були призначені для таких ситуацій, які швидше нагадували пакетну обробку завдань, ніж звичайні інтерактивні обчислення.

Процес може знаходитися всередині тільки для одного завдання (максимум).

Рис. 1.3.1.1. Зв’язок між завданнями, процесами, потоками і волокнами.

На рис. 1.3.1.1. показаний зв’язок між завданнями (jobs), процесами (processes), потоками (threads) і волокнами (fibers). Завдання містять процеси. Процеси містять потоки. Але потоки не містять волокна. Зв’язок між потоками і волокнами зазвичай має тип «багато-до-багатьох».

Волокна створюються шляхом виділення місця в стеку і структури даних волокна в режимі користувача (для зберігання регістрів і даних, пов’язаних з цим волокном). Потоки перетворюються в волокна, однак волокна можуть створюватися і незалежно від потоків. Такі волокна не будуть виконуватися до тих пір, поки те що вже виконується в потоці волокно не викличе явно SwitchToFiber (для запуску волокна). Потоки можуть спробувати переключитись на те волокно, яке вже виконується, так що програміст повинен передбачити синхронізацію (щоб уникнути цього явища). Основною перевагою волокон є те, що витрати перемикання між волокнами набагато нижче, ніж перемикання між потоками. Для перемикання між потоками треба увійти в ядро ??і вийти з нього. Перемикання між волокнами зберігає і відновлює кілька регістрів (без усякої зміни режиму).

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

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

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

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

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

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

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

Процеси також можуть використовувати різні типи об'єктів синхронізації. Windows Vista надає безліч механізмів синхронізації (включаючи семафори, мьютекс, критичні області та події). Всі ці механізми працюють з потоками (а не процесами), так що коли потік блокується на семафорі, то інші потоки цього процесу (якщо вони є) можуть продовжувати виконання.

Семафори — це об'єкти режиму ядра і тому вони мають дескриптори безпеки і описувачі. Семафор створюється за допомогою функції Create Semaphore інтерфейсу Win32 API, яка може ініціалізувати його заданим значенням, а також задати максимальне значення. Описувач для семафора може бути здубльований за допомогою Duplicate Handle і переданий в інший процес (щоб по одному і тому ж семафору могли синхронізуватися кілька процесів). Семафору можна дати ім'я в просторі імен Win32. Іноді спільне використання семафора по імені більш зручно, ніж дублювання описувача.

Мьютекс — це теж об'єкти режиму ядра, використовувані для синхронізації, але вони простіші семафорів (оскільки не мають лічильників). По суті це блокування, що мають функції API для блокування (WaitForSingleObject) і розблокування (ReleaseMutex). Подібно описувачам семафорів, описувачі мьютекс можуть дублюватися і передаватися між процесами, щоб потоки в різних процесах могли отримати доступ до одного і того ж мьютексу.

Третій механізм синхронізації називається «критична секція» (critical section). Він реалізує концепцію «критичних областей». У Windows він схожий на мьютекс, за винятком того, що він є локальним для адресного простору створюючого потоку. Оскільки критичні секції не є об'єктами режиму ядра, вони не мають явних описувачів або дескрипторів безпеки і не можуть передаватися між процесами. Блокування та розблокування виконується відповідно викликами EnterCriticalSection і LeaveCriticalSection. Оскільки ці функції API виконуються спочатку в просторі користувача і роблять виклики ядра тільки при необхідності в блокуванні, то вони набагато швидші мьютекс. Критичні секції оптимізовані для комбінованого використання спін-блокувань (на багатопроцесорних системах) і синхронізації ядра (при необхідності). У багатьох додатках більшість критичних секцій так рідко стають об'єктом суперництва або мають таке короткий час утримування, що необхідність у виділенні об'єкта синхронізації ядра ніколи не виникає. Це призводить до дуже істотної економії пам’яті ядра.

1.3.2 Реалізація процесів і потоків в Windows Vista

Розглянемо, як у Windows Vista створюється процес (і початковий потік). Процес створюється тоді, коли інший процес робить виклик CreateProcess інтерфейсу Win32. Цей виклик запускає процедуру (користувальницького режиму) з kernel. dll, яка створює:

1. Перетвориться ім'я виконуваного файлу (заданий у вигляді параметра) із маршруту Win32 в маршрут NT. Якщо виконуваний файл має тільки ім'я (без маршруту у вигляді каталогів), то пошук його ведеться в тих каталогах, які перераховані в якості каталогів за замовчуванням (вони включають і ті, що містяться в змінній оточення PATH — але не тільки їх).

2. Збираються всі параметри створення процесу і передаються (разом з повним маршрутом до виконуваній програмі) власному інтерфейсу NtCreateUserProcess. (Цей API був доданий в Windows Vista для того, щоб подробиці створення процесу можна було реалізовувати в режимі ядра, дозволяючи використовувати процеси як кордони безпеки. Попередні власні API і раніше існують, але викликом CreateProcess вони більше не використовуються.)

3. Працюючи в режимі ядра, NtCreateUserProcess обробляє параметри, а потім відкриває образ програми і створює об'єкт сегмента, який може використовуватися для відображення програми на віртуальний адресний простір нового процесу.

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

5. Диспетчер пам’яті створює адресний простір для нового процесу, виділяючи і ініціалізувавши каталоги сторінок і дескриптори віртуальних адрес, що описують режим ядра (і в тому числі специфічні для процесу області, такі як елемент каталогу сторінок self-map, який дає кожному процесу доступ в режимі ядра до фізичних сторінок всієї таблиці сторінок за допомогою віртуальних адрес ядра).

6. Для нового процесу створюється таблиця описувачів, в яку дублюються всі ті описувачі викликаної сторони, для яких дозволяється спадкування.

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

8. Виконавчий рівень створює і ініціалізує блок Process Environment Block (РЕВ) користувальницького режиму, який використовується як користувальницьким режимом, так і ядром для підтримки інформації про стан процесу.

9. У новому процесі виділяється віртуальна пам’ять, яка використовується для передачі параметрів (в тому числі рядків оточення і командного рядка).

10. Із спеціальної таблиці описувачів (яку підтримує ядро?? для ефективного виділення локально-унікальних ідентифікаторів для процесів і потоків) виділяється ідентифікатор процесу.

11. Виділяється і ініціалізується об'єкт потоку. Виділяється стек користувацького режиму і блок Thread Environment Block (Тев). Ініціалізується запис CONTEXT, який містить початкові значення регістрів процесора для потоку (в тому числі покажчики команд і стека).

12. Об'єкт процесу додається в глобальний список процесів. У таблиці описувачів викликаної сторони виділяється місце під описувачі для об'єктів процесу і потоку. Для початкового потоку виділяється ідентифікатор (з таблиці ідентифікаторів).

13. NtCreateUserProcess повертається в користувальницький режим із створеним новим процесом, що містить єдиний потік, який готовий до роботи, але перебуває в стані припинення.

14. Якщо інтерфейс NT API дає збій, то код Win32 перевіряє, чи не належить даний процес до іншої підсистемі (наприклад, WOW64). Або, можливо, дана програма позначена для виконання під управлінням відладчика. Ці спеціальні випадки обробляються спеціальним кодом користувацького режиму в CreateProcess.

15. Якщо NtCreateUserProcess відпрацював успішно, то процеси Win32 потрібно зареєструвати в процесі csrss. exe підсистеми Win32. Kernel32. dll посилає повідомлення в csrss, яке повідомляє йому про новий процес (а також передає описувачі процесу і потоку, щоб він міг себе дублювати). Процеси і потоки вносяться в таблиці підсистеми (щоб там був повний список всіх процесів і потоків Win32). Потім підсистема показує курсор (вказівник з пісочним годинником), щоб повідомити користувачеві про те, що в даний момент щось відбувається, але курсор все ж можна використовувати. Коли процес робить свій перший виклик графічного інтерфейсу користувача (зазвичай це робиться для створення вікна), то курсор зникає (якщо немає інших викликів) — тайм-аут у нього 2 с.

16. Якщо процес обмежений (як має низькі права Internet Explorer), то маркер модифікується для обмеження доступу до об'єктів з нового процесу.

17. Якщо додаток було помічено як підлягаючий виправленню (shimmed) для сумісної роботи в поточній версії Windows, то застосовуються зазначені виправлення (shims). Виправлення зазвичай укладають в оболонку виклики бібліотек, щоб модифікувати їх поведінку — наприклад, повернути сфальсифікований номер версії або відкласти звільнення пам’яті.

18. І нарешті, виклик NtResumeThread для скасування призупинення потоку і повернення викликала стороні структури, що містить ідентифікатори та описувачі для щойно створених процесу і потоку.

1.3.3 Планування

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

1. Поточний виконуючий потік блокується на семафорі, мьютекс, події, введенні-виведенні і т. д.

2. Потік сигналізує об'єкт (тобто робить up на семафорі або призводить до сигналізування події).

3. Закінчується квант.

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

У випадку 2 працюючий потік також перебуває в ядрі. Однак після сигналізації деякого об'єкта він може продовжити виконання, оскільки сигналізація об'єкта ніколи не призводить до блокування. І все одно потік повинен викликати планувальник, щоб побачити, не звільнився чи в результаті його дій потік з вищим пріоритетом планування (який готовий до виконання). Якщо це так, то відбувається перемикання потоків (оскільки Windows є повністю витісняючої, тобто переключення потоків може статися в будь-який момент, а не тільки в кінці кванта поточного потоку). Однак (у разі багатопроцесорної конфігурації) потік, який став готовим, може бути запланований на виконання на іншому процесорі, а вихідний потік може продовжувати виконання на поточному процесорі (навіть незважаючи на те, що його пріоритет планування нижче).

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

Планувальник викликається також в двох інших випадках: завершується операція введення-виведення; закінчується час очікування.

Перший випадок — потік міг очікувати цього вводу-виводу і тепер він звільнений і може виконуватися. Необхідно зробити перевірку, щоб побачити, чи повинен він витіснити виконуючий потік (оскільки не існує гарантованого мінімального часу виконання). Планувальник виконується не в самому обробнику переривання (оскільки це може призвести до відключення переривань на занадто довгий час). Замість цього в чергу ставиться відкладений виклик процедури (DPC) — він буде виконуватися після завершення обробника переривань. У другому випадку потік виконав down на семафорі або заблокувався на якомусь іншому об'єкті, але з тайм-аутом, який вже минув. І знову-таки обробникові переривання необхідно поставити в чергу DPC (щоб уникнути його виконання під час роботи обробника переривання таймера).

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

Тепер розглянемо сам алгоритм планування. Інтерфейс Win32 API надає два API для роботи з плануванням потоків. Перший — виклик SetPriorityClass, який встановлює клас пріоритету для всіх потоків викликає процесу. Допустимі значення: real-time, high, above normal, normal і idle. Клас пріоритету визначає відносний пріоритет процесу. (Починаючи з Windows Vista клас пріоритету процесу може також використовуватися процесом для того, щоб тимчасово помітити самого себе як фоновий — це означає, що він не повинен заважати ніякої іншої активності системи.) Клас пріоритету встановлюється для процесу, але впливає на реальний пріоритет кожного потоку процесу (він встановлює базове значення пріоритету, з яким стартує потік при створенні).

Другий інтерфейс Win32 API — це SetThreadPriority. Він встановлює відносний пріоритет потоку (можливо, викликаючого потоку — але це не обов’язково) по відношенню до класу пріоритету свого процесу. Допустимі значення: time critical, highest, above normal, normal, below normal, lowest і idle. Потоки time critical отримують найвищий пріоритет планування, а потоки idle — найнижчий (незалежно від класу пріоритету). Інші значення пріоритету підлаштовують базовий пріоритет потоку відносно нормального значення, визначеного класом пріоритету (+2, +1, 0, -1, -2 відповідно). Використання класів пріоритету і відносних пріоритетів потоків полегшує додаткам прийняття рішень за вказівкою пріоритетів.

Планувальник працює таким чином. В системі є 32 пріоритету з номерами від 0 до 31. Поєднання класу пріоритету і відносного пріоритету відображається на 32 абсолютних значення пріоритету (відповідно до табл. 1). Номер у таблиці визначає базовий пріоритет (base priority) потоку. Крім того, кожен потік має поточний пріоритет (current priority), який може бути вище (але не нижче) базового пріоритету.

Таблиця 1. Відповідність пріоритетів Win32 пріоритетам Windows

Класи пріоритетів процесів Win 32

Пріоритети потоків Win 32

Real-time

High

Above normal

Normal

Below Normal

Idle

Tome critical

31

15

15

15

15

15

Highest

26

15

12

10

8

6

Above normal

25

14

11

9

7

5

Normal

24

13

10

8

6

4

Below normal

23

12

9

7

5

3

Lowest

22

11

8

6

4

2

Idle

16

1

1

1

1

1

Для використання цих пріоритетів при плануванні система підтримує масив з 32 списків потоків, які відповідають усім 32 пріоритетам (від 0 до 31) в табл. 1. Кожен список містить готові потоки відповідного пріоритету. Базовий алгоритм планування робить пошук по масиву від пріоритету 31 до пріоритету 0. Як тільки буде знайдений вільний список, то вибирається потік з верху списку і виконується протягом одного кванта. Якщо квант закінчується, то потік переводиться в кінець черги свого рівня пріоритету і наступним вибирається потік з верху списку. Інакше кажучи, коли є багато готових потоків на найвищому рівні пріоритету, то вони виконуються циклічно (по одному кванту часу кожен). Якщо готових потоків немає, то процесор переходить в стан очікування, тобто переводиться в стан більш низького енергоспоживання і чекає переривання.

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

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

Для кожного потоку планувальник підтримує ідею його «ідеального процесора» (ideal processor) і намагається запланувати його виконання саме на цьому процесорі (по можливості). Це покращує продуктивність системи, оскільки використовувані потоком дані, швидше за все, вже є в наявності в приналежному його ідеальному процесору кеші. Планувальник знає про такі багатопроцесорні системи, в яких кожен процесор має свою власну пам’ять і які можуть виконувати програми з будь-якої області пам’яті (однак якщо це не локальна для даного процесора пам’ять, то вартість такої операції вище).

Рис. 1.3.3.1. Windows Vista підтримує 32 пріоритети для потоку

Масив заголовків черг показаний на рис. 1.3.3.1. На схемі показано, що реально є чотири категорії пріоритетів: real-time, user, zero і idle (значення якого фактично дорівнює -1). Це вимагає особливого пояснення. Пріоритети 16−31 називаються пріоритетами реального часу і призначені для створення систем, що задовольняють обмеженням реального часу (таким, як кінцеві терміни). Потоки з пріоритетами реального часу виконуються до потоків з динамічними пріоритетами (але не раніше DPC та ISR). Якщо додаток реального часу хоче виконуватися в системі, то йому можуть знадобитися такі драйвери пристроїв, які не мають тривалого виконання DPC або ISR (оскільки це може викликати пропуск потоками реального часу їх кінцевих термінів).

Звичайні користувачі запускати потоки реального часу не можуть. Якби користувальницький потік виконувався з більш високим пріоритетом, ніж, наприклад, потік клавіатури або миші, і зациклився б, то потік клавіатури або миші не зміг би виконуватися, що призвело б у результаті до зависання системи. Право встановлювати пріоритет реального часу вимагає наявності спеціальної привілеї в маркері процесу. Звичайні користувачі не мають цього привілею.

Потоки додатків зазвичай виконуються з пріоритетами 1−15. За допомогою установки пріоритетів процесів і потоків додаток може визначити, які потоки отримують перевагу. Системні потоки обнулення сторінок (ZeroPage) працюють з пріоритетом 0 і перетворять вільні сторінки в заповнені нулями сторінки. Для кожного реального процесора є окремий потік обнулення сторінок.

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

Тепер розглянемо, коли пріоритет потоку підвищується. По-перше, коли операція введення-виведення завершується і звільняє знаходиться в стані очікування потік, то його пріоритет підвищується (щоб він міг знову швидко запуститися і почати нову операцію вводу-виводу). Ідея полягає в тому, щоб підтримувати завантаження пристроїв введення-виведення. Величина підвищення пріоритету залежить від пристрою введення-виведення — зазвичай для диска це 1, для послідовної лінії - 2, для клавіатури — 6, а для звукової карти- 8.

ПоказатьСвернуть
Заполнить форму текущей работой