Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
MOS-labs-3.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
655.36 Кб
Скачать

Зв'язок між процесами (ipc)

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

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

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

Передача повідомлень

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

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

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

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

Qnx як мережа

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

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

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

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

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

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

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

• можливість декомпозиції задачі і модульної організації вирішення;

• зручність супроводу;

• надійність.

Концепція поділу завдання на частини, тобто на кілька незалежних задач, є дуже потужною. І саме така концепція лежить в основі QNX. Операційна система QNX складається з безлічі незалежних модулів, кожен з яких наділений деякою зоною відповідальності. Ці модулі незалежні і реалізовані в окремих процесах. Єдина можливість встановити залежність цих модулів один від одного - налагодити між ними інформаційний зв'язок за допомогою невеликої кількості строго визначених інтерфейсів.

Це природно веде до спрощення супроводу програмних продуктів, завдяки незначному числу взаємозв'язків. Оскільки кожен модуль чітко визначений, і усувати несправності в одному такому модулі буде набагато простіше - тим більше, що він не пов'язаний з іншими.

Запуск процесу

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

Запуск процесу з командного рядка

Наприклад, при запуску процесу з командного інтерпретатора ви можете ввести командний рядок:

$ program1

Ця вказівка наказує командному інтерпретатору запустити програму program1 і чекати завершення її роботи. Або, ви могли набрати:

$ Program2 &

Ця вказівка наказує командному інтерпретатору запустити програму program2 без очікування її завершення. У такому випадку говорять, що програма program2 працює у фоновому режимі.

Якщо ви бажаєте скорегувати пріоритет програми до її запуску, ви можете застосувати команду nice - точно так само, як в Unix:

$ Nice program3

Запуск процесу з програми

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

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

Розглянемо деякі з функцій, які забезпечує для запуску інших процесів (або підміни одного процесу іншим):

• system();

• сімейство функцій Ехес();

• сімейство функцій spawn();

• fork();

• vfork().

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

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

Запуск потоку

Тепер, коли ми знаємо, як запустити інший процес, давайте розглянемо, як здійснити запуск іншого потоку.

Будь-який потік може створити інший потік в тому ж самому процесі; на це не накладається ніяких обмежень (звичайно, за винятком обсягу пам'яті) Найбільш загальний шлях реалізації цього - використання виклику функцій pthread_create():

#include <pthread.h>

int int

pthread_create (pthread_t * thread, const pthread_attr_t * attr, void * (* start_routine) (void *), void * arg);

Функція pthread_create() має чотири аргументи:

• thread - покажчик на pthread_t, де зберігається ідентифікатор потоку;

• attr - атрибутний запис;

• start_routine - підпрограма, з якої починається потік;

• arg - параметр, який передається підпрограмі start_routine.

Відзначимо, що покажчик thread і атрибутний запис (attr) - необов'язкові елементи, ви можете передавати замість них NULL.

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

pthread_t tid;

pthread_create (& tid, ...

printf («Новий потік має ідентифікатор% d \ n", tid);

Таке застосування абсолютно типове, оскільки вам часто може знадобитися знати, який потік виконує яку ділянку коду.

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

Новий потік починає виконання з функції start_routine(), з параметром arg.

Атрибутний запис потоку

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

Синхронізація

Найпростіший метод синхронізації - це «приєднання» (joining) потоків. Реально ця дія означає очікування завершення.

Приєднання виконується одним потоком, що чекає завершення іншого потоку. Очікуючий потік викликає pthreadjoin():

#include <pthread.h>

int

pthread_join (pthread_t thread, void ** value_ptr);

Функції pthreadjoin() передається ідентифікатор потоку, до якого ви бажаєте приєднатися, а також необов'язковий аргумент value_ptr, який може бути використаний для збереження повертаємого приєднуваним потоком значення. (Ви можете передати замість цього параметра NULL).

Отримання ідентифікатору потоку

У функції pthread_create() в якості першого аргументу покажчик на pthread_t. Там і буде збережений ідентифікатор новоствореного потоку.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]