освободить именованный семафор; задержать на время;
}
}
основная программа()
{
создать (или открыть, если существует) именованный семафор; создать (или открыть, если существует) файл; создать поток из функции потока; ждать нажатия клавиши; установить флаг завершения потока; ждать завершения потока; закрыть файл; закрыть именованный семафор;
удалить именованный семафор;
}
Вопросы для самопроверки
1.Какие программные интерфейсы для именованных семафоров существуют?
2.В чем отличие именованных семафоров от неименованных семафоров?
3.Дайте сравнительную характеристику программных интерфейсов семафоров.
4.Как реализовать определенную очередность записи данных в файл с помощью именованного семафора (например, первый процесс всегда первым начинает запись в файл)?
5.Опишите действия, которые выполняются над именованным семафором при вызове операций sem_wait() и sem_post().
6.Какими операциями с именованным семафором можно проверить занятость ресурса без блокирования процесса?
7.Какими операциями с именованным семафором можно проверить занятость ресурса с определенной периодичностью?
16
5. ВЗАИМОДЕЙСТВИЕ ПРОЦЕССОВ ЧЕРЕЗ РАЗДЕЛЯЕМУЮ ПАМЯТЬ
Цель работы – знакомство с механизмом обмена данными между процессами – разделяемой памятью – и с системными вызовами, обеспечивающими создание разделяемой памяти, отображение ее на локальную память, передачу данных, чтение данных, закрытие и удаление разделяемой памяти.
Общие сведения
Участок разделяемой памяти создается вызовом
int shm_open(const char *name, int oflag, mode_t mode),
где name – имя участка разделяемой памяти; oflag – флаги, определяющие тип создаваемого участка разделяемой памяти; mode – права доступа к участку разделяемой памяти.
Размер участка разделяемой памяти устанавливается вызовом int ftruncate(int fd, off_t length),
где fd – дескриптор разделяемой памяти, полученный как результат вызова функции shm_open(); length – требуемый размер разделяемой памяти.
Отображение разделяемой памяти на локальный адрес создается вызовом
void *mmap(void *addr, size_t length,
int prot, int flags, int fd,
off_t offset),
где addr – начальный адрес отображения; length – размер отображения; prot – параметр, определяющий права чтения/записи отображения; flags – параметр, определяющий правила видимости отображения процессами; fd – дескриптор разделяемой памяти; offset – смещение на участке разделяемой памяти относительно начального адреса.
Удаление отображения разделяемой памяти на локальный адрес производится вызовом
int munmap(void *addr, size_t length),
где addr – локальный адрес отображения; length – размер отображения. Закрытие участка разделяемой памяти производится вызовом
17
int close(int fd),
где fd – дескриптор разделяемой памяти.
Для удаления участка разделяемой памяти используется вызов int shm_unlink(const char *name),
где name – имя участка разделяемой памяти.
Указания к выполнению работы
Написать комплект из двух программ, одна из которых посылает данные в разделяемую память, а вторая – читает эти данные. Поскольку механизм разделяемой памяти не содержит средств синхронизации записи и чтения, для синхронизации требуется применить механизм именованных семафоров.
Шаблон программы 1:
объявить флаг завершения потока; объявить идентификатор семафора записи; объявить идентификатор семафора чтения;
объявить идентификатор разделяемой памяти; объявить локальный адрес; функция потока()
{
объявить переменную; пока (флаг завершения потока не установлен)
{
присвоить переменной случайное значение; вывести значение переменной на экран;
скопировать значение переменной в локальный адрес; освободить семафор записи; ждать на семафоре чтения; задержать на время;
}
}
основная программа()
{
объявить идентификатор потока; создать (или открыть, если существует) разделяемую память;
обрезать разделяемую память до требуемого размера; отобразить разделяемую память на локальный адрес;
18
создать (или открыть, если существует) семафор записи; создать (или открыть, если существует) семафор чтения; создать поток из функции потока; ждать нажатия клавиши; установить флаг завершения потока; ждать завершения потока; закрыть семафор чтения; удалить семафор чтения; закрыть семафор записи; удалить семафор записи;
закрыть отображение разделяемой памяти на локальный адрес; удалить разделяемую память;
}
Шаблон программы 2:
объявить флаг завершения потока; объявить идентификатор семафора записи; объявить идентификатор семафора чтения;
объявить идентификатор разделяемой памяти; объявить локальный адрес; функция потока()
{
объявить переменную; пока (флаг завершения потока не установлен)
{
ждать на семафоре записи; скопировать данные из локального адреса в переменную; вывести значение переменной на экран; освободить семафор чтения;
}
}
основная программа()
{
объявить идентификатор потока; создать (или открыть, если существует) разделяемую память;
изменить размер разделяемой памяти на требуемый размер;
19
отобразить разделяемую память на локальный адрес; создать (или открыть, если существует) семафор записи; создать (или открыть, если существует) семафор чтения; создать поток из функции потока; ждать нажатия клавиши; установить флаг завершения потока; ждать завершения потока; закрыть семафор чтения; удалить семафор чтения; закрыть семафор записи; удалить семафор записи;
закрыть отображение разделяемой памяти на локальный адрес; удалить разделяемую память;
}
Вопросы для самопроверки
1.Какие программные интерфейсы существуют для получения участка разделяемой памяти?
2.Какими достоинствами и какими недостатками обладает способ взаимодействия процессов через разделяемую память?
3.На основе какого параметра функции открытия разделяемой памяти один и тот же участок становится доступным из разных процессов?
4.Каким образом участок глобальной разделяемой памяти, описываемой идентификатором, становится доступным в адресном пространстве программы?
5.С какой целью в предлагаемых шаблонах программ используется пара семафоров – семафор записи и семафор чтения?
6.Сразу при создании участок разделяемой памяти получает нулевую длину. Каким образом впоследствии обеспечивается возможность записи данных в этот участок?
20