Process- programmering höstterminen år 2002 socket() socket() bind() connect() listen() accept() inkoppling väntar på klient read() anrop write() write() svar read() Sida 1/38
INNEHÅLL Kommandorad rörledning 4 Systemanrop 4 En process startar upp en annan process i UNIX 4 Processklyvning 4 Systemanrop fork 4 Kodbyte i process 5 Systemanropet execl 5 Systemanropet execv 5 En process (=föräldraprocess) startar upp en process (=barnprocess) 6 Systemanropet wait 6 Processtruktur 7 Programmerade rörledningar 7 Fördefinierade fildeskriptorer 7 Fildeskriptor 7 Strömpekarnamn 7 Ursprunglig betydelse 7 STDIN 7 STDOUT 7 STDERR 7 systemanropet pipe 7 systemanropet write 10 Automatiskt synkronisering 10 Några viktiga systemanrop 12 Systemanropet getpid 12 Systemanropet getppid 12 Systemanropet sleep 12 Systemanropet pause 12 Systemanropet exit 12 Namngivna rörledningar 12 Hur skapas en named pipe 12 Systemanropet mknod 12 Systemanropet open 13 SIGNALER 14 Externa och interna signaler. 14 Uppfångad signal ( Signal catching ) 14 Standardiserade signaler 15 Systemanropet kill 15 Systemanropet signal 15 IPC = InterProcess Communication 17 Meddelandeköer 18 Sida 2/38
Meddelandeköer detaljbeskrivning 18 Systemanropet msgget 18 Systemanropet msgsnd 19 Systemanropet msgrev 20 Systemanropet msgctl 21 Delat minne 21 Detaljbeskrivning av delat minne 22 Systemanropet shmget 22 Systemanropet shmat 23 Systemanropet shmdt 23 Systemanropet shmctl 23 Binär semafor 24 SEMAFORER 24 Programmeringsexempel med semaforer 25 Semaforer i UNIX 26 En semafortabell tas i bruk 26 Systemanropet semget 26 Semaforer hantering 27 Systemanropet semctl 27 Semaforoperationer 28 Systemanropet semop 28 Semaforanvändning 29 Producent konsument problemet 29 a) en producent, en konsument 29 b) Flera producenter, flera konsumenter 30 Hantering av IPC objekt 31 Kommunikation mellan processer med uttag 31 Systemanropet socket 32 Systemanropet bind 33 Systemanropet listen 33 Systemanropet accept 33 Systemanropet connect 34 Synkroniserar accept 34 Systemanropet connect 34 Systemanropet shutdown 34 Kommunikation på samma dator 34 Kommunikation via TCP/IP nät 36 Hantering av IP-nummer/Internet namn 36 Systemanropet inet_addr 36 Systemanropet gethostbyname 36 Sida 3/38
Kommandoradrörledning (MS-DOS, UNIX) Programmeras pipeline prog1 prog2 prog3 Antalet steg i rörledningen begränsas endast av datorns resurser. Prog 1 Prog 2 Prog 3 Utdata från ett program (en process) används som indata till ett annat program (en annan process). Systemanrop Ett c -program kan utnyttja operativsystem kärnans ( kernel ) tjänster med sk. systemanrop (= anrop av systemfunktioner) En process startar upp en annan process i UNIX 2 skeden 1) processkloning med fork (processklyvning) 2) Klonen byter kod + data med execl eller execv Systemanrop fork() Processklyvning Klyver den ursprungliga processen i: Föräldraprocess Barnprocess Båda processerna kör samma kod med samma data fork() returnerar: Ex. Barnprocessens processnummer i föräldraprocessen Värdet 0 i barnprocessen Vid misslyckande 1 main() if(fork()) printf( Föräldraprocess \n ); else printf( Barnprocess \n ); Sida 4/38
Programmet körs Föräldraprocess Barnprocess Eller Barnprocess Föräldraprocess Beroende på vilken process som körs först Kodbyte i process Systemanropet execl int execl(char *name,char *arg1,char *arg2,,char *argn,0); name pekare på programkodens filnamn arg1 pekare på uppstartningsteckensträng argi2, arg3, arg4, pekare på varje parameter (teckensträng) sista parametern = 0 execl exekveras: koden byts till den kod som finns i den fil vars namn adresseras med första parametern. Ex. Utför UNIX- kommandot ps ax från ett c program. (ps kommandot i filen / bin/ps) #include<stdio.h> main() execl( /bin/ps, ps, -ax,0); printf( misslyckat kodbyte \n ); exit(8); Systemanropet execv int execv(char *name,char *argv[]); Utförs endast om kodbyte misslyckas name pekare på programkodens filnamn argv tabell med pekare på övriga parametrar som behövs i execl Ex. C program för ps ax med execv. #include<stdio.h> char *argv[]= ps, -ax,0; main() execv( /bin/ps,argv); printf( Kodbyte misslyckat \n )M exit(8); Sida 5/38
En process (=föräldraprocess) startar upp en process (=barnprocess) 1) Processklyvning med fork() (-1 returneras vid misslyckande) 2) Barnprocess byter kod med execl eller execv 3) Föräldraprocess fortsätter med egen kod En föräldraprocess kan skapa flera barnprocess, som identifieras med processnummer, som returneras av fork i föräldraprocessen. En Föräldraprocess kan vänta på avslutningen av en barnprocess med Systemanropet wait wait(&status); wait returnerar den avslutade barnprocessens processnummer status betecknar namnet på en heltalsvariabel som får barnprocessen slutstatus som värde. Denna slutstatus innehåller bl.a. Barnprocessens slutkod ( exit code ) som vanligen är 0. OBS! I UNIX betecknar slutkoden 0 vanligen en normal avslutning av ett program Slutkoden efter senaste avslutade program finns i omgivningsvariabeln? wait returnerar 1 vid misslyckande. wait misslyckas om det inte längre finns barnprocesser vars avslutning inte har testats med wait. om en process skapar 3 barnprocesser lyckas 3 wait och därefter returnerar varje följande wait 1. wait blir hängande tills någon barnprocess har avslutats vid misslyckande returneras 1 och följande sats utförs omedelbart. ifall en barnprocess avslutas före wait returneras processnumret och följande sats utförs genast. Om flera barnprocesser ha avslutats före wait returneras processnumret på den barnprocess som avslutades först. Ex. Skriv ett program som skapar en ny process för UNIX kommandot date och därefter meddelar att datum har skrivits ut. #include<stdio.h> main() int status,id; printf( Så skrivs datum i UNIX: \n ); if(!(id=fork())) execl( /bin/date, date,0); printf( misslyckat kodbyte \n ); exit(8); if(id= =wait(&status)) printf( Datum utskriven \n ); Så skrivs datum i UNIX: Sida 6/38
Wed Jan 7 14:25:31 1998 Datum utskrivet fork, execl eller execv, wait kan användas till process strukturering. En process kan delegera arbete till en eller flera barnprocesser som i sin tur kan delegera till egna barnprocesser. Resultatet från delegerat arbete inväntas med wait data Huvud process resultat Huvudprocess - föräldraprocess - master Slavprocess - barnprocess - slave Data och resultat skickas mellan processerna med någon data kommunikationsmetod ( rörledning, fil, meddelande kö, delade variabler, kommandorad parametrar) Processtruktur Huvud Slav process Slav1 Slav2 Slav3 Slav11 Slav12 Slav31 Slav32 Slav33 Slav21 Slav22 Ifall datakommunikation mellan processer förverkligas med filer måste beaktas att inte samma fil kan vara öppen samtidigt i flera processer Programmerade rörledningar I dubbelriktad kommunikation måste rörledningarna flyttas från kommandoraden in i c programmet. Begreppet FILEDESCRIPTOR Varje öppen fil i UNIX har en sk. Fildeskriptor = ett icke negativt heltal som är unik. Fördefinierade fildeskriptorer Fildeskriptor Strömpekarnamn Ursprunglig betydelse 0 STDIN Standard Input (tangentbordet) 1 STDOUT Standard Output (bildskärm) 2 STDERR Standard Error (skärm) En rörledning skapas med systemanropet pipe pipe(pd); // pd => int pd[2]; pd bör vara definierad som int pd[2]; En rörledning reserverar minne! Sida 7/38
Returnerar: 0 vid framgång -1 vid misslyckande Ex. if(pipe(pd)) printf( misslyckad rörledning \n ); exit(5); Efter anrop av pipe(pd) innehåller tabellen pd 2 fildeskriptorer pd[0] läsning från rörledning pd[1] skrivning i rörledningen pipe(pd) pd[1] pd[0] En fildeskriptor kan associeras med en ström pekare med systemanropet fdopen fork duplicerar rörledningens fildeskriptorer (OBS! Rörledningen dupliceras ej) Ex. int pd[2]; FILE *in, *ut; pipe(pd); in=fdopen(pd[1], w ); /* fdopen returnerar ett strömpekar värde */ ut=fdopen(pd[0], r ); fprintf(in, ); fscanf(ut, ); in Process Önskas ut Rörledning Process 1 pipe(pd1) Process 2 pipe(pd2) Sida 8/38
Stängning av en öppen fil fclose(strömpekar-namn ) eller close(fildeskriptor-värde) samma gäller för rörledning. För att 2 processer skall kunna använda samma rörledning som skapats med pipe måste den ena processen vara ättling i rakt nedstigande led minst en fork i mellan. Hur kopplas en rörledning till standard input i en barnprocess till den process som skapats rörledningen med pipe. 1) Standard Input stängs i barnprocessen close(0); 2) Rörledningens läsdeskriptor dupliceras med dup(pd[0]); dup skapar en användnings mässigt likvärdig fildeskriptor med lägsta möjliga värde (0 gjordes ledig med close(0); ) efter detta dup kan barnprocessen använda fildeskriptorn 0 för läsning från rörledningen (pd[0] kan också användas) 3) Den ursprungliga fildeskriptorn stängs i barnprocessen close(pd[0]) Föräldra process fork() Barn process pd[0] pd[1] pd[1] 0 pipe(pd) Hur kopplas en rörledning till standard output i en barnprocess till den process som skapats rörledningen med pipe. 1) Standard output stängs i barnprocessen close(1); 2) Rörledningens skrivdeskriptor dupliceras med dup(pd[1]); dup skapar en användnings mässigt likvärdig fildeskriptor med lägsta möjliga värde (1gjordes ledig med close(1); ) efter detta dup kan barnprocessen använda fildeskriptorn 1 för skrivning till rörledningen (pd[1] kan också användas) 3) Den ursprungliga fildeskriptorn stängs i barnprocessen close(pd[1]) Föräldra process pd[0] pd[1] fork() pipe(pd) Barn process 1 0 Sida 9/38
Dubbelriktad kommunikation mellan barnprocess och föräldraprocess. Föräldraprocessen bör skapa 2 rörledningar före fork. Förälder pipe(pd1) pd1[1] 0 pipe(pd2) pd2[0] 1 Barn Barnprocessen kan byta kod efter att kommunikation med rörledning vid standard input och standard output gjorts möjlig OBS! Alla deskriptorer som inte används vid kommunikation med rörledning bör helst stängas. OBS!! Data kan bli hängande i en rörledning trots att en process försöker läsa ut detta data. Medicin systemanropet fflush(strömpekar namn); i den skrivande processen. Eventuell data i rörledningen pressas då ut. Data kan skickas till en för skrivning öppnad fil (rörledning) även på basen av fildeskriptorn med systemanropet write write(fd,spek,n); fd fildeskriptor spek pekare på en byte i minnet, t.ex. på en teckensträng n antalet tecken som skall skrivas returnerar: Ett heltal = antalet skrivna tecken (vanligen n) Värdet 1 vi misslyckande Ex. if(write(pd[1], abc,3) = = -1) printf( write misslyckade \n ); En rörledning kan innehålla högst 5120 byte i UNIX OBS! Försök att skriva i en rörledning med write leder till felmeddelandet broken pipe om rörledningen läsända är stängd. Den skrivande processen avbryts. Automatisk synkronisering Om en process försöker läsa från en tom rörledning blir processen väntande på data. Den väntande processen kan fortsätta när en annan process skrivit data i rörledningen. fflush framtvingar förutsatt att data finns i rörledningen. En fildeskriptor kan även användas för läsning av data från en fil (rörledning). read(fd, minnesadress, n); Sida 10/38
fd fildeskriptor minnesadress pekare på en plats i minnet, t.ex. en teckentabell, till vilken informationen läses (datatyp void * eller char*) n antalet byte som skall läsas Returnerar Antalet lästa tecken (byte vanligen) -1 vid misslyckande Exempel int antal; char tab[100]; if((antal =read(pd[0],tab,3))==-1) printf( read misslyckade \n ); exit(1); printf( %d byte lästes \n, antal); Data från en process till en barnprocess kan förmedlas till barnprocessen i samband med barnprocessens kodbyte. Parametrarna i execl eller execv förmedlas. Parametrarna identifieras i barnprocessen som kommandoradparametrar. Med sprintf/sscanf kan programmet skriva/läsa från en given tabell. Tabellens adress som första parameter. Med execl barnproces föräldrapr Tangentbord X s ocess X realtal X Skärm Föräldraprocess #include<stdio.h> float x; int pid,stat; char strang[50]; main() printf( Ge ett realtal \n ); scanf( %f,&x); if(!(pid=fork())) sprintf(strang, %f,x); execl( barn, barn,strang,0); Barnprocessen skall kunna starts från filen barn i samma katalog. Sida 11/38
Barnprocess #include<stdio.h> main(int argc,char *argv[]) float x; sscanf(argv[1], %f,&x); printf( Realtalet %10.3f inmatades \n, x); Några viktiga systemanrop getpid() returnerar processens processnummer getppid() returnerar föräldraprocessens process nummer sleep(t) den anropade processen väntar t sekunder t>=0 pause() den anropade processen väntar exit(slutkod) processen avslutas med given slutkod heltal>=0 0=normalt Namngivna rörledningar ( Named pipes ) Finns i UNIX System V, IRIX, Solaris Versioner i UNIX -lab har både BSD och System V egenskaper En namngiven rörledning är ett objekt med eget namn i en katalog (jfr. fil, länk) Ett pipe -objekt igenkänns med ett p som första tecken efter ls l (- för fil, d för katalog). En named pipe är som en fil, som en process skriver och en annan process läser från. OBS! Dessa två processer behöver inte vara släkt via en eller flere forkrelationer som är nödvändigt för pipe -kommandot! OBS!! Flera processer kan samtidigt ha ett rörlednings objekt öppet både för läsning och skrivning (ej möjlig för filobjekt)! Hur skapas en named pip! 1) UNIX kommandot /etc/mknod 2) Systemanropet mknod i ett c -program 1) UNIX kommandot mknod /etc/mknod pipename p Härvid skapas ett objekt med namnet pipename i innevarande katalog. Detta objekt syns i katalog förteckningen efter ls eller ls l objektet har naturligtvis även en skyddskod. 2) Systemanropet mknod mknod(char *pipename,int mode,int dev); pipename pekare på teckentabell som innehåller namnet ( \0 -avslutar sträng) Sida 12/38
mode = 0010abc abc = skyddskod dev saknar betydelse t.ex dev=0 Returnerar 0 vid framgång -1 vid misslyckande Ex. mknod( pipe1,0010777,0); Skapar en named pipe med namnet pipe1 i innevarande katalog. Fritt fram för alla att använda pipe1 skyddskod = 777 pipe1 skall synas i katalogförteckningen Ett rörledningsobjekt kan användas via fildeskriptor och/eller strömpekare. En fildeskriptor till en fil eller en named pipe skapas med: int open(char *objektnamn,int mode); objektnamn pekare på teckentabell som innehåller namnet som c -sträng mode = 0 öppna för läsning 1 öppna för skrivning 2 öppna för både läsning och skrivning Returnerar: en fildeskriptor (icke negativ) -1 vid misslyckande Ex. named pipe pipe1 finns Process1 Process2 int pd1; int pd2; --------------- -------------------- pd1=open( pipe1 ).1); pd2=open( pipe1,0); /*process1 skriver */ /*process2 läser */ Process1 pipe1 Process2 Samma exempel med Strömpekare Process1 Process2 FILE *in; FILE *ut; --------------- -------------------- in=fopen( pipe1 ). w ); ut=fopen( pipe1, r ); ---------------- ---------------------- fprintf(in, ); fscanf(ut,.); Sida 13/38
/*process1 skriver */ /*process2 läser */ SIGNALER Kommunikation utan informationsöverföring. De normala förloppet hos en process kan avbrytas av en signal. Ex. En process i ett processfönster. Ctrl-c avslutar processen. Ctrl-c genererar en signal som skickas till den process som skall exekveras i processfönstret. Ex2. kill 119 Detta UNIX kommando skickar en signal till processen med processnumret 119 vanligen avslutas processen, om kill -kommandot givare äger processen. Externa och interna signaler. Interna signaler skapas av en exekverande process. Exempel: - Ogiltig adress - Felaktig instruktion - Spill vid flyttalsoperation - Skrivning av information i en rörledning med stängd lösända Externa signaler - Från tangentbordet t.ex. Ctrl-c - Från andra processer systemanropet kill - Från UNIX kommandorad kommandot kill Uppfångad signal ( Signal catching ) Alla signaler utom en (SIGKILL) kan göras uppfångbara En uppfångad signal kan - trigga exekveringen av en funktion - Ignoreras En process avslutas inte av en uppfångad signal. 15 standardiserade signaler med standardiserade signalnamn dessutom implementerings beroende signaler. Varje signaltyp har Signalnummer (1.15 standardiserade signaler) Signalnamn som definieras i signal.h Signalnamn rekommenderas i c- program Sida 14/38
Standardiserade signaler Nummer Namn Beskrivning 1. SIGHUP terminal hangup skapas efter avslutad förbindelse 2. SIGINT Terminal avbrott. Genereras vanligen med tangenten ctrl-c (eller break eller del) 3. * SIGQUIT Terminal avbrott med minnesutdrag ( core dumb ) genereras med Ctrl-\ 4. * SIGILL Genereras av ogiltig maskininstruktion 5. * SIGTRAP Spårningsavbrott ( trace trap ) används vid avlusning 6. * SIGIOT Genereras av IOT -instruktion används vid avlusning 7. * SIGEMT Genereras av EMT instruktion kan användas av processor som saknar flyttalsoperationer 8. * SIGFPE Genereras om flyttals operation förorsakar spill 9. SIGKILL Kan ej uppfångas genereras bl.a. av kill -9 processornummer 10. * SIGBUS Bussfel vanligen illegal pekarhantering i c- program 11. * SIGSEGV Segmenteringsfel Illegal pekarhantering Överskrivning av tabellgräns 12. * SIGSYS Genereras av dåligt argument i systemanrop 13. SIGPIPE Försök att skriva i en rörledning med oöppnad läsända. 14. SIGALRM Genereras av systemanropet alarm signal efter önskat antal sekunder ex. Alarm(10): pause(); /* pausen avbryts efter 10 sekunder */ 15. SIGTERM Genereras av UNIX kommandot kill processnummer * Betyder att signalen ger minnesutdrag ( core dump ) en fil Med systemanropet : kill(processnummer, signalnummer); Kan en process sända en signal till en annan process Ex. kill(119,sigterm); Samma som UNIX kommandot kill 119 Returnerar: 0 vid framgång -1 vid misslyckande Upp fångning av signaler ställs in med systemanropet signal(signalnummer,funkpek); deklareras i signal.h. Den funktion som adresseras exekveras då signalen kommer. Om funkpek=sig_ign ignoreras signalen då den kommer. Om funkpek=sig_dfl träder grundinställningen ( defalut ) för ifrågavarande signal i kraft. En inställning som görs med signal(signalnummer, funpek) Sida 15/38
gäller en och endast en signal med given nummer om funkpek SIG_IGN och funkpek SIG_DFL. Returnerar: Pekare på den funktion som ifrågavarande signaltyp är inställd på från tidigare Eller värdet SIG_IGN eller SIG_DFL -1 vid misslyckande Ex. Skriv ett program som kvitteras 2 Ctrl-c med texten Ctrl-c uppfångad och avslutas av den tredje Ctrl-c. #include<signal.h> #include<stdio.h> /* *igen är en variabel vars värde är en adress till en void -funktion */ void (*igen)(); void testfunk() printf( Ctrl-c uppfångad \n ); main() signal(sigint, testfunk); igen=signal(sigint, testfunk); printf( tryck Ctrl-c \n ); pause(); signal(sigint, igen); printf( tryck Ctrl-c\n ); pause(); /* eventuellt signal(sigint,sig_dfl); */ printf( tryck Ctrl-c \n ); pause(); programmet körs :> tryck Ctrl-c Ctrl-c uppfångad tryck Ctrl-c Ctrl-c uppfångad tryck Ctrl-c :> Deklaration av funktion signal void ( *signal ( int sig, void ( *funk () ) ) (); signal returnerar en pekare på en void- funktion som andra parameter har signal namnet på en void- funktion. Sida 16/38
void f1() void f2(). main() void (*funknamn)(); signal(sigint,f1); funknamn=signal(sigint,f2); /* variabeln funknamn har f1 som värde */ signal returnerar en pekare på senast inställda void funktion för samma signal. IPC = InterProcess Communication En samling systemanrop för processkommunikation ursprungligen i UNIX System V finns med i moderna UNIX varianter (Solaris, IRIX, Linux) IPC innehåller 3 slags kommunikationsverktyg. Meddelandeköer ( message queue ) delat minne ( Shared memory ) Semaforer ( Semaphores ) Rörledningar ( pipe ) P1 P2 P1,P2 processer Meddelande kö P1 P2 Sida 17/38
Delat minne minne P1 write write P2 Read read Semaforer behövs för att hindra rådd av delat minne som många processer använder! Meddelandeköer En meddelandekö skapas och/eller tas i bruk med systemanropet msgget (En process skapar övriga tar i bruk) Ett budskap läggs i en meddelandekö med systemanropet msgsnd Ett budskap avläses från en meddelandekö med systemanropet msgrcv En meddelandekö raderas eller tillståndet hos en meddelandekö avläses med systemanropet msgctl Användning av meddelandekö förutsätter följande inkluderingsfiler sys/types.h sys/ipc.h sys/msg.h Samma meddelandekö kan samtidigt användas av flera processer i vardera riktningen öronmärkta meddelande Meddelandeköer detaljbeskrivning En existerande meddelandekö karakteriseras av en nyckel key ett heltals värde. msgget: int msgqid; msgqid=msgget(nyckel, köflaggor); En s.k. köindikator returneras vid framgång (icke negativa heltal) misslyckande 1 köflaggor Ett heltal som vanligen byggs upp med bitwise or operatorn Komponenter: Sida 18/38
Ett heltal som betecknar skyddskod (vanligen oktal) ex. 0777 Symboliska värdet IPC_CREAT kö skall skapas för given nyckel ifall kön ej redan existerar. Symboliska värdet IPC_EXCL kön skapas av anropade process om kön redan existerar misslyckas det OBS! Som nyckel kan även användas symboliska värdet IPC_PRIVATE betydelse, se boken eller man msgget. Process1 Process2 #define MSGKEY 75 #define MSGKEY 75 ----------------------- ---------------------------- int msgqid; int msgqid; ----------------------- ---------------------------- msgqid=msgget(msgkey,0777 msgqid=msgget(msgkey,0777 IPC_CREAT); IPC_CREAT); En för alla användare brukbar meddelandekö med nyckel MSGKEY skapas av process1 eller process2 kön tas i bruk av båda processerna. Översända meddelande bör sparas i teckentabellfält i poster av typ struct msgform long mtype; char mtext[n]; ; mtype betecknar meddelandets typ ( öronmärke ) Ex. Placera ett heltals värde på en meddelandekö struct msgform msg1; int *pint,a; ----------------- scanf( %d,&a); pint=(int*)msg1.mtext; *pint=a; msg1.mtype=5; /* t.ex */ msgsnd(..); Systemanropet msgsnd: msgsnd(köidentifikator,msgpek,size,msgflag); köidentifikator det värde som msgget returnerar msgpek en pekare på en post med datatypen struct msgform size heltal som anger meddelandets längd (hur många byte) msgflag ett heltal värde som definierar åtgärd ifall meddelande inte ryms i kön msgflag=0 anropad process väntar tills det finns plats i kön msgflag= IPC_NOWAIT msgsnd misslyckas Sida 19/38
Returnerar: 0 vid framgång -1 vid misslyckande Ex. En process läger sitt eget processnummer på en meddelandekö meddelandetstyp=1 #define MSGKEY 75 struct msgform long mtype; char mtext[50]; ; int msgqid, *pint; struct msgform msg; msgqid=msgqid=msgget(msgkey,0777 IPC_CREAT); pint=(int*)msg.mtext; *pint=getpid(); msg.mtype=1; msgsnd(msgqid,&msg,sizeof(int),0); Systemanropet msgrcv: int msgrcv(köidentifikator,msgpek,maxsize,typ,msgflag); köidentifikator det värde som msgget returnerar msgpek en pekare på den post av typen struct msgform som skall ta i mot meddelandet maxsize teckentabellens storlek i den post som utpekas av msgpek typ betecknar typen på mottaget meddelande typ= 0 första meddelandet i kön typ= positivt heltal först meddelandet i kön med given typ typ= negativt heltal första meddelandet i kön vars typ är minsta möjliga heltal mindre än eller lika med absoluta värdet av given typparameter. T.ex. om kön innehåller meddelande av typ 1,2,3 och parametern typ=-2 avläses meddelandet med typvärdet 1 msgflag heltalsvärde som definierar åtgärd ifall kön är tom eller meddelandets längd > maxsize msgflag=0 anropande process blir och vänta på meddelande av rätt typ. msgrcv misslyckas om meddelandet är för långt msgflag= IPC_NOWAIT msgflag= MSG_NOERROR ett överlångt meddelande avkortas till maxsize Returnerar: meddelandetslängd (antal byte) vid framgång -1 vid misslyckande Sida 20/38
Ex. En process avläser ett meddelande av typ 1 maximilängd på meddelandet är 256 byte struct msgform long mtype; char mtext[256]; msg; int imsgqid; msgrcv(msgqid,&msg,256,1,0); Systemanropet msgctl: msgctl(köidentifikator,cmd,bufpek); köidentifikator returneras av msgget cmd ett heltal som definierar hanterings åtgärd cmd= IPC_RMID kön raderas bufpek saknar betydelse bufpek=0 cmd= IPC_STAT meddelandeköns statusinformation kopieras till fältena i den post som utpeckas av bufpek cmd= IPC_SET meddelandeköns status ställs in enligt fältena i den post som bufpek adresserar bufpek pekare på en post med type struct msqid_ds (se inkluderingsfilerna /usr/include/sys/msg.h och /usr/include/sys/ipc.h) Returnerar: 0 vid framgång -1 vid misslyckande Delat minne minne P1 P2 Processerna P1 och P2 använder Samma fysiska minnesplatser t.ex. 64 byte Skapa/taibruk gemensamt minne systemanropet shmget En process skapar och tar i bruk övriga processer tar i bruk. Skapat gemensamt minne skall inbakas i minnets adressrymd systemanropet shmat Efter detta är det delade minnet åtkomligt via en pekare som returneras av shmat (shmat måste exekveras av varje process som använder samma delade minne). Sida 21/38
Ett delat minne löskopplas från adressrymden för en process med systemanropet shmdt Tillståndet hos ett delat minne hanteras med systemanropet shmctl Som även används vid frigivning av delat minne. Endast en process friger! Detaljbeskrivning av delat minne Ett existerande delat minne måste ha en sk. Nyckel ( key ) som är ett heltalsvärde. int shmget(key_t key,int size,int shmflag); key nyckelvärdet (samma i varje process som använder samma delat minne) size hur många byte shmflag bitmönster som vanligen skapas med -operatorn ( bitwise or ) en komponent är skyddskoden (9bit) som vanligen ges i oktalform t.ex 0777 andra komponenter de symboliska värdena IPC_CREAT eller IPC_EXCL IPC_CREAT skapar ifall inte finns IPC_EXCL processen är den som skapar måste vara!! OBS! Som parameter key kan även användas IPC_PRIVATE Returnerar: identifikator för delat minne (icke negativt) -1 vid misslyckande Ex. Process1 Process2 #define shmkey 80 #define shmkey 80 ---------------------- ----------------------- int shmid; int shmid; if((shmid=shmget(shmkey,128, if((shmid=shmget(shmkey,128, 0777 IPC_CREAT))!=-1) 0777 IPC_CREAT))!=-1) /* programkod */ /* programkod */ else /* error */ else /* error */ Detta betyder att ett delat minne å 128 byte skapas och tas ibruk i process och process2. Inga användarbegränsningar (skyddskod 0777). Nödvändiga inkluderingsfiler för delat minne sys/types.h sys/ipc.h sys/shm.h Sida 22/38
Systemanropet shmat void *shmat (int shmid,void *shmaddr, int shmflag); shmid identifikator värdet som returnerades av shmget shmaddr önskad begynnelseadress för delat minne shmaddr=0 (=pekarvärdet NULL) betyder att UNIX väljer begynnelseadress shmflag bitmönster vanligen 0 eller SHM_RND eller SHM_RDONLY eller SHM_RND SHM_RDONLY SHM_RND önskad begynnelse adress shmaddr avrundas till närmaste möjliga värde SHM_RDONLY betyder att anropade process får endast läsa delat minne Returnerar: void pekare på det delade minnesområde -1 vid misslyckande Ex. Förverkliga en gemensam teckentabell Process1 char *p; int shmid; p=(char*)shmat(shmid,0,0); p[7]= a ; Process2 char *p; int shmid; p=(char*)shmat(shmid,0,0); UNIX har valt begynnelseadress för delat R/W minne i process1 och process2 Systemanropet shmdt(void *shmaddr); Utpekat delat minne frånkopplas från processens adressrymd Returnerar: 0 vid framgång -1 vid misslyckande Systemanropet int shmctl(int shmid, int cmd, struct shmid_ds *buf); shmid identifikator värde cmd IPC_RMID eller IPC_STAT eller IPC_SET IPC_RMID delat minne raders (friges) parameter buf utan betydelse kan vara buf=0 IPC_STAT det delade minnet status information kopieras till den post som adresseras av buf IPC_SET det delade minnets status ställs in av informationen i den post som adresseras av buf Sida 23/38
buf är en pekare på en post av type struct shmid_ds (se inkluderingsfiler sys/shm.h och sys/ipc.h) Returnerar: 0 vid framgång -1 vid misslyckande Ex. shmctl(shmid,ipc_rmid,0); Radering av delat minne (frigivning) motsägelsefrihet bör garanteras vid hantering av delat minne ( consistency ) Ex2. 2 processer hanterar samma tabell å 256 byte Process1 Process2 Uppdatera 50 byte UNIX byter Läser 256 byte Uppdatera 206 byte UNIX bytte körbar process mitt i uppdateringen av gemensam tabell Resultat: Behövs: Process2 avläste en delvis uppdaterad tabell Verktyg som hindrar UNIX att byta körbar process mitt i en uppdatering av delat minne. Sådant verktyg är en semafor Binär semafor Packet : Processkö Binär variabel Binär variabel: 1 = grönt 0 = rött Processerna i processkön väntar på att en röd semafor blir grön motsägelsefrihet uppnås med semaforer SEMAFORER Binär semafor: Hantering kan börja endast om semaforen är grön, röd semafor väntar i semaforens processkö tills processen ser en grön semafor efter hantering ändras semaforen till grön. Semaforhantering bygger på två (2) fundamentala semaforoperationer P och V. Sida 24/38
C- liknande pseudo- kod för P och V operationer semaphore s=init_s; /* s heltal 0 */ /* init_s 0 är begynnelsevärde */ void P(semaphore s) [if s>0 s=s-1; else enter_queue();] pause( ); P(s); [Programkod ;] processbytesförbud för UNIX med programkod exekvering. s>0 grön semafor s=0 röd semafor Om semaforen är grön kommer processen vidare. Om semaforen är röd måste processen vänta med pause() tills den blir uppväckt av en annan process. En uppväckt process anropar P(s). En process som anropar P(s) kommer inte vidare innan den ser en grön semafor. Semaforen kan bli röd då processen kommer vidare. void V(semaphore s) [ s++; if(!empty(process_queue)) wake_up(process_in_queue);] [Programkod ;] processbytesförbud för UNIX med programkod exekvering. V- operationen gör semaforen grön om den varit röd och väcker upp minst en process av de processer som väntar på grönt i semaforkön. Programmeringsexempel med semaforer Sk. Ömsesidig uteslutning, N st processer, som hanterar samma delat minne. Den programkod som hanterar delat minne är exempel på en sk. kritisk sektion. Processernas kritiska sektioner skall utesluta varandra. Program med pseudokod semaphore s=1; /* process_i, i=1,2,3,---,n*/ while(1) Ej_kritsk_sektion_i(); P(s); kritsk_sektion_i(); V(s); Sida 25/38
Semaforen är här binär eftersom den initialiseras till 1. En semafor som initialiseras till N>1 är en heltalssemafor. Binär semafor Blir alltid röd efter en P-operation Blir alltid grön efter en V-operation Heltalssemafor Kan bli röd efter en P-operation Blir alltid grön efter en V-operation Semaforer i UNIX En semafortabell (1 eller flera semaforer) skapas/tas i bruk med systemanropet semget Semaforoperationer utförs med systemanropet semop En semafortabell hanteras/raderas med systemanropet semctl Nödvändiga inkluderingsfiler sys/types.h sys/ipc.h sys/sem.h En semafortabell tas i bruk int semget(key_t key,int count,int semflg); key heltalsnyckel, samma i alla processer count antalet semaforer semflg liknande bitmönster som i msgflg, shmflg Returnerar: En semaforidentifikator vid framgång -1 vid misslyckande Ex. 2 processer använder samma 3 semaforer Process1 Process2 #define SEMKEY 80 #define SEMKEY 80 ------------------------- -------------------------- int semid; int semid; ------------------------- ------------------------- semid=semget(semkey,3, semid=semget(semkey,3, 0777 IPC_CREAT); 0777 IPC_CREAT); Fritt fram att använda dessa 3 semaforer (skyddskod 0777) Sida 26/38
Semaforer hantering int semctl(int semid, int semnum, int cmd, union semun int val; struct semid_ds *buf; unsigned short *array; arg); semid den semaforidentifikator som returnerades av semget cmd symboliskt heltalsvärde som anger hurudan operation utförs av semctl. GETVAL värdet på semaforen med index semnum returneras (fjärde parameter behövs ej, kan sättas till 0) SETVAL semaforen med index semnum får värdet arg.val (fjärde parameter skall vara ett heltal) GETPID processnummer returneras för den process som senast avläste eller ändrade värdet på semaforen med index semnum i semafortabellen. GETNCNT ett heltal 0 returneras, detta heltal anger antalet processer, som väntar på att semaforen med index semnum skall inkrementeras (ifall index =+1 väntar processerna på en V-operation) GETZCNT ett heltal 0 returneras, detta heltal anger antalet processer som väntar på att semaforen med index semnum skall få värdet 0 GETALL värden på alla semaforer kopieras till tabellen arg.array.(semnum ej betydelse t.ex.0) SETALL semafortabellens semaforer får nya värden från tabellen arg.array (semnum ej betydelse) IPC_RMID semafortabellen raderas. Alla processer som köar till någon av semaforerna väcks upp. IPC_STAT semafortabellen status info sparas i den post som arg.buf adresserar. Denna post har datatypen struct semid_ds och definieras i sys/sem.h och sys/ipc.h IPC_SET semafortabellen får nya status info från den post som arg.buf adresserar Returnerar: 0 utan för cmd=getval, GETPID, GETNCNT eller GETZCND -1 vid misslyckande OBS! Initialisering endast av en process! Ex. int semval; semctl(semid,0,setval,0); semval=semctl(semid,0,getval,0); ------------------------------------------------------ semctl(semid,0,ipc_rmid,0); Sida 27/38
Semafor med index 0 i semafortabellen får värdet 0.Detta värde tilldelas semval, Till sist raderas hela tabellen. Semaforoperationer Utförs med anrop av systemanropet int semop(int semid,struct sembuf *ops,int nsops); semid returneras av semgeet ops pekare på en tabell med semaforoperationen nsops antalet semaforoperationer En semaforoperation specificeras med en post av datatypen struct sembuf, som deklareras i inkluderingsfilen sys/sem.h struct sembuf short sem_num; /* index i semtab */ short sem_op; /* semaforoperation */ short sem_flg; /* semafor flagga */ ; sem_op definierar semaforoperationen sem_op < 0 om semaforvärde + sem_op 0. Utförs operationen: semaforvärde +=sem_op. Den anropande processen fortsätter. Annars ingår den anropande processen i ett väntetillstånd med beaktande av värdet på semflg. Efter väntetillståndet görs ett nytt försök att utföra operationen (i en P -op är sem_op=-1) sem_op > 0 nu utförs alltid semaforvärdet += sem_op. Samtidigt väcks alla processer som väntar på inkrementering av denna semafor (i en V-op är sem_op=+1) sem_op = 0 Om en semaforvärde = = 0 fortsätter den anropande processen, annars ingår den anropande processen i ett väntetillstånd med beaktande av värdet på sem_flg. En väntande process väcks upp och testar semaforen igen då någon annan process ändrar semaforvärdet till 0. Sem_flg skall få värdet IPC_NOWAIT, SEM_UNDO eller 0 IPC_NOWAIT en anropande process förhindras att ingå i ett väntetillstånd varvid semaforoperationen avslutas med retur värdet 1 SEM_UNDO Effekterna av gjorda semaforoperationen elimineras för en avslutad process. Vanligen används sem_flg=0 Returnerar: Det semaforvärde som skapas av den sista operationen i tabellen ops -1 vid misslyckande Sida 28/38
Ex. En process skall utföra P- och V operation på en semafor struct sembuf p_op,v_op; p_op.sem_op=-1; /* semväre */ p_op.sem_num=0; /* index */ p_op.sem_flg=0; / * normalfal */ v_op.sem_op=1; /* semvärde+1 */ v_op.sem_num=0; v_op.sem_flg=0; semop(semid,&p_op,1); /* P-operation */ ---------------------------- semop(semid,&v_op,1); /* V-operation */ Semaforanvändning Många processer, samma semafor semget varje process semctl initialisering en process semop varje process ( Varje process skall bygga upp egna semaforoperationer i tabeller med struct sembuf poster) Producent konsument problemet a) en producent, en konsument Producenten skickar värden till konsumenten via en kö med längden N. Producenten Konsumenten N st köplatser 2 heltalssemaforer TOM initialiserad till N FULL initialiserad till 0 Pseudokod för producent och konsumentprocessen (heltalsbuffert) /* delat minne */ int buffer[n],in=0,ut=0; /* semaforer /* semaphore TOM=N,FULL=0; /* producentprocess */ int tal; while(1) producera(&tal); P(TOM); buffer[in]=tal; in=(in+1)%n; V(FULL); Sida 29/38
/* konsumentprocess */ int tal; while(1) P(FULL); tal=buffer[ut]; ut=(ut+1)%n; V(TOM); Konsumerat(tal); b) Flera producenter, flera konsumenter Prod1 Prod2 Prod3 Con1 Con2 Con3 Problem: Alla producenter använder samma index -variabel för att placera heltal i kön. Alla konsumenter använder samma index -variabel för att hämta heltal från kön. Lösning: Ömsesidig uteslutning av producenten med binär semafor SEMIN som initialiseras till 1. Ömsesidig uteslutning av konsumenten med binär semafor SEMUT som initialiseras till 1. Pseudokod /* delat minne */ int buffer[n],in=0,ut=0; /* 4 semaforer /* semaphore TOM=N,FULL=0,SEMIN=1,SEMUT=1; /*varje producent process */ int tal; while(1) producera(&tal); P(TOM); P(SEMIN); buffer[in]=tal; in=(in+1)%n; V(SEMIN); V(FULL); /* varje konsument process */ int tal; while(1) P(FULL); P(SEMUT); tal=buffer[ut]; Sida 30/38
ut=(ut+1)%n; V(SEMUT); V(TOM); konsumera(tal); I UNIX skapas en semafortabell med 4 semaforer (semget) som initialiseras med semctl i en process. Varje par av semaforoperationer kan förverkligas med ett anrop av semop om den andra parametern är namnet på en tabell med 2 poster av datatypen struct sembuf. Det delade minnets storlek är: (N+2)*sizeof(int) i shmget - N heltal för buffern - 1 heltal för in - 1 heltal för ut int *pint; ----------- pint=(int*)shmat(shmid,0,0); ------------- buffer pint[0],pint[1],,pint[n-1]; index in är pint[n] index ut är pint[n+1] Hantering av IPC objekt med UNIX-kommandon Visa : Radera : ipcs ipcrm Kommunikation mellan processer med datakanaler ( SOCKET ) Ursprungligen 1981 i BSD UNIX. Finns med i praktiskt taget alla nya UNIX (LINUX, SOLARIS, IRIX). Kan användas: för kommunikation mellan processer - Samma dator - Olika dator Teknik: Översikt: Dataströmmar via uttag Serverprocesser Klientprocesser Server Klient Server Klient Sida 31/38
SERVER: KLIENT: socket() socket() bind() connect() listen() accept() inkoppling väntar på klient read() anrop write() write() svar read() Server bör ha utfört accept då klienten utför connect vardera processer bör skapa ett eget uttag med systemanropet socket. Server bör koppla in sin kommunikationsadress med systemanropet bind. Serverns kommunikations kö skapas med systemanropet listen. Servern inväntar en klient med systemanropet accept. Klienten kopplar sitt uttag till serverns adress med systemanropet connect. Kommunikation t.ex. med synkroniserade par av read och write. Server Klient socket Kommunikation socket Inkluderingsfiler sys/types.h sys/socket.h Kompilering i Solaris: cc -lsocket Systemanropet socket() int socket(format,typ,protokoll); format symbolisk heltalsvärde betecknar uttagets sk. adressfamilj AF_UNSPEC ospecificerad Sida 32/38
AF_UNIX kommunikation på samma UNIX -dator kräver inkluderingsfilen sys/un.h AF_INET kommunikation över ett TCP/IP nät Övriga värden se inkluderingsfilen sys/socket.h typ Symboliskt heltalsvärde, definierar data kommunikations interna struktur SOCK_STREAM är likadan struktur som vid filhantering. Övriga värden se inkluderingsfilen sys/socket.h protokoll symbolisk heltalsvärde betecknar uttagets sk. protokollfamilj 0 UNIX väljer PF_UNSPEC ospecificerad PF_UNIX samma UNIX- dator PF_INET TCP/IP- nät Övriga värden se inkluderingsfilen sys/socket.h OBS! Synkroniserade par read write förutsätter typ =SOCK_STREAM Returnerar: Uttagsdeskriptor (heltal > 0) -1 vid misslyckande Systemanropet bind bind (sd. adress, längd); sd uttagsdeskriptor, som socket har returnerat. adress pekare på en post som innehåller serverprocessens adress information längd storleken (antal byte) hos den post som adress pekar på. Returnerar: 0 vid framgång -1 vid misslyckande Systemanropet listen listen(sd, kölängd); sd uttagsdeskriptor, som socket har returnerat. kölängd maximal kölängd (antal platser i kön) bör vara 1 Systemanropet accept int accept(sd, adress, &längd); sd uttagsdeskriptor, som socket har returnerat. adress pekare på en post till vilken klientens adress information kopieras. längd adressen till en heltals variabel som innehåller storleken på den post som adress pekar på (antal byte, heltalsvariabeln skall initialiseras till detta värde). Sida 33/38
Returnerar: ett nytt datakanaldeskriptorvärde vid framgång -1 vid misslyckande Systemanropet connect Synkroniserar accept connect( sd, adress, längd); sd uttagsdeskriptor, som socket har returnerat. adress pekare på post som innehåller serverprocessens adress information. längd storlek (antal byte) på den post som adress pekar på. Systemanropet shutdown shutdown( sd, mode); sd uttagsdeskriptor, som socket har returnerat. mode mode=0 inkommande kommunikations avstängs. mode=1 utgående kommunikations avstängs. mode=2 all kommunikations avstängs Returnerar: Själva uttagsdeskriptorn Förbindelsen till inkopplad datakanal avbryts med systemanropet close(uttagsdeskriptor); Kommunikation på samma dator Adressinfo sparas i posten av typen struct sockaddr u_short sa_family; /* adress familj */ char sa_data[14]; /* tecken tabell för namn */ ; struct sockaddr deklareras i inkluderingsfilen sys/socket.h. I as_family sparas samma värde som i socket() finns som argument (AF_UNIX). I sa_data sparas namnet på sett sk. Datakanal-objekt, som skall vara ett nytt datakanal-objekt i angiven, LOCAL katalog (jfr. Named -pipe). Sida 34/38
Server process ------------------ int sd,ns,len; struct sockaddr serv,cli; len=sizeof(cli); /* initialisering */ sd=socket(af_unix,sock_stream,0); serv.sa_family=af_unix; unlink( /tmp/sockname ); /* radering */ strcpy(serv.sa_data, /tmp/sockname ); bind(sd, &serv,sizeof(serv)); listen(sd,3); while(1) ns=accept(sd,&cli,&len); if(!fork()) close(sd); /* kommunikation via ns */ shutdown(ns,2); close(ns); exit(0); close(ns); Ett s.k. datakanalobjekt med namnet sockname skapas i /tmp. Ett eventuellt redan existerande objekt raderas först med unlink. Serverprocessen skapar för varje kommunicerande klient en barnprocess som kommunicerar med en accepterad klientprocess. Serverprocessen upprepar accept för att ta emot eventuella nya klienter. Klientprocessen ----------------------- int sd; struct sockaddr serv; sd=socket(af_unix,sock_stream,0); serv.sa_family=af_unix; strcpy(ser.sa_data, /tmp/sockname ); connect(sd,&serv,sizeof(serv)); ----------------------------- /* kommunikation via sd */ ------------------------------ shutdown(sd,2); close(sd); Uttagsdeskriptorn används som fildeskriptor i read och write. OBS! Spara helst uttags -objekt i en lokal katalog inte i en nätkatalog. Sida 35/38
Kommunikation via TCP/IP nät Post för adressinfo: (inkluderingsfil netinet/in.h) struct sockaddr_in short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; ; sin_family adressfamilj (samma som socket, AF_INET) sin_port nummer på ingångsport (heltal >0). Undvik portnummer i /etc/services (färdiga tjänster) sin_zero bör nollas in_addr deklareras i netinet/in.h struct in_addr ulong s_addr; ; (IP-nummer) Hantering av IP-nummer/internetnamn Funktionen: unsigned long inet_addr(char *cp); Returnerar: IP-nummret för teckensträng i punktformat i cp. (unsigned long) (-1) vid misslyckande. Ex. unsigned long addr; addr=inet_addr( 193.167.32.5 ); inet_addr deklareras i inkluderingsfilen arpa/inet.h och returnerar (unsigned long) (-1) vid misslyckande. Erhållet värde kan inkopieras ifall struct sockaddr_in serv, kan vi använda: bcopy(&addr,&serv.sin_addr,sizeof(addr)); Även datorns lokala namn eller Internet namn kan användas med funktionen struct hostent *gethostbyname(char *name); Denna funktion och posttypen struct hostent deklareras i inkluderingsfilen netdb.h struct hostent innehåller bla. h_addr pekare på uträknat IP-nummer h_length antal byte i IP-nummer Internet namn struct sockaddr_in serv; struct hostent *addr; addr=gethostbyname( penti.sit.fi ); bcopy(addr->h_addr,&serv.sin_addr,addr->h_length); Sida 36/38
Server process #define PORT (ushort) 13760 int sd,ns,len; struct sockaddr_in serv,cli; len= sizeof(cli); sd=socket(af_inet,sock_stream,0); bzero(&serv,sizeof(serv)); serv.sin_port=htons(port); serv.sin_family=af_inet; serv.sin_addr.s_addr=htonl(inaddr_any); /* varje IP-nummer för klient duger */ bind(sd,&serv,sizeof(serv)); listen(sd,3); while(1) ns=accept(sd,&cli,&len); if(!fork()) close(sd); /* kommunikation ns */ shutdown(ns,2); close(ns); exit(0); close(ns); portnummer: 13760 htons,htonl: konverteringsmakron INADDR_ANY: deklareras i netinet/in.h Klient process #define PORT (ushort) 13760 /* samma som i servern */ int sd; unsigned long addr; struct sockaddr_in serv; sd=socket(af_inet,sock_stream,0); bzero(&serv,sizeof(serv)); serv.sin_port=htons(port); serv.sin_family=af_inet; addr=inet_addr( 193.167.33.237 ); bcopy(&addr,&serv.sin_addr,sizeof(addr)); connect(sd,&serv,sizeof(serv)); /* Kommunikation via sd */ shutdown(sd,2); close(sd); Ifall serverns Internet -namn används: Sida 37/38
Ersätt unsigned long addr med struct hostent *addr; Ersätt anrop av inet_addr med addr= gethostbyname( penti.sit.fi ); Ersätt bcopy med bcopy (addr->h_addr,&serv.sin_addr,addr->h_length); Sida 38/38