Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
6262.pdf
Скачиваний:
37
Добавлен:
13.02.2021
Размер:
6.54 Mб
Скачать

179

5.3 Задача о читателях и писателях

Аппарат разделяемых сегментов памяти предоставляет нескольким процессам возможность одновременного доступа к общей области памяти:

Ранее, мы рассмотрели доступ к разделяемой памяти, который не требовал какого-либо согласования действий между процессами.

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

В качестве средства синхронизации удобно использовать семафоры.

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

тщательно проанализировать задачу и выделить в процессах критические интервалы (области прогаммы);

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

Вкачестве учебного примера, демострирующего совместное использование синхронизации и разделяемые сегменты памяти, рассмотрим задачу «Читатели-писа- тели». Общая интерпретация этой задачи — следующая.

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

для написания книги, требуется случайный интервал времени twrite;

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

читают, если публикация имеется, на что требуется случайный интервал времени tread;

завершают работу, если публикация отсутствует.

Общие требования:

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

процесс-писатель должен дождаться завершения процесса чтения читате-

лей, но не допускает к чтению процессы, которые пришли во время его написания новой книги.

Представим решение данной задачи с помощью одного разделяемого сегмента памяти shareseg и массива из двух семафоров sembuf[2]:

sembuf[0] — число читателей, приступивших к чтению;

sembuf[1] — значение 0 — можно читать.

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

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

процесс-читатель, в бесконечном цикле, через случайный интервал времени

180 tsleep порождает дочерние процессы;

каждый дочерний процесс: ожидает возможности чтения, а затем —

завершает работу, если shareseg=0, или читает случайное время tread, если shareseg>0.

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

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

/* Процесс одного читателя */

#define tsleep (rand () % 3 + 1) #define tread (rand () % 5 + 1)

int main (int argc, char *argv []) {

key_t key1;

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

key_t key2;

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

int semid;

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

int shmid;

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

int *array;

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

int id;

// Идентификатор процесса-читателя

int no = 0;

// Номер читателя

int ts;

// Интервал времени между порождением дочерних процессов

int tr;

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

struct sembuf sembuf[2];

// Доступ к семафору

sembuf[0].sem_flg = 0;

// Операции

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

sembuf[1].sem_flg = 0;

// Операции

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

int errsem;

// Ошибка возврата при установке семафора

puts("lab11.3 - Процессы-читатели: стартовала..."); // Создание и инициализация набора 2-х семафоров

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

}

/* Пытаемся получить идентификатор разделяемой памяти.

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

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

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

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",

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