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

Текст програми

#include <stdio.h>

#include <pthread.h>

#include <sys/neutrino.h>

pthread_t thread_id1;

pthread_t thread_id2;

void * long_thread1(void *notused)

{

int n;

for(n=0;n<5;n++)

{

printf("Eto pervii potok , TID %d - N povtora %d \n", thread_id1, n );

sleep(2);

}

}

void * long_thread2(void *notused)

{

int m;

for(m=0; m<5; m++)

{

printf("Eto vtoroi potok , TID %d - N povtora %d \n", thread_id2 , m );

sleep(1);

}

}

int main(void)

{

printf("Prog threads PID %d \n",getpid());

pthread_create(&thread_id1, NULL, long_thread1, NULL);

pthread_create(&thread_id2, NULL, long_thread2, NULL);

sleep(40);

return(1);

}

Послідовність дій

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

Результати

# gcc pthread.c

# `pwd`a.out

Prog threads PID 852000

Etot pervii potok , TID 0 - N povtora 0

Etot vtoroi potok , TID 0 - N povtora 0

Etot vtoroi potok , TID 3 - N povtora 1

Etot pervii potok , TID 2 - N povtora 1

Etot vtoroi potok , TID 3 - N povtora 2

Etot vtoroi potok , TID 3 - N povtora 3

Etot pervii potok , TID 2 - N povtora 2

Etot vtoroi potok , TID 3 - N povtora 4

Etot pervii potok , TID 2 - N povtora 3

Etot pervii potok , TID 2 - N povtora 4

#

Лабораторна робота №4. Повідомлення Короткі теоретичні відомості Архітектура і структура обміну повідомленнями

Три ключові вирази:

• «Клієнт посилає (sends) повідомлення серверу»;

• «Сервер приймає (receives) повідомлення від клієнта»;

• «Сервер відповідає (replies) клієнту».

Ці вирази в точності відповідають дійсним іменам функцій, які використовуються для передачі повідомлень в QNX/Neutrino.

Мінімальний корисний набір функцій включає в себе функції ChannelCreate(), ConnectAttach(), MsgReply(), MsgSend() і MsgRecievef().

Розіб'ємо обговорення на дві частини: окремо обговоримо функції, які застосовуються на стороні клієнта, і окремо - ті, що застосовуються на стороні сервера.

Клієнт

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

Це має на увазі забезпечення двох умов: клієнт повинен «вміти» спочатку встановити з'єднання з сервером, а потім обмінюватися з ним даними за допомогою повідомлень - як в одну сторону (запит - «send»), так і в іншу (відповідь - «reply») .

Встановлення з'єднання

Перше, що потрібно зробити - це встановити з'єднання за допомогою функції ConnectAttach(), описаної в такий спосіб:

#include <sys / neutrino.h>

int ConnectAttach (int nd, pid_t pid, int chid, unsigned index, int flags);

Функції ConnectAttach() передаються три ідентифікатора;

• nd- дескриптор вузла (Node Descriptor),

• pid- ідентифікатор процесу (process ID)

• chid - ідентифікатор каналу (channel ID).

Разом ці три ідентифікатора, які зазвичай записуються у вигляді «ND / PID / CHID», однозначно ідентифікують сервер, з яким клієнт бажає з'єднатися. Аргументи index і flags ми тут просто проігноруємо (встановимо їх в нуль).

Отже, припустимо, що ми хочемо під'єднатися до процесу, що знаходиться на нашому вузлі і має ідентифікатор 77, по каналу з ідентифікатором 1. Нижче наведено приклад програми для виконання цього:

int coid;

coid = ConnectAttach (0, 77 "1, 0, 0);

Можна бачити, що присвоєнням ідентифікатору вузла (nd) нульового значення ми повідомляємо ядру про те, що ми бажаємо встановити з'єднання на локальному вузлі.

З'єднатися треба з процесом 77 і по каналу 1.

З цього моменту є ідентифікатор сполуки - невелике ціле число, яке однозначно ідентифікує з'єднання клієнта з конкретним сервером по заданому каналу.

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

ConnectDetach(coid);

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

Передача повідомлення з боку клієнта здійснюється із застосуванням функції MsgSend(). Ми розглянемо це на прикладі:

#include <sys / neutrino.h>

int MsgSend(int coid, const void * smsg, int sbytes, void * rmsg, int rbytes);

Аргументами функції MsgSend() є:

• ідентифікатор з'єднання з цільовим сервером (coid);

• покажчик на передане повідомлення (smsg);

• розмір переданого повідомлення (sbytes);

• покажчик на буфер для відповідного повідомлення (rmsg);

• розмір відповіді повідомлення (rbytes).

Передамо повідомлення процесу з ідентифікатором 77 по каналу 1:

#include <sys / neutrino.h>

char * smsg = «Це буфер виводу»; char rmsg [200];

int coid;

// Встановити з'єднання

coid = ConnectAttach (0, 77, 1, 0, 0);

if (coid == -1) {

fprintf (stderr, «Помилка ConnectAttach до 0/77/1! \n");

perror (NULL);

exit (EXIT_FAILURE);

// Зв'язатися

if (MsgSend (coid, smsg, strlen (smsg) + 1, rmsg, sizeof (rmsg)) == -1)

{Fprintf (stderr, «Помилка MsgSendXn»);

  perror (NULL);

exit (EXIT_FAILURE);

if (strlen (rmsg)> 0)

  {

printf («Процес з ID 77 вернув \"% s \"\n», rmsg);

}

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

Сервер - створення каналу

Сервер повинен створити канал - те, до чого приєднувався клієнт, коли викликав функцію ConnectAttach(). Зазвичай сервер, одного разу створивши канал, приберігає його «про запас».

Канал створюється за допомогою функції ChannelCreate() і знищується за допомогою функції ChannelDestroy():

#include <sys / neutrino.h>

int ChannelCreate (unsigned flags);

int ChannelDestroy (int chid);

Таким чином, для створення каналу сервер повинен зробити так:

int chid;

chid = ChannelCreate (0);

Тепер у нас є канал. У цьому пункті клієнти можуть під'єднатися (за допомогою функції ConnectAttach() до цього каналу і почати передачу повідомлень:

Рис. 3. Зв'язок між каналом сервера і клієнтським з'єднанням.

Обробка повідомлень

У термінах обміну повідомленнями, сервер відпрацьовує схему обміну в два етапи:

• етап «прийому» (receive);

• етап «відповіді» (reply).

Рис. 4. Взаємозв'язок функцій клієнта і сервера при обміні повідомленнями

Обговоримо два найпростіших варіанта відповідних функцій, MsgReceiveQ і MsgReply ().

#include <sys / neutrino.h>

int MsgReceive (int chid, void * rmsg, int rbytes, struct _msg_info * info);

int MsgReply (int rcvid, int status, const void * msg, int nbytes);

Чотири основні елементи:

1 Клієнт викликає функцію MsgSend() і вказує їй на буфер передачі (покажчиком smsg і довжиною sbytes). Дані передаються в буфер функції MsgReceive() на стороні сервера, за адресою rmsg і довжиною rbytes. Клієнт блокується.

2 Функція MsgReceive() сервера розблокується і повертає ідентифікатор відправника rcvid, який буде згодом використаний для відповіді. Тепер сервер може використовувати отримані від клієнта дані.

3 Сервер завершив обробку повідомлення і тепер використовує ідентифікатор відправника rcvid, отриманий від функції MsgReceive(), передаючи його функції MsgReply(). Зауважте, що місце розташування даних для передачі функції MsgReply() задається як покажчик на буфер (smsg) певного розміру (sbytes). Ядро передає дані клієнту.

4 Нарешті, ядро передає параметр sts, який використовується функцією MsgSend() клієнта для повернення значення. Після цього клієнт розблокується.

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

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