Sei sulla pagina 1di 177

Programmazione di sistema in Unix

G. Di Guglielmo, L. Di Guglielmo, N. Drago, G. Pravadelli, F. Stefanni

February 28, 2013

Introduzione

System call per il le system

System call per la gestione dei processi

System call per la Comunicazione tra Processi (IPC)

System call per Meccanismi di IPC Avanzati

Introduzione

System call per il le system

System call per la gestione dei processi

System call per la Comunicazione tra Processi (IPC)

System call per Meccanismi di IPC Avanzati

Interfaccia tramite system call

Laccesso al kernel ` e permesso soltanto tramite le system call, che permettono di passare allesecuzione in modo kernel. Dal punto di vista dellutente, linterfaccia tramite system call funziona come una normale chiamata C. In realt` a` e pi` u complicato:

Esiste una system call library contenente funzioni con lo stesso nome della system call. Le funzioni di libreria cambiano il modo user in modo kernel e fanno s` che il kernel esegua il vero e proprio codice delle system call. La funzione di libreria passa un identicatore, unico, al kernel, che identica una precisa system call. Simile a una routine di interrupt (detta operating system trap).

Alcune system call


Classe File System Call
creat() read() lseek() unlink() chmod() ioctl() fork() exit() getpid() chdir() pipe() msgrcv() semget() shmdt() open() write() dup() stat() chown() exec() signal() getppid() msgget() msgsnd() shmget() close() creat() link() fstat() umask() wait() kill() alarm() msgctl() semop() shmat()

Processi

Comunicazione tra processi

Ecienza delle system call

Lutilizzo di system call ` e in genere meno eciente delle (eventuali) corrispondenti chiamate di libreria C. ` importante ottimizzare il numero di chiamate di sistema rispetto a E quelle di libreria. Particolarmente evidente nel caso di system call per il le system.

Esempio:
1 2 3 4 5 6 7 8 9 10 11

/* PROG1 */ int main ( void ) { int c ; while (( c = getchar ()) != EOF ) putchar ( c ); } /* PROG2 */ int main ( void ) { char c ; while ( read (0 , &c , 1) > 0) if ( write (1 , &c , 1)!=1){ perror ( " write " ); exit (1)}; }

PROG1 ` e circa 5 volte pi` u veloce!

Errori nelle chiamate di sistema

In caso di errore, le system call ritornano tipicamente un valore -1, ed assegnano lo specico codice di errore nella variabile errno, denita in errno.h Per mappare il codice di errore al tipo di errore, si utilizza la funzione
# include < stdio .h > void perror ( char * str );

1 2

su stderr viene stampato:


1$ ./a.out str: messaggio-di-errore \n

Solitamente str ` e il nome del programma o della funzione. Per comodit` a si pu` o denire una funzione di errore alternativa syserr(), denita in un le mylib.c

Tutti i programmi descritti di seguito devono includere mylib.h e linkare mylib.o

La libreria mylib

1 2 3 4 5 6 7 8 9 10 11

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MODULO : mylib . h SCOPO : definizioni per la libreria mylib * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # ifndef MYLIB_H # define MYLIB_H void syserr ( char * prog , char * msg ); # endif

La libreria mylib
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MODULO : mylib . c SCOPO : libreria di funzioni d utilita * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include < stdio .h > # include < errno .h > # include " mylib . h " void syserr ( char * prog , char * msg ) { fprintf ( stderr , " % s - errore : % s \ n " , prog , msg ); perror ( " system error " ); exit (1); }

Introduzione

System call per il le system

System call per la gestione dei processi

System call per la Comunicazione tra Processi (IPC)

System call per Meccanismi di IPC Avanzati

Introduzione

In UNIX esistono i seguenti tipi di le:


1. 2. 3. 4. 5. File regolari Directory Link pipe o fo special le

Gli special le rappresentano un device (block device o character device) Non contengono dati, ma solo un puntatore al device driver:

Major number: indica il tipo del device (driver). Minor number: indica il numero di unit` a del device.

I/O non buerizzato


Le funzioni in stdio.h (fopen(), fread(), fwrite(), fclose(), printf()) sono tutte buerizzate. Per ecienza, si pu` o lavorare direttamente sui buer. Le funzioni POSIX open(), read(), write(), close() sono non buerizzate. In questo caso i le non sono pi` u descritti da uno stream ma da un descrittore (le descriptor un intero piccolo). Alla partenza di un processo, i primi tre descrittori vengono aperti automaticamente dalla shell: 0 ... stdin 1 ... stdout 2 ... stderr Per distinguere, si parla di canali anzich e di le.

Apertura di un canale
1 2 3

# include < fcntl .h > int open ( char * name , int access , mode_t mode );

Valori del parametro access (vanno messi in OR):

Uno a scelta fra:


O_RDONLY O_WRONLY O_RDWR

Uno o pi` u fra:


O_APPEND O_CREAT O_EXCL O_SYNC O_TRUNC

Valori del parametro mode: uno o pi` u fra i seguenti (in OR):
S_IRUSR S_IWUSR S_IXUSR S_IRGRP S_IWGRP S_IXGRP S_IROTH S_IWOTH S_IXOTH S_IRWXU S_IRWXG S_IRWXO

Corrispondenti ai modi di un le UNIX (u=RWX,g=RWX,o=RWX), e rimpiazzabili con codici numerici ottali (0000 ... 0777). Comunque, per portabilit` a e mantenibilit` a del codice, ` e sempre meglio usare le costanti fornite dallo standard.

Apertura di un canale

Modi speciali di open():

O_EXCL: apertura in modo esclusivo (nessun altro processo pu` o aprire/creare) O_SYNC: apertura in modo sincronizzato (le tipo lock, prima terminano eventuali operazioni di I/O in atto) O_TRUNC: apertura di le esistente implica cancellazione contenuto int fd = open("file.dat",O_RDONLY|O_EXCL,S_IRUSR); int fd = open("file.dat",O_CREAT, S_IRUSR|S_IWUSR); int fd = open("file.dat",O_CREAT, 0700);

Esempi di utilizzo:

Apertura di un canale

1 2 3

# include < fcntl .h > int creat ( char * name , int mode ); creat() crea un le (pi` u precisamente un inode) e lo apre in lettura. Parametro mode: come access.

Sebbene open() sia usabile per creare un le, tipicamente si utilizza creat() per creare un le, e la open() per aprire un le esistente da leggere/scrivere.

Creazione di una directory

1 2 3 4

# include < sys / types .h > # include < sys / stat .h > int mknod ( char * path , mode_t mode , dev_t dev );

Simile a creat(): crea un i-node per un le. Pu` o essere usata per creare un le. Pi` u tipicamente usata per creare directory e special le. Solo il super-user pu` o usarla (eccetto che per special le).

Creazione di una directory

Valori di mode: Per indicare tipo di le: S_IFIFO 0010000 FIFO special S_IFCHR 0020000 Character special S_IFDIR 0040000 Directory S_IFBLK 0060000 Block special S_IFREG 0100000 Ordinary le 0000000 Ordinary le Per indicare il modo di esecuzione: S_ISUID 0004000 Set user ID on execution S_ISGID 0002000 Set group ID on execution S_ISVTX 0001000 Save text image after execution

Creazione di una directory

Per indicare i permessi: S_IREAD 0000400 Read by owner S_IWRITE 0000200 Write by owner S_IEXEC 0000100 Execute (search on directory) by owner s_IRWXG 0000070 Read, write, execute (search) by group S_IRWXD 0000007 Read, write, execute (search) by others Il parametro dev indica il major e minor number del device, mentre viene ignorato se non si tratta di uno special le.

Creazione di una directory

La creazione con creat() di una directory non genera le entry . e .. Queste devono essere create a mano per rendere usabile la directory stessa. In alternativa (consigliato) si possono utilizzare le funzioni di libreria:
# include # include # include # include < sys / stat .h > < sys / types .h > < fcntl .h > < unistd .h >

1 2 3 4 5 6 7

int mkdir ( const char * path , mode_t mode ); int rmdir ( const char * path );

Manipolazione diretta di un le
1 2 3 4 5 6 7 8 9 10

# include < unistd .h > ssize_t read ( int fildes , void * buf , size_t n ); ssize_t write ( int fildes , void * buf , size_t n ); int close ( int fildes ); # include < sys / types .h > # include < unistd .h > off_t lseek ( int fildes , off_t o , int whence );

Tutte le funzioni restituiscono -1 in caso di errore.


n: numero di byte letti. Massima ecienza quando n = dimensione

del blocco sico (512 byte o 1K).

read() e write() restituiscono il numero di byte letti o scritti, che

pu` o essere inferiore a quanto richiesto. Valori possibili di whence: SEEK_SET SEEK_CUR SEEK_END

Esempio
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MODULO : lower . c SCOPO : esempio di I / O non bufferizzato * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include < stdio .h > # include < ctype .h > # include " mylib . h " # define BUFLEN 1024 # define STDIN 0 # define STDOUT 1 void lowerbuf ( char *s , int l ) { while (l - - > 0) { if ( isupper (* s )) * s = tolower (* s ); s ++; } }

Esempio
1 int main ( int argc , char * argv []) 2 { 3 char buffer [ BUFLEN ]; 4 int x ; 5 6 while (( x = read ( STDIN , buffer , BUFLEN )) > 0) 7 { 8 lowerbuf ( buffer , x ); 9 x = write ( STDOUT , buffer , x ); 10 if ( x == -1) 11 syserr ( argv [0] , " write () failure " ); 12 } 13 if ( x != 0) 14 syserr ( argv [0] , " read () failure " ); 15 return 0; 16 }

Duplicazione di canali

int dup ( int oldd );

Duplica un le descriptor esistente e ne ritorna uno nuovo che ha in comune con il vecchio le seguenti propriet` a:

si riferisce allo stesso le ha lo stesso puntatore (per laccesso casuale) ha lo stesso modo di accesso.

Propriet` a importante: dup() ritorna il primo descrittore libero a partire da 0!

Accesso alle directory

Sebbene sia possibile aprire e manipolare una directory con open(), per motivi di portabilit` a` e consigliato utilizzare le funzioni della libreria C (non system call).
# include < sys / types .h > # include < dirent .h > DIR * opendir ( char * dirname ); struct dirent * readdir ( DIR * dirp ); void rewinddir ( DIR * dirp ); int closedir ( DIR * dirp );

1 2 3 4 5 6 7

opendir() apre la directory specicata (cfr. fopen()) readdir() ritorna un puntatore alla prossima entry della directory dirp rewinddir() resetta la posizione del puntatore allinizio closedir() chiude la directory specicata

Accesso alle directory

Struttura interna di una directory:

1 struct dirent { 2 __ino_t d_ino ; /* inode # */ 3 __off_t d_off ; /* offset in the 4 directory structure */ 5 unsigned short int d_reclen ; /* how large this 6 structure really is */ 7 unsigned char d_type ; /* file type */ 8 char d_name [256]; /* file name */ 9 };

Accesso alle directory

Campi della struttura DIR (non tutti supportati in Linux)

1 typedef struct _dirdesc { 2 int dd_fd ; /* file descriptor of the 3 directory */ 4 long dd_loc ; /* offset in current buffer */ 5 long dd_size ; /* size of the entry */ 6 long dd_bbase ; /* base of the entry */ 7 long dd_entno ; /* entry number */ 8 long dd_bsize ; /* buffer size */ 9 char * dd_buf ; /* data buffer */ 10 } DIR ;

Esempio
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MODULO : dir . c SCOPO : ricerca in un directory e rinomina di un file * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include < string .h > # include < sys / types .h > # include < sys / dir .h > int dirsearch ( char * , char * , char *); int main ( int argc , char ** argv ) { return dirsearch ( argv [1] , argv [2] , " . " ); }

Esempio
1 int dirsearch ( char * file1 , char * file2 , char * dir ) 2 { 3 DIR * dp ; 4 struct dirent * dentry ; 5 int status = 1; 6 7 if (( dp = opendir ( dir )) == NULL ) return -1; 8 for ( dentry = readdir ( dp ); dentry != NULL ; 9 dentry = readdir ( dp )) 10 if (( strcmp ( dentry - > d_name , file1 )==0)) { 11 printf ( " Replacing entry % s with % s " , 12 dentry - > d_name , file2 ); 13 strcpy ( dentry - > d_name , file2 ); 14 return 0; 15 } 16 closedir ( dp ); 17 return status ; 18 }

Accesso alle directory

int chdir ( char * dirname );

Cambia la directory corrente e si sposta in dirname. ` necessario che la directory abbia il permesso di esecuzione. E

Gestione dei Link

1 2 3 4

# include < unistd .h > int link ( char * orig_name , char * new_name ); int unlink ( char * file_name ); link() crea un hard link a orig_name. E possibile fare riferimento al le con entrambi i nomi. unlink() Cancella un le cancellando li-number nella directory entry. Sottrae uno al link count nelli-node corrispondente. Se questo diventa zero, libera lo spazio associato al le. unlink() ` e lunica system call per cancellare le!

Esempio

1 /* Scopo : usare un file temporaneo senza che altri 2 * possano leggerlo . 3 * 1) aprire file in scrittura 4 * 2) fare unlik del file 5 * 3) usare il file , alla chiusura del processo 6 * il file sara rimosso 7 * NOTA : e solo un esempio ! Questo NON e il modo 8 * corretto per creare file temporanei . Per creare 9 * normalmente i file temporanei usare le funzioni 10 * tmpfile () o tmpfile64 (). 11 */ 12 int fd ; 13 char fname [32];

Esempio

1 2 3 4 5 6 7 8 9 10 11 12 13

... strcpy ( fname , " myfile . xxx " ); if (( fd = open ( fname , O_WRONLY )) == -1) { perror ( fname ); return 1; } else if ( unlink ( fname ) == -1) { perror ( fname ); return 2; } else { /* use temporary file */ } ...

Privilegi e accessi

1 2

# include < unistd .h > int access ( char * file_name , int access_mode ); access() verica i permessi specicati in access_mode sul le file_name.

I permessi sono una combinazione bitwise dei valori R_OK, W_OK, e X_OK. Specicando F_OK verica se il le esiste Ritorna 0 se il le ha i permessi specicati

Privilegi e accessi
# include < sys / types .h > # include < sys / stat .h > int chmod ( char * file_name , int mode ); int fchmod ( int fildes , int mode );

1 2 3 4

Permessi possibili: bitwise OR di: 04000 set user ID on execution S ISUID S ISGID 02000 set group ID on execution S ISVTX 01000 save text image after execution (sticky bit) S IRUSR 00400 read by owner S IWUSR 00200 write by owner S IXUSR 00100 execute (search on directory) by owner S IRWXG 00070 read, write, execute (search) by group S IRWXO 00007 read, write, execute (search) by others

Privilegi e accessi

1 2 3

# include < sys / types .h > # include < sys / stat .h > int chown ( char * file_name , int owner , int group ); owner = UID group = GID

ottenibili con system call getuid() e getgid() (cfr. sezione sui processi) Solo super-user!

Stato di un le

1 2 3 4 5 6

# include < sys / types .h > # include < sys / stat .h > # include < unistd .h > int stat ( char * file_name , struct stat * stat_buf ); int fstat ( int fd , struct stat * stat_buf );

Ritornano le informazioni contenute nelli-node di un le Linformazione ` e ritornata dentro stat_buf.

Stato di un le

Principali campi di struct stat:


dev_t ino_t mode_t nlink_t uid_t gid_t dev_t /* device */ /* inode */ /* protection */ /* # of hard links */ /* user ID of owner */ /* group ID of owner */ /* device type * ( if inode device ) */ off_t st_size ; /* total byte size */ unsigned long st_blksize ; /* blocksize for * filesystem I / O */ unsigned long st_blocks ; /* number of blocks * allocated */ time_t st_atime ; /* time of last access */ time_t st_mtime ; /* time of last * modification */ time_t st_ctime ; /* time of last change */ st_dev ; st_ino ; st_mode ; st_nlink ; st_uid ; st_gid ; st_rdev ;

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

Esempio
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

/* per stampare le informazioni con stat */ void display ( char * fname , struct stat * sp ) { extern char * ctime (); printf ( " FILE % s \ n " , fname ); printf ( " Major number = % d \ n " , major ( sp - > st_dev )); printf ( " Minor number = % d \ n " , minor ( sp - > st_dev )); printf ( " File mode = % o \ n " , sp - > mode ); printf ( "i - node number = % d \ n " , sp - > ino ); printf ( " Links = % d \ n " , sp - > nlink ); printf ( " Owner ID = % d \ n " , sp - > st_uid ); printf ( " Group ID = % d \ n " , sp - > st_gid ); printf ( " Size = % d \ n " , sp - > size ); printf ( " Last access = % s \ n " , ctime (& sp - > atime )); }

Stato di un le

Alcuni dispositivi (terminali, dispositivi di comunicazione) forniscono un insieme di comandi device-specic Questi comandi vengono eseguiti dai device driver Per questi dispositivi, il mezzo con cui i comandi vengono passati ai device driver ` e la system call ioctl(). Tipicamente usata per determinare/cambiare lo stato di un terminale
# include < termio .h > int ioctl ( int fd , int request , struct termio * argptr ); request ` e il comando device-specic, argptr denisce una struttura usata dal device driver eseguendo request.

1 2 3

Le variabili di ambiente
1 2 3

# include < stdlib .h > char * getenv ( char * env_var );

Ritorna la denizione della variabile dambiente richiesta, oppure NULL se non ` e denita. Si pu` o accedere anche alla seguente variabile globale:
extern char **environ;

` possibile esaminare in sequenza tutte le variabili dambiente E usando il terzo argomento del main(). Questa signature non appartiene allo standard C, ma ` e diusa nei sistemi POSIX. Pertanto ` e da evitare in programmi che vogliono essere portabili (usate uno degli altri due modi).

1 int main ( int argc , char * argv [] , char * env []);

Esempio

1 2 3 4 5 6 7 8 9 10 11 12 13

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MODULO : env . c SCOPO : elenco delle variabili d ambiente * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include < stdio .h > int main ( int argc , char * argv [] , char * env []) { puts ( " Variabili d ambiente : " ); while (* env != NULL ) puts (* env ++); return 0; }

Introduzione

System call per il le system

System call per la gestione dei processi

System call per la Comunicazione tra Processi (IPC)

System call per Meccanismi di IPC Avanzati

Gestione dei processi

Come trasforma UNIX un programma eseguibile in processo (con il comando ld)?


TEXT Pad Regions

HEADER

TEXT DATA

DATA RELOC. INFO

(BSS)

SYMBOL TABLE Executable File

STACK

USER BLOCK Process

Gestione dei processi Programma eseguibile

HEADER: denita in /usr/include/linux/a.out.h


denisce la dimensione delle altre parti denisce lentry point dellesecuzione contiene il magic number, numero speciale per la trasformazione in processo (system-dependent)

TEXT: le istruzioni del programma DATA: I dati inizializzati (statici, extern) BSS (Block Started by Symbol): I dati non inizializzati (statici, extern). Nella trasformazione in processo, vengono messi tutti a zero in una sezione separata. RELOCATION: come il loader carica il programma. Rimosso dopo il caricamento SYMBOL TABLE: Visualizzabile con il comando nm. Pu` o essere rimossa (ld -s) o con strip (programmi pi u piccoli). Contiene informazioni quali la locazione, il tipo e lo scope di variabili, funzioni, tipi.

Gestione dei processi Processo

TEXT: copia di quello del programma eseguibile. Non cambia durante lesecuzione DATA: possono crescere verso il basso (heap) BSS: occupa la parte bassa della sezione dati STACK: creato nella costruzione del processo. Contiene:

le variabili automatiche i parametri delle procedure gli argomenti del programma e le variabili dambiente riallocato automaticamente dal sistema cresce verso lalto

USER BLOCK: sottoinsieme delle informazioni mantenute dal sistema sul processo

Creazione di processi

1 # include < unistd .h > 2 3 pid_t fork ( void )

Crea un nuovo processo, glio di quello corrente, che eredita dal padre:

I le aperti (non i lock) Le variabili di ambiente Directory di lavoro

Al glio viene ritornato 0. Al padre viene ritornato il PID del glio (o -1 in caso di errore). NOTA: un processo solo chiama fork, ma ` e come se due processi ritornassero!

Creazione di processi - Esempio


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MODULO : fork . c SCOPO : esempio di creazione di un processo * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include < stdio .h > # include < sys / types .h > # include " mylib . h " int main ( int argc , char * argv []){ pid_t status ; if (( status = fork ()) == -1) syserr ( argv [0] , " fork () fallita " ); if ( status == 0) { sleep (10); puts ( " Io sono il figlio ! " ); } else { sleep (2); printf ( " Io sono il padre e " ); printf ( " mio figlio ha PID =% d )\ n " , status ); } }

Esecuzione di un programma
1 2 3 4 5 6 7 8

# include < unistd .h > int execl ( char * file , char int execlp ( char * file , char int execle ( char * file , char char * envp []) int execv ( char * file , char int execvp ( char * file , char int execve ( char * file , char

* arg0 , char * arg1 , ... ,0) * arg0 , char * arg1 , ... ,0) * arg0 , char * arg1 , ... ,0 , * argv []) * argv []) * argv [] , char * envp [])

Sostituiscono allimmagine attualmente in esecuzione quella specicata da file, che pu` o essere:

un programma binario un le di comandi (i.e., interpreter script) avente come prima riga #! interpreter

In altri termini, exec trasforma un eseguibile in processo. NOTA: exec non ritorna!!

La Famiglia di exec

execl utile quando so in anticipo il numero e gli argomenti, execv utile altrimenti. execle e execve ricevono anche come parametro la lista delle variabili dambiente. execlp e execvp utilizzano la variabile PATH per cercare il comando file.

Esecuzione di un programma - Esempio

1 2 3 4 5 6 7 8 9 10 11 12 13 14

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MODULO : exec . c SCOPO : esempio d uso di exec () * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include < stdio .h > # include < unistd .h > # include " mylib . h " int main ( int argc , char * argv []) { puts ( " Elenco dei file in / tmp " ); execl ( " / bin / ls " , " ls " , " / tmp " , NULL ); syserr ( argv [0] , " execl () fallita " ); }

fork e exec

Tipicamente fork viene usata con exec. Il processo glio generato con fork viene usato per fare la exec di un certo programma. Esempio:
int pid = fork (); if ( pid == -1) { perror ( " " ); } else if ( pid == 0) { char * args [2]; args [0] = " ls " ; args [1] = NULL ; execvp ( args [0] , args ); exit (1); /* vedi dopo */ } else { printf ( " Sono il padre " ); printf ( " e mio figlio e % d .\ n " , pid ); }

1 2 3 4 5 6 7 8 9 10 11 12

Sincronizzazione tra padre e gli


1 2 3 4 5 6

# include < sys / types .h > # include < sys / wait .h > void exit ( status ) void _exit ( status ) pid_t wait ( int * status )

exit ` e un wrapper alleettiva system call _exit() wait sospende lesecuzione di un processo no a che uno dei gli termina.

Ne restituisce il PID ed il suo stato di terminazione, tipicamente ritornato come argomento dalla exit. Restituisce -1 se il processo non ha gli.

Un glio resta zombie da quando termina a quando il padre ne legge lo stato (con wait()).

Sincronizzazione tra padre e gli

Lo stato pu` o essere testato con le seguenti macro:


WIFEXITED ( status ) WEXITSTATUS ( status ) // se WIFEXITED ritorna true WIFSIGNALED ( status ) WTERMSIG ( status ) // se WIFSIGNALED ritorna true WIFSTOPPED ( status ) WSTOPSIG ( status ) // se WIFSTOPPED ritorna true

1 2 3 4 5 6

Informazione ritornata da wait

Se il glio ` e terminato con exit


Byte 0: tutti zero Byte 1: largomento della exit Byte 0: il valore del segnale Byte 1: tutti zero

Se il glio ` e terminato con un segnale


Comportamento di wait modicabile tramite segnali (v.dopo)

La Famiglia di wait
1 2 3 4 5 6

# include < sys / time .h > # include < sys / resource .h > pid_t waitpid ( pid_t pid , int * status , int options ) pid_t wait3 ( int * status , int options , struct rusage * rusage )

waitpid attende la terminazione di un particolare processo

pid = -1: tutti i gli

wait(&status); e equivalente a waitpid(-1, &status, 0);

pid = 0: tutti i gli con stesso GID del processo chiamante pid < -1 : tutti i gli con GID = |pid| pid > 0: il processo pid

wait3 e simile a waitpid, ma ritorna informazioni aggiuntive sulluso delle risorse allinterno della struttura rusage. Vedere man getrusage per ulteriori informazioni.

Uso di wait() - Esempio - pt. 1


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MODULO : wait . c SCOPO : esempio d uso di wait () * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include < stdio .h > # include < stdlib .h > # include < unistd .h > # include < sys / wait .h > # include < sys / types .h > # include " mylib . h " int main ( int argc , char * argv []){ pid_t child ; int status ; if (( child = fork ()) == 0) { sleep (5); puts ( " figlio 1 - termino con stato 3 " ); exit (3); }

Uso di wait() - Esempio - pt. 2

1 2 3 4 5 6 7 8 9 10 11 12 13

if ( child == -1) syserr ( argv [0] , " fork () fallita " ); if (( child = fork ()) == 0) { puts ( " figlio 2 - sono in loop infinito , " ); punts ( " uccidimi con : " ); printf ( " kill -9 % d \ n " , getpid ()); while (1) ; } if ( child == -1) syserr ( argv [0] , " fork () fallita " );

Uso di wait() - Esempio - pt. 3


1 while (( child = wait (& status )) != -1) { 2 printf ( " il figlio con PID % d e " , child ); 3 if ( WIFEXITED ( status )) { 4 printf ( " terminato ( stato di uscita : % d )\ n \ n " , 5 WEXITSTATUS ( status )); 6 } else if ( WIFSIGNALED ( status )) { 7 printf ( " stato ucciso ( segnale omicida : % d )\ n \ n " , 8 WTERMSIG ( status )); 9 } else if ( WIFSTOPPED ( status )) { 10 puts ( " stato bloccato " ); 11 printf ( " ( segnale bloccante : % d )\ n \ n " , 12 WSTOPSIG ( status )); 13 } else if ( WIFCONTINUED ( status )) { 14 puts ( " stato sbloccato " ); 15 } else 16 puts ( " non c e piu !? " ); 17 } 18 return 0; 19 }

Informazioni sui processi

1 2 3 4 5

# include < sys / types .h > # include < unistd .h > pid_t uid = getpid () pid_t gid = getppid ()

getpid ritorna il PID del processo corrente getppid ritorna il PID del padre del processo corrente

Informazioni sui processi - Esempio


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MODULO : fork2 . c SCOPO : funzionamento di getpid () e getppid () * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include < stdio .h > # include < sys / types .h > # include " mylib . h " int main ( int argc , char * argv []) { pid_t status ; if (( status = fork ()) == -1) { syserr ( argv [0] , " fork () fallita " ); } if ( status == 0) { puts ( " Io sono il figlio :\ n " ); printf ( " PID = % d \ tPPID = % d \ n " , getpid () , getppid ()); } else { printf ( " Io sono il padre :\ n " ); printf ( " PID = % d \ tPPID = % d \ n " , getpid () , getppid ()); }}

Informazioni sui processi (cont.)


1 2 3 4 5 6 7

# include < sys / types .h > # include < unistd .h > uid_t uid_t uid_t uid_t

uid = getuid () gid = getgid () euid = geteuid () egid = getegid ()

Ritornano la corrispondente informazione del processo corrente geteuid e getegid ritornano linformazione sulleective UID e GID. NOTA: la dierenza tra eective e real sta nelluso dei comandi suid/sgid che permettono di cambiare UID/GUID per permettere operazioni normalmente non concesse agli utenti. Il real UID/GID si riferisce sempre ai dati reali dellutente che lancia il processo. Leective UID/GID si riferisce ai dati ottenuti lanciando il comando suid/sgid.

Segnalazioni tra processi

possibile spedire asincronamente dei segnali ai processi: E

1 # include < sys / types .h > 2 # include < signal .h > 3 4 int kill ( pid_t pid , int sig )

Valori possibili di pid: (pid > 0) segnale inviato al processo con PID=pid (pid = 0) segnale inviato a tutti i processi con gruppo uguale a quello del processo chiamante (pid 1) segnale inviato a tutti i processi (tranne quelli di sistema) (pid < 1) segnale inviato a tutti i processi nel gruppo -pid Gruppo di processi: insieme dei processi aventi un antenato in comune.

Segnalazioni tra processi (cont.)

Il processo che riceve un segnale asincrono pu` o specicare una routine da attivarsi alla sua ricezione.

1 # include < signal .h > 2 typedef void (* sighandler_t )( int ); 3 sighandler_t signal ( int signum , sighandler_t func );

func ` e la funzione da attivare, anche detta signal handler. Pu` o essere una funzione denita dallutente oppure: SIG_DFL per specicare il comportamento di default SIG_IGN per specicare di ignorare il segnale Allarrivo di un segnale lhandler e resettato a SIG_DFL.

Segnalazioni tra processi (cont.)

Segnali disponibili (Linux): con il comando kill -l o su man 7 signal


1+ 3* 5* 7* 9+@ 11* 13+ 15+ 17# 19$@ 21$ 23# 25* 27+ 29+ 31+ Hangup Quit Trace trap Bus error Kill Segm. viol. write on pipe Software termination signal Child stop/termination Stop process Background read from tty Urgent condition on socket File size limit Proling timer alarm I/O now possible Bad args to system call SIGINT SIGILL SIGABRT SIGFPE SIGUSR1 SIGUSR2 SIGALRM SIGCONT SIGTSTP SIGTTOU SIGXCPU SIGVTALRM SIGWINCH SIGPWR 2+ 4* 6* 8* 10+ 12+ 14 16 18 20$ 22$ 24* 26+ 28# 30+ Interrupt Illegal instr. Abort signal. FP exception User dened 1 User dened 2 Alarm clock Continue after stop Stop typed at tty Background write to tty Cpu time limit Virtual time alarm Window size change Power failure

SIGHUP SIGQUIT SIGTRAP SIGBUS SIGKILL SIGSEGV SIGPIPE SIGTERM SIGCHLD SIGSTOP SIGTTIN SIGURG SIGXFSZ SIGPROF SIGIO SIGSYS

Segnalazioni tra processi (cont.)


Segnali con +: azione di default = terminazione Segnali con *: azione di default = terminazione e scrittura di un core le Segnali con #: azione di default = ignorare il segnale Segnali con $: azione di default = stoppare il processo Segnali con @: non possono essere n` e ignorati n` e intercettati. I segnali 10 e 12 sono a disposizione dellutente per gestire dei meccanismi di interrupt ad hoc. Sono tipicamente utilizzati insieme al comando kill per attivare la funzione desiderata in modo asincrono Esempio: Se un programma include listruzione signal(SIGUSR1,int_proc);, la funzione int_proc verr` a eseguita tutte le volte che eseguo il comando kill -10 <PID del processo che esegue la signal> .

Segnalazioni tra processi - Esempio 1 - pt. 1


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

# include < stdio .h > # include < unistd .h > # include < signal .h >

/* /* * /* *

standard I / O functions standard unix functions , like getpid () signal name macros , and the signal () prototype

*/ * */ * */

/* first , here is the signal handler */ void catch_int ( int sig_num ) { /* re - set the signal handler again to catch_int , * for next time */ signal ( SIGINT , catch_int ); printf ( " Don t do that \ n " ); fflush ( stdout ); }

Segnalazioni tra processi - Esempio 1 - pt. 2

1 int main ( int argc , char * argv []) 2 { 3 /* set the INT ( Ctrl - C ) signal handler 4 * to catch_int */ 5 signal ( SIGINT , catch_int ); 6 7 /* now , lets get into an infinite loop of doing 8 * nothing . */ 9 for ( ;; ) 10 pause (); 11 }

Segnalazioni tra processi - Esempio 2 - pt. 1


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MODULO : signal . c SCOPO : esempio di ricezione di segnali * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include < stdio .h > # include < limits .h > # include < math .h > # include < signal .h > # include < stdlib .h > long maxprim = 0; long np =0; void usr12_handler ( int s ) { printf ( " \ nRicevuto segnale n .% d \ n " ,s ); printf ( " Il piu grande primo trovato e " ); printf ( " % ld \ n " , maxprim ); printf ( " Totale dei numeri primi =% d \ n " , np ); }

Segnalazioni tra processi - Esempio 2 - pt. 2


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

void good_bye ( int s ) { printf ( " \ nIl piu grande primo trovato e " ); printf ( " % ld \ n " , maxprim ); printf ( " Totale dei numeri primi =% d \ n " , np ); printf ( " Ciao !\ n " ); exit (1); } int is_prime ( long x ) { long fatt ; long maxfatt = ( long ) ceil ( sqrt (( double ) x )); if ( x < 4) return 1; if ( x % 2 == 0) return 0; for ( fatt =3; fatt <= maxfatt ; fatt +=2) return ( x % fatt == 0 ? 0: 1); }

Segnalazioni tra processi - Esempio 2 - pt. 3


1 int main ( int argc , char * argv []) { 2 long n ; 3 4 signal ( SIGUSR1 , usr12_handler ); 5 /* signal ( SIGUSR2 , usr12_handler ); */ 6 signal ( SIGHUP , good_bye ); 7 8 printf ( " Usa kill - SIGUSR1 % d per vedere il numero 9 primo corrente \ n " , getpid ()); 10 printf ( " Usa kill - SIGHUP % d per uscire " , getpid ()); 11 fflush ( stdout ); 12 13 for ( n =0; n < LONG_MAX ; n ++) 14 if ( is_prime ( n )) { 15 maxprim = n ; 16 np ++; 17 } 18 }

Segnali e terminazione di processi

Il segnale SIGCLD viene inviato da un processo glio che termina al padre Lazione di default ` e quella di ignorare il segnale (che causa lo sblocco della wait()) Pu` o essere intercettato per modicare lazione corrispondente

Timeout e Sospensione

1 # include < unistd .h > 2 unsigned int alarm ( unsigned seconds )

alarm invia un segnale (SIGALRM) al processo chiamante dopo seconds secondi. Se seconds vale 0, lallarme ` e annullato. La chiamata resetta ogni precedente allarme Utile per implementare dei timeout, fondamentali per risorse utilizzate da pi` u processi. Valore di ritorno:

0 nel caso normale Nel caso esistano delle alarm() con tempo residuo, il numero di secondi che mancavano allallarme.

Per cancellare eventuali allarmi sospesi: alarm(0);

Timeout e sospensione - Esempio - pt. 1


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

# include < stdio .h > # include < unistd .h > # include < signal .h >

/* /* * /* *

standard I / O functions standard unix functions , like getpid () signal name macros , and the signal () prototype

*/ * */ * */

/* buffer to read user name from the user */ char user [40]; /* define an alarm signal handler . */ void catch_alarm ( int sig_num ) { printf ( " Operation timed out . Exiting ...\ n \ n " ); exit (0); }

Timeout e sospensione - Esempio - pt. 2


1 int main ( int argc , char * argv []) 2 { 3 /* set a signal handler for ALRM signals */ 4 signal ( SIGALRM , catch_alarm ); 5 6 /* prompt the user for input */ 7 printf ( " Username : " ); 8 fflush ( stdout ); 9 /* start a 10 seconds alarm */ 10 alarm (10); 11 /* wait for user input */ 12 scanf ( " % s " , user ); 13 /* remove the timer , now that we ve got 14 * the user s input */ 15 alarm (0); 16 17 printf ( " User name : % s \ n " , user ); 18 return 0; 19 }

Timeout e Sospensione - (cont.)

1 # include < unistd .h > 2 void pause ()


Sospende un processo no alla ricezione di un qualunque segnale. Ritorna sempre -1 N.B.: se si usa la alarm per uscire da una pause bisogna inserire listruzione alarm(0) dopo la pause per disabilitare lallarme. Questo serve per evitare che lallarme scatti dopo anche se la pause e gi` a uscita a causa di unaltro segnale.

Introduzione

System call per il le system

System call per la gestione dei processi

System call per la Comunicazione tra Processi (IPC)

System call per Meccanismi di IPC Avanzati

Introduzione

UNIX e IPC Pipe FIFO (named pipe) Code di messaggi (message queue) Memoria condivisa (shared memory) Semafori

UNIX e IPC

ipcs: riporta lo stato di tutte le risorse, o selettivamente, con le seguenti opzioni:


-s informazioni sui semafori; -m informazioni sulla memoria condivisa; -q informazioni sulle code di messaggi. Nel caso di terminazioni anomale, le risorse possono rimanere allocate Le opzioni sono quelle ipcs Va specicato un ID di risorsa, come ritornato da ipcs

ipcrm: elimina le risorse (se permesso) dal sistema.


UNIX e IPC - (cont.)

Esempio:

host : user> i p c s IPC s t a t u s from / dev /kmem a s o f Wed Oct 16 1 2 : 3 2 : 1 3 1996 Message Queues : T ID KEY MODE OWNER GROUP No me s s a g e q u e u e s a r e c u r r e n t l y d e f i n e d S h a r e d Memory T ID KEY m 1300 m 1301 m 1302

MODE 0 Drw 0 Drw 0 Drw

OWNER root root root

GROUP system system system

Semaphores T ID KEY MODE OWNER GROUP No s e m a p h o r e s a r e c u r r e n t l y d e f i n e d

Pipe

Il modo pi` u semplice di stabilire un canale di comunicazione unidirezionale e sequenziale in memoria tra due processi consiste nel creare una pipe:

1 # include < unistd .h > 2 3 int pipe ( int fildes [2])


La chiamata ritorna zero in caso di successo, -1 in caso di errore. Il primo descrittore ([0]) viene usato per leggere, il secondo [1] per scrivere. NOTA: Linterfaccia ` e quella dei le, quindi sono applicabili le system call che utilizzano le descriptor.

Pipe - Esempio - part. 1


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MODULO : pipe . c SCOPO : esempio di IPC mediante pipe * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include < stdio .h > # include < stdlib .h > # include < unistd .h > # include < sys / wait .h > int main ( int argc , char * argv []) { int status , p [2]; char buf [64]; pipe ( p ); if (( status = fork ()) == -1) /* errore */ syserr ( argv [0] , " fork () fallita " );

Pipe - Esempio - part. 2


1 else if ( status == 0) { /* figlio */ 2 close ( p [1]); 3 if ( read ( p [0] , buf , BUFSIZ ) == -1) 4 syserr ( argv [0] , " read () fallita " ); 5 printf ( " Figlio - ricevuto : % s \ n " , buf ); 6 exit (0); 7 } else { /* padre */ 8 close ( p [0]); 9 printf ( " Padre - invio nella pipe : " ); 10 printf ( " In bocca al lupo \ n " ); 11 write ( p [1] , " In bocca al lupo " , 17); 12 wait (& status ); 13 exit (0); 14 } 15 }

Pipe e I/O

Non ` e previsto laccesso random (no lseek). La dimensione sica delle pipe ` e limitata (dipendente dal sistema BSD classico = 4K). Loperazione di write su una pipe ` e atomica La scrittura di un numero di Byte superiore a questo numero:

Blocca il processo scrivente no a che non si libera spazio la write viene eseguita a pezzi, con risultati non prevedibili (es. pi` u processi che scrivono)

La read si blocca su pipe vuota e si sblocca non appena un Byte ` e disponibile (anche se ci sono meno dati di quelli attesi!) Chiusura prematura di un estremo della pipe:

scrittura: le read ritornano 0. lettura: i processi in scrittura ricevono il segnale SIGPIPE (broken pipe)

Pipe e comandi - Esempio - part. 1

1 2 3 4 5 6 7 8 9 10 11 12

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MODULO : pipe2 . c SCOPO : Realizzare il comando " ps | sort " * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include < sys / types .h > # include < stdlib .h > # include < unistd .h > main () { pid_t pid ; int pipefd [2];

Pipe e comandi - Esempio - part. 2

1 2 3 4 5 6 7 8 9 10 11 12 13 14 }

pipe ( pipefd ); if (( pid = fork ()) == 0) { /* figlio */ close (1); /* close stdout */ dup ( pipefd [1]); close ( pipefd [0]); execlp ( " ps " , " ps " , NULL ); } else if ( pid > 0) { /* padre */ close (0); /* close stdin */ dup ( pipefd [0]); close ( pipefd [1]); execlp ( " sort " , " sort " , NULL ); }

Pipe e I/O non bloccante

E possibile forzare il comportamento di write e read rimuovendo la limitazione del bloccaggio. Realizzato tipicamente con fcntl per impostare la ag 0_NONBLOCK sul corrispondente le descriptor (0 o 1)

1 int fd [2]; 2 fcntl ( fd [0] , F_SETFL , O_NONBLOCK );

Utile per implementare meccanismi di polling su pipe.

Se la ag 0_NONBLOCK ` e impostata, una write su una pipe piena ritorna subito 0, e una read su una pipe vuota ritorna subito 0.

Pipe - (cont.)

Limitazioni

possono essere stabilite soltanto tra processi imparentati (es., un processo ed un suo progenitore, o tra due discendenti di un unico processo) Non sono permanenti e sono distrutte quando il processo che le crea termina

Soluzione: assegnare un nome unico alla pipe: named pipe dette anche FIFO. Funzionamento identico, ma il riferimento avviene attraverso il nome anzich` e attraverso il le descriptor. Esistono sicamente su disco e devono essere rimossi esplicitamente con unlink

Named Pipe (FIFO)

Si creano con mknod (largomento dev viene ignorato)


char * name = // creazione // scrittura int result = " my_fifo " ; di una FIFO con permessi di lettura e per l utente mknod ( name , S_IFIFO | S_IRUSR | S_IWUSR , 0);

1 2 3 4 5

valore di ritorno: 0 in caso di successo, -1 in caso di errore.

apertura, lettura/scrittura, chiusura avvengono come per un normale le Possono essere usate da processi non in relazione, in quanto il nome del le ` e unico nel sistema. Le operazioni di I/O su FIFO sono atomiche I/O normalmente bloccante, ma ` e possibile aprire (con open e ag O_NONBLOCK) un FIFO in modo non bloccante. In tal modo sia read che write saranno non bloccanti.

Named Pipe (FIFO) - (cont.)

Utilizzabile anche la funzione mkfifo()

1 # include < sys / types .h > 2 # include < sys / stat .h > 3 int mkfifo ( const char * pathname , mode_t mode );

Named Pipe (FIFO) - Example - part. 1


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MODULO : fifo . c SCOPO : esempio di IPC mediante named pipe USO : Lanciare due copie del programma su due shell separate , una con flag -0 e l altra con flag -1. Lanciare prima quella con flag -1 che crea la fifo . La copia del programma con flag -0 leggera quanto scritto dalla copia con flag -1. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include < stdio .h > # include < stdlib .h > # include < fcntl .h > # include < sys / types .h > # include < sys / stat .h > # include < unistd .h >

Named Pipe (FIFO) - Example - part. 2


1 int main ( int argc , char * argv []) { 2 int i , fd ; 3 char buf [64]; 4 5 if ( argc != 2) { 6 printf ( " Usage : fifo . x -[0|1]\ n \ t -0 to " ); 7 printf ( " read \ n \ t -1 to write \ n " ); 8 exit (1); 9 } else if ( strncmp ( argv [1] , " -1 " , 2)==0){ 10 if ( mknod ( " fifo " , S_IFIFO |0777 ,0)== -1) { 11 perror ( " mknod " ); 12 exit (1); 13 } 14 if (( fd = open ( " fifo " , O_WRONLY ))== -1){ 15 perror ( " FIFO : -1 " ); 16 unlink ( " fifo " ); 17 exit (1); 18 }

Named Pipe (FIFO) - Example - part. 3


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

} else if ( strncmp ( argv [1] , " -0 " , 2)==0){ if (( fd = open ( " fifo " , O_RDONLY ))== -1){ perror ( " FIFO : -1 " ); unlink ( " fifo " ); exit (1); } } else { printf ( " Wrong parameter : % s \ n " , argv [1]); unlink ( " fifo " ); exit (1); } for ( i =0; i <20; i ++) { if ( strncmp ( argv [1] , " -1 " , 2)==0){ write ( fd , " HELLO " ,6); printf ( " Written HELLO % d \ n " , i ); } else { read ( fd , buf ,6); printf ( " Read % s % d \ n " ,buf , i );}}}

Introduzione

System call per il le system

System call per la gestione dei processi

System call per la Comunicazione tra Processi (IPC)

System call per Meccanismi di IPC Avanzati

Meccanismi di IPC Avanzati

IPC SystemV:

Code di messaggi Memoria condivisa Semafori Una primitiva get per:


Caratteristiche comuni:

creare una nuova entry, recuperare una entry esistente vericare lo stato di una entry, cambiare lo stato di una entry, rimuovere una entry.

Una primitiva ctl (control) per:


Meccanismi di IPC Avanzati - (cont.)

La primitiva get richiede la specica di due informazioni:

Una chiave, usata per la creazione/recupero delloggetto di sistema

Valore intero arbitrario; Permessi relativi allaccesso (tipo rwxrwxrwx) IPC_CREAT: si crea una nuova entry se la chiave non esiste IPC_CREAT + IPC_EXCL: si crea una nuova entry ad uso esclusivo da parte del processo

Dei ag di utilizzo:

Meccanismi di IPC Avanzati - (cont.)

Lidenticatore ritornato dalla get (se diverso da -1) ` e un descrittore utilizzabile dalle altre system call La creazione di un oggetto IPC causa anche linizializzazione di:

una struttura dati, che varia a seconda dei tipi di oggetto, contenente informazioni su

UID, GID PID dellultimo processo che lha modicata Tempi dellultimo accesso o modica

una struttura di permessi ipc_perm:

struct ipc_perm { key_t key ; /* Key . */ uid_t uid ; /* Owner s user ID . */ gid_t gid ; /* Owner s group ID . */ uid_t cuid ; /* Creator s user ID . */ gid_t cgid ; /* Creator s group ID . */ unsigned short int mode ; /* R / W perm . */ }

Meccanismi di IPC Avanzati - (cont.)

La primitiva ctl richiede la specica di informazioni diverse in base alloggetto di sistema In tutti i casi, la primitiva ctl richiede:

Un descrittore, usato per accedere alloggetto di sistema

Valore intero ritornato dalla primitiva get ; Cancellare Modicare Leggere informazioni relative agli oggetti

Dei comandi di utilizzo:


Code di Messaggi

Un messaggio ` e una unit` a di informazione di dimensione variabile, senza un formato predenito Una coda ` e un oggetto di sistema, identicato da una chiave key, contenente uno o pi` u messaggi
# include < sys / types .h > # include < sys / ipc .h > # include < sys / msg .h > int msgget ( key_t key , int flag )

Serve a ottenere lidenticatore di una coda di messaggi se trova una corrispondenza, altrimenti restituisce un errore; Serve a creare una coda di messaggi data la chiave key (di tipo long) nel caso in cui

key = IPC_PRIVATE, oppure key = IPC_PRIVATE e flag & IPC_CREAT ` e vero.

Code di Messaggi: Gestione


# include < sys / types .h > # include < sys / ipc .h > # include < sys / msg .h > int msgctl ( int id , int cmd , struct msqid_ds * buf )

La funzione permette di accedere ai valori della struttura msqid ds, mantenuta allindirizzo buf, per la coda specicata dalla chiave id. id ` e il descrittore ritornato da msgget Il comportamento della funzione dipende dal valore dellargomento cmd, che specica il tipo di azione da eseguire: IPC_RMID Cancella la coda (buffer non usato) IPC_STAT Ritorna informazioni relative alla coda nella struttura puntata da buffer (contiene info su UID, GID, stato della coda) IPC_SET Modica un sottoinsieme dei campi contenuti nella struct

Code di Messaggi: Gestione - (cont.)

buf ` e un puntatore a una struttura denita in sys/msg.h contenente (campi utili):

s t r u c t msqid ds { s t r u c t i p c p e r m msg perm ; / p e r m i s s i o n s ( r w x r w x r w x ) / t i m e t msg stime ; / t i m e o f l a s t msgsnd command / t i m e t msg rtime ; / t i m e o f l a s t m s g r c v command / t i m e t msg ctime ; / t i m e o f l a s t c h a n g e / unsigned long i n t msg cbytes ; / c u r r e n t #b y t e s on queue / msgqnum t msg qnum ; / #m e s s a g e s c u r r e n t l y on queue / msglen t msg qbytes ; / max #b y t e s a l l o w e d on queue / pid t msg lspid ; / p i d o f l a s t msgsnd ( ) / pid t msg lrpid ; / p i d o f l a s t m s g r c v ( ) / };

Code di Messaggi: Scambio di Informazione


#i n c l u d e < s y s / t y p e s . h> #i n c l u d e < s y s / i p c . h> #i n c l u d e < s y s /msg . h> i n t msgsnd ( i n t i d , s t r u c t msgbuf msg , s i z e t size , int flag )

La funzione invia un messaggio sulla coda id (id ` e il descrittore ritornato da msgget) Il messaggio ha lunghezza specicata da size, ed ` e passato attraverso il largomento msg La funzione restituisce 0 in caso di successo, oppure -1 in caso di errore Struttura msgbuf dei messaggi:
struct msgbuf { long mtype ; char mtext [1]; }; /* message type */ /* message text */

Da interpretare come template di messaggio! In pratica, si usa una struct costruita dallutente

Code di Messaggi: Scambio di Informazione - (cont.)

#i n c l u d e < s y s / t y p e s . h> #i n c l u d e < s y s / i p c . h> #i n c l u d e < s y s /msg . h> i n t m s g r c v ( i n t i d , s t r u c t msgbuf msg , s i z e t s i z e , long type , i n t flag );

Legge un messaggio dalla coda id, lo scrive sulla struttura puntata da msg e ritorna il numero di byte letti. Una volta estratto, il messaggio sar` a rimosso dalla coda Largomento size indica la lunghezza massima del testo del messaggio Se il testo del messaggio ha lunghezza superiore a size il messaggio non viene estratto e la funzione ritorna con un errore.

Code di Messaggi: Scambio di Informazione - (cont.)

flag: IPC_NOWAIT MSG_NOERRROR

(msgsnd e msgrcv) (msgrcv)

non si blocca se non ci sono messaggi da leggere tronca i messaggi a size byte senza errore

type indica quale messaggio prelevare: 0 Il primo messaggio, indipendentemente dal tipo > 0 Il primo messaggio di tipo type < 0 Il primo messaggio con tipo pi` u vicino al valore assoluto di type

Code di Messaggi: Esempio (Server - part 1)


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * PROCESSO SERVER * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include < sys / types .h > # include < unistd .h > # include < sys / wait .h > # include < sys / ipc .h > # include < sys / msg .h > # include < stdio .h > # include < stdlib .h > # define MSGKEY 75 # define MSGTYPE 1 int main ( int argc , char ** argv ) { key_t msgkey ; int msgid , pid ;

Code di Messaggi: Esempio (Server - part 2)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 }

s t r u c t msg { l o n g mtype ; char mtext [ 2 5 6 ] ; } Message ; if } / l e g g o d a l l a coda / m s g r c v ( msgid ,& Message , s i z e o f ( Message . m t e x t ) ,MSGTYPE, 0 ) ; p r i n t f ( R e c e i v e d from c l i e n t : %s \ n , Message . m t e x t ) ; / s c r i v o i n un m e s s a g i o i l p i d e l o i n v i o / pid = getpid ( ) ; s p r i n t f ( Message . mtext , %d , p i d ) ; Message . mtype = MSGTYPE ; msgsnd ( msgid ,& Message , s i z e o f ( Message . m t e x t ) , 0 ) ; / WAIT / ( ( m s g i d = msgget (MSGKEY, ( 0 6 6 6 | IPC CREAT | IPC EXCL ) ) ) == 1) { perror ( argv [ 0 ] ) ;

/ WAIT /

Code di Messaggi: Esempio (Client - part 1)


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * PROCESSO CLIENT * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include < sys / types .h > # include < unistd .h > # include < sys / wait .h > # include < sys / ipc .h > # include < sys / msg .h > # include < stdio .h > # include < stdlib .h > # define MSGKEY 75 # define MSGTYPE 1 int main ( int argc , char ** argv ) { key_t msgkey ; int msgid , pid ;

Code di Messaggi: Esempio (Client - part 2)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 }

s t r u c t msg { l o n g mtype ; char mtext [ 2 5 6 ] ; } Message ; if ( ( m s g i d = msgget (MSGKEY, 0 6 6 6 ) ) == 1) { perror ( argv [ 0 ] ) ;

} / s c r i v o i l PID i n un m e s s a g i o e l o i n v i o n e l l a coda / pid = getpid ( ) ; s p r i n t f ( Message . mtext , %d , p i d ) ; Message . mtype = MSGTYPE ; msgsnd ( msgid ,& Message , s i z e o f ( Message . m t e x t ) , 0 ) ;

/ WAIT /

/ R i c e v o d a l l a coda i l m e s s a g g i o d e l s e r v e r / m s g r c v ( msgid ,& Message , s i z e o f ( Message . m t e x t ) ,MSGTYPE, 0 ) ; / WAIT / p r i n t f ( R e c e i v e d m e s s a g e from s e r v e r : %s \ n , Message . m t e x t ) ;

Code di Messaggi: Esempio (Controller - part 1)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

/ MODULO: m s g c t l . c SCOPO : I l l u s t r a r e i l f u n z . d i m s g c t l ( ) / #i n c l u d e < s t d i o . h> #i n c l u d e < s t d l i b . h> #i n c l u d e < s y s / t y p e s . h> #i n c l u d e < s y s / i p c . h> #i n c l u d e < s y s /msg . h> #i n c l u d e < t i m e . h> void do msgctl ( ) ; c h a r w a r n i n g m e s s a g e [ ] = I f you remove r e a d p e r m i s s i o n f o r y o u r s e l f , t h i s program w i l l f a i l f r e q u e n t l y ! ; i n t main ( i n t a r g c , c h a r a r g v [ ] ) { s t r u c t m s q i d d s b u f ; / b u f f e r p e r m s g c t l ( ) / i n t cmd ; / comando p e r m s g c t l ( ) / i n t msqid ; / ID d e l l a coda da p a s s a r e a m s g c t l ( ) /

Code di Messaggi: Esempio (Controller - part 2)

1 2 3 4 5 6 7 8 9 10 11 12 13

i f ( a r g c !=2) { p r i n t f ( Usage : m s g c t l . x <msgid >\n ) ; exit (1); } msqid = a t o i ( a r g v [ 1 ] ) ; fprintf fprintf fprintf fprintf ( stderr ( stderr ( stderr ( stderr , , , , \ tIPC RMID = %d \ n , IPC RMID ) ; \ tIPC SET = %d \ n , IPC SET ) ; \ tIPC STAT = %d \ n , IPC STAT ) ; \ n S c e g l i e r e l o p z i o n e d e s i d e r a t a : ) ;

s c a n f ( %i , &cmd ) ;

Code di Messaggi: Esempio (Controller - part 3)


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
s w i t c h ( cmd ) { c a s e IPC SET : f p r i n t f ( s t d e r r , Prima d e l l a IPC SET , c o n t r o l l a i v a l o r i c o r r e n t i : ) ; / n o t a r e : non e s t a t o i n s e r i t o i l b r e a k , q u i n d i d i s e g u i t o v e n g o n o e s e g u i t e l e i s t r u z i o n i d e l c a s e IPC STAT / c a s e IPC STAT : d o m s g c t l ( msqid , IPC STAT , &b u f ) ; f p r i n t f ( s t d e r r , msg perm . u i d = %d \ n , b u f . msg perm . u i d ) ; f p r i n t f ( s t d e r r , msg perm . g i d = %d \ n , b u f . msg perm . g i d ) ; f p r i n t f ( s t d e r r , msg perm . c u i d = %d \ n , b u f . msg perm . c u i d ) ; f p r i n t f ( s t d e r r , msg perm . c g i d = %d \ n , b u f . msg perm . c g i d ) ; f p r i n t f ( s t d e r r , msg perm . mode = %#o , , b u f . msg perm . mode ) ; f p r i n t f ( s t d e r r , a c c e s s p e r m i s s i o n s = %#o \ n , b u f . msg perm . mode & 0 7 7 7 ) ; f p r i n t f ( s t d e r r , m s g c b y t e s = %d \ n , b u f . m s g c b y t e s ) ; f p r i n t f ( s t d e r r , m s g q b y t e s = %d \ n , b u f . m s g q b y t e s ) ; f p r i n t f ( s t d e r r , msg qnum = %d \ n , b u f . msg qnum ) ; f p r i n t f ( s t d e r r , m s g l s p i d = %d \ n , b u f . m s g l s p i d ) ; f p r i n t f ( s t d e r r , m s g l r p i d = %d \ n , b u f . m s g l r p i d ) ;

Code di Messaggi: Esempio (Controller - part 4)

1 2 3 4 5 6 7 8 9 10 11 12 13 14

i f ( buf . msg stime ) { f p r i n t f ( s t d e r r , m s g s t i m e = %s \ n , c t i m e (& b u f . m s g s t i m e ) ) ; } i f ( buf . msg rtime ) { f p r i n t f ( s t d e r r , m s g r t i m e = %s \ n , c t i m e (& b u f . m s g r t i m e ) ) ; } f p r i n t f ( s t d e r r , m s g c t i m e = %s , c t i m e (& b u f . m s g c t i m e ) ) ; / s e i l comando o r i g i n a r i o e r a IPC STAT a l l o r a e s c o d a l c a s e , a l t r i m e n t i p r o s e g u o con l e o p e r a z i o n i s p e c i f i c h e p e r l a m o d i f i c a d e i p a r a m e t r i d e l l a coda . / i f ( cmd == IPC STAT ) break ;

Code di Messaggi: Esempio (Controller - part 5)


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 }
/ M o d i f i c h i a m o a l c u n i p a r a m e t r i d e l l a coda / f p r i n t f ( s t d e r r , E n t e r msg perm . u i d : ) ; s c a n f ( %h i , &b u f . msg perm . u i d ) ; f p r i n t f ( s t d e r r , E n t e r msg perm . g i d : ) ; s c a n f ( %h i , &b u f . msg perm . g i d ) ; f p r i n t f ( s t d e r r , %s \ n , w a r n i n g m e s s a g e ) ; f p r i n t f ( s t d e r r , E n t e r msg perm . mode : ) ; s c a n f ( %h i , &b u f . msg perm . mode ) ; f p r i n t f ( s t d e r r , E n t e r m s g q b y t e s : ) ; s c a n f ( %h i , &b u f . m s g q b y t e s ) ; d o m s g c t l ( msqid , IPC SET , &b u f ) ; break ; c a s e IPC RMID : default : / Rimuove l a coda d i m e s s a g g i . / d o m s g c t l ( msqid , cmd , ( s t r u c t m s q i d d s )NULL ) ; break ; } exit (0);

Code di Messaggi: Esempio (Controller - part 6)

1 v o i d d o m s g c t l ( i n t msqid , i n t cmd , s t r u c t m s q i d d s 2 i n t r t r n ; / p e r m e m o r i z z a r e i l v a l o r e d i r i t o r n o 3 4 f p r i n t f ( s t d e r r , \ n m s g c t l : C a l l i n g m s g c t l (%d , %d , 5 msqid , cmd , b u f ? &b u f : ( s t r u c t m s q i d 6 r t r n = m s g c t l ( msqid , cmd , b u f ) ; 7 8 i f ( r t r n == 1) { 9 p e r r o r ( m s g c t l : m s g c t l f a i l e d ) ; 10 exit (1); 11 } else { 12 f p r i n t f ( s t d e r r , m s g c t l : m s g c t l r e t u r n e d %d \ n , 13 } 14 }

buf ) { d e l l a m s g c t l ( ) / %s ) \ n , d s )NULL ) ;

rtrn );

Analisi Esempi
Per provare gli esempi precedenti:

lanciare il programma server lanciare il programma client su una shell separata

client e server si scambieranno un messaggio e termineranno lasciando la coda di messaggi attiva.

eseguire il comando ipcs -q per vericare che eettivamente esista una coda attiva. lanciare il programma msgctl.c passandogli come parametro il valore msquid visualizzato dal comando ipcs -q provare le varie opzioni del programma msgctl.c, in particolare usare IPC_SET per variare le caratteristiche della coda e IPC_RMID per rimuovere la coda

Memoria Condivisa

Due o pi` u processi possono comunicare anche condividendo una parte del loro spazio di indirizzamento (virtuale). Questo spazio condiviso ` e detto memoria condivisa (shared memory), e la comunicazione avviene scrivendo e leggendo questa parte di memoria.
# include < sys / shm .h > # include < sys / ipc .h > shm_id

shmget ( key_t key , int size , int flags );

I parametri hanno lo stesso signicato di quelli utilizzati da msgget. size indica la dimensione in byte della regione condivisa.

Memoria Condivisa (cont.)

Una volta creata, larea di memoria non ` e subito disponibile. Deve essere collegata allarea dati dei processi che vogliono utilizzarla.
# include < sys / shm .h > # include < sys / ipc .h > char * shmat ( int shmid , char * shmaddr , int flag )

shmaddr indica lindirizzo virtuale dove il processo vuole attaccare il segmento di memoria condivisa. Il valore di ritorno rappresenta lindirizzo di memoria condivisa eettivamente risultante.

Memoria Condivisa (cont.)

In base ai valori di flag e di shmaddr si determina il punto di attacco del segmento:


shmaddr = 0 && (flag & SHM_RND) shmaddr != 0 && !(flag & SHM_RND)
al primo indirizzo disponibile allindirizzo indicato da shmaddr

Il segmento ` e attaccato in lettura se flag & SHM_RDONLY ` e vero, contemporaneamente in lettura e scrittura altrimenti. Un segmento attaccato in precedenza pu` o essere staccato (detached) con shmdt
int shmdt ( char * shmaddr )

shmaddr ` e lindirizzo che individua il segmento di memoria condivisa.

Non viene passato lID della regione perch` e` e possibile avere pi` u aree di memoria identicate dallo stesso ID (cio` e attaccate ad indirizzi diversi);

Memoria Condivisa: Gestione


# include < sys / types .h > # include < sys / ipc .h > # include < sys / shm .h > int shmctl ( int shmid , int cmd , struct shmid_ds * buffer );

shmid ` e il descrittore ritornato da shmget Valori di cmd: IPC_RMID Cancella il segm. di memoria condivisa IPC_STAT Ritorna informazioni relative allarea di memoria condivisa nella struttura puntata da buffer (contiene info su UID, GID, permessi, stato della memoria) IPC_SET Modica un sottoinsieme dei campi contenuti nella struct (UID, GID, permessi) SHM_LOCK Impedisce che il segmento venga swappato o paginato

Memoria Condivisa: Gestione (Cont.)

buffer ` e un puntatore a una struttura denita in sys/shm.h contenente:


struct shmid_ds { struct ipc_perm shm_perm ; size_t shm_segsz ; /* __time_t shm_atime ; /* __time_t shm_dtime ; /* __time_t shm_ctime ; /* __pid_t shm_cpid ; /* __pid_t shm_lpid ; /* shmatt_t shm_nattch ; /* /* o p e r a t i o n p e r m i s s i o n struct */ size of segment in bytes */ time of last shmat () */ time of last shmdt () */ time of last change by shmctl () */ pid of creator */ pid of last shmop */ number of current a t t a c h e s */

Memoria Condivisa: Esempio (Controller - part 1)


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MODULO : shmctl . c SCOPO : Illustrare il funz . di shmctl () USO : Lanciare il programma e fornire l ID di un segmento di memoria condivisa precedentemente creato . Usare il comando della shell ipcs per vedere i segmenti di memoria condivisa attivi * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include < stdio .h > # include < stdlib .h > # include < sys / types .h > # include < sys / ipc .h > # include < sys / shm .h > # include < time .h > void do_shmctl ();

Memoria Condivisa: Esempio (Controller - part 2)

1 int main ( int argc , char * argv []) { 2 int cmd ; /* comando per shmctl () */ 3 int shmid ; /* ID dell area di memoria c o n d i v i s a */ 4 struct shmid_ds shmid_ds ; /* s t r u t t u r a per il c o n t r o l l o 5 dell area di memoria c o n d i v i s a */ 6 7 fprintf ( stderr , " Inserire l ID del segmento di memoria condiviso : " ); 8 scanf ( " % i " , & shmid ); 9 10 fprintf ( stderr , " Comandi validi :\ n " ); 11 fprintf ( stderr , " \ tIPC_RMID =\ t % d \ n " , IPC_RMID ); 12 fprintf ( stderr , " \ tIPC_SET =\ t % d \ n " , IPC_SET ); 13 fprintf ( stderr , " \ tIPC_STAT =\ t % d \ n " , IPC_STAT ); 14 fprintf ( stderr , " \ tSHM_LOCK =\ t % d \ n " , SHM_LOCK ); 15 fprintf ( stderr , " \ tSHM_UNLOCK =\ t % d \ n " , SHM_UNLOCK ); 16 fprintf ( stderr , " Scegliere il comando desiderato : " ); 17 scanf ( " % i " , & cmd );

Memoria Condivisa: Esempio (Controller - part 3)


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
switch ( cmd ) { case IPC_STAT : /* Le i n f o r m a z i o n i sullo stato della memoria c o n d i v i s a vengono r e c u p e r a t e con la c h i a m a t a alla f u n z i o n e d o _ s h m c t l ( shmid , cmd , & s h m i d _ d s ) e s e g u i t a al termine del case . Le i n f o r m a z i o n i saranno i n s e r i t e nella s t r u t t u r a s h m i d _ d s */ break ; case IPC_SET : /* v i s u a l i z z a z i o n e dello stato attuale della memoria */ do_shmctl ( shmid , IPC_STAT , & shmid_ds ); /* Lettura da t a s t i e r a dei valori */ /* di UID , GID , e p e r m e s s i da settare */ fprintf ( stderr , " \ nInserire shm_perm . uid : " ); scanf ( " % hi " , & shmid_ds . shm_perm . uid ); fprintf ( stderr , " Inserire shm_perm . gid : " ); scanf ( " % hi " , & shmid_ds . shm_perm . gid ); fprintf ( stderr , " N . B .: Mantieni il permesso di lettura per te stesso !\ n " ); fprintf ( stderr , " Inserire shm_perm . mode : " ); scanf ( " % hi " , & shmid_ds . shm_perm . mode ); break ;

Memoria Condivisa: Esempio (Controller - part 4)

1 case IPC_RMID : /* Rimuove il s e g m e n t o */ 2 break ; 3 case SHM_LOCK : /* Esegui il lock sul s e g m e n t o */ 4 break ; 5 case SHM_UNLOCK : /* Esegui unlock sul s e g m e n t o */ 6 break ; 7 default : /* Comando s c o n o s c i u t o passato a d o _ s h m c t l */ 8 break ; 9 } 10 /* La f u n z i o n e d o _ s h m c t l esegue il comando scelto dall utente */ 11 do_shmctl ( shmid , cmd , & shmid_ds ); 12 exit (0); 13 } 14 15 void do_shmctl ( int shmid , int cmd , struct shmid_ds * buf ) { 16 int rtrn ; /* valore di ritorno della shmctl */ 17 18 fprintf ( stderr , " shmctl : Chiamo shmctl (% d , %d , buf )\ n " , shmid , cmd );

Memoria Condivisa: Esempio (Controller - part 5)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

if ( cmd == IPC_SET ) { fprintf ( stderr , " \ tbuf - > shm_perm . uid == % d \ n " , buf - > shm_perm . uid ); fprintf ( stderr , " \ tbuf - > shm_perm . gid == % d \ n " , buf - > shm_perm . gid ); fprintf ( stderr , " \ tbuf - > shm_perm . mode == %# o \ n " , buf - > shm_perm . mode ); } if (( rtrn = shmctl ( shmid , cmd , buf )) == -1) { perror ( " shmctl : shmctl fallita . " ); exit (1); } else { fprintf ( stderr , " shmctl : shmctl ha ritornato % d \ n " , rtrn ); } if ( cmd != IPC_STAT && cmd != IPC_SET ) return ; /* ritorno perche il comando e stato e s e g u i t o e non devo v i s u a l i z z a r e nessuna i n f o r m a z i o n e sullo stato */

Memoria Condivisa: Esempio (Controller - part 6)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 }

/* Stampa lo stato c o r r e n t e del s e g m e n t o */ fprintf ( stderr , " \ nCurrent status :\ n " ); fprintf ( stderr , " \ tshm_perm . uid = % d \ n " , buf - > shm_perm . uid ); fprintf ( stderr , " \ tshm_perm . gid = % d \ n " , buf - > shm_perm . gid ); fprintf ( stderr , " \ tshm_perm . cuid = % d \ n " , buf - > shm_perm . cuid ); fprintf ( stderr , " \ tshm_perm . cgid = % d \ n " , buf - > shm_perm . cgid ); fprintf ( stderr , " \ tshm_perm . mode = %# o \ n " , buf - > shm_perm . mode ); fprintf ( stderr , " \ tshm_perm . key = %# x \ n " , buf - > shm_perm . __key ); fprintf ( stderr , " \ tshm_segsz = % d \ n " , buf - > shm_segsz ); fprintf ( stderr , " \ tshm_lpid = % d \ n " , buf - > shm_lpid ); fprintf ( stderr , " \ tshm_cpid = % d \ n " , buf - > shm_cpid ); fprintf ( stderr , " \ tshm_nattch = % d \ n " , buf - > shm_nattch ); if ( buf - > shm_atime ) fprintf ( stderr , " \ tshm_atime = % s " , ctime (& buf - > shm_atime )); if ( buf - > shm_dtime ) fprintf ( stderr , " \ tshm_dtime = % s " , ctime (& buf - > shm_dtime )); fprintf ( stderr , " \ tshm_ctime = % s " , ctime (& buf - > shm_ctime ));

Memoria Condivisa: Esempio (Shm1 - part 1)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * NOME : shm1 . c SCOPO : a t t a c c a r e due volte un area di memoria c o n d i v i s a R i c o r d a r s i di r i m u o v e r e la memoria c o n d i v i s a al termine del p r o g r a m m a l a n c i a n d o shmctl . c oppure tramite il comando della shell ipcrm . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include < stdlib .h > # include < stdio .h > # include < sys / types .h > # include < sys / ipc .h > # include < sys / shm .h > # define K 1 # define SHMKEY 75 # define N 10

Memoria Condivisa: Esempio (Shm1 - part 2)

1 int shmid ; 2 int main ( int argc , char ** argv ) { 3 int i , * pint ; 4 char * addr1 , * addr2 ; 5 6 /* Creao il s e g m e n t o c o n d i v i s o di d i m e n s i o n e 128* K byte */ 7 shmid = shmget ( SHMKEY , 128* K , 0777| IPC_CREAT ); 8 /* Attacco il s e g m e n t o in due zone diverse */ 9 addr1 = shmat ( shmid ,0 ,0); 10 addr2 = shmat ( shmid ,0 ,0); 11 12 printf ( " Addr1 = 0 x % x \ t Address2 = 0 x % x \ t \ n " , addr1 , addr2 );

Memoria Condivisa: Esempio (Shm1 - part 3)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 }

/* scrivo nella regione 1 */ pint = ( int *) addr1 ; for ( i =0; i < N ; i ++) { * pint = i ; printf ( " Writing : Index %4 d \ tValue : %4 d \ tAddress : Ox % x \ n " , i ,* pint , pint ); pint ++; } /* leggo dalla regione 2 */ pint = ( int *) addr2 ; for ( i =0; i < N ; i ++) { printf ( " Reading : Index %4 d \ tValue : %4 d \ tAddress : Ox % x \ n " , i ,* pint , pint ); pint ++; }

Memoria Condivisa: Esempio (Shm2 - part 1)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * NOME : shm2 . c SCOPO : a t t a c c a r s i ad un area di memoria c o n d i v i s a USO : l a n c i a r e prima il p r o g r a m m a shm1 . c per creare la memoria condivisa . R i c o r d a r s i di r i m u o v e r e la memoria c o n d i v i s a al termine del p r o g r a m m a l a n c i a n d o shmctl . c oppure tramite il comando della shell ipcrm . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include < sys / types .h > # include < sys / ipc .h > # include < sys / shm .h > # include < stdio .h > # define K 1 # define N 20 # define SHMKEY 75 int shmid ;

Memoria Condivisa: Esempio (Shm2 - part 2)

1 int main ( int argc , char ** argv ) { 2 int i , * pint ; 3 char * addr ; 4 5 /* mi attacco alla regione creata dal p r o g r a m m a shm1 . c */ 6 shmid = shmget ( SHMKEY , 128* K , 0777); 7 addr = shmat ( shmid ,0 ,0); 8 printf ( " Address = 0 x % x \ n " , addr ); 9 pint = ( int *) addr ; 10 /* leggo dalla regione a t t a c c a t a in p r e c e d e n z a */ 11 for ( i =0; i < N ; i ++) { 12 printf ( " Reading : ( Value = %4 d )\ n " ,* pint ++); 13 } 14 }

Memoria Condivisa: Esempio (Server - part 1)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MODULO : s h m _ s e r v e r . c SCOPO : server memoria c o n d i v i s a USO : l a n c i a r e il p r o g r a m m a s h m _ s e r v e r . c in una shell e il p r o g r a m m a s h m _ c l i e n t . c in un altra shell R i c o r d a r s i di r i m u o v e r e la memoria c o n d i v i s a creata dal server al termine del p r o g r a m m a l a n c i a n d o shmctl . c oppure tramite il comando della shell ipcrm . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include < stdio .h > # include < stdlib .h > # include < sys / types .h > # include < sys / ipc .h > # include < sys / shm .h > # include < stdio .h > # define SHMSZ 27

Memoria Condivisa: Esempio (Server - part 2)


1 int main ( int argc , char * argv []) { 2 char c ; 3 int shmid ; 4 key_t key ; 5 char * shm , * s ; 6 key = 5678; 7 8 9 /* Creo il s e g m e n t o */ 10 if (( shmid = shmget ( key , SHMSZ , IPC_CREAT | 0666)) < 0) { 11 perror ( " shmget " ); 12 exit (1); 13 } 14 /* Attacco il s e g m e n t o all area dati del p r o c e s s o */ 15 if (( shm = shmat ( shmid , NULL , 0)) == ( char *) -1) { 16 perror ( " shmat " ); 17 exit (1); 18 }

Memoria Condivisa: Esempio (Server - part 3)

1 2 3 4 5 6 7 8 9 10 11 }

s = shm ; for ( c = a ; c <= z ; c ++) * s ++ = c ; * s = NULL ; while (* shm != * ) sleep (1); printf ( " Received * . Exiting ...\ n " ); exit (0);

Memoria Condivisa: Esempio (Client - part 1)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MODULO : s h m _ c l i e n t . c SCOPO : client memoria c o n d i v i s a USO : l a n c i a r e il p r o g r a m m a s h m _ s e r v e r . c in una shell e il p r o g r a m m a s h m _ c l i e n t . c in un altra shell R i c o r d a r s i di r i m u o v e r e la memoria c o n d i v i s a creata dal server al termine del p r o g r a m m a l a n c i a n d o shmctl . c oppure tramite il comando della shell ipcrm . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include < stdio .h > # include < stdlib .h > # include < sys / types .h > # include < sys / ipc .h > # include < sys / shm .h > # include < stdio .h > # define SHMSZ 27

Memoria Condivisa: Esempio (Client - part 2)


1 int main ( int argc , char * argv []) { 2 int shmid ; 3 key_t key ; 4 char * shm , * s ; 5 key = 5678; 6 7 /* R e c u p e r o il s e g m e n t o creato dal server */ 8 if (( shmid = shmget ( key , SHMSZ , 0666)) < 0) { 9 perror ( " shmget " ); 10 exit (1); 11 } 12 13 /* Attacco il s e g m e n t o all area dati del p r o c e s s o */ 14 if (( shm = shmat ( shmid , NULL , 0)) == ( char *) -1) { 15 perror ( " shmat " ); 16 exit (1); 17 }

Memoria Condivisa: Esempio (Client - part 3)

1 2 3 4 5 6 7 8 9 10 11 }

/* Stampo il c o n t e n u t o della memoria */ printf ( " Contenuto del segmento condiviso con il server : " ); for ( s = shm ; * s != NULL ; s ++) putchar (* s ); putchar ( \ n ); sleep (3); /* ritorno * al server a f f i n c h e possa t e r m i n a r e */ * shm = * ; exit (0);

Sincronizzazione tra Processi

I semafori permettono la sincronizzazione dellesecuzione di due o pi` u processi


Sincronizzazione su un dato valore Mutua esclusione piuttosto diversi da semafori classici pesanti dal punto di vista della gestione

Semafori SystemV:

Disponibili varie API (per es. POSIX semaphores)

Semafori
I semanfori sono dierenti dalle altre forme di IPC. Sono contatori usati per controllare laccesso a risorse condivise da processi diversi. Il protocollo per accedere alla risorsa e il seguente
1. testare il semaforo; 2. se > 0, allora si pu o usare la risorsa (e viene decrementato il semaforo); 3. se == 0, processo va in sleep nch e il semaforo non ridiventa > 0, a quel punto wake up e goto step 1;

Quando ha terminato con la risorsa, incrementa il semaforo. Se il semaforo e a 0 signica che si sta utilizzando il 100% della risorsa. Un semaforo binario (valori possibili 0 e 1) e detto mutex.

Semafori - (cont.)

importante notare che per implementare correttamente un E semaforo loperazione di verica del valore del semaforo ed il decremento/incremento devono costituire una operazione atomica. Per questa ragione i semafori sono implementati allinterno del kernel.

Semafori - (cont.)

Il semaforo binario e la forma pi u semplice.


Controlla ununica risorsa ed il suo valore e inizializzato a 1. E.g., stampante capace di gestire 1 sola stampa alla volta. Inizializzato ad un valore positivo indicante il numero di risorse che sono a disposizione per essere condivise. E.g., stampante capace di gestire 10 stampe alla volta.

Forma pi u complessa.

Semafori (System V API)

Non ` e possibile allocare un singolo semaforo, ma ` e necessario crearne un insieme (vettore di semafori) Struttura interna di un semaforo
Semaphore Table Semaphore arrays 0 1 2 3 4 0 0 1 2

Semafori (SystemV API)


# include < sys / types .h > # include < sys / ipc .h > # include < sys / sem .h > int semget ( int key , int nsems , short semflg );

I valori di key e di semflg sono identici al caso delle code di messaggi e della shared memory nsems ` e il numero di semafori identicati dal semid (quanti semafori sono contenuti nel vettore).

Il primo vettore del semaforo ha indice 0 semid = semget(key, 1, IPC_CREAT|IPC_EXCL|0666); crea un insieme con un solo semaforo semid = semget(key, 0, 0666); recupera un semaforo esistente

Esempi

Operazioni su Semafori
int semctl ( int semid , int semnum , int cmd , union semun * args ); union semun { int val ; /* SETVAL */ struct semid_ds * buffer ; /* IPC_STAT , IPC_SET */ unsigned short * array ; /* GET_ALL , SET_ALL */ struct seminfo * __buf ; /* solo per Linux */ };

Esegue loperazione di controllo specicata da cmd sullinsieme di semafori specicato da semid o sul semnum-esimo semaforo dellinsieme. A seconda del comando il quarto parametro e opzionale. Operazioni (cmd):
IPC_RMID IPC_SET IPC_STAT GETVAL GETALL SETVAL SETALL GETPID GETNCNT GETZCNT Rimuove il set di semafori Modica il set di semafori Statistiche sul set di semafori legge il valore del semaforo semnum in args.val legge tutti i valori in args.array assegna il valore del semaforo semnum in args.val assegna tutti i semafori con i valori in args.array Valore di PID dellultimo processo che ha fatto operazioni numero di processi in attesa che un semaforo aumenti numero di processi in attesa che un semaforo diventi 0

Operazioni su Semafori

buffer ` e un puntatore ad una struttura semid_ds denita in sys/sem.h:


struct semid_ds { struct ipc_perm sem_perm ; time_t sem_otime ; time_t sem_ctime ; unsigned short sem_nsems ; }; /* /* /* /* o p e r a t i o n p e r m i s s i o n struct */ Last semop time */ Last change time */ No . of s e m a p h o r e s */

La union semun deve essere denita nel codice del processo che chiama la semctl() ipc_perm e una struttura denita in sys/ipc.h:
struct ipc_perm { key_t __key ; uid_t uid ; gid_t gid ; uid_t cuid ; gid_t cgid ; unsigned short mode ; unsigned short __seq ; }; /* /* /* /* /* /* /* Key s u p p l i e d to semget (2) */ E f f e c t i v e UID of owner */ E f f e c t i v e GID of owner */ E f f e c t i v e UID of creator */ E f f e c t i v e GID of creator */ P e r m i s s i o n s */ S e q u e n c e number */

Operazioni su Semafori

La semctl() ritorna valori diversi a seconda del comando eseguito :

GETNCNT: il numero di processi in attesa che il semaforo semnum venga incrementato GETPID: il PID dellultimo processo che ha eettuato semop() sul semaforo semnum dellinsieme GETVAL: il valore del semaforo semnum dellinsieme GETZCNT: il numero di processi in attesa che il semaforo semnum diventi 0

Semafori: Esempio (Controller - part 1)

1 2 3 4 5 6 7 8 9 10 11 12 13 14

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MODULO : semctl . c SCOPO : I l l u s t r a r e il funz . di semctl () USO : prima di l a n c i a r e semctl . c () creare un s e m a f o r o usando un altro p r o g r a m m a ( ad esempio sem . x ) * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include < stdlib .h > # include < stdio .h > # include < sys / types .h > # include < sys / ipc .h > # include < sys / sem .h > # include < time .h > struct semid_ds semid_ds ;

Semafori: Esempio (Controller - part 2)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

/* e x p l i c i t d e c l a r a t i o n r e q u i r e d */ union semun { int val ; struct semid_ds * buf ; unsigned short int * array ; struct seminfo * __buf ; } arg ; void do_semctl ( int semid , int semnum , int cmd , union semun arg ); void do_stat (); char w a r n i n g _ m e s s a g e [] = " If you remove read permission for yourself , this program will fail frequently ! " ; int main ( int argc , union semun arg ; int cmd ; int i ; int semid ; int semnum ; char * argv []) { /* union to pass to semctl () */ /* command to give to semctl () */ /* semid to pass to semctl () */ /* semnum to pass to semctl () */

Semafori: Esempio (Controller - part 3)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

fprintf ( stderr , " Enter semid value : " ); scanf ( " % i " , & semid ); fprintf ( stderr , " Valid semctl cmd values are :\ n " ); fprintf ( stderr , " \ tGETALL = % d \ n " , GETALL ); fprintf ( stderr , " \ tGETNCNT = % d \ n " , GETNCNT ); fprintf ( stderr , " \ tGETPID = % d \ n " , GETPID ); fprintf ( stderr , " \ tGETVAL = % d \ n " , GETVAL ); fprintf ( stderr , " \ tGETZCNT = % d \ n " , GETZCNT ); fprintf ( stderr , " \ tIPC_RMID = % d \ n " , IPC_RMID ); fprintf ( stderr , " \ tIPC_SET = % d \ n " , IPC_SET ); fprintf ( stderr , " \ tIPC_STAT = % d \ n " , IPC_STAT ); fprintf ( stderr , " \ tSETALL = % d \ n " , SETALL ); fprintf ( stderr , " \ tSETVAL = % d \ n " , SETVAL ); fprintf ( stderr , " \ nEnter cmd : " ); scanf ( " % i " , & cmd );

Semafori: Esempio (Controller - part 4)


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/* Do some setup o p e r a t i o n s needed by m u l t i p l e c o m m a n d s . */ switch ( cmd ) { case GETVAL : case SETVAL : case GETNCNT : case GETZCNT : /* Get the s e m a p h o r e number for these c o m m a n d s . */ fprintf ( stderr , " \ nEnter semnum value : " ); scanf ( " % i " , & semnum ); break ; case GETALL : case SETALL : /* A l l o c a t e a buffer for the s e m a p h o r e values . */ fprintf ( stderr , " Get number of semaphores in the set .\ n " ); arg . buf = & semid_ds ; /* when I P C _ S T A T is called the second a r g u m e n t of semctl () is ignored . I P C _ S T A T is called to r e t r i e v e info s e m i d _ d s on the s e m a p h o r e set */ do_semctl ( semid , 0 , IPC_STAT , arg ); if ( arg . array = ( u_short *) malloc ( ( unsigned ) ( semid_ds . sem_nsems * sizeof ( u_short )))) { /* Break out if you got what you needed */ break ; }

Semafori: Esempio (Controller - part 5)


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
fprintf ( stderr , " semctl : unable to allocate spacefor % d values \ n " , semid_ds . sem_nsems ); exit (2); } /* Get the rest of the a r g u m e n t s needed for the s p e c i f i e d command . */ switch ( cmd ) { case SETVAL : /* Set value of one s e m a p h o r e . */ fprintf ( stderr , " \ nEnter semaphore value : " ); scanf ( " % i " , & arg . val ); do_semctl ( semid , semnum , SETVAL , arg ); /* Fall through to verify the result . */ fprintf ( stderr , " Executing semctl GETVAL command to verify results ...\ n " ); case GETVAL : /* Get value of one s e m a p h o r e . */ arg . val = 0; do_semctl ( semid , semnum , GETVAL , arg ); break ;

Semafori: Esempio (Controller - part 6)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

case GETPID : /* Get PID of the last process that s u c c e s s f u l l y c o m p l e t e s a semctl ( SETVAL ) , semctl ( SETALL ) , or semop () on the s e m a p h o r e . */ arg . val = 0; do_semctl ( semid , 0 , GETPID , arg ); break ; case GETNCNT : /* Get number of p r o c e s s e s waiting for s e m a p h o r e value to i n c r e a s e . */ arg . val = 0; do_semctl ( semid , semnum , GETNCNT , arg ); break ; case GETZCNT : /* Get number of p r o c e s s e s waiting for s e m a p h o r e value to become zero . */ arg . val = 0; do_semctl ( semid , semnum , GETZCNT , arg ); break ;

Semafori: Esempio (Controller - part 7)


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
case SETALL : /* Set the values of all s e m a p h o r e s in the set . */ fprintf ( stderr , " There are % d semaphores in the set .\ n " , semid_ds . sem_nsems ); fprintf ( stderr , " Enter semaphore values :\ n " ); for ( i = 0; i < semid_ds . sem_nsems ; i ++) { fprintf ( stderr , " Semaphore % d : " , i ); scanf ( " % hi " , & arg . array [ i ]); } do_semctl ( semid , 0 , SETALL , arg ); /* Fall through to verify the results . */ fprintf ( stderr , " Do semctl GETALL command to verify results .\ n " ); case GETALL : /* Get and print the values of all s e m a p h o r e s in the set . */ do_semctl ( semid , 0 , GETALL , arg ); fprintf ( stderr , " The values of the % d semaphores are :\ n " , semid_ds . sem_nsems ); for ( i = 0; i < semid_ds . sem_nsems ; i ++) fprintf ( stderr , " % d " , arg . array [ i ]); fprintf ( stderr , " \ n " ); break ;

Semafori: Esempio (Controller - part 8)


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
case IPC_SET : /* Modify mode and / or o w n e r s h i p . */ arg . buf = & semid_ds ; do_semctl ( semid , 0 , IPC_STAT , arg ); fprintf ( stderr , " Status before IPC_SET :\ n " ); do_stat (); fprintf ( stderr , " Enter sem_perm . uid value : " ); scanf ( " % hi " , & semid_ds . sem_perm . uid ); fprintf ( stderr , " Enter sem_perm . gid value : " ); scanf ( " % hi " , & semid_ds . sem_perm . gid ); fprintf ( stderr , " % s \ n " , w a r n i n g _ m e s s a g e ); fprintf ( stderr , " Enter sem_perm . mode value : " ); scanf ( " % hi " , & semid_ds . sem_perm . mode ); do_semctl ( semid , 0 , IPC_SET , arg ); /* Fall through to verify changes . */ fprintf ( stderr , " Status after IPC_SET :\ n " ); case IPC_STAT : /* Get and print current status . */ arg . buf = & semid_ds ; do_semctl ( semid , 0 , IPC_STAT , arg ); do_stat (); break ;

Semafori: Esempio (Controller - part 9)

1 case IPC_RMID : 2 /* Remove the s e m a p h o r e set . */ 3 arg . val = 0; 4 do_semctl ( semid , 0 , IPC_RMID , arg ); 5 break ; 6 default : 7 /* Pass unknown command to semctl . */ 8 arg . val = 0; 9 do_semctl ( semid , 0 , cmd , arg ); 10 break ; 11 } 12 exit (0); 13 } 14 15 void do_semctl ( int semid , int semnum , int cmd , union semun arg ) { 16 int i ; 17 18 fprintf ( stderr , " \ nsemctl : Calling semctl (% d , %d , %d , " , 19 semid , semnum , cmd );

Semafori: Esempio (Controller - part 10)


1 void do_semctl ( int semid , int semnum , int cmd , union semun arg ) { 2 int i ; 3 4 fprintf ( stderr , " \ nsemctl : Calling semctl (% d , %d , %d , " , 5 semid , semnum , cmd ); 6 switch ( cmd ) { 7 case GETALL : 8 fprintf ( stderr , " arg . array = %# x )\ n " , arg . array ); 9 break ; 10 case IPC_STAT : 11 case IPC_SET : 12 fprintf ( stderr , " arg . buf = %# x )\ n " , arg . buf ); 13 break ; 14 case SETALL : 15 fprintf ( stderr , " arg . array = [ " ); 16 for ( i = 0; i < semid_ds . sem_nsems ; i ++) { 17 fprintf ( stderr , " % d " , arg . array [ i ]); 18 if ( i < semid_ds . sem_nsems ) 19 fprintf ( stderr , " , " ); 20 } 21 fprintf ( stderr , " ])\ n " ); 22 break ; 23 }

Semafori: Esempio (Controller - part 11)


1 /* call to semctl () */ 2 i = semctl ( semid , semnum , cmd , arg ); 3 if ( i == -1) { 4 perror ( " semctl : semctl failed " ); 5 exit (1); 6 } 7 fprintf ( stderr , " semctl : semctl returned % d \ n " , i ); 8 return ; 9 } 10 11 void do_stat () { 12 fprintf ( stderr , " sem_perm . uid = % d \ n " , semid_ds . sem_perm . uid ); 13 fprintf ( stderr , " sem_perm . gid = % d \ n " , semid_ds . sem_perm . gid ); 14 fprintf ( stderr , " sem_perm . cuid = % d \ n " , semid_ds . sem_perm . cuid ); 15 fprintf ( stderr , " sem_perm . cgid = % d \ n " , semid_ds . sem_perm . cgid ); 16 fprintf ( stderr , " sem_perm . mode = %# o , " , semid_ds . sem_perm . mode ); 17 fprintf ( stderr , " access permissions = %# o \ n " , 18 semid_ds . sem_perm . mode & 0777); 19 fprintf ( stderr , " sem_nsems = % d \ n " , semid_ds . sem_nsems ); 20 fprintf ( stderr , " sem_otime = % s " , 21 semid_ds . sem_otime ? ctime (& semid_ds . sem_otime ) : " Not Set \ n " ); 22 fprintf ( stderr , " sem_ctime = % s " , ctime (& semid_ds . sem_ctime )); 23 }

Operazioni su Semafori
int oldval = semop ( int semid , struct sembuf * sops , int nsops );

Applica linsieme (array) sops di operazioni (in numero pari a nsops) allinsieme di semafori semid. Le operazioni, contenute in un array opportunamente allocato, sono descritte dalla struct sembuf:
struct sembuf { short sem_num ; short sem_op ; short sem_flg ; };

sem_num: semaforo su cui loperazione (i-esima) viene applicata sem_op: loperazione da applicare sem_flag: le modalit` a con cui loperazione viene applicata

Operazioni su Semafori

Valori di sem_op: equivale a P si blocca se sem_val - |sem_op| < 0 altrimenti decrementa il semaforo della quantit` a ops.sem_op =0 In attesa che il valore del semaforo diventi 0 >0 equivalente a V Incrementa il semaforo della quantit` a ops.sem_op Valori di sem_flg: IPC_NOWAIT Per realizzare P e V non bloccanti (comodo per realizzare polling) SEM_UNDO Ripristina il vecchio valore quando termina (serve nel caso di terminazioni precoci)

<0

Operazioni su Semafori

NOTA BENE: Linsieme di operazioni inserite in una chiamata alla system call semop() viene eseguito in modo atomico. Se una delle operazioni non pu` o essere eseguita, il comportamento della system call semop() dipende dalla ag IPC_NOWAIT: se IPC_NOWAIT ` e settato, semop() fallisce e ritorna -1;

se IPC_NOWAIT non ` e settato, il processo viene bloccato;

Semafori: Esempio (sem - part 1)

1 2 3 4 5 6 7 8 9 10 11 12 13 14

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MODULO : sem . c SCOPO : I l l u s t r a r e il funz . di semop () e semctl () USO : L a n c i a r e il p r o g r a m m a sem . x e quindi semop . x o semctl . x in una shell s e p a r a t a * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include # include # include # include # include < stdio .h > < stdlib .h > < sys / types .h > < sys / ipc .h > < sys / sem .h >

# define KEY 14

Semafori: Esempio (sem - part 2)


1 int main ( int argc , char * argv []) { 2 3 int semid ; /* i d e n t i f i c a t o r e dei s e m a f o r i */ 4 int i ; 5 /* s t r u t t u r a per le o p e r a z i o n i sui s e m a f o r i */ 6 struct sembuf * sops = 7 ( struct sembuf *) malloc ( sizeof ( struct sembuf )); 8 9 /* 10 C r e a z i o n e di due s e m a f o r i con p e r m e s s i di lettura e s c r i t t u r a 11 per tutti . Le flag I C P _ C R E A T e I P C _ E X C L fanno si che la 12 f u n z i o n e semget ritorni errore se esiste gi` a un vettore di 13 s e m a f o r i con chiave KEY . Vedere man semget . 14 */ 15 if (( semid = semget ( KEY , 2 , IPC_CREAT | IPC_EXCL | 0666)) == -1) { 16 perror ( " semget " ); 17 exit (1); 18 } 19 20 /* I n i z i a l i z z o i due s e m a f o r i a 1 */

Semafori: Esempio (sem - part 3)


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/* Per e s e g u i r e o p e r a z i o n i A T O M I C H E sui s e m a f o r i si usa la f u n z i o n e semop (). Vedere man semop . Alla f u n z i o n e semop () vengono passati 3 a r g o m e n t i : - l i d e n t i f i c a t o r e dell array di s e m a f o r i su cui e s e g u i r e l operazione - il p u n t a t o r e alla s t r u t t u r a sembuf n e c e s s a r i a per e s e g u i r e le o p e r a z i o n i - il numero di o p e r a z i o n i da e s e g u i r e Per ogni o p e r a z i o n e da e s e g u i r e ` e n e c e s s a r i o creare una s t r u t t u r a di tipo sembuf . La s t r u t t u r a c o n t i e n e 3 campi : - il numero del s e m a f o r o da u t i l i z z a r e . R i c o r d a r e che la semget ritorna array di s e m a f o r i . - un intero N che r a p p r e s e n t a l o p e r a z i o n e da e s e g u i r e . Se l intero N e > 0 il valore del s e m a f o r o viene i n c r e m e n t a t o di tale q u a n t i t ` a . Se N = 0 la semop blocca il p r o c e s s o in attesa che il valore del s e m a f o r o diventi 0. Se N < 0 la semop blocca il p r o c e s s o in attesa che il valore del s e m a f o r o meno N sia m a g g i o r e o uguale a 0. - una e v e n t u a l e flag ( I P C _ N O W A I T o S E M _ U N D O ) I P C _ N O W A I T serve per avere s e m a f o r i non b l o c c a n t i S E M _ U N D O serve per r i p r i s t i n a r e il vecchio valore del s e m a f o r o in caso di t e r m i n a z i o n i precoci . */

Semafori: Esempio (sem - part 4)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

sops - > sem_num = 0; sops - > sem_op = 1;

/* s e m a f o r o 0 */ /* i n c r e m e n t a di uno il valore del s e m a f o r o 0 */ sops - > sem_flg = 0; /* nessuna flag settata */ /* e s e c u z i o n e dell o p e r a z i o n e sul s e m a f o r o 0 */ semop ( semid , sops , 1); sops - > sem_num = 1; sops - > sem_op = 1; /* s e m a f o r o 1 */ /* i n c r e m e n t a di uno il valore del s e m a f o r o 1 */

sops - > sem_flg = 0; /* e s e c u z i o n e dell o p e r a z i o n e sul s e m a f o r o 1 */ semop ( semid , sops , 1); printf ( " I semafori sono stati inizializzati a 1.\ n " ); printf ( " Lanciare il programma semctl . x o semop . x su un altra shell e fornire semid =% d \ n " , semid );

Semafori: Esempio (sem - part 5)


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 }
while (1) { sops - > sem_num = 0; /* s e m a f o r o 0 */ sops - > sem_op = 0; /* attende che il s e m a f o r o valga zero */ sops - > sem_flg = 0; /* quando viene e s e g u i t a questa o p e r a z i o n e il codice si blocca in attesa che il valore del s e m a f o r o 0 diventi 0 */ semop ( semid , sops , 1); /* Quando il s e m a f o r o diventa 0 stampo che il p r o c e s s o e stato s b l o c c a t o */ printf ( " Sbloccato 1\ n " ); sops - > sem_num = 1; sops - > sem_op = 0; sops - > sem_flg = 0; /* s e m a f o r o 1 */ /* attende che il s e m a f o r o valga zero */

/* quando viene e s e g u i t a questa o p e r a z i o n e il codice si blocca in attesa che il valore del s e m a f o r o 1 diventi 0 */ semop ( semid , sops , 1); /* Quando il s e m a f o r o diventa 0 stampo che il p r o c e s s o e stato s b l o c c a t o */ printf ( " Sbloccato 2\ n " ); }

Semafori: Esempio (semop - part 1)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MODULO : semop . c SCOPO : I l l u s t r a r e il funz . di semop () * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include < stdio .h > # include < stdlib .h > # include < sys / types .h > # include < sys / ipc .h > # include < sys / sem .h > union semun { int val ; struct semid_ds * buf ; unsigned short * array ; struct seminfo * __buf ; }; int ask ( int * semidp , struct sembuf ** sopsp );

/* /* /* /*

Valore per SETVAL */ Buffer per IPC_STAT , IPC_SET */ Array per GETALL , SETALL */ Buffer per I P C _ I N F O ( s p e c i f i c o di Linux ) */

Semafori: Esempio (semop - part 2)


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
static struct semid_ds semid_ds ; /* stato del set di s e m a f o r i */ static char error_mesg1 [] = " semop : Non posso allocare spazio per un vettore di % d valori .\ n " ; static char error_mesg2 [] = " semop : Non posso allocare spazio per % d strutture sembuf . \ n " ; int main ( int argc , char * argv []) { int i; int nsops ; /* numero di o p e r a z i o n i da fare */ int semid ; /* semid per il set di s e m a f o r i */ struct sembuf * sops ; /* p u n t a t o r e alle o p e r a z i o n i da e s e g u i r e */ /* Cicla finche l utente vuole e s e g u i r e o p e r a z i o n i c h i a m a n d o la f u n z i o n e ask */ while ( nsops = ask (& semid , & sops )) { /* I n i z i a l i z z a il vettore di o p e r a z i o n i da e s e g u i r e */ for ( i = 0; i < nsops ; i ++) { fprintf ( stderr , " \ nInserire il valore per l operazione % d di % d .\ n " ,i +1 , nsops ); fprintf ( stderr , " sem_num ( i valori validi sono 0 <= sem_num <% d ): " , semid_ds . sem_nsems ); scanf ( " % d " , & sops [ i ]. sem_num );

Semafori: Esempio (semop - part 3)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

fprintf ( stderr , " sem_op : " ); scanf ( " % d " , & sops [ i ]. sem_op ); fprintf ( stderr , " Possibili flag per sem_flg :\ n " ); fprintf ( stderr , " \ tIPC_NOWAIT =\ t %#6.6 o \ n " , IPC_NOWAIT ); fprintf ( stderr , " \ tSEM_UNDO =\ t %#6.6 o \ n " , SEM_UNDO ); fprintf ( stderr , " \ tNESSUNO =\ t %6 d \ n " ,0); fprintf ( stderr , " sem_flg : " ); /* c o n t r o l l a r e cosa fa % i su man scanf */ scanf ( " % i " , & sops [ i ]. sem_flg ); } /* R i c a p i t o l a la c h i a m a t a da fare a semop () */ fprintf ( stderr , " \ nsemop : Chiamo semop (% d , & sops , % d ) with : " , semid , nsops ); for ( i = 0; i < nsops ; i ++) { fprintf ( stderr , " \ nsops [% d ]. sem_num = %d , " ,i , sops [ i ]. sem_num ); fprintf ( stderr , " sem_op = %d , " , sops [ i ]. sem_op ); fprintf ( stderr , " sem_flg = % o \ n " , sops [ i ]. sem_flg ); }

Semafori: Esempio (semop - part 4)


1 /* Chiama la semop () e riporta il r i s u l t a t o */ 2 if (( i = semop ( semid , sops , nsops )) == -1) { 3 perror ( " semop : semop failed " ); 4 } else { 5 fprintf ( stderr , " semop : valore di ritorno = % d \ n " , i ); 6 } 7 } 8 } 9 10 11 int ask ( int * semidp , struct sembuf ** sopsp ) { 12 static union semun arg ; /* a r g o m e n t o per semctl () */ 13 static int nsops = 0; /* d i m e n s i o n e del vettore di sembuf */ 14 static int semid = -1; /* semid del set di s e m a f o r i */ 15 static struct sembuf * sops ; /* p u n t a t o r e al vettore di sembuf */ 16 int i ; 17 18 if ( semid < 0) { 19 /* Prima c h i a m a t a alla f u n z i o n e ask () 20 R e c u p e r i a m o semid dall utente e lo stato c o r r e n t e del set di 21 s e m a f o r i */ 22 fprintf ( stderr , 23 " Inserire semid del set di semafori su cui operare : " ); 24 scanf ( " % d " , & semid );

Semafori: Esempio (semop - part 5)


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
* semidp = semid ; arg . buf = & semid_ds ; /* c h i a m a t a a semctl () */ if ( semctl ( semid , 0 , IPC_STAT , arg ) == -1) { perror ( " semop : semctl ( IPC_STAT ) fallita . " ); /* Notare che se semctl () fallisce , s e m i d _ d s viene r i e m p i t a con 0 , e s u c c e s s i v i test per c o n t r o l l a r e il numero di s e m a f o r i r i t o r n e r a n n o 0. Se invece semctl () va a buon fine , arg . buf verra r i e m p i t o con le i n f o r m a z i o n i r e l a t i v e al set di s e m a f o r i */ /* a l l o c a z i o n e della memoria per un vettore di interi la cui d i m e n s i o n e dipende dal numero di s e m a f o r i inclusi nel set */ } else if (( arg . array = ( ushort *) malloc ( sizeof ( ushort ) * semid_ds . sem_nsems )) == NULL ) { fprintf ( stderr , error_mesg1 , semid_ds . sem_nsems ); exit (1); } }

Semafori: Esempio (semop - part 6)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

/* Stampa i valori c o r r e n t i dei s e m a f o r i */ if ( semid_ds . sem_nsems != 0) { fprintf ( stderr , " Ci sono % d semaphores .\ n " , semid_ds . sem_nsems ); /* Chiama la f u n z i o n e semctl per r e c u p e r a r e i valori di tutti i s e m a f o r i del set . Nel caso di GETALL il secondo a r g o m e n t o della semctl () viene i g n o r a t o e si u t i l i z z a il campo array della union semun arg */ if ( semctl ( semid , 0 , GETALL , arg ) == -1) { perror ( " semop : semctl ( GETALL ) fallita " ); } else { fprintf ( stderr , " I valori correnti dei semafori sono : " ); for ( i = 0; i < semid_ds . sem_nsems ; i ++) fprintf ( stderr , " % d " , arg . array [ i ]); fprintf ( stderr , " \ n " ); } }

Semafori: Esempio (semop - part 7)


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 }
/* A l l o c a z i o n e dello spazio per le o p e r a z i o n i che l utente d e s i d e r a e s e g u i r e */ fprintf ( stderr , " Quante operazioni vuoi eseguire con la prossima semop ()?\ n " ); fprintf ( stderr , " Inserire 0 or control - D per uscire : " ); i = 0; if ( scanf ( " % d " , & i ) == EOF || i == 0) exit (0); if ( i > nsops ) { if ( nsops != 0) /* libero la memoria p r e c e d e n t e m e n t e a l l o c a t a */ free (( char *) sops ); nsops = i ; /* A l l o c a z i o n e della memoria per le o p e r a z i o n i da e s e g u i r e */ if (( sops = ( struct sembuf *) malloc ( ( nsops * sizeof ( struct sembuf )))) == NULL ) { fprintf ( stderr , error_mesg2 , nsops ); exit (2); } } * sopsp = sops ; return ( i );

Semafori: Esempio (semaph - part 1)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * MODULO : semaph . c SCOPO : U t i l i z z o di s e m a f o r i * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include < stdio .h > # include < sys / types .h > # include < sys / ipc .h > # include < sys / sem .h > # include < stdlib .h > union semun { int val ; struct semid_ds * buf ; ushort * array ; }; int main ( int argc , char * argv []) { int i , j ; int pid ;

Semafori: Esempio (semaph - part 2)


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
int semid ; /* semid of s e m a p h o r e set */ key_t key = 1234; /* key to pass to semget () */ int semflg = IPC_CREAT | 0666; /* semflg to pass to semget () */ int nsems = 1; /* nsems to pass to semget () */ int nsops ; /* number of o p e r a t i o n s to do */ struct sembuf * sops = ( struct sembuf *) malloc (2* sizeof ( struct sembuf )); /* ptr to o p e r a t i o n s to perform */ /* set up s e m a p h o r e */ fprintf ( stderr , " \ nsemget : Setting up seamaphore : semget (%# lx , %d , %# o )\ n " ,key , nsems , semflg ); if (( semid = semget ( key , nsems , semflg )) == -1) { perror ( " semget : semget failed " ); exit (1); } else fprintf ( stderr , " semget : semget succeeded : semid = % d \ n " , semid ); /* get child process */ if (( pid = fork ()) < 0) { perror ( " fork " ); exit (1); }

Semafori: Esempio (semaph - part 3)


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
if ( pid == 0) { /* child */ i = 0; while ( i < 3) { /* allow for 3 s e m a p h o r e sets */ nsops = 2; /* wait for s e m a p h o r e to reach zero */ sops [0]. sem_num = 0; /* We only use one track */ sops [0]. sem_op = 0; /* wait for s e m a p h o r e flag to become zero */ sops [0]. sem_flg = SEM_UNDO ; /* take off s e m a p h o r e a s y n c h r o n o u s */ sops [1]. sem_num = 0; sops [1]. sem_op = 1; /* i n c r e m e n t s e m a p h o r e -- take control of track */ sops [1]. sem_flg = SEM_UNDO | IPC_NOWAIT ; /* take off s e m a p h o r e */ /* Recap the call to be made . */ fprintf ( stderr , " \ nsemop : Child Calling semop (% d ,& sops ,% d ) \ with : " , semid , nsops );

Semafori: Esempio (semaph - part 4)


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
for ( j = 0; j < nsops ; j ++) { fprintf ( stderr , " \ n \ tsops [% d ]. sem_num = %d , " , j , sops [ j ]. sem_num ); fprintf ( stderr , " sem_op = %d , " , sops [ j ]. sem_op ); fprintf ( stderr , " sem_flg = %# o \ n " , sops [ j ]. sem_flg ); } /* Make the semop () call and report the results . */ if (( j = semop ( semid , sops , nsops )) == -1) { perror ( " semop : semop failed " ); } else { fprintf ( stderr , " \ tsemop : semop returned % d \ n " , j ); fprintf ( stderr , " \ n \ nChild Process Taking Control of Track : % d /3 times \ n " , i +1); sleep (5); /* DO Nothing for 5 seconds */ } nsops = 1; /* wait for s e m a p h o r e to reach zero */ sops [0]. sem_num = 0; sops [0]. sem_op = -1; /* Give UP COntrol of track */ sops [0]. sem_flg = SEM_UNDO | IPC_NOWAIT ; /* take off semaphore , async */

Semafori: Esempio (semaph - part 5)


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
if (( j = semop ( semid , sops , nsops )) == -1) { perror ( " semop : semop failed " ); } else { fprintf ( stderr , " Child Process Giving up Control of Track : % d /3 times \ n " , i +1); sleep (5); /* halt process to allow parent to catch s e m a p h o r change first */ } ++ i ; } } else /* parent */ { i = 0; while ( i < 3) { /* allow for 3 s e m a p h o r e sets */ nsops = 2; /* wait for s e m a p h o r e to reach zero */ sops [0]. sem_num = 0; sops [0]. sem_op = 0; /* wait for s e m a p h o r e flag to become zero */ sops [0]. sem_flg = SEM_UNDO ; /* take off s e m a p h o r e a s y n c h r o n o u s */

Semafori: Esempio (semaph - part 6)


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
sops [1]. sem_num = 0; sops [1]. sem_op = 1; /* i n c r e m e n t s e m a p h o r e -- take control of track */ sops [1]. sem_flg = SEM_UNDO | IPC_NOWAIT ; /* take off s e m a p h o r e */ /* Recap the call to be made . */ fprintf ( stderr , " \ nsemop : Parent Calling semop (% d , & sops , % d ) \ with : " , semid , nsops ); for ( j = 0; j < nsops ; j ++) { fprintf ( stderr , " \ n \ tsops [% d ]. sem_num = %d , " , j , sops [ j ]. sem_num ); fprintf ( stderr , " sem_op = %d , " , sops [ j ]. sem_op ); fprintf ( stderr , " sem_flg = %# o \ n " , sops [ j ]. sem_flg ); } /* Make the semop () call and report the results . */ if (( j = semop ( semid , sops , nsops )) == -1) { perror ( " semop : semop failed " ); } else { fprintf ( stderr , " semop : semop returned % d \ n " , j ); fprintf ( stderr , " Parent Process Taking Control of Track : % d /3 times \ n " , i +1); sleep (5); /* Do nothing for 5 seconds */ nsops = 1;

Semafori: Esempio (semaph - part 7)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 } 19 }

/* wait for s e m a p h o r e to reach zero */ sops [0]. sem_num = 0; sops [0]. sem_op = -1; /* Give UP COntrol of track */ sops [0]. sem_flg = SEM_UNDO | IPC_NOWAIT ; /* take off semaphore , a s y n c h r o n o u s */ if (( j = semop ( semid , sops , nsops )) == -1) { perror ( " semop : semop failed " ); } else { fprintf ( stderr , " Parent Process Giving up Control of Track : % d /3 times \ n " , i +1); sleep (5); /* halt process to allow child to catch s e m a p h o r change first */ } ++ i ; } }