Producent - Konsument z użyciem potoków nazwnych

0

Cześć.
Mam za zadanie napisać rozwiązanie tego problemu za pomocą potoków nazwanych w trzech wariantach:

  • Jeden producent - Jeden konsument
  • Wielu producentów - Jeden konsument
  • Jeden producent - Wielu konsumentów

Udało mi się napisać program dla wariantu jeden producent - jeden konsument jednak nie mam zbytnio pomysłu jak się zabrać za dwie pozostałe opcje, poprosił bym o jakieś sugestie jak można było byo do tego podejść, dodatkowo dodam że jest mi to potrzebne do pracy magisterskiej wiec chce aby kod był jak najprostszy a zarazem działał jak należy.

Poniżej wrzucam program dla pierwszego wariantu:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>

const int dataAmountToProduce = 10;
int value = 0;
int const buforSize = 50;

void processProducer()
{
    int savePipe;
    while (value < dataAmountToProduce)
    {
        savePipe = open("pipe", O_WRONLY);
        value++;
        char str[buforSize];
        sprintf(str, "%d", value);
        printf("Producer %d produces value: %s\n", getpid(), str);
        write(savePipe, str, buforSize);
        if (value == dataAmountToProduce)
        {
            break;
        }
    }
    close(savePipe);
}

void processConsumer()
{
    int readPipe;
    while (value < dataAmountToProduce)
    {
        readPipe = open("pipe", O_RDONLY);
        char buf[buforSize];
        read(readPipe, buf, buforSize);
        printf("Consumer %d consumes value: %s\n", getpid(), buf);
        value = atoi(buf);
        if (value == dataAmountToProduce)
        {
            break;
        }
    }
    close(readPipe);
}

main()
{
    mkfifo("pipe", 0600);

    if (fork() == 0)
    {
        printf("Creating producer process %d\n", getpid());
        processProducer();
        printf("Producer process %d finished work\n", getpid());
        exit(0);
    }

    if (fork() == 0)
    {
        printf("Creating consumer process %d\n", getpid());
        processConsumer();
        printf("Consumer process %d finished work\n", getpid());
        exit(0);
    }

    wait(NULL);
    printf("Both child processes of process %d finished work.\n", getpid());
    exit(0);
}
1
PhanthomOfTheOpera napisał(a):
  • Wielu producentów - Jeden konsument
  • Jeden producent - Wielu konsumentów

Pytanie:
Producent może być jednocześnie konsumentem? Zakładam, że tak.

Masz kilka opcji.
A) Centralny broker i 2 kolejki per uczestnik.

  • Każdy uczestnik może posiadać 2 kolejki - wejściową i wyjściową. Broker aktywnie przerzuca wiadomości pomiędzy kolejkami. W takim zestawieniu w przypadku komunikacji A->C broker B jest czytelnikiem kolejki wyjściowej uczestnika A i pisarzem kolejki wejściowej uczestnika C, czyli powstaje A->B->C.

B) 1 Kolejka per uczestnik. Bez jawnego brokera (będzie nim system operacyjny).
- W przypadku wielu do jednego producenci piszą bezpośrednio do kolejki konsumenta.
- W przypadku jeden do wielu producent pisze bezpośrednio do kolejki każdego konsumenta.

C) Adresowanie.

  • Musisz wiedzieć jak napisać do konkretnego konsumenta. Każdy uczestnik potrzebuje identyfikatora.
  • W najprostszym przypadku nazwa potoku to adres konsumenta.
    a) Jeżeli wszystko stworzyło się przy pomocy fork z procesu startowego (wszystkie potoki zostały stworzone w procesie głównym), to każdy już ma dostęp do każdego.
    b) W przeciwnym wypadku każdy uczestnik powinien otworzyć 1 potok do odczytu i n-1 potoków do zapisu (zobacz ad. 2). Oczywiście żeby je otworzyć, to uczestnik potrzebuje znać ich nazwy, więc należy je jakoś przekazać. Może się tu pojawić problem synchronizacji - co jeżeli właściciel kolejki jeszcze nie wystartował?
    c) W przypadku centralnego brokera otwieramy 2 potoki do komunikacji z brokerem, adresat musi być umieszczony w nagłówku wiadomości, który odczyta broker.
  • Jeżeli chcesz wspierać pisanie selektywne do części pisarzy to potrzebujesz mapowania pomiędzy identyfikatorem konsumenta a jego kolejką. W Twoim przypadku wystarczy zwykła lista albo tablica uczestników. Bardziej skalowalnym rozwiązaniem jest oczywiście tablica haszująca (std::unordered_map w przypadku c++). Nawet jeżeli nie potrzebujesz logiki multicast do wybranej liczby uczestników, wiadomości to broadcast do każdego to i tak takie mapowanie będzie pomocne by nie pisać do samego siebie.

D) Serializowanie danych.

  • Potoki to strumienie bajtów, musisz wiedzieć jak odkodować z nich wiadomość. W przypadku wielu do jednego dostaniesz wiele wiadomości od różnych uczestników, a musisz wiedzieć gdzie kończy się jedna wiadomość a zaczyna druga.
  • Najprostsze opcje to:
    a) użycie separatora null ( '\0' ) do oznaczenia końca wiadomości.
    b) użycie znaku nowej linii do oznaczenia końca wiadomości,
    c) Kodowanie [nagłówek] [dane], gdzie nagłówek ma stałą długość i zawiera informację o długości danych, które po nim występują.

Ad. 1) Drobne wyjaśnienie - często odnosiłem się tutaj do kolejki, jako koncepcji abstrakcyjnej. Potok to oczywiście rodzaj kolejki first-in-first-out.
Ad. 2) W przypadku, gdy konsument nie może być producentem sytuacja się upraszcza, gdyż nie musisz robić multipleksownania zadań (jednoczesnej obsługi kolejki wejściowej i wyjściowych), wystarczy 1 rodzaj kolejki per uczestnik.

0

Dzięki wielkie za cenne rady ogólnie to ma być tak że jest jeden proces producenta oraz n procesów konsumenta (i na odwrót) i proces producenta otwiera potok do zapisu a procesy konsumenta otwierają go tylko do odczytu, dlatego w tym przypadku wydawało mi się że dodanie pętli do utworzenia kilku procesów konsumenta wystarczy ale widzę że konsola wypluwa głupoty i to jest właśnie mój problem, i tak za bardzo nie wiem gdzie może leżeć tego przyczyna.

1 użytkowników online, w tym zalogowanych: 0, gości: 1