lab10
.docxМИНОБРНАУКИ РОССИИ
Санкт-Петербургский государственный
электротехнический университет
«ЛЭТИ» им. В.И. Ульянова (Ленина)
Кафедра вычислительной техники
Отчет по лабораторной работе №10
по дисциплине «Организация процессов и программирование в среде Linux»
Студент гр. |
|
Преподаватель |
Разумовский Г.В. |
Тема: СИНХРОНИЗАЦИЯ ПРОЦЕССОВ
С ПОМОЩЬЮ СЕМАФОРОВ
Цель работы: знакомство с организацией семафоров, системными
функциями, обеспечивающими управление семафорами, и их использованием для решения задач взаимоисключения и синхронизации.
Задание:
Написать две программы, экземпляры которых запускаются параллельно и с различной частотой обращаются к общему файлу. Каждый процесс из первой группы (Писатель) пополняет файл определенной строкой символов и выводит ее на экран вместе с именем программы. Процессы второй группы (Читатели) считывают весь файл и выводят его на экран. Писатели имеют приоритет перед Читателями. Пока один Писатель записывает строку в файл, другим Писателям и всем Читателям запрещено обращение к файлу. Читатели могут одновременно читать файл, если нет Писателей, готовых к записи в файл. Писатель заканчивает работу, после того как выполнит N-кратную запись строки в файл. Читатель заканчивает работу после прочтения текущего содержимого файла. Синхронизация процессов должна выполняться с помощью семафоров.
Ход работы
Результат работы двух писателей и двух читателей (начало)
Результат работы двух писателей и двух читателей (конец)
Текст выходного файла
Текст writer.cpp
// на всякий случай: ipcrm -s sem_id
#include <iostream>
#include <fstream>
#include <string>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
/*
Номер семаформа в множественном семафоре:
0-й -- для файла (для отслеживания писателей, которые хотят писать)
1-й -- количество писателей, которые хотят записать
2-й -- количество читателей, которые хотят прочитать
3-й -- общее количество работающих программ
*/
using namespace std;
int sem_key = 420; // ключ для создания/открытия семафора
int nProg = 4;
string file_name = "file.txt"; // имя файла, чтобы записывать/считывать
//{индекс простого семафора, код операции, флаг операции}
//semaphore_file_decrease
struct sembuf sem_file_dec = {0, -1, 0}; // уменьшаем семафор на 1, отвечающий за доступ 1-го писателя к файлу
struct sembuf sem_file_in = {0, 1, 0}; // увеличиваем семафор на 1, отвечающий за доступ 1-го писателя к файлу
struct sembuf sem_writer_com = {1, 0, 0}; // сравниваем активных писателей с 0 и ждём, пока они станут =0
//semaphore_writers_decrease
struct sembuf sem_writer_dec = {1, -1, 0}; // уменьшаем семафор на 1, отвечающий за активных писателей
struct sembuf sem_writer_in = {1, 1, 0}; // увеличиваем семафор на 1, отвечающий за активных писателей
//semaphore_readers_compare
struct sembuf sem_reader_com = {2, 0, 0}; // сравниваем активных читателей с 0 и ждём, пока они станут =0
struct sembuf sem_reader_dec = {2, -1, 0}; // уменьшаем семафор на 1, отвечающий за активных читателей
struct sembuf sem_reader_in = {2, 1, 0}; // увеличиваем семафор на 1, отвечающий за активных читателей
//semaphore_processes_decrease
struct sembuf sem_proc_dec = {3, -1, 0}; // уменьшаем семафор на 1, отвечающий за число активных программ
struct sembuf sem_proc_in = {3, 1, 0}; // увеличиваем семафор на 1, отвечающий за число активных программ
using namespace std;
int main (int argc, char* argv[]) // 1 -- количество строк, 2 -- время задержки
{
int sem_id; // ID семафора, возвращаемый при создании/открытии
ofstream file; // файл для записи
cout << "Процесс id=" << getpid() << "\n" << "Имя файла " << file_name << "\n" << "Ключ семафора " << sem_key << "\n";
// semget(ключ семафора, количество семафоров в наборе (4 штуки), флаги для создания семафора и с правом доступа у всех)
sem_id = semget(sem_key, nProg, IPC_CREAT | IPC_EXCL | 0666); // создаём семафор
if (sem_id != -1)
{
cout << "Семафор id=" << sem_id << " создан процессом id=" << getpid() << "\n";
// при создании семафора увеличиваем его на 1
// этот семафор нужен только для писателей; для отслеживания возможности записи в файл в данный момент
// при входе в цикл делаем -1, и тогда он будет = 0 (потому что он уже +1 ПРИ создании), поэтому остальные (писатели) не могут сделать -1 в цикле
// так как не стоит флаг IPC_NOWAIT, который сразу возвращает ошибку,
// и остальные программы вынуждены ждать, пока семафор будет =1,
// чтобы снова сделать -1 (пока =0 они ждут, так как <0 быть не может)
semop(sem_id, &sem_file_in, 1);
}
else
{
sem_id = semget(sem_key, nProg, IPC_CREAT); // открываем семафор
if (sem_id == -1){
cout << "Семафор не был открыт процессом id=" << getpid() << "\n";
exit(-1);
}
cout << "Семафор id=" << sem_id << " открыт процессом id=" << getpid() << "\n";
}
// увеличиваем количество активных процессов
semop(sem_id, &sem_proc_in, 1);
cout << "Текущее количество работающих программ " << semctl(sem_id, 3, GETVAL, 0) << "\n\n";
for (int i = 0; i < atoi(argv[1]); i++) // начинаем запись в файл
{
cout << "Итерация на запись writer'ом строки " << i + 1 << " началась\n";
//cout << "Увеличиваем число активных writer'ов в семафоре процессом id=" << getpid() << "\n";
semop(sem_id, &sem_writer_in, 1);
//cout << "Ждём окончания всех активных reader'ов в семафоре процессом id=" << getpid() << "\n";
semop(sem_id, &sem_reader_com, 1);
// cout << "Уменьшаем возможность доступа к файлу в семафоре процессом id=" << getpid() << " и ЖДЁМ остальных\n";
// то же самое, что при создании, только здесь идет декремент
semop(sem_id, &sem_file_dec, 1); // уменьшаем семафор файла, чтобы другие не могли получить доступ к файлу
cout << "Открываем файл на запись \"" << file_name << "\" процессом id=" << getpid() << "\n";
file.open(file_name, ios::app);
cout << "Записываем строку " << i + 1 << " процессом id=" << getpid() << "\n" << "Процесс id=" << getpid() << ", строка " << i + 1 << "\n";
file << "Процесс id=" << getpid() << ", строка " << i + 1 << "\n";
cout << "Закрываем файл \"" << file_name << "\" на запись процессом id=" << getpid() << "\n";
file.close();
cout << "Увеличиваем возможность доступа к файлу в семафоре процессом id=" << getpid() << " (возвращаем как было)\n";
semop(sem_id, &sem_file_in, 1); // увеличиваем семафор файла, чтобы другие снова могли получить доступ к файлу (возвращаем всё, как было)
cout << "Уменьшаем число активных writer'ов в семафоре процессом id=" << getpid() << "\n";
semop(sem_id, &sem_writer_dec, 1);
cout << "Итерация на запись writer'ом строки " << i + 1 << " завершена\n\n";
sleep(atoi(argv[2])); // ждём указанное в качестве аргумента время
}
// уменьшаю количество активных процессов в семафоре на 1
// в итоге у самой последней программы после этого шага будет 0, и она всё удалит
semop(sem_id, &sem_proc_dec, 1);
// сравниваю количество процессов (если == 0, то захожу, если нет, пропускаю)
if (semctl(sem_id, 3, GETVAL, 0) == 0)
{
semctl(sem_id, IPC_RMID, 0); // удаляю семафор последней программой (последний процесс удаляет все 4 семафора)
cout << "Семафор id=" << sem_id << " удалён\n\n";
}
return 0;
}
Текст reader.cpp
// ./reader
#include <iostream>
#include <fstream>
#include <string>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
/*
Номер семаформа в множественном семафоре:
0-й -- для файла (для отслеживания писателей, которые хотят писать)
1-й -- количество писателей, которые хотят записать
2-й -- количество читателей, которые хотят прочитать
3-й -- общее количество работающих программ
*/
using namespace std;
int sem_key = 420; // ключ для создания/открытия семафора
int nProg = 4;
string file_name = "file.txt"; // имя файла, чтобы записывать/считывать
struct sembuf sem_file_dec = {0, -1, 0}; // уменьшаем семафор на 1, отвечающий за доступ 1-го писателя к файлу
struct sembuf sem_file_in = {0, 1, 0}; // увеличиваем семафор на 1, отвечающий за доступ 1-го писателя к файлу
struct sembuf sem_writer_com = {1, 0, 0}; // сравниваем активных писателей с 0 и ждём, пока они станут =0
struct sembuf sem_writer_dec = {1, -1, 0}; // уменьшаем семафор на 1, отвечающий за активных писателей
struct sembuf sem_writer_in = {1, 1, 0}; // увеличиваем семафор на 1, отвечающий за активных писателей
struct sembuf sem_reader_com = {2, 0, 0}; // сравниваем активных читателей с 0 и ждём, пока они станут =0
struct sembuf sem_reader_dec = {2, -1, 0}; // уменьшаем семафор на 1, отвечающий за активных читателей
struct sembuf sem_reader_in = {2, 1, 0}; // увеличиваем семафор на 1, отвечающий за активных читателей
struct sembuf sem_proc_dec = {3, -1, 0}; // уменьшаем семафор на 1, отвечающий за число активных программ
struct sembuf sem_proc_in = {3, 1, 0}; // увеличиваем семафор на 1, отвечающий за число активных программ
using namespace std;
int main ()
{
int sem_id; // ID семафора, возвращаемый при создании/открытии
char file_buf[80]; // буфер для чтения файла
ifstream file; // файл для чтения
cout << "Процесс id=" << getpid() << "\n" << "Имя файла " << file_name << "\n" << "Ключ семафора " << sem_key << "\n";
// semget(ключ семафора, количество семафоров в наборе (4 штуки), флаги для создания семафора и с правом доступа у всех)
sem_id = semget(sem_key, nProg, IPC_CREAT | IPC_EXCL | 0666); // создаём множественный семафор
if (sem_id != -1)
{
cout << "Семафор id=" << sem_id << " создан процессом id=" << getpid() << "\n";
// при создании семафора увеличиваем его на 1
// этот семафор нужен только для писателей; для отслеживания возможности записи в файл в данный момент
// при входе в цикл делаем -1, и тогда он будет = 0 (потому что он уже +1 ПРИ создании), поэтому остальные (писатели) не могут сделать -1 в цикле
// так как не стоит флаг IPC_NOWAIT, который сразу возвращает ошибку,
// и остальные программы вынуждены ждать, пока семафор будет =1,
// чтобы снова сделать -1 (пока =0 они ждут, так как <0 быть не может)
semop(sem_id, &sem_file_in, 1);
}
else
{
sem_id = semget(sem_key, nProg, IPC_CREAT); // открываем семафор
if (sem_id == -1){
cout << "Семафор не был открыт процессом id=" << getpid() << "\n";
exit(-1);
}
cout << "Семафор id=" << sem_id << " открыт процессом id=" << getpid() << "\n";
}
// увеличиваем количество активных процессов
semop(sem_id, &sem_proc_in, 1);
cout << "Текущее количество работающих программ " << semctl(sem_id, 3, GETVAL, 0) << "\n\n";
// начинаем чтение из файла
cout << "Ждём окончания всех активных writer'ов в семафоре процессом id=" << getpid() << "\n";
semop(sem_id, &sem_writer_com, 1);
cout << "Увеличиваем число активных reader'ов в семафоре процессом id=" << getpid() << "\n";
semop(sem_id, &sem_reader_in, 1);
// здесь мы ждём только писателей, но не отслеживаем других читателей
// (как это было сделано у писателей с семафором, у которого при создании выставляется 1),
// потому что чтение этого не требует (в отличие от записи)
// из-за этого и получается ситуация, когда при отсутствии писателей читают все,
// а при наличии писателей в числе готовых, читает только один, так как идёт очередь из процессов,
// и следующим (скорее всего) будет не читатель, а писатель
cout << "Открываем файл на чтение \"" << file_name << "\" процессом id=" << getpid() << "\n";
file.open(file_name);
cout << "Читаем строки процессом id=" << getpid() << "\n";
while (file.getline(file_buf, 80))
{
cout << file_buf << "\n";
sleep(1);
}
cout << "Закрываем файл на чтение \"" << file_name << "\" процессом id=" << getpid() << "\n";
file.close();
cout << "Уменьшаем число активных reader'ов в семафоре процессом id=" << getpid() << "\n\n";
semop(sem_id, &sem_reader_dec, 1);
// уменьшаю количество активных процессов в семафоре на 1
// в итоге у самой последней программы после этого шага будет 0, и она всё удалит
semop(sem_id, &sem_proc_dec, 1);
// сравниваю количество процессов (если=0, то значит захожу, если нет, то пропускаю)
if (semctl(sem_id, 3, GETVAL, 0) == 0)
{
semctl(sem_id, IPC_RMID, 0); // удаляю семафор последней программой (последний процесс удаляет все 4 семафора)
cout << "Семафор id=" << sem_id << " удалён\n\n";
}
return 0;
}
Санкт-Петербург
2022