W systemie działa serwer i pewna liczba procesów. Każdy proces jest albo ugodowy albo neutralny albo marudny. Typ procesu podaje się przy jego uruchomieniu w linii poleceń: proces u, proces n lub proces m Proces w pętli nieskończonej: wykonuje własne sprawy, czyli po prostu usypia na losowy czas --- od 1 do 10 sekund, następnie wypisuje na standardowy strumień diagnostyczny komunikat "Proces %d, czekam na zasób.n", zgłasza się do serwera i czeka, aż serwer przydzieli mu zasoby, otrzymuje od serwera informacje o typie przydzielonego zasobu, korzysta z zasobu --- wypisuje na standardowy strumień diagnostyczny komunikat o typie otrzymanego zasobu: "Proces %d, otrzymalem zasób %d.n" i pracuje z zasobem, czyli usypia na losowy czas --- od 1 do 10 sekund wypisuje na standardowy strumień diagnostyczny komunikat "Proces %d, skonczylemn" i oddaje zasób serwerowi. Wszystkie komunikaty są w formacie stosowanym w fprintf i mają zawierać PID procesu. Poza tym procesy nie wypisują żadnych innych komunikatów. Zarządzaniem procesami i zasobami zajmuje się serwer. Zarządza on zasobami dwóch typów: lepszym i gorszym. Jest on wywoływany z dwoma parametrami: N - oznaczającym liczbę dostępnych zasobów lepszych, M - oznaczającym liczbę dostępnych zasobów gorszych. Serwer komunikuje się z procesami za pomocą kolejki (lub kolejek) komunikatów. W pierwszej kolejności odbiera on komunikaty o zwrocie zasobów, które obsługuje sam. W następnej kolejności odbiera komunikaty z prośbą o przydzielenie zasobu. Po odbiorze takiego komunikatu serwer tworzy wątek odpowiedzialny za obsługę tego żądania i ponownie oczekuje na zlecenia od procesów. Wątek odpowiedzialny za obsługę żądań działa w następujący sposób: jeżeli żądanie pochodzi od procesu marudnego, wątek oczekuje aż będzie dostępny przynajmniej jeden egzemplarz zasobu lepszego, po czym wysyła do procesu komunikat z informacją o przydzieleniu mu zasobu i kończy się, jeżeli żądanie pochodzi od procesu neutralnego, wątek oczekuje aż będzie dostępny przynajmniej jeden egzemplarz zasobu dowolnego typu (preferując jednak o ile to możliwe zasoby lepsze), po czym wysyła do procesu komunikat z informacją o typie przydzielonego zasobu i kończy się, jeżeli żądanie pochodzi od procesu ugodowego, wątek oczekuje aż będzie dostępny przynajmniej jeden egzemplarz zasobu dowolnego typu (bez żadnych preferencji), po czym wysyła do procesu komunikat z informacją o typie przydzielonego zasobu i kończy się. Zaimplementuj opisany powyżej schemat. Rozwiązanie powinno: zawierać co najmniej dwa pliki wykonywalne: serwer oraz proces, wykorzystywać do komunikacji między serwerem a procesami kolejki komunikatów IPC, wykorzystywać do synchronizacji wątków muteksy i zmienne warunkowe, dbać o to, aby przerwanie działania serwera za pomocą CTRl+C nie zostawiało po sobie żadnych niezwolnionych zasobów IPC, zawierać prostą obsługę błędów - wystarczy, że proces wykryjący błąd zakończy się. Wszystkie pliki źródłowe oraz Makefile należy spakować do jednego pliku i wysłać go jako załącznik na adres mengel@mimuw.edu.pl do dnia 14 czerwca 2010, godz. 23:59. Każdy rozpoczęty tydzień spóźnienia skutkuje ,,premią'' w wysokości -2pkt.
A oto rozwiązanie:
[adsense]
Makefile:
all: serwer proces serwer: serwer.cpp g++ serwer.cpp -o serwer -lpthread proces: proces.cpp g++ proces.cpp -o proces -lpthread
proces.cpp :
#include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; struct wiadomo { long mtype; int wiadomosc; }; int main(int argc, char* argv[]) { //Tworzymy kolejke: key_t key = ftok("./", 1); int msqid = msgget(key, 0666 | IPC_CREAT); srand ( time(NULL) ); pid_t pid=getpid(); while(1) { sleep(rand()%10+1); fprintf(stderr,"Proces %d, czekam na grupe.n",pid); //cerr<<"Proces "<<pid<<", czekam na grupe.n"; struct wiadomo wiadomosc={1,10}; //Pukamy do drzwi serwera: msgsnd(msqid, &wiadomosc, sizeof(wiadomosc), 0); struct wiadomo wiadomosc2; //Czekamy na odpowiedz z numerem grupy msgrcv(msqid, &wiadomosc2, sizeof(wiadomosc), 2, 0); int nr_grupy=wiadomosc2.wiadomosc; //cout<<"Przydzielono do grupy: "<<nr_grupy<<"n"; struct wiadomo wiadomosc3; //A moze jakies zasoby? msgrcv(msqid, &wiadomosc3, sizeof(wiadomosc), 3+2*nr_grupy, 0); int zasobow=wiadomosc3.wiadomosc; //cout<<"Przydzielono zasobow: "<<zasobow<<"n"; //No i mamy zasoby. Teraz chcemy sie zesynchronizowac! int key2 = ftok("./", 2+nr_grupy); int semid = semget(key2,1, 0666 ); static struct sembuf buf; buf.sem_num=0; buf.sem_op=-1; //Hapsamy semafor buf.sem_flg=0; semop(semid,&buf,1); //cout<<"Jestesmy w sekcji krytycznej. Czekamy "<<zasobow<<" sekund. n"; //cerr<<"Proces "<<pid<<", otrzymalem "<<zasobow<<" zasobow.n"; fprintf(stderr,"Proces %d, otrzymalem %d zasobow.n",pid,zasobow); //Robimy swoje: sleep(zasobow); //cout<<"Opuszczamy. "<<zasobow<<"n"; buf.sem_op=1; semop(semid,&buf,1); struct wiadomo wiadomosc4; wiadomosc4.mtype=4+2*nr_grupy; wiadomosc4.wiadomosc=pid; //cout<<"["<<wiadomosc4.mtype<<"]n"; msgsnd(msqid, &wiadomosc4, sizeof(wiadomosc4), 0); //skonczylismy. Na razie! //cerr<<"Proces "<<pid<<", skonczylemn"; fprintf(stderr,"Proces %d, skonczylemn",pid); } return 0; }
serwers.cpp :
#include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; //zmienne key_t key; int msqid; int nr_grupa=1; int start_zasobow=3; int zasoby=0;//char2int(argv[1]); int grupy=3;//char2int(argv[2]); int watki=2;//char2int(argv[3]); int potrzeba_zasobow=0; pthread_mutex_t mut,mut2; pthread_cond_t cond,cond_zas1,cond_zas2; int pytan=0; struct wiadomo { long mtype; int wiadomosc; }; void* watek(void *argument) { while(1) { //Bedziemy czekac na grupe: pthread_mutex_lock(&mut); if(pytan<grupy) { pthread_cond_wait(&cond, &mut); } pytan=pytan-grupy; int numer=nr_grupa; nr_grupa++; pthread_mutex_unlock(&mut);// mutex sluzy nam glownie do czytania numeru. //Grupa jest. Do akcji! //Akceptacja polaczenia z informacja do ktorej grupy przydzial: struct wiadomo wiadomosc; wiadomosc.mtype=2; wiadomosc.wiadomosc=numer; for(int i=0;i<grupy;i++) { msgsnd(msqid, &wiadomosc, sizeof(wiadomosc), 0); } int potrzeba=rand()%(start_zasobow-1)+1;//ile chcemy zasobow? //cerr<<"Grupa "<<numer<<" oczekuje na "<<potrzeba<<" zasobow.n"; fprintf(stderr,"Grupa %d oczekuje na %d zasobow.n",numer,potrzeba); //cout<<zasoby<0) { //cout<<zasoby<<"n"; pthread_cond_wait(&cond_zas1, &mut2); } //wpuscili nas do korytarza czekania. mowimy ile potrzebujemy zasobow. potrzeba_zasobow=potrzeba; while(zasoby<potrzeba_zasobow) { //cout<<zasoby<<"n"; pthread_cond_wait(&cond_zas2, &mut2); } //wpuszczono nas i dano tyle zasobow ile potrzebujemy. zasoby=zasoby-potrzeba_zasobow; potrzeba_zasobow=0; //Wpuszczamy do korytarza czekania. pthread_cond_signal(&cond_zas1); pthread_mutex_unlock(&mut2); //koniec czekania na zasoby //cerr<<"Grupa "<<numer<<" otrzymuje "<<potrzeba<<" zasobow.n"; fprintf(stderr,"Grupa %d otrzymuje %d zasobow.n", numer,potrzeba); //informujemy o uzbieranych zasobach. //struct wiadomo wiadomosc; wiadomosc.mtype=3+2*numer; wiadomosc.wiadomosc=potrzeba; //Tworzymy semaforki int key2 = ftok("./", 2+numer); int semid = semget(key2,1, 0666| IPC_CREAT); semctl(semid,0,SETVAL,1); //Utworzone. for(int i=0;i<grupy;i++) { msgsnd(msqid, &wiadomosc, sizeof(wiadomosc), 0); } //Wyslalismy zasoby. Teraz zbieramy odpowiedzi. //Teraz beda porzetwarzac int zrodlo=4+2*numer; //cout<<"["<<zrodlo<<"]n"; for(int i=0;i0)&&(zasoby>=potrzeba_zasobow)) { pthread_cond_signal(&cond_zas2); //ktos czeka na zasoby. wpuszczamy } pthread_mutex_unlock(&mut2); fprintf(stderr,"Koniec obslugi grupy %d.n",numer); //cerr<<"Koniec obslugi grupy "<<numer<<".n"; } } int char2int(char* dane) { int wynik=0; for(int i=0;i<strlen(dane);i++) { wynik=wynik*10+(int)(dane[i]-48); } return wynik; } void koncz(int sig) { //Czyszczenie struktur danych. //Mutexy i inne takie pthread_mutex_destroy(&mut); pthread_mutex_destroy(&mut2); pthread_cond_destroy(&cond); pthread_cond_destroy(&cond_zas1); pthread_cond_destroy(&cond_zas2); //Kolejki msgctl(msqid, IPC_RMID, NULL); exit(0); } int main(int argc, char* argv[]) { if(argc==4) { start_zasobow=char2int(argv[1]); grupy=char2int(argv[2]); watki=char2int(argv[3]); srand ( time(NULL) ); (void) signal(SIGINT, koncz);//lapanie ctrl-c zasoby=start_zasobow; //Tworzenie kolejki 'glownej:' key = ftok("./", 1); msqid = msgget(key, 0666 | IPC_CREAT); //Zmienna warunkowa - kolejka procesow. pthread_cond_init(&cond, 0); //Zmienna warunkowa - tlum czekajacy na zasoby. pthread_cond_init(&cond_zas1, 0); //Zmienna warunkowa - pokoj czekania. pthread_cond_init(&cond_zas2, 0); //Mutex czekania na grupe pthread_mutex_init(&mut, 0); //Mutex czekania na zasoby pthread_mutex_init(&mut2, 0); //Tworzymy watki: for(int i=0;i=grupy) { //Nowa grupa! pthread_cond_signal(&cond); } pthread_mutex_unlock(&mut); } } else { exit(1); } return 0; }