/* * Primjer programa koji bi trebao prikazati protokol nasljeđivanja prioriteta * ako ga sustav podržava! * Sustav dretvi modeliran je prema slici: * "Slika 4.12: Primjer korištenja protokola nasljeđivanja prioriteta" * iz skripte: L. Jelenković, "Sustavi za rad u stvarnom vremenu", 2012. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include /* prevođenje: # gcc nas_prio2.c -pthread -lrt (opcionalno uključit i optimizaciju, npr. -03) pokretanje (mora se kao root, inače neće se moći postaviti RT raspoređivanje): # ./a.out (ili sudo ./a.out) (sustav moze postat 'čudan', obzirom da se ovo vrti prije sveg ostaloga!) */ /* BROJAC = puno = broj iteracija koje neće dostići unutar sekunde */ #define BROJAC 10000000000000ULL /* * makro za poziv funkcije i provjeru povratne vrijednosti: * ako povratna vrijednost nije nula ispisi grešku i stani */ #define PF(FUN, ...) \ ({ \ int pv = FUN ( __VA_ARGS__ ); \ if ( pv != 0 ) \ { \ fprintf ( stderr, "Greska: %s (%d)\n", #FUN, __LINE__ );\ exit(1); \ } \ pv; \ }) /* * npr. poziv: if ( neka_funkcija(p1, p2, p3) ) { * fprintf ( stderr, "Greska!\n" ); * exit (1); * } * * treba zapisati kao: a = PF(neka_funkcija, p1, p2, p3); */ volatile static unsigned long long brojac = BROJAC; /* broj iteracija za 1 s */ volatile static unsigned int kraj = 0; /* simulira rad - trošenje procesorskog vremena */ static void broji(int v, char ime, char dio) { unsigned long long k = 0; int j; for (j = 1; j <= v && !kraj; j++) { printf("Dretva %c :: dio %c (%d/%d)\n", ime, dio, j, v); for (k = 0; k < brojac && !kraj; k++) asm volatile ("":::"memory"); /* ništa korisno, ali da i nakon optimizacije ipak ostane petlja */ } if (kraj) { /* je li mjerim? ako da zapamti "brojac" za 1 s */ brojac = k; printf("1 s = %lld iter\n", brojac); kraj = 0; } } /* * poziva se nakon isteka alarm-a (iz nove dretve) * za mjerenje koliko iteracija treba za jednu sekundu * dosta neprecizno, ali za ovaj primjer zadovoljavajuce */ static void alarm_f (union sigval a) { kraj = 1; return; } /* opis posla jedne dretve */ struct pd { int a_dio, b_dio, c_dio, d_dio; /* trajanje pojedinih elemenata u s */ pthread_mutex_t *mon_az, *mon_bz, *mon_cz, *mon_dz; pthread_mutex_t *mon_ao, *mon_bo, *mon_co, *mon_do; int pocetna_odgoda; int prio; char ime; }; /*simuliranje obavljanje "dijela" posla */ static void dio ( int trajanje, pthread_mutex_t *monz, pthread_mutex_t *mono, char ime, char dio ) { if ( monz != NULL ) pthread_mutex_lock ( monz ); broji ( trajanje, ime, dio ); if ( mono != NULL ) pthread_mutex_unlock ( mono ); } /* pridjela brojeva dretvama (nije zaštićeno, ali...) */ static int id_dretve () { static int id = 1; return id++; } static void *dretva ( void *param ) { struct pd *p = param; int id = id_dretve(); struct timespec koliko; PF ( pthread_setschedprio, pthread_self(), p->prio ); /* može se i komentirati, jer je već i prije stvaranja postavljen */ koliko.tv_nsec = 0; koliko.tv_sec = p->pocetna_odgoda; nanosleep ( &koliko, NULL ); printf("Dretva %c pocela (id=%d, prio=%d)\n", p->ime, id, p->prio); dio ( p->a_dio, p->mon_az, p->mon_ao, p->ime, 'A' ); dio ( p->b_dio, p->mon_bz, p->mon_bo, p->ime, 'B' ); dio ( p->c_dio, p->mon_cz, p->mon_co, p->ime, 'C' ); dio ( p->d_dio, p->mon_dz, p->mon_do, p->ime, 'D' ); return NULL; } int main () { int min, max, pocetni, i; struct sigevent dog; timer_t timerid; struct itimerspec koliko; pthread_attr_t thread_attr; pthread_mutex_t mon1, mon2; pthread_t dr[5]; cpu_set_t cpu_mask; struct sched_param prio; printf ( "Obzirom da se koriste RT dretve ispis s printf-om moze biti" " odgodjen! Prekinuti nakon nekog vremena s Ctrl+C pa ce se" " pojaviti i ispis!\n" ); /*! ograniči rad na prvom procesoru */ CPU_ZERO ( &cpu_mask ); CPU_SET ( 0, &cpu_mask ); PF ( sched_setaffinity, (pid_t) 0, sizeof (cpu_set_t), &cpu_mask ); //ovo bi i ostale stvorene dretve trebalo zadržati na istom procesoru //jer one naslijeđuju to svojstvo /*! postavi SCHED_FIFO za ovaj proces */ min = sched_get_priority_min(SCHED_FIFO); max = sched_get_priority_max(SCHED_FIFO); printf("Prioriteti za SCHED_FIFO su u rangu: %d - %d\n", min, max); pocetni = (min + max) / 2; prio.sched_priority = pocetni; PF ( sched_setscheduler, (pid_t) 0, SCHED_RR, &prio ); /* ponekad, ako se stavi SCHED_FIFO može biti problem u aktivaciji * alarma. Zato je ovdje promijenjeno u SCHED_RR s kojim radi */ /*! definiraj svojstva za dretvu alarma (i ostale dretve poslije) */ PF ( pthread_attr_init, &thread_attr ); PF ( pthread_attr_setinheritsched, &thread_attr, PTHREAD_EXPLICIT_SCHED ); PF ( pthread_attr_setschedpolicy, &thread_attr, SCHED_FIFO ); prio.sched_priority = max; //najveći, ali ipak ne pomaže uvijek! PF ( pthread_attr_setschedparam, &thread_attr, &prio ); /*! alarm za brojanje iteracija u 1 s */ /* što kad se "događaj dogodi"? */ dog.sigev_notify = SIGEV_THREAD; /* stvori dretvu koja će obraditi događaj */ /* SIGEV_SIGNAL za samo signal */ /* koji signal, ako se signal šalje */ dog.sigev_signo = 0; /* vrijednost kao parametar dretvi ili uz signal: _int ili _ptr */ dog.sigev_value.sival_int = 0; /* početna funkcija za dretvu, ako dretvom obrađujemo događaj */ dog.sigev_notify_function = alarm_f; dog.sigev_notify_attributes = &thread_attr; /* stvaranje alarma, ali nije automatski i "pokrenut" */ PF ( timer_create, CLOCK_REALTIME, &dog, &timerid ); koliko.it_value.tv_sec = 1; koliko.it_value.tv_nsec = 0; koliko.it_interval.tv_sec = 0; koliko.it_interval.tv_nsec = 0; /* postavi vrijeme aktivacije i pokreni prethodni alarm */ PF ( timer_settime, timerid, 0, &koliko, NULL ); /* dok alarm ne istekne "vrti" petlju - gledaj koliko iteracija treba */ broji ( 1, 'G', 'A' ); /* obriši alarm */ PF ( timer_delete, timerid ); /*! inicijaliziraj atribute za monitor */ pthread_mutexattr_t mutex_attr; PF ( pthread_mutexattr_init, &mutex_attr ); PF ( pthread_mutexattr_settype, &mutex_attr, PTHREAD_MUTEX_RECURSIVE ); PF ( pthread_mutexattr_setprotocol, &mutex_attr, PTHREAD_PRIO_INHERIT ); /* inicijaliziraj monitor */ PF ( pthread_mutex_init, &mon1, &mutex_attr ); PF ( pthread_mutex_init, &mon2, &mutex_attr ); struct pd params[5] = { /* parametri za zadatke/dretve */ { 3,9,0,3, NULL, &mon1, NULL, NULL, NULL, &mon1, NULL, NULL, 0, pocetni - 5, 'A' }, { 3,0,0,3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 6, pocetni - 4, 'B' }, { 3,3,3,3, NULL, &mon2, &mon1, NULL, NULL, NULL, &mon2, &mon1, 9, pocetni - 3, 'C' }, { 3,0,0,3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 18, pocetni - 2, 'D' }, { 3,3,0,3, NULL, &mon2, NULL, NULL, NULL, &mon2, NULL, NULL, 21, pocetni - 1, 'E' } }; /*! stvori dretve */ for ( i = 0; i < 5; i++ ) { prio.sched_priority = params[i].prio; PF ( pthread_attr_setschedparam, &thread_attr, &prio ); /* stvaranje nove dretve */ PF ( pthread_create, &dr[i], &thread_attr, dretva, ¶ms[i] ); } /*! čekaj da sve stvorene dretve završe s radom */ for ( i = 0; i < 5; i++ ) PF ( pthread_join, dr[i], NULL ); /* obriši mutexe (nije neophodno ako nisu u zajedničkoj memoriji) */ PF ( pthread_mutex_destroy, &mon1 ); PF ( pthread_mutex_destroy, &mon2 ); return 0; } /* *** Pokretanje na 1-procesorskom računalu: *** $ gcc nas_prio.c -lrt -pthread $ ./a.out Dretva G :: dio A (1/1) 1 s = 402595210 iter Greska: sched_setscheduler (197) $ sudo ./a.out Dretva G :: dio A (1/1) 1 s = 402008846 iter Dretva A pocela (id=1, prio=45) Dretva A :: dio A (1/3) Dretva A :: dio A (2/3) Dretva A :: dio A (3/3) Dretva A :: dio B (1/9) Dretva A :: dio B (2/9) Dretva A :: dio B (3/9) Dretva B pocela (id=2, prio=46) Dretva B :: dio A (1/3) Dretva B :: dio A (2/3) Dretva B :: dio A (3/3) Dretva C pocela (id=3, prio=47) Dretva C :: dio A (1/3) Dretva C :: dio A (2/3) Dretva C :: dio A (3/3) Dretva C :: dio B (1/3) Dretva C :: dio B (2/3) Dretva C :: dio B (3/3) Dretva A :: dio B (4/9) Dretva A :: dio B (5/9) Dretva D pocela (id=4, prio=48) Dretva D :: dio A (1/3) Dretva D :: dio A (2/3) Dretva D :: dio A (3/3) Dretva E pocela (id=5, prio=49) Dretva E :: dio A (1/3) Dretva E :: dio A (2/3) Dretva E :: dio A (3/3) Dretva A :: dio B (6/9) Dretva A :: dio B (7/9) Dretva A :: dio B (8/9) Dretva A :: dio B (9/9) Dretva C :: dio C (1/3) Dretva C :: dio C (2/3) Dretva C :: dio C (3/3) Dretva E :: dio B (1/3) Dretva E :: dio B (2/3) Dretva E :: dio B (3/3) Dretva E :: dio D (1/3) Dretva E :: dio D (2/3) Dretva E :: dio D (3/3) Dretva D :: dio D (1/3) Dretva D :: dio D (2/3) Dretva D :: dio D (3/3) Dretva C :: dio D (1/3) Dretva C :: dio D (2/3) Dretva C :: dio D (3/3) Dretva B :: dio D (1/3) Dretva B :: dio D (2/3) Dretva B :: dio D (3/3) Dretva A :: dio D (1/3) Dretva A :: dio D (2/3) Dretva A :: dio D (3/3) $ *** Primjer pokretanja (na 2 procesorskom računalu): *** $ sudo ./a.out Dretva G :: dio A (1/1) 1 s = 400624873 iter Dretva A pocela (id=1, prio=45) Dretva A :: dio A (1/3) Dretva A :: dio A (2/3) Dretva A :: dio A (3/3) Dretva A :: dio B (1/9) Dretva A :: dio B (2/9) Dretva A :: dio B (3/9) Dretva A :: dio B (4/9) Dretva B pocela (id=2, prio=46) Dretva B :: dio A (1/3) Dretva A :: dio B (5/9) Dretva B :: dio A (2/3) Dretva A :: dio B (6/9) Dretva B :: dio A (3/3) Dretva C pocela (id=3, prio=47) Dretva C :: dio A (1/3) Dretva B :: dio D (1/3) Dretva C :: dio A (2/3) Dretva B :: dio D (2/3) Dretva C :: dio A (3/3) Dretva B :: dio D (3/3) Dretva C :: dio B (1/3) Dretva A :: dio B (7/9) Dretva C :: dio B (2/3) Dretva A :: dio B (8/9) Dretva C :: dio B (3/3) Dretva A :: dio B (9/9) Dretva D pocela (id=4, prio=48) Dretva D :: dio A (1/3) Dretva A :: dio D (1/3) Dretva C :: dio C (1/3) Dretva D :: dio A (2/3) Dretva C :: dio C (2/3) Dretva E pocela (id=5, prio=49) Dretva E :: dio A (1/3) Dretva D :: dio A (3/3) Dretva E :: dio A (2/3) Dretva D :: dio D (1/3) Dretva E :: dio A (3/3) Dretva D :: dio D (2/3) Dretva D :: dio D (3/3) Dretva C :: dio C (3/3) Dretva C :: dio D (1/3) Dretva E :: dio B (1/3) Dretva E :: dio B (2/3) Dretva C :: dio D (2/3) Dretva E :: dio B (3/3) Dretva C :: dio D (3/3) Dretva E :: dio D (1/3) Dretva A :: dio D (2/3) Dretva E :: dio D (2/3) Dretva A :: dio D (3/3) Dretva E :: dio D (3/3) $ */