Материал: metod_ukaz

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

Указания к выполнению работы

Написать комплект из двух программ, одна из которых посылает данные в разделяемую память, а вторая – читает эти данные. Поскольку механизм разделяемой памяти не содержит средств синхронизации записи и чтения, для синхронизации требуется применить механизм именованных семафоров.

Студент, который находится в списке группы под четным номером, использует стандарт POSIX для получения сегмента разделяемой памяти.

Студент, который находится в списке группы под нечетным номером, использует стандарт SVID для получения сегмента разделяемой памяти.

Шаблон программы 1 представлен ниже:

объявить флаг завершения потока;

объявить идентификатор семафора записи;

объявить идентификатор семафора чтения;

объявить идентификатор разделяемой памяти;

объявить локальный адрес;

функция потока()

{

объявить переменную;

пока (флаг завершения потока не установлен)

{

присвоить переменной случайное значение;

вывести значение переменной на экран;

скопировать значение переменной в локальный адрес;

освободить семафор записи;

ждать семафора чтения;

задержать на время;

}

}

основная программа()

{

объявить идентификатор потока;

создать (или открыть, если существует) разделяемую память;

обрезать разделяемую память до требуемого размера;

отобразить разделяемую память на локальный адрес;

создать (или открыть, если существует) семафор записи;

создать (или открыть, если существует) семафор чтения;

создать поток из функции потока;

ждать нажатия клавиши;

установить флаг завершения потока;

ждать завершения потока;

закрыть семафор чтения;

удалить семафор чтения;

закрыть семафор записи;

удалить семафор записи;

закрыть отображение разделяемой памяти на локальный адрес;

удалить разделяемую память;

}

Шаблон программы 2 представлен ниже:

объявить флаг завершения потока;

объявить идентификатор семафора записи;

объявить идентификатор семафора чтения;

объявить идентификатор разделяемой памяти;

объявить локальный адрес;

функция потока()

{

объявить переменную;

пока (флаг завершения потока не установлен)

{

ждать семафора записи;

скопировать данные из локального адреса в переменную;

вывести значение переменной на экран;

освободить семафор чтения;

}

}

основная программа()

{

объявить идентификатор потока;

создать (или открыть, если существует) разделяемую память;

изменить размер разделяемой памяти на требуемый;

отобразить разделяемую память на локальный адрес;

создать (или открыть, если существует) семафор записи;

создать (или открыть, если существует) семафор чтения;

создать поток из функции потока;

ждать нажатия клавиши;

установить флаг завершения потока;

ждать завершения потока;

закрыть семафор чтения;

удалить семафор чтения;

закрыть семафор записи;

удалить семафор записи;

закрыть отображение разделяемой памяти на локальный адрес;

удалить разделяемую память;

}

Вопросы для самопроверки

  1. Какие программные интерфейсы существуют для получения участка разделяемой памяти?

  2. Какими достоинствами, и какими недостатками обладает способ взаимодействия процессов через разделяемую память?

  3. На основе какого параметра функции открытия разделяемой памяти один и тот же участок становится доступным из разных процессов?

  4. Каким образом участок глобальной разделяемой памяти, описываемой идентификатором, становится доступным в адресном пространстве программы?

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

  6. Сразу при создании участок разделяемой памяти получает нулевую длину. Каким образом впоследствии обеспечивается возможность записи данных в этот участок?

7. Взаимодействие процессов через именованные каналы

Цель работы – знакомство с механизмом взаимодействия процессов через именованные каналы и изучение системных вызовов, обеспечивающих создание, открытие, запись, чтение, закрытие и удаление именованных каналов.

Общие сведения

Создание именованного канала производится вызовом:

int mkfifo(const char *pathname, mode_t mode),

где:

pathname – имя именованного канала;

mode – права доступа к именованному каналу.

Открытие именованного канала производится вызовом:

int open(const char *pathname, int flags),

где:

pathname – имя именованного канала;

flags – флаги, задающие режим доступа к именованному каналу.

Запись данных в именованный канал производится вызовом:

ssize_t write(int fd, const void *buf, size_t count),

где:

fd – дескриптор именованного канала;

buf – буфер для записи данных;

count – количество записанных данных.

Чтение данных из именованного канала производится вызовом:

ssize_t read(int fd, void *buf, size_t count),

где:

fd – дескриптор именованного канала;

buf – буфер для чтения данных;

count – размер буфера.

Закрытие именованного канала производится вызовом:

int close(int fd),

где:

fd – дескриптор именованного канала.

Удаление именованного канала производится вызовом:

int unlink(const char *pathname),

где:

pathname – имя именованного канала.

Указания к выполнению работы

Написать комплект из двух программ, одна из которых записывает данные в именованный канал, а вторая – считывает эти данные. Проверить работу функций с блокировкой и без блокировки.

Шаблон программы 1 представлен ниже:

объявить флаг завершения потока;

объявить дескриптор именованного канала;

функция потока()

{

объявить буфер;

пока (флаг завершения потока не установлен)

{

сформировать сообщение в буфере;

записать сообщение из буфера в именованный канал;

задержать на время;

}

}

основная программа()

{

объявить идентификатор потока;

создать именованный канал;

открыть именованный канал для записи;

создать поток из функции потока;

ждать нажатия клавиши;

установить флаг завершения потока;

ждать завершения потока;

закрыть именованный канал;

удалить именованный канал;

}

Шаблон программы 2 представлен ниже:

объявить флаг завершения потока;

объявить дескриптор именованного канала;

Функция потока()

{

объявить буфер;

пока (флаг завершения потока не установлен)

{

очистить буфер сообщения;

прочитать сообщение из именованного канала в буфер;

вывести сообщение на экран;

}

}

основная программа()

{

объявить идентификатор потока;

создать именованный канал;

открыть именованный канал для чтения;

создать поток из функции потока;

ждать нажатия клавиши;

установить флаг завершения потока;

ждать завершения потока;

закрыть именованный канал;

удалить именованный канал;

8. Взаимодействие процессов через очереди сообщений

Цель работы - знакомство студентов с механизмом взаимодействия процессов – очередями сообщений и с системными вызовами, обеспечивающими создание, закрытие, удаление очередей сообщений, а также передачу и прием сообщений.

Общие сведения

Очередь сообщений – это средство, предоставляемое процессам для взаимодействия. Очереди сообщений содержат встроенный механизм синхронизации, обеспечивающий невозможность чтения сообщения из пустой очереди и записи сообщения в полную очередь.

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

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

Существует несколько видов программных интерфейсов для создания очередей сообщений.

На первом этапе рассмотрим программный интерфейс POSIX.

Очередь сообщений создается следующим вызовом:

mqd_t mq_open(const char *name,

int oflag,

mode_t mode,

struct mq_attr *attr),

где:

name – имя очереди сообщений;

oflag - флаг, управляющий операцией создания очереди сообщений, при создании очереди сообщений необходимо указать флаг O_CREAT;

mode – права доступа к очереди сообщений;

attr – параметры очереди сообщений.

Сообщение помещается в очередь следующим вызовом:

int mq_send(mqd_t mqdes,

const char *msg_ptr,

size_t msg_len,

unsigned msg_prio),

где:

mqdes - идентификатор очереди сообщений;

msg_ptr – указатель на сообщение;

msg_len – длина сообщения;

msg_prio – приоритет сообщения.

Сообщение извлекается из очереди следующим вызовом:

ssize_t mq_receive(mqd_t mqdes,

char *msg_ptr,

size_t msg_len,

unsigned *msg_prio),

где:

mqdes - идентификатор очереди сообщений;

msg_ptr – указатель на буфер для приема сообщения;

msg_len – размер буфера;

msg_prio – приоритет

сообщения.

Очередь сообщений закрывается следующим вызовом:

int mq_close(mqd_t mqdes).

Очередь сообщений удаляется следующим вызовом:

int mq_unlink(const char *name).

В программном интерфейсе SVID очередь создается вызовом:

int msgget(key_t key, int msgflg);

где:

key_t key – ключ, получаемый функцией ftok();

int msgflg – флаг, задающий права на выполнение операций, типичным значением флага является значение IPC_CREAT|0644.

После создания очереди передача сообщений осуществляется вызовом:

int msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg);

где:

int msqid – идентификатор очереди;

struct msgbuf *msgp – сообщение, сформированное в структуре:

struct msgbuf {

long mtype; /* тип сообщения, должен быть > 0 */

}

Вопросы для самопроверки

  1. Перечислите отличия именованного канала от неименованного канала.

  2. Как осуществляется синхронизация чтения и записи в именованном канале?

  3. Каким образом обеспечить открытие, закрытие, запись и чтение данных из именованного канала «без ожидания»?

  4. Где ОС хранит данные, записываемые процессом в именованный канал?

  5. Как создать именованный канал в терминальном режиме?

  6. В чем отличие именованных каналов ОС семейства Linux от именованных каналов ОС семейства Windows?

Источник: https://studfile.net/preview/16676187/