Теорія операційної системи

:: Меню ::

Головна
Представлення даних в обчислювальних системах
Машинні мови
Завантаження програм
Управління оперативною пам'яттю
Сегментна і сторінкова віртуальна пам'ять
Комп'ютер і зовнішні події
Паралелізм з точки зору програміста
Реалізація багатозадачності на однопроцесорних комп'ютерах  
Зовнішні пристрої
Драйвери зовнішніх пристроїв
Файлові системи
Безпека
Огляд архітектури сучасних ОС

:: Друзі ::

Карта сайту
 

:: Статистика ::

 

 

 

 

 

Привороты отвороты, шапка с отворотом купить москва, отворот жены, отворот школа магии в москве. | постельное белье интернет магазин постелька

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

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

  • struct Thread; У тексті обговорюватиметься, що має бути ця структура, звана дескриптором нитки.
  • Thread * Threadcreate(void (*Threadbody)(void)); Створити нитку, виконуючу функцію Threadbody.
  • void Threadswitch(); Ця функція припиняє поточну нитку і активізує чергову, готову до виконання.
  • void Threadexit () ; Припиняє виконання поточної нитки.

Зараз ми не обговорюємо методів синхронізації ниток і взаємодії
між ними (для синхронізації були б корисні також функції void
Deactivatethread(); І void Activatethread(struct Thread *);). Нас цікавить лише питання: що ж ми повинні сделать, щоб перемкнути нитки?
функція Threadswitch називається диспетчером або планувальником (scheduler) і поводиться таким чином.

  • Вона передає управління на наступну активну нитку.
  • Поточна нитка залишається активна, і через деякий час знову отримає управління.
  • При цьому вона отримає управління так, як ніби Threadswitch була звичайною функцією і повернула управління в крапку, з якої вона була викликана.

Очевидно, що функцію Threadswitch не можна реалізувати на мові високого рівня, начеб З, бо це має бути функція, яка не повертає [негайно] управління в ту крапку, з якої вона була викликана. Вона викликається з однієї нитки, а передає управління в іншу. Це вимагає прямих маніпуляцій стеком і записом активізації і зазвичай досягається використанням асемблера або асемблерних вставок. Деякі ЯВУ (Ada, Java, Occam) надають примітиви створення і перемикання ниток у вигляді спеціальних синтаксичних конструкцій.
Найпростішим варіантом, здавалося б, буде проста передача управління на нову нитку, наприклад, командою безумовної передачі управління по покажчику. При цьому весь описувач нитки (struct Thread) складатиметься лише з адреси, на яку треба передати управління. Беда лише в тому, що цей варіант не працюватиме.
Дійсно, кожна з ниток виконує програму, що складається з вкладених викликів процедур. Для того, щоб нитка нормально продовжила виконання, нам потрібно відновити не лише адресу поточної команди, але і стек викликів (див. разд. Побічно-регістровий режим із зсувом). Тому ми приходимо до такої архітектури.

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

Приклад 8.1. Кооперативний перемикач потоків

Thread * thread_queue_head;
Thread * thread_queue_tail;
Thread * current_tread;
Thread * old__thread;
void Taskswitch () { old_thread=current_thread; add_to_queue_tail(current_thread); current_thread=get_from_queue_head(); asm { .
move bx, old_thread
push bp
move ах, sp
move thread_sp[bx], ах
move bx, current_thread
move ах, rhread_sp[bx]
pop bp
}
return;
}

Якщо система програмування передбачає, що при виклику функції повинні зберігатися певні регістри (як, наприклад, С-компілятори для х86 зберігають при викликах регістри SI і DI (Esi/edi в 1386)), то вони також зберігаються в стеку. Тому запропонований нами варіант також автоматично зберігатиме і відновлюватиме всі необхідні регістри.
Зрозуміло, що окрім покажчиків стека і стекового кадру struct Thread повинна містити ще деякі поля. Як мінімум, вона повинна містити покажчик на наступну активну нитку. Система повинна зберігати покажчики описувач поточної нитки і на кінець списку. При цьому Threadswitch переставляє поточну нитку в кінець списку, а поточною робить наступну за нею в списку. Всі нитки, що знов активізуються, також ставляться в кінець списку. При цьому список не зобов'язаний бути двонаправленим, адже ми витягуємо елементи лише з початку, а додаємо лише в кінець.
Часто в літературі такий список називають чергою ниток (thread queue) або чергою процесів. Така черга присутня у всіх відомих авторові реалізаціях багатозадачних систем. Крім того, черги ниток використовуються і при організації черг чекання різних подій, наприклад, при реалізації семафорів Дейкстри.
Планувальник, заснований на Threadswitch тобто на принципі перемикання за ініціативою активної нитки, використовується у ряді експериментальних і учбових систем. Цей же принцип, званий кооперативною багатозадачністю реалізований в бібліотеках мов Simula 67 і Modula-2. MS Windows 3.x також мають засіб для організації кооперативного перемикання завдань — системний виклик Getnextevent.
Часто кооперативні нитки називають не нитками, а співпрограмами — адже вони викликають один одного, подібно до підпрограм. Єдина відзнака такого виклику від виклику процедури полягає в тому, що такий виклик не іє-рархичен — викликана програма може знов передати управління початковою і залишитися при цьому активною.
Основною перевагою кооперативної багатозадачності є простота відладки планувальника. Крім того, знімаються всі колізії, пов'язані з критичними секціями і тому подібними труднощами, — адже нитка може просто не віддавати нікому управління, поки не буде готова до цього.
З іншого боку, кооперативна багатозадачність має і серйозні недоліки.
По-перше, необхідність включати в програму виклики Threadswitch ускладнює програмування взагалі і перенесення програм з однозадачних або інакше організованих багатозадачних систем зокрема.
Особливо неприємна вимога регулярно викликати Threadswitch для обчислювальних програм. Найчастіше такі програми виконують відносно короткий внутрішній цикл, швидкість роботи якого визначає швидкість всієї програми. Для "плавної" багатозадачності необхідно викликати Threadswitch з тіла цього циклу. Робити виклик на кожному кроці Циклу недоцільно, тому необхідно буде написати код, схожий на приведений в прикладі 8.2.

Приклад 8.2. Внутреній цикл програми в кооперативно багатозадачній середі

int counter; // змінна-лічильник
while(condition){
// Викликати Threadswitch кожні rate циклів.
counter++;
if (counter % rate == 0) Threadswitch();
.... // Власне обчислення j
}

Умовний оператор і виклик функції у внутрішньому циклі сильно ускладнюють роботу оптимізуючим компіляторам і приводять до розривів конвеєра команд, що може дуже помітно понизити продуктивність. Виклик функції на кожному кроці циклу приводить до ще більших накладних витрат і, відповідно, до ще більшого уповільнення.
По-друге, зловмисна нитка може захопити управління і нікому не віддавати його. Просто не викликати Threadswitch і все. Це може статися не лише із-за злих намірів, але і просто помилково.
Тому така схема виявляється непридатна для розрахованих на багато користувачів систем і часто не дуже зручна для інтерактивних однопользовательських.
Чомусь більшість комерційних програм для Win16, у тому числі і що поставлялися самою фірмою Microsoft, недостатньо активно використовували виклик Getnextevent. Замість цього такі програми монопольно захоплювали процесор і малювали відомі всім користувачам цієї системи "пісочний годинник". В цей час система ніяк не реагує на запити і інші дії користувача окрім натиснення кнопки RESET або клавіш <Ctrl>+<alt>+<del>.
По-третє, кооперативна ОС не може виконуватися на симетричній багатопроцесорній машині, а додатки, написані з розрахунку на таку ОС, не можуть скористатися перевагами многопроцессорності.
Простий аналіз показує, що кооперативні багатозадачні системи придатні лише для учбових проектів або тих ситуацій, коли програмістові на швидку руку необхідно створити багатозадачне ядро. Друга ситуація видається декілька дивною — навіщо для серйозної роботи може потрібно швидко зроблене ядро, якщо існує багато готових систем реального часу, а також загальнодоступних (freeware або public domain) у вигляді вихідних текстів реалізацій таких ядер?


:: Реклама ::

Adaptor de plastic ABS тут.

 

:: Посилання ::


 

 

 


Copyright © Kivik, 2017