Нитки
В операційних системах нитки є паралельними потоками виконання у складі одного процесу. (Деякі перекладачі переводять термін "thread" -
"нитка" - як "потік"). У складі процесу може бути запущено кілька ниток,
які виконуються паралельно (або квазипараллельно - у режимі поділу
часу процесора). Можна вважати (і в деяких операційних системах це
дійсно так), що в будь-якому процесі є принаймні одна нитка -
головна - та, в якій виконується функція
. Головна нитка може породжувати інші нитки. У програмі процесу нитка має вигляд
процедури/функції, яка викликається спеціальним системним викликом і після
виклику виконується паралельно з запустила її ниткою.
Нитки, що належать одному процесу, спільно використовують майже всі ресурси
свого процесу. Однак у кожної нитки є і деякі власні ресурси.
Очевидно, що першим таким власним ресурсом нитки є процесорний
час. Два інших власних ресурсу нитки - вектор стану і стек. Наявність у
нитки власного вектора стану дозволяє системі переривати і відновлювати
виконання ниток, тобто, планувати нитки в режимі витісняє
багатозадачності. Наявність у нитки власного стека дозволяє запускати на
паралельне виконання кілька екземплярів однієї і тієї ж процедури/функції.
Оскільки локальні змінні функції розміщуються в стеці, кожна нитка має
свій набір локальних змінних, які незалежні від інших ниток.
Статичні ж змінні програми - загальні для всіх ниток, також всі нитки
використовують один і той же екземпляр коду функції.
При введенні поняття ниток розробники систем мали на увазі два можливих
призначення:
-
по-перше, створення ниток відбувається швидше, ніж створення процесу, таким
чином, нитки підходять для завдань, що вимагають швидкого розпаралелювання з
мінімальними накладними витратами (наприклад, розпаралелювання виконання
запити до бази даних);
-
по-друге, оскільки нитки спільно використовують ресурси свого процесу, вони
є зручним способом програмування тісно пов'язаних паралельних робіт,
тобто таких, які використовують великий обсяг спільних даних і інших
ресурсів.
У клонах операційної системи Unix є два підходи до забезпечення механізму
ниток:
-
У деяких операційних системах цього сімейства (наприклад, Open Unix) в ядро
системи включені "віртуальні процесори", так звані, легковагі
процеси, спеціально підтримують багатопоточність. Відповідно, системні
виклики, що забезпечують роботу з нитками, звертаються безпосередньо до ядра і
використовують механізм легковагих процесів.
-
Розробники інших операційних систем (наприклад, Free BSD) виходили з того,
що в клонах Unix створення "повноцінного" процесу - операція сама по собі
швидка, таким чином, вони бачили своє основне завдання у забезпеченні
можливості спільного використання ресурсів нитками. Цю задачу вони вирішили
введенням в ядро такого розширення системного виклику fork, яке
забезпечує спадкування дочірнім процесом будь-яких (за вибором програміста)
ресурсів батьківського процесу. Для сумісності з раніше розробленого
програмним забезпеченням сам системний виклик fork залишається без
змін, а його розширення отримує інше ім'я (Free BSD - pfork).
Нитку в таких системах фактично являє собою окремий процес,
використовує ресурси батьківського процесу разом з ним.
Оскільки статична пам'ять програми - загальна для всіх ниток, при роботі з
разом використовуваними змінними можливі конфлікти доступу. Конфлікти можуть
виникати при одночасному зверненні до таких змінних. Виявляються такі
конфлікти не в відмову в доступі або в фатальних помилок, а в можливе
порушенні цілісності спільно використовуваних даних. Відповідальність за
збереження такої цілісності лежить на програміста. Програміст повинен
забезпечити, щоб логічно закінчені операції над спільно використовуваними
даними виконувалися як транзакції, тобто, під час їх виконання інші
нитки, які працюють паралельно, не могли змінювати значення цих даних.
Ділянки коду програми, які виконують таку роботу спільно з використовуваними
ресурсами, називаються критичними секціями. Для забезпечення цілісності
потрібно, щоб дві або більше ниток не могли перебувати своїх критичних
секціях одночасно. Це - проблема, найбільш часто виникає при
програмуванні багатопоточних додатків, однак, завдання синхронізації та
підрахунку ресурсів в таких додатках також виникають. Для вирішення цієї
проблеми можна застосовувати звичайні семафори - як і в аналогічних завдання для
процесів. Однак цілей роботи з нитками у багатьох операційних системах
вводяться спеціальні засоби, призначені для забезпечення взаємодії
тільки в межах одного процесу. Це "полегшені" семафори-лічильники
ресурсів, исключа.щие семафори і сигналізують семафори. Фактично всі ці
кошти являють собою звичайні семафори, що використовуються тільки в межах
одного процесу, однак, інтерфейси цих коштів відрізняється від інтерфейсу
семафорів процесів і є більш специфічними і зручним у застосуванні.
Системні виклики Unix/Linux
Стандартами POSIX і Single Unix Specification встановлений API для ниток. R
операційній системі Linux механізм реалізації ниток слід підходу BSD:
системі є виклик clone, безпосередньо звертається до ядра
системи і представляє собою розширену версію fork,
стандартний ж API забезпечується функціями бібліотеки pthread, які
є надбудовою над викликом clone
.r
Наступні функції библитеки ниток, що відповідають стандарту, забезпечують
основні пов'язані з роботою ниток операції в Linux. (Подані не всі
можливості бібліотеки pthread).
Виконання ниток
Функція pthread_create створює
нову гілку. Цієї функції передається вказівник на атрибути виконання нитки
(більшість ниток виконується зі стандартними атрибутами), вказівник на
потокову функцію (функцію, виконувану в нитки), параметр потокової функції,
адреса змінної типу pthread_t, в яку
pthread_create записує ідентифікатор створеної нитки.
Функція pthread_create
запускає потокову функцію в нової нитки, і вона виконується паралельно з
іншими нитками процесу.
Потокове функція має прототип:
void * имя_функции(void *);
Та виберіть параметр, і повертає значення такої функції - покажчики, таким чином,
функція може приймати і повертати будь-яку інформацію.
Нитка завершується при завершенні виконання потокової функції або при виконанні
функції pthread_exit
.r
Функція pthread_join застосовується
для ниток так само, як системний виклик wait
застосовується для процесів: вона змушує що викликала її нитка очікувати завершення
зазначеної у виклику нитка і дозволяє що викликала нитки отримати значення, яке
повернула завершилася нитку.
Виконання нитки може бути примусово припинено з іншого нитки за допомогою
функції pthread_cancel,
якої задається ідентифікатор "тої, яку вбивають" нитки.
Виключають семафори
Виключає семафор видається в Linux-програмі змінної типу pthread_mutex_t.
Семафор повинен бути ініціалізований за допомогою функції
pthread_mutex_init
. Як правило, додатки задовольняють стандартні параметри ініціалізації
виключають семафор.
Функції pthread_mutex_lock
і pthread_mutex_unlock
аналогічні семафорным операціями і V відповідно.
Функція pthread_mutex_trylock
аналогічна pthread_mutex_lock
, але при неможливості захопити семафор не переводить нитка в очікування, а
возвращает відповідний ознака.
Фактично виключає семафор є звичайним двійковим семафор з початковим
значенням 1.
Сигналізують семафори
Сигналізуючий семафор видається в Linux-програмі змінної типу pthread_cond_t.
Фактично такий семафор є звичайним двійковим семафор з початковим
значенням 0. Семафор повинен бути ініціалізований за допомогою функції
pthread_cond_init
. Параметрів ініціалізації немає.
Функція pthread_cond_wait
реалізує на сигнальному семафорі семафорную операцію
. Вона блокує що викликала її нитка до тих пір, поки не буде отримано сигнал про
установки семафора 1.
Функція pthread_cond_signal
аналогічна семафорної операції V вона розблокує одну з ниток,
чекаючих на семафорі. Функція pthread_cond_broadcast
розблокує всі нитки, які очікують на семафорі.
Лічильники ресурсів
Лічильники ресурсів для ниток - це звичайні загальні семафори. Такий сигнал (семафор)
видається у програмі змінної типу sem_t. Такий сигнал (семафор)
повинен бути ініціалізований за допомогою функції sem_init,
а потім до нього можуть застосовуватися функції sem_wait
і sem_post, що виконують
відповідно і V
-операції.
Функція sem_getvalue
дозволяє дізнатися поточне значення семафора.
Увага! При підготовці програми, яка містить звернення до
функцій бібліотеки pthread, слід явно вказувати компілятору
необхідність підключення цієї бібліотеки. Так, програма, розглянута нами в
прикладі до даної лабораторній роботі компілюється командою:
cc-o ganesha8 ganesha8.o-lpthread
Увага! Хоча ми рекомендуємо нашим студентам книгу М.Митчел,
Дж.Оулдем, А.Самьюэл "Програмування для Linux", радимо ставитися
до прикладів цієї книги обережно: так, програмний код, наведений у ній на
стор. 90-91 (Голова - "Потоки") , веде до безвиході.
Приклад виконання наведено тут.
Довідковий матеріал
Обрані системні виклики Linux/Unix. Короткий опис.
Довідник бібліотечних функція мови З: частина 1,
частина 2 (кодування кирилиця ibm866).