Материал: 6262

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

181

}

//Отображаем разделяемую память в адресное пространство текущего

//процесса. Обратите внимание на то, что для правильного сравнения мы

//явно преобразовываем значение -1 к указателю на целое.

if((array = (int *)shmat(shmid, NULL, 0)) == (int *)(-1)){ perror("lab11.3: Не могу подсоединить разделяемую память\n"); exit(-1);

}

while(1){

// Процессы-читатели создаются через случайное время ts ts = tsleep; sleep (ts);

no++;

if((id = fork()) < 0){

printf("Ошибка fork() для процесса %d\n", no); continue;

}

if(id == 0) {

// Дочерние процессы-читатели

printf ("Стартовал читатель %d: semid=%d shmid=%d\n", no, semid, shmid);

// Читатель пытается начать

чтение

sembuf[0].sem_num = 0;

// Увеличивает значение семафора №0 на 1

sembuf[0].sem_op

= 1;

 

 

sembuf[1].sem_num =

1;

// Ждет нуля семафора №1

sembuf[1].sem_op

=

0;

 

 

if (semop (semid, sembuf, 2) < 0) {

perror ("SEMOP - читатель ожидание 0:\n"); exit (-1);

}

if(*array == 0){

printf ("Читатель %d: нет информации для чтения...\n", no); semctl (semid, 0, SETVAL, 0);

exit(-1);

}

// Читает

printf ("Читатель %d читает: книгу %d\n", no, array[0]); tr = tread; sleep (tr);

// Завершает чтение: уменьшает значение семафора №0 на 1 errsem = semctl (semid, 0, GETVAL);

if (errsem < 0) {

perror ("SETVAL0"); exit (-2);

}

if(errsem > 0) { sembuf[0].sem_num = 0; sembuf[0].sem_op = -1;

if (semop (semid, sembuf, 1) < 0) {

perror ("SEMOP - немогу уменьшить семафор:\n"); exit (-3);

}

}

printf("Читатель %i: завершил чтение и вышел...\n", no); exit(0);

}

// Удаляем "зомби"

while((id=waitpid(-1, NULL, WNOHANG)) > 0) // Ждем без блокировки printf("ДП id=%i - завершил работу...\n", id);

}

while((id=wait(NULL)) > 0){

printf("ДП id=%i - завершил работу...\n", id);

}

puts("lab11.3 - Все процессы-читатели: завершили работу..."); return 0;

}

182

Замечание

Для правильного запуска процессов-читателей — смотри пункт 5.5.2 по выполнению лабораторной работы.

Алгоритм процесса-писателя, представлен на листинге 5.4:

создаются, если не созданы, ключи key1 и key2, массив семафоров sembuf[2] и сегмент разделяемой памяти shareseg;

процесс-писатель обнуляет значения семафоров независимо от того, создал он их или использует уже созданные;

выполняется цикл по количеству задуманных публикаций;

вкаждом цикле:

блокируется подключение новых процессов-читателей;

ожидается завершение чтения, - уже читающих;

объявляется о написании новой книги и выполняется сам процесс — случайный интервал времени twrite;

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

нового произведения случайный интервал времени tsleep;

после завершения всех циклов:

ожидается завершение всех читателей и обнуляется shareseg;

печатается сообщение о завершении работы программы и осуществляется выход.

Листинг 5.4. Алгоритм, моделирующий действия процесса-писателя

#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/sem.h> #include <sys/shm.h> #include <sys/types.h> #include <sys/wait.h>

/* Программа-писатель */

#define tsleep

(rand () % 3 + 1)

#define twrite

(rand () % 5 + 1)

int main (void) {

 

key_t key1;

// Ключ набора семафоров

key_t key2;

// IPC ключ сегмента разделяемой памяти

int semid;

 

// Идентификатор набора семафоров

int shmid;

 

// Идентификатор разделяемой памяти

int *array;

// Указатель на разделяемую память

int ts;

 

// Время обдумывания нового произведения

int tw;

 

// Время продолжительности очередной записи

struct

sembuf sembuf[2];

// Доступ к семафору — набор из двух семафоров

sembuf[0].sem_flg = 0;

// Операции

с блокировкой

sembuf[1].sem_flg = 0;

// Операции

с блокировкой

struct

shmid_ds ds;

// Структура параметров разделяемой памяти

// Создание и инициализация набора 2-х семафоров if((key1 = ftok ("/home/upk/lab11", 2)) < 0){

printf("lab11.4: Не могу сгенерировать ключ: key1\n");

183

exit(-1);

}

if ((semid = semget (key1, 2, 0666 | IPC_CREAT)) < 0) { perror ("SEMGET");

return (-1);

}

//Генерируем IPC ключ из имени файла и номера экземпляра области

//разделяемой памяти 1

if((key2 = ftok("/home/upk/lab11",3)) < 0){ printf("lab11.4: Не могу сгенерировать ключ: key2\n"); exit(-1);

}

//Пытаемся создать разделяемую память для сгенерированного ключа.

//Размер памяти определяем как размер массива из одного целого числа,

//права доступа 0666 – чтение и запись разрешены для всех

if((shmid = shmget(key2, sizeof(int), 0666|IPC_CREAT)) < 0){ /* В случае возникновения ошибки — завершаем работу */ printf("lab11.4: Не могу открыть разделяемую память\n"); exit(-1);

}

//Пытаемся отобразить разделяемую память в адресное пространство текущего

//процесса. Обратите внимание на то, что для правильного сравнения мы

//явно преобразовываем значение -1 к указателю на целое.

if((array = (int *)shmat(shmid, NULL, 0)) == (int *)(-1)){ printf("lab11.4: Не могу подсоединить разделяемую память\n"); exit(-1);

}

//Начальное значение разделяемой памяти и печать идентификаторов array[0] = 0;

printf ("Писатель стартовал: semid=%d shmid=%d\n", semid, shmid);

//Обнуление значений семафоров

if (semctl (semid, 0, SETVAL, 0) < 0) {// Обнуляем семафор №0 perror ("SETVAL0");

return (-2);

}

if (semctl (semid, 1, SETVAL, 0) < 0) {// Обнуляем семафор №1 perror ("SETVAL1");

return (-2);

}

while (array[0] < 10) {

// Цикл из 10 публикаций

// Остановка процессов-читателей

 

if (semctl (semid, 1, SETVAL, 1) < 0) { perror ("SETVAL1");

return (-2);

}

// Читаем параметры разделяемой памяти if (shmctl (shmid, IPC_STAT, &ds) < 0) {

perror ("IPC_STAT:"); return (-2);

}

printf("Писатель: число подключений к разд.памяти=%i\n", (int)ds.shm_nattch);

// Писатель ожидает

printf ("Писатель ожидает завершения процесса чтения...\n"); sembuf[0].sem_num = 0; // Ждет нуля семафора №0 sembuf[0].sem_op = 0;

if (semop (semid, sembuf, 1) < 0) {

perror ("SEMOP - писатель ожидание 0:\n"); exit (-1);

}

array[0] +=1;

printf("Писатель объявляет: semid=%i shmid=%i книга=%i\n",

184

semid, shmid, array[0]); // Пишет

tw = twrite; sleep (tw);

printf ("Писатель записал: книгу %d и дает возможность ее прочесть\n", array[0]);

// Разрешение на чтение для процессов-читателей if (semctl (semid, 1, SETVAL, 0) < 0) {

perror ("SETVAL1 - set 0 to sem №1:"); return (-2);

}

// Писатель обдумывает новое произведение ts = tsleep; sleep (3*ts);

}

sembuf[0].sem_num = 0; // Ждет нуля семафора №0 sembuf[0].sem_op = 0;

if (semop (semid, sembuf, 1) < 0) {

perror ("SEMOP - писатель ожидание 0:\n"); exit (-1);

}

array[0] = 0;

//Когда цикл — завершен: Отсоединяем разделяемую память от

//адресного пространства текущего процесса и завершаем работу if(shmdt(array) < 0){

printf("lab11.4: Не могу отсоединить разделяемую память\n"); exit(-1);

}

printf("lab11.4: Нормально завершила работу...\n"); return 0;

}

Замечание

Программа-писатель работает в паре с программой-читателем, поэтому для правильного их запуска — смотри подраздел 5.5.2 по выполнению лабораторной работы.

5.4 Передача сообщений

Третьим и последним средством, входящим в System V IPC, являются очереди сообщений.

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

Такая модель позволяет задействовать один и тот же канал связи для передачи данных в двух направлениях между несколькими процессами.

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

инициализации их, для организации взаимодействия процессов;

специальных действий, для освобождения системных ресурсов по окончании

взаимодействия.

Пространством имен очередей сообщений является то же самое множество зна-

185

чений ключа, генерируемых с помощью функции ftok(), а для передачи данных используются системные примитивы, в виде функций send() и receive(), которым в качестве параметра передаются IPC-дескрипторы очередей сообщений, однозначно идентифицирующие эти данные во всей вычислительной системе.

Очереди сообщений имеют следующие особенности:

располагаются в адресном пространстве ядра операционной системы в

виде однонаправленных списков и имеют ограничение по объему информации, хранящейся в каждой очереди.

каждый элемент списка представляет собой отдельное сообщение.

каждое сообщения имеет атрибут, называемый типом сообщения.

Выборка сообщений из очереди, соответствующая примитиву receive(), может быть выполнена тремя способами:

В порядке FIFO, независимо от типа сообщения.

В порядке FIFO, для сообщений конкретного типа.

Первым выбирается сообщение с минимальным типом, не превышающим

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

Реализация примитивов send() и receive() обеспечивает скрытое от пользователя взаимоисключение, во время помещения сообщения в очередь или его получения из очереди. Она также обеспечивает:

блокировку процесса, при попытке выполнить примитив receive() над пустой

очередью или очередью, в которой отсутствуют сообщения запрошенного типа;

блокировку процесса, при попытке выполнить примитив send() для очереди, в которой нет свободного места.

Замечание

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

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

#include <types.h> #include <ipc.h> #include <msg.h>

int msgget(key_t key, int msgflg);

int msgsnd(int msqid, struct msgbuf *ptr, int length, int flag);

int msgrcv(int msqid, struct msgbuf *ptr, int length, long type, int flag); int msgctl(int msqid, int cmd, struct msqid_ds *buf);

Системный вызов msgget() предназначен для выполнения операции доступа к очереди сообщений и, в случае ее успешного завершения, возвращает дескриптор System V IPC для этой очереди: целое неотрицательное число, однозначно характеризующее очередь сообщений внутри вычислительной системы и использующееся

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