3. ВЗАИМОДЕЙСТВИЕ ПОТОКОВ ЧЕРЕЗ НЕИМЕНОВАННЫЕ КАНАЛЫ
Цель работы – знакомство со средством взаимодействия потоков и процессов – неименованными каналами – и с системными вызовами, обеспечивающими создание и закрытие неименованных каналов, а также передачу и прием данных через неименованные каналы.
Общие сведения
Одним из средств взаимодействия процессов и потоков является неименованный канал. Канал не только обеспечивает передачу данных, но и поддерживает синхронизацию между потоками и процессами. Свойствами канала являются следующие положения: «при попытке записать данные в полный канал процесс блокируется» и «при попытке чтения данных из пустого канала процесс блокируется».
Канал создается с помощью вызова
int pipe(int filedes[2]),
где filedes[2] – массив из двух файловых дескрипторов, один из которых используется для записи данных (filedes[1]), а второй (filedes[0]) – для чтения данных.
Чтение данных из канала производится операцией ssize_t read(int fd, void *buf, size_t count),
где fd – файловый дескриптор для чтения; buf – адрес буфера для чтения данных; count – размер буфера.
Для записи данных в канал используется операция ssize_t write(int fd, const void *buf, size_t count),
где fd – файловый дескриптор для записи; buf – адрес буфера для записи данных; count – количество байт, предназначенных для записи.
Каждый из дескрипторов канала отдельно закрывается следующим вызовом int close(int fd).
Указания к выполнению работы
Написать программу, содержащую 2 потока, обменивающихся информацией через неименованный канал (pipe).
Шаблон программы:
объявить флаг завершения потока 1; объявить флаг завершения потока 2;
11
объявить идентификатор неименованного канала; функция потока 1()
{
объявить буфер; пока (флаг завершения потока 1 не установлен)
{
сформировать сообщение в буфере; записать сообщение из буфера в неименованный канал; задержать на время;
}
}
функция потока 2()
{
объявить буфер; пока (флаг завершения потока 2 не установлен)
{
очистить буфер; прочитать сообщение из неименованного канала и записать его в буфер;
вывести сообщение на экран;
}
}
основная программа()
{
объявить идентификатор потока 1; объявить идентификатор потока 2; создать неименованный канал; создать поток из функции потока 1; создать поток из функции потока 2; ждать нажатия клавиши; установить флаг завершения потока 1;
установить флаг завершения потока 2; ждать завершения потока 1; ждать завершения потока 2; закрыть неименованный канал;
}
12
Вопросы для самопроверки
1.Как обеспечивается синхронизация записи и чтения в неименованном канале?
2.Как использовать неименованный канал для взаимодействия процессов?
3.Как для неименованного канала организовать чтение и запись данных «без ожидания»?
4.Как реализовать функциональность неименованного канала с помощью семафоров?
5.Как с помощью неименованных каналов организовать двунаправленное взаимодействие?
6.Каким отношением должны быть связаны процессы для организации взаимодействия между ними через неименованные каналы?
13
4. СИНХРОНИЗАЦИЯ ПРОЦЕССОВ С ПОМОЩЬЮ ИМЕНОВАННЫХ СЕМАФОРОВ
Цель работы – знакомство со средством синхронизации процессов – именованными семафорами – и с системными вызовами, обеспечивающими создание, закрытие и удаление именованных семафоров, а также захват и освобождение именованных семафоров.
Общие сведения
Именованные семафоры позволяют организовать синхронизацию процессов в операционной системе. За счет того, что при создании и открытии именованного семафора ему передается «имя» – цепочка символов, 2 процесса могут получить указатель на один и тот же семафор. Другими словами, в отличие от мьютексов и неименованных семафоров именованные семафоры могут координировать доступ к критическому ресурсу не только на уровне нескольких потоков одной программы, но и на уровне нескольких выполняющихся программ – процессов.
В системе этот семафор реализуется в виде специального файла, время жизни которого не ограничено временем жизни процесса, его создавшего.
Существует несколько видов программных интерфейсов для создания именованных семафоров. В данном случае рассматривается программный интерфейс POSIX.
Именованный семафор создается следующим вызовом: sem_t *sem_open(const char *name,
int oflag, mode_t mode,
unsigned int value),
где name – имя семафора; oflag – флаг, управляющий операцией создания семафора, при создании семафора необходимо указать флаг O_CREAT; mode
– права доступа к семафору, могут быть установлены, например, в 0644; value – начальное состояние семафора.
Именованный семафор закрывается вызовом int sem_close(sem_t *sem).
При входе в критический участок необходимо вызвать функцию int sem_wait(sem_t *sem).
При выходе из критического участка необходимо вызвать функцию int sem_post(sem_t *sem).
14
Именованный семафор удаляется вызовом
int sem_unlink(const char *name).
Указания к выполнению работы
Поскольку именованные семафоры используются для координации взаимодействия процессов, то для выполнения работы необходимо написать две программы.
Необходимо выбрать общий ресурс, например файл, и записывать в него данные из двух программ.
Обе программы должны создать (или открыть, если создан) один и тот же именованный семафор, должны создать (или открыть, если создан) один и тот же файл, но записывать в файл разные символы.
Необходимо убедиться, что при отсутствии именованного семафора процессы выводят символы в файл в произвольном порядке, например:
1212121212121212121212121212121212121212121212121212121212121.
При использовании именованного семафора процессы выводят символы в файл в определенном порядке, например:
111111111122222222221111111111222222222211111111112222222222.
Шаблон одной из программ (вторая программа отличается от первой выводом в файл другого символа):
объявить флаг завершения потока; объявить идентификатор именованного семафора; объявить дескриптор файла; функция потока()
{
объявить переменную типа символ и присвоить ей значение ‘1’; пока (флаг завершения потока не установлен)
{
захватить именованный семафор; в цикле несколько раз выполнять
{
выводить символ в файл; задержать на время;
}
15