From: Simone Piccardi Date: Mon, 20 Jan 2014 08:38:05 +0000 (+0000) Subject: Revisione IPC Posix X-Git-Url: https://gapil.gnulinux.it/gitweb/?a=commitdiff_plain;h=7f880b411dd147595d2309696cf56f3f7bc37d01;p=gapil.git Revisione IPC Posix --- diff --git a/ipc.tex b/ipc.tex index 48cc7b8..2b34ded 100644 --- a/ipc.tex +++ b/ipc.tex @@ -3852,7 +3852,7 @@ funzione di sistema \funcd{mq\_unlink}, il cui prototipo è: {La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual caso \var{errno} assumerà gli uno dei valori: - \begin{errlist} + \begin{errlist} \item[\errcode{EACCES}] non si hanno i permessi per cancellare la coda. \item[\errcode{ENAMETOOLONG}] il nome indicato è troppo lungo. \item[\errcode{ENOENT}] non esiste una coda con il nome indicato. @@ -3891,8 +3891,8 @@ funzioni \funcd{mq\_getattr} e \funcd{mq\_setattr}, i cui prototipi sono: struct mq\_attr *omqstat)} \fdesc{Modifica gli attributi di una coda di messaggi POSIX.} } - -{Entrambe le funzioni ritornano $0$ in caso di successo e $-1$ per un errore, +{ +Entrambe le funzioni ritornano $0$ in caso di successo e $-1$ per un errore, nel qual caso \var{errno} assumerà i valori \errval{EBADF} o \errval{EINVAL} nel loro significato generico. } @@ -3955,7 +3955,8 @@ I rispettivi prototipi sono: \item[\errcode{ETIMEDOUT}] l'inserimento del messaggio non è stato effettuato entro il tempo stabilito (solo \func{mq\_timedsend}). \end{errlist} - ed inoltre \errval{EBADF}, \errval{EINTR} nel suo significato generico. } + ed inoltre \errval{EBADF} e \errval{EINTR} nel loro significato generico. +} \end{funcproto} Entrambe le funzioni richiedono un puntatore ad un buffer in memoria @@ -4090,7 +4091,6 @@ usando la funzione \funcd{mq\_notify}, il cui prototipo è: esistente. \end{errlist} ed inoltre \errval{ENOMEM} nel suo significato generico. - } \end{funcproto} @@ -4199,24 +4199,35 @@ questo caso è l'unità di allocazione elementare. La funzione che permette di aprire un segmento di memoria condivisa POSIX, ed eventualmente di crearlo se non esiste ancora, è \funcd{shm\_open}; il suo prototipo è: -\begin{functions} - \headdecl{sys/mman.h} - \headdecl{sys/stat.h} - \headdecl{fcntl.h} - \funcdecl{int shm\_open(const char *name, int oflag, mode\_t mode)} +\begin{funcproto}{ +\fhead{sys/mman.h} +\fhead{sys/stat.h} +\fhead{fcntl.h} +\fdecl{int shm\_open(const char *name, int oflag, mode\_t mode)} + +\fdesc{Apre un segmento di memoria condivisa.} +} + +{La funzione ritorna un file descriptor in caso di successo e $-1$ per un + errore, nel qual caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EACCES}] non si hanno i permessi di aprire il segmento nella + modalità scelta o si richiesto \const{O\_TRUNC} per un segmento su cui non + si ha il permesso di scrittura. + \item[\errcode{EINVAL}] si è utilizzato un nome non valido. + \end{errlist} + ed inoltre \errval{EEXIST}, \errval{EMFILE}, \errval{ENAMETOOLONG}, + \errval{ENFILE} e \errval{ENOENT} nello stesso significato che hanno per + \func{open}. +} +\end{funcproto} - Apre un segmento di memoria condivisa. - - \bodydesc{La funzione restituisce un file descriptor positivo in caso di - successo e -1 in caso di errore; nel quel caso \var{errno} assumerà gli - stessi valori riportati da \func{open}.} -\end{functions} La funzione apre un segmento di memoria condivisa identificato dal nome \param{name}. Come già spiegato in sez.~\ref{sec:ipc_posix_generic} questo nome può essere specificato in forma standard solo facendolo iniziare per -``\file{/}'' e senza ulteriori ``\file{/}''. Linux supporta comunque nomi +``\texttt{/}'' e senza ulteriori ``\texttt{/}''. Linux supporta comunque nomi generici, che verranno interpretati prendendo come radice \file{/dev/shm}.\footnote{occorre pertanto evitare di specificare qualcosa del tipo \file{/dev/shm/nome} all'interno di \param{name}, perché questo @@ -4246,36 +4257,52 @@ i seguenti: \end{basedescript} In caso di successo la funzione restituisce un file descriptor associato al -segmento di memoria condiviso con le stesse modalità di -\func{open}\footnote{in realtà, come accennato, \func{shm\_open} è un semplice - wrapper per \func{open}, usare direttamente quest'ultima avrebbe lo stesso - effetto.} viste in sez.~\ref{sec:file_open_close}; in particolare viene impostato -il flag \const{FD\_CLOEXEC}. Chiamate effettuate da diversi processi usando -lo stesso nome, restituiranno file descriptor associati allo stesso segmento -(così come, nel caso di file di dati, essi sono associati allo stesso -\itindex{inode} inode). In questo modo è possibile effettuare una chiamata ad -\func{mmap} sul file descriptor restituito da \func{shm\_open} ed i processi -vedranno lo stesso segmento di memoria condivisa. - -Quando il nome non esiste il segmento può essere creato specificando +segmento di memoria condiviso con le stesse modalità di \func{open} viste in +sez.~\ref{sec:file_open_close}. Inoltre sul file descriptor viene sempre +impostato il flag \const{FD\_CLOEXEC}. Chiamate effettuate da diversi +processi usando lo stesso nome restituiranno file descriptor associati allo +stesso segmento, così come, nel caso di file ordinari, essi sono associati +allo stesso \itindex{inode} inode. In questo modo è possibile effettuare una +chiamata ad \func{mmap} sul file descriptor restituito da \func{shm\_open} ed +i processi vedranno lo stesso segmento di memoria condivisa. + +Quando il nome non esiste si può creare un nuovo segmento specificando \const{O\_CREAT}; in tal caso il segmento avrà (così come i nuovi file) -lunghezza nulla. Dato che un segmento di lunghezza nulla è di scarsa utilità, -per impostarne la dimensione si deve usare \func{ftruncate} (vedi -sez.~\ref{sec:file_file_size}), prima di mapparlo in memoria con \func{mmap}. -Si tenga presente che una volta chiamata \func{mmap} si può chiudere il file -descriptor (con \func{close}), senza che la mappatura ne risenta. - -Come per i file, quando si vuole effettivamente rimuovere segmento di memoria -condivisa, occorre usare la funzione \funcd{shm\_unlink}, il cui prototipo è: -\begin{prototype}{sys/mman.h} -{int shm\_unlink(const char *name)} - -Rimuove un segmento di memoria condivisa. - -\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di - errore; nel quel caso \var{errno} assumerà gli stessi valori riportati da - \func{unlink}.} -\end{prototype} +lunghezza nulla. Il nuovo segmento verrà creato con i permessi indicati +da \param{mode} (di cui vengono usati solo i 9 bit meno significativi, non si +applicano pertanto i permessi speciali di sez.~\ref{sec:file_special_perm}) +filtrati dal valore dell'\textit{umask} del processo. Come gruppo ed utente +propritario del segmento saranno presi quelli facenti parte del gruppo +\textit{effective} del processo chiamante. + +Dato che un segmento di lunghezza nulla è di scarsa utilità, una vola che lo +si è creato per impostarne la dimensione si devrà poi usare \func{ftruncate} +(vedi sez.~\ref{sec:file_file_size}) prima di mapparlo in memoria con +\func{mmap}. Si tenga presente che una volta chiamata \func{mmap} si può +chiudere il file descriptor ad esso associato (semplicemente con +\func{close}), senza che la mappatura ne risenta, e che questa può essere +rimossa usando \func{munmap}. + +Come per i file, quando si vuole rimuovere completamente un segmento di +memoria condivisa occorre usare la funzione \funcd{shm\_unlink}, il cui +prototipo è: + +\begin{funcproto}{ +\fhead{sys/mman.h} +\fdecl{int shm\_unlink(const char *name)} + +\fdesc{Rimuove un segmento di memoria condivisa.} +} + +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EACCES}] non si è proprietari del segmento. + \end{errlist} + ed inoltre \errval{ENAMETOOLONG} e \errval{ENOENT}, nel loro significato + generico. +} +\end{funcproto} La funzione è del tutto analoga ad \func{unlink}, e si limita a cancellare il nome del segmento da \file{/dev/shm}, senza nessun effetto né sui file @@ -4287,7 +4314,21 @@ con lo stesso nome, la chiamata a \func{shm\_open} fallirà, a meno di non aver usato \const{O\_CREAT}, in quest'ultimo caso comunque si otterrà un file descriptor che fa riferimento ad un segmento distinto da eventuali precedenti. -\begin{figure}[!htbp] +Dato che i segmenti di memoria condivisa sono trattati come file del +filesystem \texttt{tmpfs}, si possono usare su di essi, con lo stesso +significato che assumono sui file ordinari, anche funzioni come quelle delle +famiglie \func{fstat}, \func{fchown} e \func{fchmod}. Inoltre a partire dal +kernel 2.6.19 per i permessi sono supportate anche le ACL illustrate in +sez.~\ref{sec:file_ACL}. + +Come esempio dell'uso delle funzioni attinenti ai segmenti di memoria +condivisa POSIX, vediamo come è possibile riscrivere una interfaccia +semplificata analoga a quella vista in fig.~\ref{fig:ipc_sysv_shm_func} per la +memoria condivisa in stile SysV. Il codice completo, di cui si sono riportate +le parti esseziali in fig.~\ref{fig:ipc_posix_shmmem}, è contenuto nel file +\file{SharedMem.c} dei sorgenti allegati. + +\begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{\codesamplewidth} \includecodesample{listati/MemShared.c} @@ -4298,39 +4339,33 @@ descriptor che fa riferimento ad un segmento distinto da eventuali precedenti. \label{fig:ipc_posix_shmmem} \end{figure} -Come esempio per l'uso di queste funzioni vediamo come è possibile riscrivere -una interfaccia semplificata analoga a quella vista in -fig.~\ref{fig:ipc_sysv_shm_func} per la memoria condivisa in stile SysV. Il -codice, riportato in fig.~\ref{fig:ipc_posix_shmmem}, è sempre contenuto nel -file \file{SharedMem.c} dei sorgenti allegati. - La prima funzione (\texttt{\small 1--24}) è \func{CreateShm} che, dato un nome nell'argomento \var{name} crea un nuovo segmento di memoria condivisa, accessibile in lettura e scrittura, e ne restituisce l'indirizzo. Anzitutto si definiscono (\texttt{\small 8}) i flag per la successiva (\texttt{\small 9}) chiamata a \func{shm\_open}, che apre il segmento in lettura e scrittura (creandolo se non esiste, ed uscendo in caso contrario) assegnandogli sul -filesystem i permessi specificati dall'argomento \var{perm}. In caso di errore -(\texttt{\small 10--12}) si restituisce un puntatore nullo, altrimenti si -prosegue impostando (\texttt{\small 14}) la dimensione del segmento con -\func{ftruncate}. Di nuovo (\texttt{\small 15--16}) si esce immediatamente -restituendo un puntatore nullo in caso di errore. Poi si passa (\texttt{\small - 18}) a mappare in memoria il segmento con \func{mmap} specificando dei -diritti di accesso corrispondenti alla modalità di apertura. Di nuovo si -restituisce (\texttt{\small 19--21}) un puntatore nullo in caso di errore, -altrimenti si inizializza (\texttt{\small 22}) il contenuto del segmento al -valore specificato dall'argomento \var{fill} con \func{memset}, e se ne -restituisce (\texttt{\small 23}) l'indirizzo. +filesystem i permessi specificati dall'argomento \var{perm}. + +In caso di errore (\texttt{\small 10--12}) si restituisce un puntatore nullo, +altrimenti si prosegue impostando (\texttt{\small 14}) la dimensione del +segmento con \func{ftruncate}. Di nuovo (\texttt{\small 15--16}) si esce +immediatamente restituendo un puntatore nullo in caso di errore. Poi si passa +(\texttt{\small 18}) a mappare in memoria il segmento con \func{mmap} +specificando dei diritti di accesso corrispondenti alla modalità di apertura. +Di nuovo si restituisce (\texttt{\small 19--21}) un puntatore nullo in caso di +errore, altrimenti si inizializza (\texttt{\small 22}) il contenuto del +segmento al valore specificato dall'argomento \var{fill} con \func{memset}, e +se ne restituisce (\texttt{\small 23}) l'indirizzo. La seconda funzione (\texttt{\small 25--40}) è \func{FindShm} che trova un -segmento di memoria condiviso già esistente, restituendone l'indirizzo. In -questo caso si apre (\texttt{\small 31}) il segmento con \func{shm\_open} -richiedendo che il segmento sia già esistente, in caso di errore -(\texttt{\small 31--33}) si ritorna immediatamente un puntatore nullo. -Ottenuto il file descriptor del segmento lo si mappa (\texttt{\small 35}) in -memoria con \func{mmap}, restituendo (\texttt{\small 36--38}) un puntatore -nullo in caso di errore, o l'indirizzo (\texttt{\small 39}) dello stesso in -caso di successo. +segmento di memoria condiviso esistente, restituendone l'indirizzo. In questo +caso si apre (\texttt{\small 31}) il segmento con \func{shm\_open} richiedendo +che il segmento sia già esistente, in caso di errore (\texttt{\small 31--33}) +si ritorna immediatamente un puntatore nullo. Ottenuto il file descriptor del +segmento lo si mappa (\texttt{\small 35}) in memoria con \func{mmap}, +restituendo (\texttt{\small 36--38}) un puntatore nullo in caso di errore, o +l'indirizzo (\texttt{\small 39}) dello stesso in caso di successo. La terza funzione (\texttt{\small 40--45}) è \func{RemoveShm}, e serve a cancellare un segmento di memoria condivisa. Dato che al contrario di quanto @@ -4351,66 +4386,76 @@ dei semafori POSIX che li realizzava solo a livello di \itindex{thread} erano visibili solo all'interno dei \itindex{thread} \textit{thread} creati da un singolo processo, e non potevano essere usati come meccanismo di sincronizzazione fra processi diversi.} fornita attraverso la sezione delle -estensioni \textit{real-time} della \acr{glibc}.\footnote{quelle che si - accedono collegandosi alla libreria \texttt{librt}.} Esisteva inoltre una -libreria che realizzava (parzialmente) l'interfaccia POSIX usando le funzioni -dei semafori di \textit{SysV-IPC} (mantenendo così tutti i problemi -sottolineati in sez.~\ref{sec:ipc_sysv_sem}). +estensioni \textit{real-time} della \acr{glibc} (quelle che si accedono +collegandosi alla libreria \texttt{librt}). Esisteva inoltre una libreria che +realizzava (parzialmente) l'interfaccia POSIX usando le funzioni dei semafori +di \textit{SysV-IPC} (mantenendo così tutti i problemi sottolineati in +sez.~\ref{sec:ipc_sysv_sem}). A partire dal kernel 2.5.7 è stato introdotto un meccanismo di -sincronizzazione completamente nuovo, basato sui cosiddetti -\textit{futex},\footnote{la sigla sta per \textit{fast user mode mutex}.} con -il quale è stato possibile implementare una versione nativa dei semafori -POSIX. Grazie a questo con i kernel della serie 2.6 e le nuove versioni della -\acr{glibc} che usano questa nuova infrastruttura per quella che viene che -viene chiamata \textit{New Posix Thread Library}, sono state implementate -anche tutte le funzioni dell'interfaccia dei semafori POSIX. +sincronizzazione completamente nuovo, basato sui cosiddetti \textit{futex} (la +sigla sta per \textit{fast user mode mutex}) con il quale è stato possibile +implementare una versione nativa dei semafori POSIX. Grazie a questo con i +kernel della serie 2.6 e le nuove versioni della \acr{glibc} che usano questa +nuova infrastruttura per quella che viene che viene chiamata \textit{New Posix + Thread Library}, sono state implementate anche tutte le funzioni +dell'interfaccia dei semafori POSIX. Anche in questo caso è necessario appoggiarsi alla libreria per le estensioni \textit{real-time} \texttt{librt}, questo significa che se si vuole utilizzare questa interfaccia, oltre ad utilizzare gli opportuni file di definizione, -occorrerà compilare i programmi con l'opzione \texttt{-lrt}. +occorrerà compilare i programmi con l'opzione \texttt{-lrt} o con +\texttt{-lpthread} se si usano questi ultimi. La funzione che permette di creare un nuovo semaforo POSIX, creando il relativo file, o di accedere ad uno esistente, è \funcd{sem\_open}, questa prevede due forme diverse a seconda che sia utilizzata per aprire un semaforo esistente o per crearne uno nuovi, i relativi prototipi sono: -\begin{functions} - \headdecl{semaphore.h} - - \funcdecl{sem\_t *sem\_open(const char *name, int oflag)} - - \funcdecl{sem\_t *sem\_open(const char *name, int oflag, mode\_t mode, + +\begin{funcproto}{ +\fhead{semaphore.h} +\fhead{sys/stat.h} +\fhead{fcntl.h} +\fdecl{sem\_t *sem\_open(const char *name, int oflag)} +\fdecl{sem\_t *sem\_open(const char *name, int oflag, mode\_t mode, unsigned int value)} - Crea un semaforo o ne apre uno esistente. - - \bodydesc{La funzione restituisce l'indirizzo del semaforo in caso di - successo e \const{SEM\_FAILED} in caso di errore; nel quel caso - \var{errno} assumerà i valori: - \begin{errlist} +\fdesc{Crea un semaforo o ne apre uno esistente.} +} +{La funzione ritorna l'indirizzo del semaforo in caso di successo e + \const{SEM\_FAILED} per un errore, nel qual caso \var{errno} assumerà uno + dei valori: + \begin{errlist} \item[\errcode{EACCES}] il semaforo esiste ma non si hanno permessi sufficienti per accedervi. \item[\errcode{EEXIST}] si sono specificati \const{O\_CREAT} e \const{O\_EXCL} ma il semaforo esiste. \item[\errcode{EINVAL}] il valore di \param{value} eccede - \const{SEM\_VALUE\_MAX}. + \const{SEM\_VALUE\_MAX} o il nome è solo ``\texttt{/}''. \item[\errcode{ENAMETOOLONG}] si è utilizzato un nome troppo lungo. \item[\errcode{ENOENT}] non si è usato \const{O\_CREAT} ed il nome specificato non esiste. - \end{errlist} - ed inoltre \errval{ENFILE} ed \errval{ENOMEM}.} -\end{functions} + \end{errlist} + ed inoltre \errval{EMFILE}, \errval{ENFILE} ed \errval{ENOMEM} nel loro + significato generico. + +} +\end{funcproto} L'argomento \param{name} definisce il nome del semaforo che si vuole utilizzare, ed è quello che permette a processi diversi di accedere allo -stesso semaforo. Questo deve essere specificato con un \textit{pathname} nella -forma \texttt{/qualchenome}, che non ha una corrispondenza diretta con un -\textit{pathname} reale; con Linux infatti i file associati ai semafori sono -mantenuti nel filesystem virtuale \texttt{/dev/shm}, e gli viene assegnato -automaticamente un nome nella forma \texttt{sem.qualchenome}.\footnote{si ha - cioè una corrispondenza per cui \texttt{/qualchenome} viene rimappato, nella - creazione tramite \func{sem\_open}, su \texttt{/dev/shm/sem.qualchenome}.} +stesso semaforo. Questo deve essere specificato nella stessa forma utilizzata +per i segmenti di memoria condivisa, con un nome che inizia con ``\texttt{/}'' +e senza ulteriori ``\texttt{/}'', vale a dire nella forma +\texttt{/nomesemaforo}. + +Con Linux i file associati ai semafori sono mantenuti nel filesystem virtuale +\texttt{/dev/shm}, e gli viene assegnato automaticamente un nome nella forma +\texttt{sem.nomesemaforo}, si ha cioè una corrispondenza per cui +\texttt{/nomesemaforo} viene rimappato, nella creazione tramite +\func{sem\_open}, su \texttt{/dev/shm/sem.nomesemaforo}. Per questo motivo la +dimensione massima per il nome di un semaforo, a differenza di quanto avviene +per i segmenti di memoria confivisa, è pari a \const{NAME\_MAX}$ - 4$. L'argomento \param{oflag} è quello che controlla le modalità con cui opera la funzione, ed è passato come maschera binaria; i bit corrispondono a quelli @@ -4425,26 +4470,25 @@ funzione, in cui si devono specificare sia un valore iniziale con l'argomento quanto avviene per i semafori del \textit{SysV-IPC}, effettuare in maniera atomica creazione ed inizializzazione di un semaforo usando una unica funzione.} che una maschera dei permessi con l'argomento -\param{mode};\footnote{anche questo argomento prende gli stessi valori - utilizzati per l'analogo di \func{open}, che si sono illustrati in dettaglio - sez.~\ref{sec:file_perm_overview}.} questi verranno assegnati al semaforo -appena creato. Se il semaforo esiste già i suddetti valori saranno invece +\param{mode}; se il semaforo esiste già questi saranno semplicemente ignorati. Usando il flag \const{O\_EXCL} si richiede invece la verifica che il -semaforo non esiste, usandolo insieme ad \const{O\_CREAT} la funzione fallisce -qualora un semaforo con lo stesso nome sia già presente. +semaforo non esista, ed usandolo insieme ad \const{O\_CREAT} la funzione +fallisce qualora un semaforo con lo stesso nome sia già presente. + +Si tenga presente che, come accennato in sez.~\ref{sec:ipc_posix_generic}, i +semafori usano la semantica standard dei file per quanto riguarda i controlli +di accesso, questo significa che un nuovo semaforo viene sempre creato con +l'\ids{UID} ed il \ids{GID} effettivo del processo chiamante, e che i permessi +indicati con \param{mode} vengono filtrati dal valore della \itindex{umask} +\textit{umask} del processo. Inoltre per poter aprire un semaforo è +necessario avere su di esso sia il permesso di lettura che quello di +scrittura. La funzione restituisce in caso di successo un puntatore all'indirizzo del semaforo con un valore di tipo \ctyp{sem\_t *}, è questo valore che dovrà -essere passato alle altre funzioni per operare sul semaforo stesso. Si tenga -presente che, come accennato in sez.~\ref{sec:ipc_posix_generic}, i semafori -usano la semantica standard dei file per quanto riguarda i controlli di -accesso. - -Questo significa che un nuovo semaforo viene sempre creato con l'\ids{UID} ed -il \ids{GID} effettivo del processo chiamante, e che i permessi indicati con -\param{mode} vengono filtrati dal valore della \itindex{umask} \textit{umask} -del processo. Inoltre per poter aprire un semaforo è necessario avere su di -esso sia il permesso di lettura che quello di scrittura. +essere passato alle altre funzioni per operare sul semaforo stesso, e non sarà +più necessario fare riferimento al nome, che potrebbe anche essere rimosso con +\func{sem\_unlink}. Una volta che si sia ottenuto l'indirizzo di un semaforo, sarà possibile utilizzarlo; se si ricorda quanto detto all'inizio di @@ -4453,57 +4497,63 @@ relativi ai semafori, le operazioni principali sono due, quella che richiede l'uso di una risorsa bloccando il semaforo e quella che rilascia la risorsa liberando il semaforo. La prima operazione è effettuata dalla funzione \funcd{sem\_wait}, il cui prototipo è: -\begin{functions} - \headdecl{semaphore.h} - - \funcdecl{int sem\_wait(sem\_t *sem)} - - Blocca il semaforo \param{sem}. - - \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di - errore; nel quel caso \var{errno} assumerà i valori: - \begin{errlist} + +\begin{funcproto}{ +\fhead{semaphore.h} +\fdecl{int sem\_wait(sem\_t *sem)} + +\fdesc{Blocca un semaforo.} +} + +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà uno dei valori: + \begin{errlist} \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale. \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. - \end{errlist} -} -\end{functions} + \end{errlist} +} +\end{funcproto} La funzione cerca di decrementare il valore del semaforo indicato dal puntatore \param{sem}, se questo ha un valore positivo, cosa che significa che la risorsa è disponibile, la funzione ha successo, il valore del semaforo -viene diminuito di 1 ed essa ritorna immediatamente; se il valore è nullo la -funzione si blocca fintanto che il valore del semaforo non torni -positivo\footnote{ovviamente per opera di altro processo che lo rilascia - chiamando \func{sem\_post}.} così che poi essa possa decrementarlo con -successo e proseguire. - -Si tenga presente che la funzione può sempre essere interrotta da un segnale -(nel qual caso si avrà un errore di \const{EINTR}) e che questo avverrà -comunque, anche se si è richiesta la semantica BSD installando il relativo -gestore con \const{SA\_RESTART} (vedi sez.~\ref{sec:sig_sigaction}) per -riavviare le \textit{system call} interrotte. +viene diminuito di 1 ed essa ritorna immediatamente consentendo la +prosecuzione del processo. + +Se invece il valore è nullo la funzione si blocca (fermando l'esecuzione del +processo) fintanto che il valore del semaforo non ritorna positivo (cosa che a +questo punto può avvenire solo per opera di altro processo che rilascia il +semaforo con una chiamata a \func{sem\_post}) così che poi essa possa +decrementarlo con successo e proseguire. + +Si tenga presente che la funzione può sempre essere interrotta da un segnale, +nel qual caso si avrà un errore di \const{EINTR}; inoltre questo avverrà +comunque, anche qualora si fosse richiesta la gesione con la semantica BSD, +installando il gestore del suddetto segnale con l'opzione \const{SA\_RESTART} +(vedi sez.~\ref{sec:sig_sigaction}) per riavviare le \textit{system call} +interrotte. Della funzione \func{sem\_wait} esistono due varianti che consentono di gestire diversamente le modalità di attesa in caso di risorsa occupata, la prima di queste è \funcd{sem\_trywait}, che serve ad effettuare un tentativo di acquisizione senza bloccarsi; il suo prototipo è: -\begin{functions} - \headdecl{semaphore.h} - - \funcdecl{int sem\_trywait(sem\_t *sem)} - - Tenta di bloccare il semaforo \param{sem}. - - \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di - errore; nel quel caso \var{errno} assumerà gli stessi valori: - \begin{errlist} + +\begin{funcproto}{ +\fhead{semaphore.h} +\fdecl{int sem\_trywait(sem\_t *sem)} + +\fdesc{Tenta di bloccare un semaforo.} +} +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà uno dei valori: + \begin{errlist} \item[\errcode{EAGAIN}] il semaforo non può essere acquisito senza bloccarsi. - \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. - \end{errlist} + \item[\errcode{EINVAL}] l'argomento \param{sem} non indica un semaforo + valido. + \end{errlist} } -\end{functions} +\end{funcproto} La funzione è identica a \func{sem\_wait} ed se la risorsa è libera ha lo stesso effetto, vale a dire che in caso di semaforo diverso da zero la @@ -4514,77 +4564,88 @@ programma possa proseguire. La seconda variante di \func{sem\_wait} è una estensione specifica che può essere utilizzata soltanto se viene definita la macro \macro{\_XOPEN\_SOURCE} -ad un valore di 600 prima di includere \headfile{semaphore.h}, la funzione è -\funcd{sem\_timedwait}, ed il suo prototipo è: -\begin{functions} - \headdecl{semaphore.h} +ad un valore di almeno 600 o la macro \macro{\_POSIX\_C\_SOURCE} ad un valore +uguale o maggiore di \texttt{200112L} prima di includere +\headfile{semaphore.h}, la funzione è \funcd{sem\_timedwait}, ed il suo +prototipo è: - \funcdecl{int sem\_timedwait(sem\_t *sem, const struct timespec +\begin{funcproto}{ +\fhead{semaphore.h} +\fdecl{int sem\_timedwait(sem\_t *sem, const struct timespec *abs\_timeout)} - - Blocca il semaforo \param{sem}. - - \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di - errore; nel quel caso \var{errno} assumerà gli stessi valori: - \begin{errlist} - \item[\errcode{ETIMEDOUT}] è scaduto il tempo massimo di attesa. - \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. - \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale. - \end{errlist} + +\fdesc{Blocca un semaforo.} } -\end{functions} + +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale. + \item[\errcode{EINVAL}] l'argomento \param{sem} non indica un semaforo + valido. + \item[\errcode{ETIMEDOUT}] è scaduto il tempo massimo di attesa. + \end{errlist} +} +\end{funcproto} Anche in questo caso il comportamento della funzione è identico a quello di -\func{sem\_wait}, la sola differenza consiste nel fatto che con questa -funzione è possibile impostare tramite l'argomento \param{abs\_timeout} un -tempo limite per l'attesa, scaduto il quale la funzione ritorna comunque, -anche se non è possibile acquisire il semaforo. In tal caso la funzione -fallirà, riportando un errore di \errval{ETIMEDOUT}. - -La seconda funzione principale utilizzata per l'uso dei semafori è -\funcd{sem\_post}, che viene usata per rilasciare un semaforo occupato o, in -generale, per aumentare di una unità il valore dello stesso anche qualora non -fosse occupato;\footnote{si ricordi che in generale un semaforo viene usato - come indicatore di un numero di risorse disponibili.} il suo prototipo è: -\begin{functions} - \headdecl{semaphore.h} - - \funcdecl{int sem\_post(sem\_t *sem)} - - Rilascia il semaforo \param{sem}. - - \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di - errore; nel quel caso \var{errno} assumerà i valori: - \begin{errlist} - \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. - \end{errlist} +\func{sem\_wait}, ma è possibile impostare un tempo limite per l'attesa +tramite la struttura \struct{timespec} (vedi +fig.~\ref{fig:sys_timespec_struct}) puntata +dall'argomento \param{abs\_timeout}, indicato in secondi e nonosecondi a +partire dalla cosiddetta \textit{Epoch} (00:00:00, 1 January 1970 +UTC). Scaduto il limite la funzione ritorna anche se non è possibile acquisire +il semaforo fallendo con un errore di \errval{ETIMEDOUT}. + +La seconda funzione principale utilizzata per l'uso dei semafori è quella che +viene usata per rilasciare un semaforo occupato o, in generale, per aumentare +di una unità il valore dello stesso anche qualora non fosse occupato (si +ricordi che in generale un semaforo viene usato come indicatore di un numero +di risorse disponibili). Detta funzione è \funcd{sem\_post} ed il suo +prototipo è: + +\begin{funcproto}{ +\fhead{semaphore.h} +\fdecl{int sem\_post(sem\_t *sem)} + +\fdesc{Rilascia un semaforo.} } -\end{functions} + +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EINVAL}] l'argomento \param{sem} non indica un semaforo + valido. + \item[\errcode{EOVERFLOW}] si superato il massimo valore di un semaforo. + \end{errlist} +} +\end{funcproto} La funzione incrementa di uno il valore corrente del semaforo indicato dall'argomento \param{sem}, se questo era nullo la relativa risorsa risulterà sbloccata, cosicché un altro processo (o \itindex{thread} \textit{thread}) -eventualmente bloccato in una \func{sem\_wait} sul semaforo potrà essere +eventualmente bloccato in una \func{sem\_wait} sul semaforo possa essere svegliato e rimesso in esecuzione. Si tenga presente che la funzione è sicura \index{funzioni!sicure} per l'uso all'interno di un gestore di segnali (si ricordi quanto detto in sez.~\ref{sec:sig_signal_handler}). -Se invece di operare su un semaforo se ne vuole solamente leggere il valore, -si può usare la funzione \funcd{sem\_getvalue}, il cui prototipo è: -\begin{functions} - \headdecl{semaphore.h} - - \funcdecl{int sem\_getvalue(sem\_t *sem, int *sval)} - - Richiede il valore del semaforo \param{sem}. - - \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di - errore; nel quel caso \var{errno} assumerà i valori: - \begin{errlist} - \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. - \end{errlist} +Se invece di operare su un semaforo se ne volesse semplicemente leggere il +valore, si potrà usare la funzione \funcd{sem\_getvalue}, il cui prototipo è: + +\begin{funcproto}{ +\fhead{semaphore.h} +\fdecl{int sem\_getvalue(sem\_t *sem, int *sval)} + +\fdesc{Richiede il valore di un semaforo.} } -\end{functions} +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EINVAL}] l'argomento \param{sem} non indica un semaforo + valido. + \end{errlist} +} +\end{funcproto} La funzione legge il valore del semaforo indicato dall'argomento \param{sem} e lo restituisce nella variabile intera puntata dall'argomento @@ -4602,34 +4663,38 @@ essere già stato modificato al ritorno della funzione. Una volta che non ci sia più la necessità di operare su un semaforo se ne può terminare l'uso con la funzione \funcd{sem\_close}, il cui prototipo è: -\begin{functions} - \headdecl{semaphore.h} - - \funcdecl{int sem\_close(sem\_t *sem)} - - Chiude il semaforo \param{sem}. - - \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di - errore; nel quel caso \var{errno} assumerà i valori: - \begin{errlist} - \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. - \end{errlist} + +\begin{funcproto}{ +\fhead{semaphore.h} +\fdecl{int sem\_close(sem\_t *sem)} + +\fdesc{Chiude un semaforo.} } -\end{functions} - -La funzione chiude il semaforo indicato dall'argomento \param{sem}; questo -comporta che tutte le risorse che il sistema può avere assegnato al processo -nell'uso dello stesso vengono rilasciate. Questo significa che un altro -processo bloccato sul semaforo a causa della acquisizione da parte del -processo che chiama \func{sem\_close} potrà essere riavviato. - -Si tenga presente poi che come per i file all'uscita di un processo tutti i -semafori che questo aveva aperto vengono automaticamente chiusi; questo -comportamento risolve il problema che si aveva con i semafori del \textit{SysV - IPC} (di cui si è parlato in sez.~\ref{sec:ipc_sysv_sem}) per i quali le -risorse possono restare bloccate. Si tenga poi presente che, a differenza di -quanto avviene per i file, in caso di una chiamata ad \func{execve} tutti i -semafori vengono chiusi automaticamente. + +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EINVAL}] l'argomento \param{sem} non indica un semaforo + valido. + \end{errlist} +} +\end{funcproto} + +La funzione chiude il semaforo indicato dall'argomento \param{sem}, che non +potrà più essere utilizzato nelle altre funzioni. La chiusura comporta anche +che tutte le risorse che il sistema poteva avere assegnato al processo +nell'uso del semaforo vengono immediatamente rilasciate. Questo significa che +un eventuale altro processo bloccato sul semaforo a causa della acquisizione +dello stesso da parte del processo che chiama \func{sem\_close} potrà essere +immediatamente riavviato. + +Si tenga presente poi che come avviene per i file, all'uscita di un processo +anche tutti i semafori che questo aveva aperto vengono automaticamente chiusi. +Questo comportamento risolve il problema che si aveva con i semafori del +\textit{SysV IPC} (di cui si è parlato in sez.~\ref{sec:ipc_sysv_sem}) per i +quali le risorse possono restare bloccate. Si tenga infine presente che, a +differenza di quanto avviene per i file, in caso di una chiamata ad +\func{execve} tutti i semafori vengono chiusi automaticamente. Come per i semafori del \textit{SysV-IPC} anche quelli POSIX hanno una persistenza di sistema; questo significa che una volta che si è creato un @@ -4637,23 +4702,24 @@ semaforo con \func{sem\_open} questo continuerà ad esistere fintanto che il kernel resta attivo (vale a dire fino ad un successivo riavvio) a meno che non lo si cancelli esplicitamente. Per far questo si può utilizzare la funzione \funcd{sem\_unlink}, il cui prototipo è: -\begin{functions} - \headdecl{semaphore.h} - - \funcdecl{int sem\_unlink(const char *name)} - - Rimuove il semaforo \param{name}. - - \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di - errore; nel quel caso \var{errno} assumerà i valori: - \begin{errlist} + +\begin{funcproto}{ +\fhead{semaphore.h} +\fdecl{int sem\_unlink(const char *name)} + +\fdesc{Rimuove un semaforo.} +} + +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà uno dei valori: + \begin{errlist} \item[\errcode{EACCES}] non si hanno i permessi necessari a cancellare il semaforo. \item[\errcode{ENAMETOOLONG}] il nome indicato è troppo lungo. \item[\errcode{ENOENT}] il semaforo \param{name} non esiste. - \end{errlist} -} -\end{functions} + \end{errlist} +} +\end{funcproto} La funzione rimuove il semaforo indicato dall'argomento \param{name}, che prende un valore identico a quello usato per creare il semaforo stesso con @@ -4670,23 +4736,23 @@ dovrà porre la variabile che contiene l'indirizzo del semaforo in un tratto di memoria che sia accessibile a tutti i processi in gioco. La funzione che consente di inizializzare un semaforo anonimo è \funcd{sem\_init}, il cui prototipo è: -\begin{functions} - \headdecl{semaphore.h} - - \funcdecl{int sem\_init(sem\_t *sem, int pshared, unsigned int value)} - Inizializza il semaforo anonimo \param{sem}. - - \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di - errore; nel quel caso \var{errno} assumerà i valori: - \begin{errlist} +\begin{funcproto}{ +\fhead{semaphore.h} +\fdecl{int sem\_init(sem\_t *sem, int pshared, unsigned int value)} +\fdesc{Inizializza un semaforo anonimo.} +} + +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà uno dei valori: + \begin{errlist} \item[\errcode{EINVAL}] il valore di \param{value} eccede \const{SEM\_VALUE\_MAX}. \item[\errcode{ENOSYS}] il valore di \param{pshared} non è nullo ed il sistema non supporta i semafori per i processi. - \end{errlist} -} -\end{functions} + \end{errlist} +} +\end{funcproto} La funzione inizializza un semaforo all'indirizzo puntato dall'argomento \param{sem}, e come per \func{sem\_open} consente di impostare un valore @@ -4709,33 +4775,32 @@ potrà essere ottenuto direttamente sia con \func{shmget} (vedi sez.~\ref{sec:ipc_sysv_shm}) che con \func{shm\_open} (vedi sez.~\ref{sec:ipc_posix_shm}), oppure, nel caso che tutti i processi in gioco abbiano un genitore comune, con una mappatura anonima con \func{mmap} (vedi -sez.~\ref{sec:file_memory_map}),\footnote{si ricordi che i tratti di memoria - condivisa vengono mantenuti nei processi figli attraverso la funzione - \func{fork}.} a cui essi poi potranno accedere. +sez.~\ref{sec:file_memory_map}) a cui essi poi potranno accedere (si ricordi +che i tratti di memoria condivisa vengono mantenuti nei processi figli +attraverso la funzione \func{fork}). Una volta inizializzato il semaforo anonimo con \func{sem\_init} lo si potrà utilizzare nello stesso modo dei semafori normali con \func{sem\_wait} e \func{sem\_post}. Si tenga presente però che inizializzare due volte lo stesso semaforo può dar luogo ad un comportamento indefinito. -Una volta che non si intenda più utilizzare un semaforo anonimo questo può -essere eliminato dal sistema; per far questo di deve utilizzare una apposita +Qualora non si intenda più utilizzare un semaforo anonimo questo può essere +eliminato dal sistema; per far questo di deve utilizzare una apposita funzione, \funcd{sem\_destroy}, il cui prototipo è: -\begin{functions} - \headdecl{semaphore.h} - - \funcdecl{int sem\_destroy(sem\_t *sem)} - Elimina il semaforo anonimo \param{sem}. - - \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di - errore; nel quel caso \var{errno} assumerà i valori: - \begin{errlist} - \item[\errcode{EINVAL}] il valore di \param{value} eccede - \const{SEM\_VALUE\_MAX}. - \end{errlist} +\begin{funcproto}{ +\fhead{semaphore.h} +\fdecl{int sem\_destroy(sem\_t *sem)} +\fdesc{Elimina un semaforo anonimo.} } -\end{functions} +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EINVAL}] l'argomento \param{sem} non indica un semaforo + valido. + \end{errlist} +} +\end{funcproto} La funzione prende come unico argomento l'indirizzo di un semaforo che deve essere stato inizializzato con \func{sem\_init}; non deve quindi essere @@ -4789,7 +4854,7 @@ segmento di memoria condivisa e del semaforo (il default scelto è Come prima istruzione (\texttt{\small 10}) si è provveduto ad installare un gestore di segnale che consentirà di effettuare le operazioni di pulizia (usando la funzione \func{Signal} illustrata in -fig.~\ref{fig:sig_Signal_code}), dopo di che (\texttt{\small 10--16}) si è +fig.~\ref{fig:sig_Signal_code}), dopo di che (\texttt{\small 12--16}) si è creato il segmento di memoria condivisa con la funzione \func{CreateShm} che abbiamo appena trattato in sez.~\ref{sec:ipc_posix_shm}, uscendo con un messaggio in caso di errore. @@ -4810,13 +4875,13 @@ permessi specificati dal terzo argomento, (indicante lettura e scrittura in notazione ottale). Infine il semaforo verrà inizializzato ad un valore nullo (il quarto argomento), corrispondete allo stato in cui risulta bloccato. -A questo punto (\texttt{\small 23}) si potrà inizializzare il messaggio posto +A questo punto (\texttt{\small 22}) si potrà inizializzare il messaggio posto nel segmento di memoria condivisa usando la stringa passata come argomento al programma. Essendo il semaforo stato creato già bloccato non ci si dovrà preoccupare di eventuali \itindex{race~condition} \textit{race condition} qualora il programma di modifica del messaggio venisse lanciato proprio in questo momento. Una volta inizializzato il messaggio occorrerà però -rilasciare il semaforo (\texttt{\small 25--28}) per consentirne l'uso; in +rilasciare il semaforo (\texttt{\small 24--27}) per consentirne l'uso; in tutte queste operazioni si provvederà ad uscire dal programma con un opportuno messaggio in caso di errore. @@ -4828,7 +4893,7 @@ controllo. Il primo passo (\texttt{\small 30--34}) è quello di acquisire (con semaforo ad inizio del ciclo; seguito (\texttt{\small 35--36}) dal tempo corrente. -\begin{figure}[!htbp] +\begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{\codesamplewidth} \includecodesample{listati/HandSigInt.c} @@ -4840,14 +4905,14 @@ corrente. \end{figure} Prima della stampa del messaggio invece si deve acquisire il semaforo -(\texttt{\small 31--34}) per evitare accessi concorrenti alla stringa da parte +(\texttt{\small 30--33}) per evitare accessi concorrenti alla stringa da parte del programma di modifica. Una volta eseguita la stampa (\texttt{\small 41}) il semaforo dovrà essere rilasciato (\texttt{\small 42--45}). Il passo finale (\texttt{\small 46}) è attendere per un secondo prima di eseguire da capo il ciclo. -Per uscire in maniera corretta dal programma sarà necessario interromperlo con -il break da tastiera (\texttt{C-c}), che corrisponde all'invio del segnale +Per uscire in maniera corretta dal programma sarà necessario fermarlo con una +interruzione da tastiera (\texttt{C-c}), che corrisponde all'invio del segnale \signal{SIGINT}, per il quale si è installato (\texttt{\small 10}) una opportuna funzione di gestione, riportata in fig.~\ref{fig:ipc_posix_sem_shm_message_server_handler}. La funzione è molto @@ -4855,7 +4920,7 @@ semplice e richiama le funzioni di rimozione sia per il segmento di memoria condivisa che per il semaforo, garantendo così che possa essere riaperto ex-novo senza errori in un futuro riutilizzo del comando. -\begin{figure}[!htbp] +\begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{\codesamplewidth} \includecodesample{listati/message_setter.c}