Problem producenta i konsumenta - Bad address funkcji write()

0

Witam. Obecnie zajmuję się problemem producenta i konsumenta z użyciem semaforów, pamięci dzielonej oraz bufora cyklicznego. Napisałem od nowa sekcje krytyczne obu procesów. Niestety, konsument zwraca błąd funkcji write(): Bad address. Szukałem przyczyny, lecz nie mogę jej znaleźć. Proszę o pomoc, podpowiedź, gdzie jest błąd. Poniżej załączam kod:

main.c:

#include "mem_func.h"
#include "sem_func.h"

#define SIZE 25

int main(int argc, char const *argv[])
{
    const char prod_path[]="./producer.x";
    const char prod_name[]="producer.x";
    const char cons_path[]="./consumer.x";
    const char cons_name[]="consumer.x";

    const int wait_val=2;

    if(argc<1)
    {
        perror("Za malo argumentow\n");
        exit(8);
    }

    const key_t sem_key=createSem();
    const int id_sem=semAccess(sem_key);

    const key_t mem_key=memCreate();
    const int mem_id=memAccess(mem_key, sizeof(Towar));

    const int producer=fork();

    char semid[SIZE];
    char memid[SIZE];

    sprintf(semid, "%d", id_sem);
    sprintf(memid, "%d", mem_id);

    if (producer==-1)
    {
        perror("Nie udalo sie utworzyc procesu producenta\n");
        exit(6);
    }

    else if(producer==0)
    {
        if(execl(prod_path, prod_name, semid, memid, argv[3], NULL)==-1)
        {
            perror("Nie udalo sie uruchomic procesu producenta\n");
            exit(7);
        }
    }

    //sleep(SLV);

    const int consumer=fork();

    if (consumer==-1)
    {
        perror("Nie udalo sie utworzyc procesu konsumenta\n");
        exit(6);
    }

    else if(consumer==0)
    {
        if(execl(cons_path, cons_name, semid, memid, argv[4], NULL)==-1)
        {
            perror("Nie udalo sie uruchomic procesu kosumenta\n");
            exit(7);
        }
    }

    //sleep(SLV);

    for (int i=0; i<wait_val; ++i)
    {
        int status;
        const pid_t child=wait(&status);
        printf("Proces o ID: %d zakonczyl prace z kodem: %d\n", child, status);
    }

    semDelete(id_sem, VAL2);
    //semDelete(id_sem, SEM1);
    memDel(mem_id);

    return 0;
}

producer.c:

#include "mem_func.h"
#include "sem_func.h"

int main(int argc, char const *argv[])
{
    const int id_sem=atoi(argv[1]);
    const int mem_id=atoi(argv[2]);

    char buf[BUF_SIZE];

    const int file1=open(argv[3], O_RDONLY, 0644);
    if(file1==-1)
    {
        perror("Blad funkcji open() - proc. producenta\n");
        exit(9);
    }

    Towar *bufor;
    bufor=(Towar*) memAtt(mem_id, O_WRONLY);

    /*bufor[0].begin=0;
    bufor[0].end=0;*/

    //setValue(id_sem, SEM0, VAL);

    //int bytes_read=0;
    //int bytes_written=0;

    bufor->begin=0;
    bufor->end=0;
    int counter=0;

    while(read(file1, buf, sizeof(buf)))
    {
        setValue(id_sem, SEM0, (EMPTY-counter));
        for(int i=0; i<(strlen(buf)+1); ++i)
        {
            down(id_sem, SEM0);
            bufor->share[bufor->end]=buf[i];
            ++bufor->end;
            printf("Producent:\n");
            printf("%c\n", buf[i]);

            if(bufor->end==50)
            bufor->end=0;
            up(id_sem, SEM1);
        }
        ++counter;
        if(counter==50)
        {
            sleep(1);
            counter=0;
        }
    }

    down(id_sem, SEM0);
    bufor->share[bufor->end]='~';
    up(id_sem, SEM1);

    if(close(file1)==-1)
    {
        perror("Blad funkcji close() - proces producenta\n");
        exit(11);
    }
    delAtt(bufor);

    return 0;
}

consumer.c:

#include "mem_func.h"
#include "sem_func.h"

int main(int argc, char const *argv[])
{
    const int id_sem=atoi(argv[1]);
    const int mem_id=atoi(argv[2]);

    char buf[BUF_SIZE];

    const int file2=open(argv[3], O_WRONLY|O_CREAT, 0644);
    if(file2==-1)
    {
        perror("Blad funkcji open() - proc. konsumenta\n");
        exit(9);
    }

    Towar *bufor;
    bufor=(Towar*) memAtt(mem_id, O_RDONLY);

    char read;
    bufor->begin=0;
    bufor->end=0;

    //setValue(id_sem, SEM1, VAL);

    //int bytes_read=0;
    //int bytes_written=0;

    int counter=0;

    while(1)
    {
        setValue(id_sem, SEM1, (FULL+counter));
        down(id_sem, SEM1);
        read=bufor->share[bufor->begin];

        printf("Konsument:\n");
        printf("%c"-'\0', read);
        printf("\n");

        if(read=='~')
        {
            ++bufor->begin;
            if(bufor->begin==50)
            {
                bufor->begin=0;
            }
            down(id_sem, SEM0);
        }


        if(write(file2, &read, (sizeof(read)))==-1)
        {
            perror("Blad zapisu do pliku\n");
            exit(10);
        }

        ++counter;
        if(counter==50)
        {
            sleep(1);
            counter=0;
        }
        ++bufor->begin;
        if(bufor->begin==50)
        {
            bufor->begin=0;
        }
        up(id_sem, SEM1);
    }

    if(close(file2)==-1)
    {
        perror("Blad funkcji close() - proces konsumenta\n");
        exit(11);
    }

    delAtt(bufor);

    return 0;
}

mem_func.c:

#include "mem_func.h"

int memCreate()
{
    const int key=ftok(PATH, 'b');
    if(key==-1)
    {
        perror("Blad funkcji memCreate\n");
        exit(12);
    }

    return key;
}

int memAccess(int key, int size)
{
    const int mem_id=shmget(key, size, IPC_CREAT|IPC_EXCL|MODE);
    if(mem_id==-1)
    {
        perror("Blad funkcji memAccess\n");
        exit(13);
    }

    return mem_id;
}

void memDel(int mem_id)
{
    struct shmid_ds buf;
    if(shmctl(mem_id, IPC_RMID, (struct shmid_ds*)0)==-1)
    {
        perror("Blad funkcji memDel()\n");
        exit(14);
    }

    else printf("Usunieto pamiec dzielona\n");
}

void *memAtt(int mem_id, int mode)
{
    void *shm=shmat(mem_id, NULL, mode);
    if(shm==(void*)-1)
    {
        fprintf(stderr, "Blad funkcji memAtt()\n");
        exit(15);
    }

    else printf("Utworzono dowiazanie\n");

    return shm;
}

void delAtt(void *shm)
{
    if(shmdt(shm)==-1)
    {
        fprintf(stderr, "Blad funkcji delAtt()\n");
        exit(16);
    }
    else printf("Usunieto dowiazanie\n");
}

int memSize(int mem_id)
{
    struct shmid_ds x;
    if(shmctl(mem_id, IPC_STAT, &x)==-1)
    {
        perror("Blad funkcji memSize()\n");
        exit(17);
    }
    
    return x.shm_segsz;
}

sem_func.c:

#include "sem_func.h"
#define SEM_NR 2            //liczba semaforów
#define MODE 0600           //tryb
#define PATH "./main.x"   //ścieżka pliku dla funkcji ftok()

int semAccess(key_t key)
{
    int semid;
    semid=semget(key, SEM_NR, IPC_CREAT|MODE);
    if(semid==-1)
    {
        perror("Blad funkcji semAccess()\n");
        exit(4);
    }
    return semid;
}

void setValue(int semid, int nsem, int val)
{
    union semun ctrl;
    ctrl.val=val;
    if(semctl(semid, nsem, SETVAL, ctrl)==-1)  //SETVAL orzymuje wartość 1
    {
        perror("Blad funkcji setValue()\n");
        exit(5);
    }
}

void up(int semid, int nsem)
{
    struct sembuf do_op = {
	.sem_num=nsem,
	.sem_op=1,
	.sem_flg=0
	};
    //struktura zawiera kolejno: nr semafora, operację, flagę
    //czyli: semnum, sem_op, sem_flg
    //0 - operacja blokująca
    if(semop(semid, &do_op, VAL)==-1)
    {
        perror("Blad funkcji up()\n");
        exit(2);
    }
}

void down(int semid, int nsem)
{
    struct sembuf do_op = {
	.sem_num=nsem,
	.sem_op=-1,
	.sem_flg=0
	};

    if(semop(semid, &do_op, VAL)==-1)
    {
        perror("Blad funkcji down()\n");
        exit(2);
    }
}

void semDelete(int semid, int nsem)   //usuwanie semafora
{
    if(semctl(semid, nsem, IPC_RMID)==-1)   //obsługa błędów
    {
        perror("Blad funkcji semDelete\n");
        exit(1);
    }
    else    //komunikat
    {
        printf("Semafor zostal usuniety\n");
    }
}

key_t createSem()   //uzyskanie dostępu do semafora
{
    int key;
    if((key=ftok(PATH, 'a'))==-1)
    {
        perror("Blad funkcji createSem()\n");
        exit(3);
    }
    return key;
}

mem_func.h:

//biblioteka dla pamięci dzielonej

#ifndef MEM_FUNC_H
#define MEM_FUNC_H

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

#define PATH "./consumer.x"
#define MODE 0600
#define MEM_SIZE 50
#define BUF_SIZE 50

typedef struct
{
    char share[MEM_SIZE];
    int begin;
    int end;
} Towar;


int memCreate();	                //tworzenie pamięci dzielonej
int memAccess(int key, int size);	//dostęp do pamięci dzielonej
void memDel(int mem_id);			//usuwanie pamięci dzielonej
void *memAtt(int mem_id, int mode); //tworzenie dowiązania
void delAtt(void *mem_id);          //usuwanie dowiązania
int memSize(int mem_id);			//rozmiar pamięci dzielonej

#endif

sem_func.h:

//biblioteka dla semaforów

#ifndef SEM_FUNC_H
#define SEM_FUNC_H

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
#include <time.h>

#define EMPTY 50
#define FULL 0

#ifdef _SEM_SEMUN_UNDEFINED

union semun {
int val;				    //wartosc dla SETVAL
struct semid_ds *buf;	    //bufor dla IPC_STAT, IPC_SET
unsigned short *array;	    //tablica dla GETALL, SETALL
struct seminfo *__buf;	    //bufor dla IPC_INFO (specyfika Linuksa)
};

#endif  //_SEM_SEMUN_UNDEFINED

enum {SEM0, SEM1, VAL=1, VAL2, SLV=5};

int semAccess(key_t key);   //utworzenie semafora
void setValue(int semid, int nsem, int val);
//przypisanie wartośći (inicjalizacja)

void up(int semid, int nsem);         //podniesienie semafora...
void down(int semid, int nsem);       //...i jego opuszczenie
key_t createSem();                    //dostęp do semafora
//int queue(int semid);               //kolejka
void semDelete(int semid, int nsem);  //usunięcie semafora

#endif  //FUNC_H

1
char read;
write(file2, read, (sizeof(read)+1)

To nie ma sensu, przeczytaj manual write(2).

0

Dodałem ampersand do read. Niestety, program dociera do pewnego momentu i się zawiesza. Zamiana wartości semaforów na 1 skutkuje działaniem programu w nieskończoność.

1

Masz bufor 1-bajtowy. To nie ma żadnego sensu, a sam fakt, że próbujesz wysłać z 1-bajtowego bufora 2 bajty to UB.

0

@kq - Mógłbyś mi wyjaśnić? Nie za bardzo pojmuję, o co chodzi. Wiem, że typ char ma 1 bajt, ale skąd nagle to przesyłanie 2 bajtów?

EDIT: Już rozumiem, w czym rzecz. Zmieniłem typ char na wchar_t. W tej chwili program "działa" tak, że na ekranie wypisuje tylko pierwszą literę, po czym się zawiesza. Po ubiciu wszystkich procesów plik wynikowy zawiera tylko pierwsze słowo z pliku przeznaczonego do skopiowania, spację oraz jakiś śmieć. Kompilator ostrzega też, że wchar_t nie jest prawidłowym argumentem funkcji strlen().

2
write(file2, read, (sizeof(read)+1)

trzeciem argumentem write jest liczba bajtów do zapisania. sizeof(read) to 1. sizeof(read)+1 to 2. read ma 1 bajt, a ty z jego adresu chcesz przeczytać 2 bajty = UB. Zmiana na wchar_t nie naprawi błędu, bo teraz wczytujesz 3 bajty z 2-bajtowej zmiennej.

0

Ok, poprawione. Niestety, teraz plik wynikowy jest pusty.

EDIT: Wprowadziłem pewne zmiany w kodzie (dla oszczędności miejsca wprowadziłem je też w pierwszym poście). W tej chwili cały program blokuje się po obsłużeniu ok. 50 znaków, czyli tyle, ile wynosi wielkość bufora.

0

Witam ponownie. W międzyczasie musiałem skupić się na innych zadaniach, stąd długi czas zmian. Napisałem od nowa sekcje krytyczne, Towar ma trochę inną formę, a semafory inicjowane są już tylko raz. Niestety, wciąż występuje problem blokowania się programu. Producent wykonuje swoją sekcję krytyczną 5 razy, konsument tylko raz i już w niej zostaje. Jestem już praktycznie w 100% pewny, że winę ponoszą semafory, ale niestety nie wiem, co z nimi jest nie tam. Proszę o podpowiedź, co należy z tym zrobić. Ze względu na czytelność, pliki programu wrzucam w formie paczki.

2

Nie znam uniksowych semaforów, ale dlaczego producent i konsument wywołują down (wait?) tylko dla SEM0 i up tylko dla SEM1?

0

@_0x666_: Dzięki za zwrócenie uwagi. Jakimś cudem tego nie zauważyłem. Oczywiście producent podczas wejścia do sekcji krytycznej opuszcza swój semafor, a w trakcie wyjścia podnosi semafor konsumenta. Konsument natomiast opuszcza własny semafor, by potem podnieść semafor producenta.
Teraz program wykonuje się od początku do końca. Niestety, w pliku wynikowym zwraca jakieś azjatyckie znaczki :D, ale tym już się zająłem.

EDIT: Program prawidłowo wykonuje się tylko raz na kilka uruchomień. Stąd też moje pytanie: co zrobić, aby za każdym razem obydwa procesy potomne były uruchamiane jednocześnie?

1

Nie da się. Zresztą po to używasz synchronizacji, żeby nie kombinować w ten sposób.

Pokaż kod po poprawkach.

0

@_0x666_: Już rozwiązane. Wprowadziłem opóźnianie sekcji krytycznych. Wszystkie próby zakończone powodzeniem. Kod ponownie zamieściłem w paczce .zip.

Paczka z działającym kodem.

2

IMO to opóźnianie to taka proteza dobrze napisanej synchronizacji.

0

@_0x666_: Zastanawiałem się nad użyciem sygnałów, ale i tak musiałbym zastosować opóźnianie obu procesów - taki był jeden z wymogów w zadaniu.

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