Материал: 396_OS_uchebnoe_posobie_part1_2012

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

Листинг 20. Работа с семафорами

10.semop(semid,&sop_unlock[0], 1);

6.3.6. Разделяемая память

Интенсивный обмен данными между процессами с использованием рассмотренных механизмов межпроцессного взаимодействия (каналы, FIFO, очереди сообщений) может вызвать падение производительности системы. Это, в первую очередь, связано с тем, что данные, передаваемые с помощью этих объектов, копируются из буфера передающего процесса в буфер ядра и затем в буфер принимающего процесса.

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

Безусловно, процессы должны предварительно «договориться» о правилах использования разделяемой памяти. Например, пока один из процессов производит запись данных в разделяемую память, другие процессы должны воздержаться от работы с ней. К счастью, задача кооперативного использования разделяемой памяти, заключающаяся в синхронизации выполнения процессов, легко решается с помощью семафоров.

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

1)Сервер получает доступ к разделяемой памяти, используя семафор.

2)Сервер производит запись данных в разделяемую память.

3)После завершения записи сервер освобождает разделяемую память с помощью семафора.

4)Клиент получает доступ к разделяемой памяти, «запирая» ресурс с помощью семафора.

5)Клиент производит чтение данных из разделяемой памяти и освобождает её, используя семафор.

Для создания или для доступа к уже существующей разделяемой памяти используется системный вызов shmget:

#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h>

int shmget(key_t key, int size, int shmflag);

Функция возвращает дескриптор разделяемой памяти в случае успеха, и –1 в случае неудачи. Аргумент size определяет размер создаваемой области памяти в байтах. Значения аргумента shmflag задают права доступа к объекту и специальные флаги IPC_CREAT и IPC_EXCL. Заметим, что вызов shmget лишь создаёт или обеспечивает доступ к разделяемой памяти, но не позволяет

106

работать с ней. Для работы с разделяемой памятью (чтение и запись) необходимо сначала присоединить (англ. attach) область вызовом shmat:

#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h>

char *shmat(int shmid, char *shmaddr, int shmflag);

Вызов shmat возвращает адрес начала области в адресном пространстве процесса размером size, заданным предшествующим вызовом shmget. В этом адресном пространстве взаимодействующие процессы могут размещать требуемые структуры данных для обмена информацией. Правила получения этого адреса следующие:

1)Если аргумент shmaddr нулевой, то система самостоятельно выбирает адрес.

2)Если аргумент shmaddr отличен от нуля, значение возвращаемого адреса зависит от наличия флажка SHM_RND в аргументе shmflag:

Если флажок SHM_RND не установлен, система присоединяет разделяемую память к указанному shmaddr адресу.

Если флажок SHM_RND установлен, система присоединяет разделяемую память к адресу, полученному округлением в меньшую сторону shmaddr до некоторой определённой величины SHMLBA.

По умолчанию разделяемая память присоединяется с правами на чтение и запись. Эти права можно изменить, указав флажок SHM_RDONLY в аргументе shmflag.Таким образом, несколько процессов могут отображать область разделяемой памяти в различные участки собственного виртуального адресного пространства. Окончив работу с разделяемой памятью, процесс отключает (англ. detach) область вызовом shmdt:

#incluude <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h>

int shmdt(char *shmaddr);

При работе с разделяемой памятью необходимо синхронизировать выполнение взаимодействующих процессов: когда один из процессов записывает данные в разделяемую память, остальные процессы ожидают завершения операции. Обычно синхронизация обеспечивается с помощью семафоров, назначение и число которых определяется конкретным использованием разделяемой памяти. Можно привести примерную схему обмена данными между двумя процессами (клиентом и сервером) с использованием разделяемой памяти.

107

Для синхронизации процессов использована группа из двух семафоров. Первый семафор служит для блокирования доступа к разделяемой памяти, его разрешающий сигнал – 0, а 1 является запрещающим сигналом. Второй семафор служит для сигнализации серверу о том, что клиент начал работу. Необходимость применения второго семафора обусловлена следующими обстоятельствами: начальное состояние семафора, синхронизирующего работу с памятью, является открытым (0), и вызов сервером операции mem_lock заблокирует обращение к памяти для клиента. Таким образом, сервер должен вызвать операцию mem_lock только после того, как разделяемую память заблокирует клиент. Назначение второго семафора заключается в уведомлении сервера, что клиент начал работу, заблокировал разделяемую память и начал записывать данные в эту область. Теперь, при вызове сервером операции mem_lock его выполнение будет приостановлено до освобождения памяти клиентом, который делает это после окончания записи строки «Здравствуй, Мир!».

 

 

Листинг

21. Файл заголовков shmem.h

 

1.

 

#define

MAXBUFF

80

 

2.

 

#define

PERM

0666

 

3.

 

/*Структура данных разделяемой памяти*/

4.

 

typedef

struct mem_msg{

5.

 

int

segment;

 

 

6.

 

char

buff[MAXBUFF];

7.

 

} Message;

 

 

8.

 

/*0жидание начала выполнения клиента*/

9.

 

static struct sembuf proc_wait[1] = { 1, -1, 0 };

10.

 

/*Уведомление сервера о том, что клиент начал работу*/

11.

 

static struct sembuf proc_start[1] = { 1, 1, 0 };

12.

 

/*Блокирование разделяемой памяти*/

13.

 

static struct sembuf mem_lock[2] = { 0, 0, 0,

14.

 

 

 

0, 1, 0 };

15.

 

/*Освобождение ресурса*/

16.

 

static struct sembuf mem_unlock[1] = { 0, -1, 0 };

Листинг 22. Программа, реализующая сервер

1.#include <sys/types.h>

2.#include <sys/ipc.h>

3.#include <sys/sem.h>

4.#include <sys/shm.h>

5.#include “shmem.h”

6.

7.int main(void){

8.Message * msgptr;

9.

key_t

key;

10.

int

shmid, semid;

108

Листинг 22. Программа, реализующая сервер

11.

12.if ((key = ftok(“server”, ‘A’)) < 0){

13.printf(“Невозможно получить ключ\n”); exit(1);

14.}

15.

16./*Создадим область разделяемой памяти*/

17.if ((shmid = shmget(key, sizeof(Message), PERM | IPC_CREAT)) < 0){

18.printf(“Невозможно создать область\n”); exit(1);

19.}

20.

21.if ((msgptr = (Message *)shmat(shmid, 0, 0)) < 0){

22.printf (“Ошибка присоединения\n”); exit(1);

23.}

24.

25./*Создадим группу из двух семафоров */

26.

27.if ((semid = semget(key, 2, PERM | IPC_CREAT)) < 0){

28.printf(“Невозможно создать семафор\n”); exit(1);

29.}

30.

31./* Ждм, пока клиент начнет работу и *

* заблокирует разделяемую память

*/

32.if (semop(semid, &proc_wait[0], 1) < 0){

33.printf(“Невозможно выполнить операцию\n”); exit(1);

34.}

35.

36./* Ждём, пока клиент закончит запись в *

*разделяемую память и освободит её. *

* После этого заблокируем её

*/

37.if (semop(semid, &mem_lock[0], 2) < 0){

38.printf(“Невозможно выполнить операцию\n”); exit(1);

39.}

40.

41./* Выведем сообщение на терминал */

42.printf(“%s”, msgptr->buff);

43.

44./* Oсвободим разделяемую память */

45.if (semop(semid, &mem_unlock[0], 1) < 0){

46.printf(“Невозможно выполнить операцию\n”); exit(1);

47.}

109

Листинг 22. Программа, реализующая сервер

48.

49./*Oтключимся от области*/

50.if (shmdt(msgptr) < 0){

51.printf(“Ошибка отключения\n”); exit(1);

52.}

53.

54./* Всю остальную работу по удалению объектов *

* сделает клиент

*/

55. exit(1);

 

56. }

 

Листинг 23. Программа, реализующая клиент

1.#include <sys/types.h>

2.#include <sys/ipc.h>

3.#include <sys/sem.h>

4.#include <sys/shm.h>

5.#include “shmem.h”

6.int main (void){

7.Message * msgptr;

8.

key_t

key;

9.

int

shmid, semid;

10.if ((key = ftok(“server”, ‘A’)) < 0){

11.printf(“Невозможно получить ключ\n”); exit(1);

12.}

13.if ((shmid = shmget(key, sizeof(Message), 0)) < 0){

14.printf(“Ошибка доступа\n”); exit(1);

15.}

16.if ((msgptr = (Message *) shmat(shmid, 0, 0)) < 0){

17.printf(“Ошибка присоединения\n”);

18.exit(1);

19.}

20./* Получим доступ к семафору*/

21.if ((semid = semget(key, 2, PERM)) < 0){

22.printf(“Ошибка доступа\n”); exit(1);

23.} /*3аблокируем разделяемую память*/

24.if (semop(semid, &mem_lock[0], 2) < 0){

25.printf(“Невозможно выполнить операцию\n”); exit(1);

26.} /*Уведомим сервер о начале работы*/

27.if (semop(semid, &proc_start[0], 1) < 0){

28.printf(“Невозможно выполнить операцию\n”); exit(1);

29.}

30./*3апишем в разделяемую память сообщение*/

110

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