Dziwne dzialanie funkcji semop

0

moj program polega na tym ze 3 razy sobie forkuje tworzac 3 procesy o tym samym rodzicu. proces glowny tworzy 2 pamieci dzielone (jedna dla wczytanej linijki, druga dla zliczonej ilosci znakow). proces p1 zapisuje do pamieci dzielonej wczytana linijke, ale przedtym musi sprawdzic czy zostal opuszczony semafor.
problem jest gdzies tu:
proces p1 probuje opuscic semafor[0] i kiedy mu sie uda to przechodzi do wczytywania linijki:

while(opusc(semID, 0) != 0);

funkcja wyglada tak:

int opusc(int klucz, int nr){
    semafor.sem_num=nr;
    semafor.sem_op=-1;
    semafor.sem_flg=0;
    if(semop(klucz, &semafor, 1)==-1)
        return 1;
  return 0;
}

program wchodzi do funkcji opusc ale po wykonaniu funkcji semop

  if(semop(klucz, &semafor, 1)==-1)
        return 1; 

program sie rozplywa i nie mam pojecia dlaczego, nawet nie wiem gdzie jestem.
probowalem cos z debuggerem ale na linuxie z forkami to srednio dziala
caly moj kod:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <signal.h>

#define KLUCZ_CHAR 256
#define ROZMIAR_CHAR 128

#define KLUCZ_INT (KLUCZ_CHAR + 1)
#define ROZMIAR_INT 4

#define KLUCZ_SEM 512
char wybor='0';
int sygnal, P1, P2, P3;
struct sembuf semafor;
int stop1 = 0, stop2 = 0, stop3 = 0;
int term1 = 0, term2 = 0, term3 = 0;

int podnies(int klucz, int nr){
    semafor.sem_num=nr;
    semafor.sem_op=1;
    semafor.sem_flg=0;
    if(semop(klucz, &semafor, 1)==-1)
  		return 1;
  	return 0;
}

int opusc(int klucz, int nr){
    semafor.sem_num=nr;
    semafor.sem_op=-1;
    semafor.sem_flg=0;
    if(semop(klucz, &semafor, 1)==-1)
        return 1;
  	return 0;
}

void menu(){
	printf("Menu\n");
	printf("1. Wczytywanie z stdin\n");
	printf("2. Wczytywanie z stdfile\n");
	//printf("%c\n",wybor);
	wybor=getchar();
	printf("Wybrano: %c\n",wybor);
	fflush(stdin);
}

int main(){
	int pamiecDzielonaChar = shmget(KLUCZ_CHAR, ROZMIAR_CHAR, IPC_CREAT|0666);
    char *bufferChar = (char*)shmat(pamiecDzielonaChar, NULL, 0);

	int pamiecDzielonaInt = shmget(KLUCZ_INT, ROZMIAR_INT, IPC_CREAT|0666);
    int *bufferInt = (int*)shmat(pamiecDzielonaInt, NULL, 0);

	int semID = semget(KLUCZ_SEM, 3, IPC_CREAT|0666);
    semctl(semID, 0, SETVAL, (int)1);
    semctl(semID, 1, SETVAL, (int)0);
    semctl(semID, 2, SETVAL, (int)0);

	P1 = fork();

	switch(P1)
	{
		//printf("jestem tutaj");
		case 0://proces P1
			while(!term1){
				menu();
				switch(wybor){
					char nazwaPliku[80];
					FILE *plik;
					char tekst[ROZMIAR_CHAR];
					case '1'://stdin						
                        while(!stop1) {//i dopoki sem 0 jest
							while(opusc(semID, 0) != 0);
							fgets(tekst, ROZMIAR_CHAR, stdin);
							if (tekst[0] == '.' && tekst[1] == '\n')
								break;
							strcpy(bufferChar, tekst);

							podnies(semID, 1);
						}
					break;

					case '2'://stdfile NIESKONCZONE
						printf("Podaj nazwa pliku: ");
						scanf("%s", nazwaPliku);
						if ((plik = fopen(nazwaPliku, "rt")) == NULL) {
							printf ("Nie mogę otworzyć pliku \"%s\" do zapisu!\n", nazwaPliku);
							exit(1);
						}
						int i = 0;
						tekst[i++] = getc(plik);
						while(tekst[i - 1] != EOF){
							tekst[i++] = getc(plik);
						}
						fclose(plik);
						plik = NULL;
						opusc(semID, 0);
						strcpy(bufferChar, tekst);
						podnies(semID, 1);
					break;
				}
			}
			exit(0);
		break;

		case -1:
       		printf("err\n");
			exit(1);
		break;

		default:
			switch(P2 = fork())
			{
				case 0://proces P2
				while(!term2){
					while(!stop2){
						while(!opusc(semID, 0)){
							opusc(semID, 1);
							bufferInt[0] = strlen(bufferChar);
							podnies(semID, 2);
						}
					}
				}
				break;

				case -1:
			  		printf("err\n");
					exit(1);
				break;

				default:
					switch(P3 = fork())
					{
						case 0://proces P3
							printf("%d", getpid());
							while(!term3){
								while(!stop3){
								while(!opusc(semID, 2)){
									printf("%d", bufferInt[0]);
									printf("Zapisalem do inta");
									podnies(semID, 0);
									}
								}
							}
						break;

						case -1:
					  		printf("err\n");
							exit(1);
						break;

						default:
						break;
					}
				break;
			}
		break;
	}
	wait(NULL);
	wait(NULL);
	wait(NULL);

	unlink("kolejka");

	semctl(semID, 0, IPC_RMID);
	semctl(semID, 1, IPC_RMID);
	semctl(semID, 2, IPC_RMID);

    shmdt(bufferChar);
    shmdt(bufferInt);

    shmctl(pamiecDzielonaChar, IPC_RMID, 0);
    shmctl(pamiecDzielonaInt, IPC_RMID, 0);
	return 0;
}
 
0

prostszy przyklad, te same funkcje i robie to samo
http://ideone.com/opKKrq
konsola pokazuje '0' czyli opuszczenie semafora sie powiodlo, dlaczego w moim kodzie tak nie dziala?

2

Cześć,

Muszę przyznać, że przez powyższy kod jest bardzo nieczytelny przez użycie switch/case do "obsługi" fork(). Zazwyczaj się to jednak robi za pomocą ifów.
Do tego, ponieważ masz kilka procesów, to bardzo by się przydało, żeby każdy był zaimplementowany w osobnej funkcji - zdecydowanie by to zwiększyło czytelność.
Nie za bardzo też rozumiem co znaczy, że program się "rozpływa".

Ad meritum:
Przy debugowaniu wielu procesów przydaje się kilka "sztuczek".

  1. strace -f -o syscall.log ./a.out, który zapisze Ci per proces wszystkie wywołane syscalle, co powinno dać Ci obraz "gdzie się znajdujesz".
  2. Odpal program bez debuggera i zobacz (np. na innej konsoli) jakie pidy mają jego dzieci (pstree -p | grep a.out) i podłącz osobno debuggery dla każdego dziecka (gdb -p PID).

Anyway, z tego co na szybko rzuciłem okiem, to P2 od razu zajmuje semafor 0, przez co P1 nie może zrobić na nim opusc() i się na tym zawiesza. Coś tam masz z logiką tego nie tak.
W każdym razie do tego można dojść korzystając z pierwszej podpowiedzi wyżej:

...
17371 semctl(0, 0, SETVAL, 0x1)         = 0
17371 semctl(0, 1, SETVAL, 0)           = 0
17371 semctl(0, 2, SETVAL, 0)           = 0
17371 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fc8b1f9ba10) = 17372
...
17371 clone( <unfinished ...>
...
17371 <... clone resumed> child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fc8b1f9ba10) = 17373
17373 semop(0, {{0, -1, 0}}, 1 <unfinished ...>
...
17373 <... semop resumed> )             = 0
...
17373 semop(0, {{1, -1, 0}}, 1 <unfinished ...>
...
(reszta jest nieistotna)

Jak widać powyżej:

  • 17371 to proces rozdzic
  • 17371 tworzy inicjuje semafory i tworzy (clone) pierwsze dziecko, które dostaje PID 17372
  • po tym 17371 tworzy (clone) drugie dziecko, które dostaje PID 17373
  • 17373 od razu robi semop(0, ... -1, ...), co się udaje (17373 <... semop resumed> ) = 0)
  • po czym 17373 blokuje się próbując zając semafor 1
    W tym czasie 17372 dopiero jest w menu() btw, więc jak dojdzie w końcu do while(opusc(semID, 0) != 0); to nie może zająć tego semafora, bo P2 ma go już zajętego.

Anyway, spróbuj to zdebugować. Jeśli będziesz miał problemy, daj znać.

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