C - wykonywanie procesów zatrzymuje się w pewnym momencie

Witam. Jestem w trakcie zmagań z problemem producenta i konsumenta. Jako rozwiązanie używam semaforów zliczających, pamięci dzielonej oraz bufora cyklicznego. Niestety, po uruchomieniu oba procesy dość szybko się zatrzymują, pomaga jedynie ich ubicie. W pliku wynikowym pojawia się od jednej do trzech liter + śmietnik lub plik jest pusty. Już od dłuższego czasu próbuję dojść do ładu z tym programem, pisałem sekcje krytyczne od zera, ale to nic nie dało. Kompletnie nie wiem, co jest źle. Proszę o pomoc, poradę. Poniżej załączam pliki programu.

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];
        if(read=='~')
        {
            ++bufor->begin;
            if(bufor->begin==50)
            {
                bufor->begin=0;
            }
            down(id_sem, SEM0);
        }
        printf("Konsument:\n");
        printf("%c"-'\0', read);

        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;
}

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.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.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

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>
#include <string.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

EDIT: Poprawiłem jeden znaleziony przeze mnie błąd. Został on także poprawiony w powyższym kodzie. Teraz program zachowuje się różnie, ale zawieszanie się pozostało. Teraz plik wyjściowy jest albo niezdatny do odczytu, albo zawiera najwyżej kilkanaście słów + śmieci.
Program dostaje zwiechy po ok. 50 znakach, czyli tyle, ile wynosi wielkość bufora.

@krystian0120 kilka sugestii na start. Postaraj się maksymalnie ułatwić zadanie osobom, które mogłyby Ci tutaj pomóc. W tym przypadku:

Wrzuć gdzieś źródła. Jeśli nawet nie na gita/svna to paczkę zip. Tak żeby nikt nie musiał tego kopiować plik po pliku.
Opisz, która binarka za co odpowiada. To pozwala dużo szybciej wskoczyć w kod. Jak powinny się nazywać, jak ze sobą współpracują i jakie przyjmują parametry.
Dołączaj Makefile, który pozwoli na skompilowanie tego jednym poleceniem. Np.

all: main producer consumer

main: sem_func.o mem_func.o main.o
	gcc sem_func.o mem_func.o main.o -o main -Wall

producer: producer.o sem_func.o mem_func.o
	gcc producer.o sem_func.o mem_func.o -o producer -Wall

consumer: consumer.o sem_func.o mem_func.o
	gcc consumer.o sem_func.o mem_func.o -o consumer -Wall

Bez kompilacji mogę zasugerować zmianę tego fragmentu:

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

Liczba argumentów rzadko spada do 0 ;). A Ty ich wykorzystujesz sporo więcej niż tylko 1.
Funkcje nie wydają się robić tego, co sugerują ich nazwy. Czy funkcje “create” tworzą semafor, albo współdzielony obszar pamięci?

Liczba argumentów już poprawiona. Dołączam także paczkę z plikami. Co do nazewnictwa - było inne, ale prowadzący uznał za stosowne, by właśnie te funkcje miały w nazwie “create”.
problem.zip (5,1 KB)

Eh. Ciężko Ci pomóc, póki nie wykażesz trochę zaangażowania. Makefile skopiowałeś - nazwy produkowanych binarek na pewno mają być takie jak przykładowo podałem?

Napisz też:

  • co ma robić każda z binarek?
  • jakie parametry przyjmuje?

Bo można się domyślać, że czytają z jednego pliku, zapisują do drugiego. Ale bez dokładnego opisu ciężko wnioskować, co poszło nie tak. I w którym kierunku poszło nie tak.

if(argc<4)

Co mają oznaczać te argumenty? Jak podam 10 (>4) to jest ok?

problem cat main.c| grep argv
int main(int argc, char const *argv[])
        if(execl(prod_path, prod_name, semid, memid, argv[3], NULL)==-1)
        if(execl(cons_path, cons_name, semid, memid, argv[4], NULL)==-1)

argv[4] to 5ty argument. A co z argv[2] i argv[1]?

@NotRated: Wybacz. W międzyczasie skupiłem się na innych zadaniach. Dopiero dzisiaj znalazłem czas na ten program. W tej chwili od nowa piszę sekcje krytyczne oraz strukturę towaru. Inicjowanie semaforów odbywa się już tylko raz.
Jestem w 100% pewien, że winne wszystkiemu są semafory. Po ilości cykli równej wartości ustalonej w setValue któryś z semaforów zostaje opuszczony i już nie pozwala się podnieść.
argv[1] i argv[2] to kolejno nazwy plików producenta i konsumenta.

Niestety, wciąż nie potrafię rozwiązać problemu z semaforami. Producent czyta kilka razy, natomiast konsument tylko raz, na dodatek nie wychodzi nawet poza sekcję krytyczną.

problem2.zip (5,4 KB)

Witam ponownie. Doprowadziłem już program do niemal całkowicie poprawnego działania. Muszę jedynie doprowadzić do sytuacji, gdzie obydwa procesy potomne będą startowały w minimalnych odstępach czasu (tj. możliwie najbliższych zeru).

EDIT: Jako, że rozwiązałem już wszystkie problemy związane z programem, postanowiłem pochwalić się gotowym kodem: rozwiazane.zip (5,6 KB).

1 polubienie

Niestety miałem wolne od Internetu :wink:

Pozostaje mi pogratulować!