for(int i = 0; i < NumChildren; i++){
pid_t pid = fork();
if(pid > 0){ // мы в родителе
Записать строку сообщ "Hello, " + toString(i) в общ. обл. памяти.
}else if(pid == 0){ // мы в потомке
читать общую обл. памяти.
}else{ // Ошибка создания процесса
cout << "Вы все дураки!" << endl;
}
}
Передача info через общ. обл памяти между процессами
Задание:
Родить 2 процесса-потомка. От родителя передать каждому уникальную строку сообщения вида, предположим, "Hello, my 1 son!" через ОБЩУЮ ОБЛАСТЬ ПАМЯТИ. Для синхронизации этого дела использовать семафоры.
Итак,реализовываем это дело без семафоров. Не очень правильно, но главное - идея:
Код:
Безусловно, такой подход неверен. Есть куча проблем, например:
1) Первый потомок начал читать общую обл. памяти до того, как родитель туда что-то написал
2) Второй потомок прочитал из общей памяти сообщение для предыдущего потомка, так как родитель не успел записать туда новое сообщение
3) Потомок читает сообщение во время его записи в общую обл памяти и получает кусок норм. сообщ + мусор
4) ...
Для избежания этих проблем я вижу 1 хороший вариант, который и хочу реализовать:
Код:
for(int i = 0; i < NumChildren; i++){
pid_t pid = fork();
if(pid > 0){ // мы в родителе
Заблокировать выполнение только что порожденного потомка
Записать строку сообщ "Hello, " + toString(i) в общ. обл. памяти.
Разблокировать выполнение потомка
}else if(pid == 0){ // мы в потомке
Ожидать, пока родитель разблокирует выполнение
Блокировать родителя, чтоб не писал новых сообщений в общ. обл. памяти
читать общую обл. памяти.
разблокировать родителя
}else{ // Ошибка создания процесса
cout << "Вы все дураки!" << endl;
}
}
pid_t pid = fork();
if(pid > 0){ // мы в родителе
Заблокировать выполнение только что порожденного потомка
Записать строку сообщ "Hello, " + toString(i) в общ. обл. памяти.
Разблокировать выполнение потомка
}else if(pid == 0){ // мы в потомке
Ожидать, пока родитель разблокирует выполнение
Блокировать родителя, чтоб не писал новых сообщений в общ. обл. памяти
читать общую обл. памяти.
разблокировать родителя
}else{ // Ошибка создания процесса
cout << "Вы все дураки!" << endl;
}
}
Теперь вопрос: как это дело реализовать на семафорах? Не надо ничего расписывать, нужна сама идея, приведите в контексте этого псевдокода базовые операции с семафором.
Возможные трудности: Смущает использование 1 семафора для родителя и потомка. Не знаю, как они наследуются, т.п.
Предложите решение, пожалуйста. У меня как-то не укладывается в голове эта абстракция, дальнейшего продвижения нет.
Застрял. Подсобите маленько, пожалуйста.
Родитель:
- инициализирует семафор dataReady количества готовых данных нулем.
- инициализирует двоичный семафор dataWait, сигнализирующий о том, что данные нужно записать в общую область, единицей
- порождает потомков
- выполняет цикл передачи:
Код:
while (!isTermainateRequested)
{
dataWait.decrement(); // как только что-то сигнаизирует о том, что нужно записать новую порцию данных, этот семафор станет больше единицы, система сделает ему декремент и пропустит родителя дальше
// тут пишем
dataReady.increment(); // сигнализируем о том, что готова новая порция данных
}
{
dataWait.decrement(); // как только что-то сигнаизирует о том, что нужно записать новую порцию данных, этот семафор станет больше единицы, система сделает ему декремент и пропустит родителя дальше
// тут пишем
dataReady.increment(); // сигнализируем о том, что готова новая порция данных
}
В свою очередь, дочерние процессы:
- ждет на семафоре dataReady (декремент), система не пропустит потомка дальше, пока что-то не увеличит значение этого семафора (изначально равен нулю)
- как только его пропустило дальше, процесс читает готовые данные
- как только данные прочитаны - можно писать туда новую порцию. О чем и просигнализируем инкрементом семафора dataWait. Как только это будет сделано - родитель снова проснется и запишет новую порцию данных
Код:
while (!isTermainateRequested)
{
dataReady.decrement();
// тут читаем
dataWait.increment(); // сигнализируем о том, что пора писать новую порцию данных
}
{
dataReady.decrement();
// тут читаем
dataWait.increment(); // сигнализируем о том, что пора писать новую порцию данных
}
____
приписка потом: если будешь запускать пример - корректное завершение у меня только по сигналу SIGTERM для процесса main. Если грохнуть его по CTRL-C, система шлет SIGINT, все моментально завершается, и никакие ресурсы IPC освобождены не будут.
Я для корректного завершения открывал вторую консоль, ну а там:
ps -a
kill <main_pid>
Код:
for(int i = 0; i < NumChildren; i++){
pid_t pid = fork();
if(pid > 0){ // мы в родителе
Записать строку сообщ "Hello, " + toString(i) в общ. обл. памяти.
post в sem1 - семафорим потомку готовность данных
wait sem2 - ждём маяка потомка о прочтении данных
}else if(pid == 0){ // мы в потомке
wait sem1 - ждём пока родитель отсемафорит готовность данных
читать общую обл. памяти.
post sem2 - семафорим родителю о факте прочтения данных
}else{ // Ошибка создания процесса
cout << "fork failed!" << endl;
}
}
pid_t pid = fork();
if(pid > 0){ // мы в родителе
Записать строку сообщ "Hello, " + toString(i) в общ. обл. памяти.
post в sem1 - семафорим потомку готовность данных
wait sem2 - ждём маяка потомка о прочтении данных
}else if(pid == 0){ // мы в потомке
wait sem1 - ждём пока родитель отсемафорит готовность данных
читать общую обл. памяти.
post sem2 - семафорим родителю о факте прочтения данных
}else{ // Ошибка создания процесса
cout << "fork failed!" << endl;
}
}
Привожу непосредственно реализацию:
main.cpp
Код:
#include "head.h"
int main(){
cout<<flush<<"Starting of programm! Parent's process is running\n"<<flush;
/* get program filename */
char EXE_NAME_LINK[] = "/proc/self/exe";
char EXE_NAME[256];
memset(EXE_NAME, 0, 256);
readlink(EXE_NAME_LINK, EXE_NAME, 256);
printf("[inf] exe_name = %s\n", EXE_NAME);
/* generate IPC key*/
key_t IPC_KEY = ftok(EXE_NAME, MAGIC_NUMBER);
printf("[inf] IPC_KEY = %d\n", IPC_KEY);
// Получаем идентификатор общей области памяти
int IDmem = shmget(IPC_KEY, MAX_STR_LENGTH, IPC_CREAT|0x1ff);
// Получаем указатель на общую область памяти
char* mem = (char*) shmat(IDmem, 0, 0);
/* create semaphore */
int IDsemParentReady = binary_semaphore_allocation(IPC_KEY, IPC_CREAT|0x1ff);
int IDsemChildReady = binary_semaphore_allocation(IPC_KEY+1, IPC_CREAT|0x1ff);
pid_t child[NumChildren];
for(unsigned int i = 0; i < NumChildren; i++){
string message = "";
child = fork();
if(child > 0){ //находимся в процессе родителя
message = "Hello, son " +toString(i)+"! My parent's PID is " + toString(getppid()) +
", my own PID is " + toString(getpid()) +
", your PID is " + toString(child) + "\n";
// Тут надо записать сообщение в общую область памяти
strcpy(mem, message.c_str());
binary_semaphore_post(IDsemParentReady);
binary_semaphore_wait(IDsemChildReady);
// Записали в общую область памяти сообщение для процесса
}else if(child == 0){ // находимся в процессе дитя
binary_semaphore_wait(IDsemParentReady);
// Read shared memory
cout<<flush<<"Child's process # "<< i << " is running."
<<" Information from parent:\n" << flush;
char msg[MAX_STR_LENGTH];
strcpy(msg, mem);
write(1, msg, strlen(msg));
binary_semaphore_post(IDsemChildReady);
exit(0);
}
else
cout << flush<<"Error during creating process\n"<<flush;
}
cout<<flush<<"Waiting for finishing all children's processes.\n" << flush;
for(unsigned int j = 0; j < NumChildren; j++){
waitpid(child[j], NULL, 0);
}
binary_semaphore_deallocate(IDsemParentReady);
binary_semaphore_deallocate(IDsemChildReady);
shmdt(mem);
// Удалили область отображения в локальном адресном пространстве
shmctl(IDmem, IPC_RMID, 0);
// Удалили блок разделяемой памяти
cout<<flush<<"All children's processes are finished. Parent's process is terminating.\n"<<flush;
return EXIT_SUCCESS;
}
int main(){
cout<<flush<<"Starting of programm! Parent's process is running\n"<<flush;
/* get program filename */
char EXE_NAME_LINK[] = "/proc/self/exe";
char EXE_NAME[256];
memset(EXE_NAME, 0, 256);
readlink(EXE_NAME_LINK, EXE_NAME, 256);
printf("[inf] exe_name = %s\n", EXE_NAME);
/* generate IPC key*/
key_t IPC_KEY = ftok(EXE_NAME, MAGIC_NUMBER);
printf("[inf] IPC_KEY = %d\n", IPC_KEY);
// Получаем идентификатор общей области памяти
int IDmem = shmget(IPC_KEY, MAX_STR_LENGTH, IPC_CREAT|0x1ff);
// Получаем указатель на общую область памяти
char* mem = (char*) shmat(IDmem, 0, 0);
/* create semaphore */
int IDsemParentReady = binary_semaphore_allocation(IPC_KEY, IPC_CREAT|0x1ff);
int IDsemChildReady = binary_semaphore_allocation(IPC_KEY+1, IPC_CREAT|0x1ff);
pid_t child[NumChildren];
for(unsigned int i = 0; i < NumChildren; i++){
string message = "";
child = fork();
if(child > 0){ //находимся в процессе родителя
message = "Hello, son " +toString(i)+"! My parent's PID is " + toString(getppid()) +
", my own PID is " + toString(getpid()) +
", your PID is " + toString(child) + "\n";
// Тут надо записать сообщение в общую область памяти
strcpy(mem, message.c_str());
binary_semaphore_post(IDsemParentReady);
binary_semaphore_wait(IDsemChildReady);
// Записали в общую область памяти сообщение для процесса
}else if(child == 0){ // находимся в процессе дитя
binary_semaphore_wait(IDsemParentReady);
// Read shared memory
cout<<flush<<"Child's process # "<< i << " is running."
<<" Information from parent:\n" << flush;
char msg[MAX_STR_LENGTH];
strcpy(msg, mem);
write(1, msg, strlen(msg));
binary_semaphore_post(IDsemChildReady);
exit(0);
}
else
cout << flush<<"Error during creating process\n"<<flush;
}
cout<<flush<<"Waiting for finishing all children's processes.\n" << flush;
for(unsigned int j = 0; j < NumChildren; j++){
waitpid(child[j], NULL, 0);
}
binary_semaphore_deallocate(IDsemParentReady);
binary_semaphore_deallocate(IDsemChildReady);
shmdt(mem);
// Удалили область отображения в локальном адресном пространстве
shmctl(IDmem, IPC_RMID, 0);
// Удалили блок разделяемой памяти
cout<<flush<<"All children's processes are finished. Parent's process is terminating.\n"<<flush;
return EXIT_SUCCESS;
}
BinSemaphore.cpp
Код:
#include "BinSemaphore.h"
/* Получить идентификатор двоичного семафора, выделяя его в случае необходимости. */
int binary_semaphore_allocation(key_t key, int sem_flags){
return semget (key, 1, sem_flags);
}
/* Освобождаем двоичной семафор. Все пользователи должны закончить использование семафора
к этому моменту. Возвращает -1 при ошибке. */
int binary_semaphore_deallocate(int semid){
union semun ignored_argument;
return semctl (semid, 1, IPC_RMID, ignored_argument);
}
/* обслужить двойной семафор. блокировка, пока значение семафора положительное, заетм декремент на 1. */
int binary_semaphore_wait(int semid){
struct sembuf operations[1];
/* использовать первый (и только) семафор. */
operations[0].sem_num = 0;
/* декремент 1. */
operations[0].sem_op = -1;
/* разрешается отмена операции*/
operations[0].sem_flg = SEM_UNDO;
return semop (semid, operations, 1);
}
/* пост к двоичному семафору: увеличиваем его значение на 1. это выполнится немедленно. */
int binary_semaphore_post(int semid){
struct sembuf operations[1];
/* используем первый семафор. */
operations[0].sem_num = 0;
/* increment by 1. */
/* приращение на 1. */
operations[0].sem_op = 1;
/*разрешаем отмену операции. */
operations[0].sem_flg = SEM_UNDO;
return semop (semid, operations, 1);
}
/* Получить идентификатор двоичного семафора, выделяя его в случае необходимости. */
int binary_semaphore_allocation(key_t key, int sem_flags){
return semget (key, 1, sem_flags);
}
/* Освобождаем двоичной семафор. Все пользователи должны закончить использование семафора
к этому моменту. Возвращает -1 при ошибке. */
int binary_semaphore_deallocate(int semid){
union semun ignored_argument;
return semctl (semid, 1, IPC_RMID, ignored_argument);
}
/* обслужить двойной семафор. блокировка, пока значение семафора положительное, заетм декремент на 1. */
int binary_semaphore_wait(int semid){
struct sembuf operations[1];
/* использовать первый (и только) семафор. */
operations[0].sem_num = 0;
/* декремент 1. */
operations[0].sem_op = -1;
/* разрешается отмена операции*/
operations[0].sem_flg = SEM_UNDO;
return semop (semid, operations, 1);
}
/* пост к двоичному семафору: увеличиваем его значение на 1. это выполнится немедленно. */
int binary_semaphore_post(int semid){
struct sembuf operations[1];
/* используем первый семафор. */
operations[0].sem_num = 0;
/* increment by 1. */
/* приращение на 1. */
operations[0].sem_op = 1;
/*разрешаем отмену операции. */
operations[0].sem_flg = SEM_UNDO;
return semop (semid, operations, 1);
}
BinSemaphore.h
Код:
#ifndef _BINSEMAPHORE_H
#define _BINSEMAPHORE_H
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
/* Мы должны определить union semun самостоятельно. */
union semun {
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
};
int binary_semaphore_allocation (key_t key, int sem_flags);
int binary_semaphore_deallocate (int semid);
int binary_semaphore_wait (int semid);
int binary_semaphore_post (int semid);
#endif /* _BINSEMAPHORE_H */
#define _BINSEMAPHORE_H
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
/* Мы должны определить union semun самостоятельно. */
union semun {
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
};
int binary_semaphore_allocation (key_t key, int sem_flags);
int binary_semaphore_deallocate (int semid);
int binary_semaphore_wait (int semid);
int binary_semaphore_post (int semid);
#endif /* _BINSEMAPHORE_H */
head.h
Код:
#ifndef _HEAD_H
#define _HEAD_H
#include <sys/types.h>
#include <sys/wait.h>
#include "BinSemaphore.h"
#include <sys/ipc.h>
#include <cstdlib>
#include <sys/sem.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <cstdio>
#include <sstream>
#include <iostream>
using namespace std;
extern int errno;
const int MAGIC_NUMBER = 777;
const int MAX_STR_LENGTH = 255;
const int NumChildren = 2;
template <typename T>
std::string toString(T val)
{
std::ostringstream oss;
oss<< val;
return oss.str();
}
template<typename T>
T fromString(const std::string& s)
{
std::istringstream iss(s);
T res;
iss >> res;
return res;
}
/* Пример использования
std::string str;
int iVal;
float fVal;
str = toString(iVal);
str = tiString(fVal);
*/
#endif /* _HEAD_H */
#define _HEAD_H
#include <sys/types.h>
#include <sys/wait.h>
#include "BinSemaphore.h"
#include <sys/ipc.h>
#include <cstdlib>
#include <sys/sem.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <cstdio>
#include <sstream>
#include <iostream>
using namespace std;
extern int errno;
const int MAGIC_NUMBER = 777;
const int MAX_STR_LENGTH = 255;
const int NumChildren = 2;
template <typename T>
std::string toString(T val)
{
std::ostringstream oss;
oss<< val;
return oss.str();
}
template<typename T>
T fromString(const std::string& s)
{
std::istringstream iss(s);
T res;
iss >> res;
return res;
}
/* Пример использования
std::string str;
int iVal;
float fVal;
str = toString(iVal);
str = tiString(fVal);
*/
#endif /* _HEAD_H */