Skip to content

Instantly share code, notes, and snippets.

@elyzov
Last active September 30, 2015 04:34
Show Gist options
  • Save elyzov/c326b5c09f77cb6208ef to your computer and use it in GitHub Desktop.
Save elyzov/c326b5c09f77cb6208ef to your computer and use it in GitHub Desktop.
#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