-
-
Save elyzov/c326b5c09f77cb6208ef to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <fstream> | |
#include <string> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <time.h> | |
#include <signal.h> | |
#include <sys/wait.h> | |
#include <sys/sem.h> | |
#include <sys/shm.h> | |
#include <unistd.h> | |
/*! Используемые ключи для очереди сообщений | |
и семафоров. */ | |
#define SHMKEY 4 | |
#define SEMKEY 3 | |
/*! Вспомогательные константы. */ | |
#define TMDIV 5 //!< Интервал возможного сна. | |
#define DSIZE 300 //!< Размер буфера сообщения. | |
#define GET_LINES 4 //!< Кол-во строк в сообщении. | |
#define ACCESS 0644 //!< Права доступа к IPC. | |
/*! Выполнение команды с проверкой на результат. */ | |
#define RUN(cmd) run( cmd, #cmd , __LINE__ ) | |
using namespace std; | |
typedef unsigned short int us_int; | |
typedef short int s_int; | |
/*! Типы сообщений. */ | |
enum msg_type_t{ DATA_MSG = 4, STOP_MSG }; | |
/*! Типы процессов. */ | |
enum proc_t{ ODD, EVEN }; | |
/*! Структура, описывающая сообщение. */ | |
struct Message | |
{ | |
long type; | |
long proc; | |
char data[DSIZE]; | |
}; | |
/*! Проверяет результат выполненной команды. | |
\param res результат выполненной команды. | |
\param command текст комманды. */ | |
void run( int res, char * command, int line ) | |
{ | |
if( res < 0 ) | |
{ | |
cerr << "Can't execute command " << command << "on line #" << line << endl; | |
exit(1); | |
} | |
} | |
/*! Упаковывает данные в сообщение. | |
\param data указатель на область данных. | |
\param from поток для получения данных. | |
\return 0 - данные полученны успешно. | |
-1 - ошибка получения данных. | |
1 - нет данных для подготовки.*/ | |
int get_data(ifstream & from, char * data) | |
{ | |
int i, n_count; | |
//!< Если файл закончился, то выходим. | |
if (from.eof()) return 1; | |
//!< Если файл не существует, то выходим | |
//!< и прекращаем родительский процесс. | |
if(from == NULL) | |
{ | |
cerr << "PID " << getpid() << ": Can't open file." << endl; | |
kill(getppid(), SIGINT); | |
return -1; | |
} | |
//!< Иначе считываем данные. | |
for(i = 0, n_count = 0; n_count < GET_LINES && i < DSIZE; i++) | |
{ | |
data[i] = from.get(); | |
if (data[i] == EOF) break; | |
else if (data[i] == '\n') n_count++; | |
} | |
data[i-1] = '\0'; | |
//!< Если не хватило буфера, то выходим | |
//!< и завершаем родительский процесс. | |
if (i == DSIZE) | |
{ | |
cerr << "Small buffer." << endl; | |
kill(getppid(), SIGINT); | |
return -1; | |
} | |
//!< Если пустой файл, то выходим. | |
if(i == 0) return 1; | |
//! Иначе всё хорошо. | |
return 0; | |
} | |
/*! Устанавливает операцию под семафором. | |
\param op описатель операции. | |
\param sem_num номер семафора. | |
\param sem_op операция. | |
\param sem_flg флаг операции. */ | |
void set_op(sembuf & op, us_int sem_num, s_int sem_op, s_int sem_flg) | |
{ | |
op.sem_num = sem_num; | |
op.sem_op = sem_op; | |
op.sem_flg = sem_flg; | |
} | |
/*! Проверяет состояние семафора для заданного процесса. | |
\param semid идентификатор набора семафоров. | |
\param pr тип процесса. */ | |
void check_sem(int semid, proc_t pr) | |
{ | |
sembuf op; | |
set_op(op, pr, 0, 0); | |
RUN( semop(semid, &op, 1) ); | |
} | |
/*! Изменяет состояние семафора. | |
\param semid идентификатор набора семафоров. | |
\param op_1 операция над первым семафором. | |
\param op_2 операция над вторым семафором. */ | |
void change_sem(int semid, s_int op_1, s_int op_2) | |
{ | |
sembuf op_[2]; | |
set_op(op_[0], 0, op_1, 0); | |
set_op(op_[1], 1, op_2, 0); | |
RUN( semop(semid, op_, 2) ); | |
} | |
void await_shm(int semid) { | |
sembuf op; | |
set_op(op, 2, 0, 0); | |
RUN( semop(semid, &op, 1) ); | |
} | |
void empty_shm(int semid) { | |
sembuf op; | |
set_op(op, 2, 1, 0); | |
RUN( semop(semid, &op, 1) ); | |
} | |
void full_shm(int semid) { | |
sembuf op; | |
set_op(op, 2, -1, 0); | |
RUN( semop(semid, &op, 1) ); | |
} | |
/*! Удаляет очередь сообщений и набор семафоров. */ | |
void clear() | |
{ | |
int shmid = shmget(SHMKEY, sizeof(Message), 0); | |
int semid = semget(SEMKEY, 1, 0); | |
shmctl(shmid, IPC_RMID, NULL); | |
semctl(semid, 0, IPC_RMID); | |
} | |
/*! Удаляет очередь сообщений и набор семафоров и | |
завершает работу программы. | |
\param status статус завершения. */ | |
void stop(int status) | |
{ | |
clear(); | |
exit(status); | |
} | |
/*! Имитирует загрузку процесса некоторой работой. | |
Усыпляет процесс на случайное время в диапазоне | |
от 1 сек. до TMDIV сек. */ | |
void todo_job() | |
{ | |
static pid_t pID = getpid(); | |
srand(time(NULL) + pID); | |
int sleepTime = rand() % TMDIV + 1; | |
sleep(sleepTime); | |
} | |
/*! Печать ошибки и выход из программы. | |
\param msg сообщение об ошибке. */ | |
void print_err(const char *msg) | |
{ | |
cerr << msg << endl; | |
exit(1); | |
} | |
int main(int argc, char *argv[]) | |
{ | |
//!< Значения по умолчанию. | |
string file[2]; | |
file[ODD] = "odd.txt"; | |
file[EVEN] = "even.txt"; | |
//!< Чтение параметров. | |
for (int i = 1; i < argc; i++) | |
file[i-1] = argv[i]; | |
//clear(); | |
//!< Создание очереди сообщений. | |
int shmid = shmget(SHMKEY, sizeof(Message), ACCESS | IPC_CREAT); | |
if (shmid < 0) | |
print_err("Can't allocate shared memory."); | |
//!< Создание семафоров. | |
int semid = semget(SEMKEY, 3, ACCESS | IPC_CREAT); | |
if (semid < 0) | |
print_err("can't create semafor."); | |
/*! Пытаемся отобразить разделяемую память в адресное | |
пространство текущего процесса. */ | |
void * shm_ptr = shmat(shmid, NULL, 0); | |
if (shm_ptr == (void *) -1) | |
print_err("can't attach shared memory."); | |
Message * msg = (Message *)shm_ptr; | |
/*! Задаем начальное состояние семафоров: | |
приоритет на четный процесс. */ | |
change_sem(semid, 0, 1); | |
empty_shm(semid); //!< Изначально разделяемая память пуста. | |
/*! Переопределяем сигнал прерывания для | |
корректного завершения работы. */ | |
signal(SIGINT, stop); | |
//!< Создание дочерних процессов. | |
pid_t pid_odd = fork(); | |
if (pid_odd == 0) | |
{ | |
//!< Первый (нечетный) дочерний процесс. | |
ifstream from(file[ODD].c_str()); | |
//!< Подготавливаем и отправляем данные. | |
while (true) | |
{ | |
todo_job(); //!< Выполняем работу. | |
check_sem(semid, ODD); //!< Проверяем семафор. | |
msg->proc = ODD; | |
if (get_data(from, msg->data) != 0) break; | |
msg->type = DATA_MSG; | |
full_shm(semid); //!< Сообщаем, что появились данные в разделяемой памяти. | |
} | |
//!< Отправляем соощение об окончании работы. | |
msg->type = STOP_MSG; | |
strcpy(msg->data, "Process 1 (odd) stop workig."); | |
full_shm(semid); //!< Сообщаем, что появились данные в разделяемой памяти. | |
exit(0); | |
} | |
else if (pid_odd < 0) | |
print_err("Can't create first child."); | |
pid_t pid_even = fork(); | |
if (pid_even == 0) | |
{ | |
//!< Второй (четный) дочерний процесс. | |
ifstream from(file[EVEN].c_str()); | |
//!< Подготавливаем и отправляем данные. | |
while (true) | |
{ | |
todo_job(); //!< Выполняем работу. | |
check_sem(semid, EVEN); //!< Проверяем семафор. | |
msg->proc = EVEN; | |
if (get_data(from, msg->data) != 0) break; | |
msg->type = DATA_MSG; | |
full_shm(semid); //!< Сообщаем, что появились данные в разделяемой памяти. | |
} | |
//!< Отправляем соощение об окончании работы. | |
msg->type = STOP_MSG; | |
strcpy(msg->data, "Process 2 (even) stop workig."); | |
full_shm(semid); //!< Сообщаем, что появились данные в разделяемой памяти. | |
exit(0); | |
} | |
else if (pid_even < 0) | |
print_err("Can't create second child."); | |
//!< Родительский процесс. | |
bool even_stoped = false; //!< "Четный" процесс завершился? | |
bool odd_stoped = false; //!< "Нечетный" процесс завершился? | |
while(true) { | |
await_shm(semid); //!< Ожидаем сообщения в разделяемой памяти. | |
/*! Получаем и печатаем сообщения. */ | |
if (msg->type == DATA_MSG) { | |
//cout << msg->data << endl; | |
} | |
else if (msg->type == STOP_MSG) { | |
if (msg->proc == ODD) odd_stoped = true; | |
else if (msg->proc == EVEN) even_stoped = true; | |
} | |
cout << msg->data << endl; | |
if (even_stoped && odd_stoped) break; //!< Если дочерние процессы завершились, то выходим из цикла. | |
if (msg->proc == ODD) change_sem(semid, 1, -1); //!< Изменяем приоритет нечетного. | |
else if (msg->proc == EVEN) change_sem(semid, -1, 1); //!< Изменяем приоритет четного. | |
empty_shm(semid); //!< Помечаем память как прочитанную. | |
} | |
//!< Ожидание завершения родительского процесса. | |
waitpid(pid_odd, NULL, 0); | |
waitpid(pid_even, NULL, 0); | |
RUN ( shmdt(shm_ptr) ); | |
/*! Удаление очереди сообщений и семафоров | |
и завершение работы. */ | |
stop(0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment