8.12 fcntl -- Le chiamate di sistema fcntl() e ioctl()

Disponibilità: Unix.

Questo modulo esegue controlli di file e controlli di I/O su descrittori di file. È un'interfaccia per le procedure Unix fcntl() e ioctl().

Tutte le funzioni in questo modulo richiedono un descrittore di file fd come loro primo parametro. Questo può essere un descrittore di file intero, come restituito da sys.stdin.fileno(), o un oggetto file, come lo stesso sys.stdin, che fornisce un metodo fileno() il quale restituisce un descrittore di file genuino.

Il modulo definisce le seguenti funzioni:

fcntl( fd, op[, arg])
Esegue l'operazione richiesta sul descrittore di file fd (gli oggetti file che forniscono un metodo fileno() vengono comunque accettati). L'operazione viene definita da op e dipende dal sistema operativo. Questi codici si trovano anche nel modulo fcntl. L'argomento arg è facoltativo ed il suo valore predefinito è l'intero di valore 0. Quando è presente, può assumere sia un valore intero che una stringa. Se l'argomento è assente o è un valore intero, il valore restituito da questa funzione è lo stesso della chiamata C fcntl(). Quando l'argomento è una stringa, rappresenta una struttura binaria, per esempio quella creata da struct.pack(). Il dato binario viene copiato in un buffer il cui indirizzo viene passato alla chiamata C fcntl(). Il valore restituito dopo una chiamata andata a buon fine è il contenuto del buffer, convertito in un oggetto stringa. La lunghezza della stringa restituita sarà la stessa dell'argomento arg. Questa lunghezza è limitata a 1024 byte. Se l'informazione restituita nel buffer dal sistema operativo è più grande di 1024 byte, molto probabilmente causerà una violazione della segmentazione oppure una corruzione di dati più subdola.

Se fcntl() fallisce, viene sollevata un'eccezione IOError.

ioctl( fd, op[, arg[, mutate_flag]])
Questa funzione è identica a fcntl(), a parte il fatto che le operazioni vengono tipicamente definite nel modulo libreria termios e la gestione degli argomenti è persino un po' più complicata.

Il parametro arg può essere un intero, può essere assente (e quindi trattato come l'intero 0), piò essere un oggetto che supporta l'interfaccia buffer in sola lettura (simile ad una stringa di testo Python) oppure un oggetto che supporta l'interfaccia buffer in lettura e scrittura.

In tutti i casi tranne l'ultimo, il comportamento è identico a quello della funzione fcntl().

Se viene passato un buffer variabile, allora il comportamento di questa funzione verrà determinato dal valore del parametro mutate_flag.

Se è falso, la mutabilità del buffer verrà ignorata ed il comportamento sarà quello di un buffer in sola lettura, tranne che verrà evitato il limite sopra menzionato dei 1024 byte - finchè passate un buffer sufficientemente ampio da contenere ciò che il sistema operativo intende porvi, le cose dovrebbero funzionare.

Se mutate_flag è vero, allora il buffer viene (in effetti) passato alla chiamata di sistema sottostante ioctl(), l'ultimo codice restituito verrà passato alla chiamata Python ed i nuovi contenuti del buffer rifletterano l'azione della ioctl(). Questa è una flebile semplificazione, poiché se il buffer fornito è grande meno di 1024 byte, viene prima copiato in un buffer statico lungo 1024 bytes, poi passato alla ioctl() ed infine copiato di nuovo nel buffer di iniziale.

Se mutate_flag non viene fornito, allora in Python 2.3 il valore predefinito è falso. È in previsione di cambiare questo dettaglio nelle prossime versioni di Python: in 2.4, non fornire mutate_flag causerà un avvertimento, ma la funzione lavorerà normalmente, mentre in una versione successiva alla 2.5 il valore predefinito sarà true.

Un esempio:

>>> import array, fcntl, struct, termios, os
>>> os.getpgrp()
13341
>>> struct.unpack('h', fcntl.ioctl(0, termios.TIOCGPGRP, "  "))[0]
13341
>>> buf = array.array('h', [0])
>>> fcntl.ioctl(0, termios.TIOCGPGRP, buf, 1)
0
>>> buf
array('h', [13341])

flock( fd, op)
Compie l'operazione op di lock sul descrittore di file fd (gli oggetti file che forniscono un metodo fileno() vengono comunque accettati). Per i dettagli, vedete la pagina di manuale Unix flock(3)(.) Su alcuni sistemi questa funzione viene emulata tramite fcntl().

lockf( fd, operation, [len, [start, [whence]]])
Questo è essenzialmente un incapsulatore per le chiamate di locking fcntl(). fd è il descrittore del file su cui eseguire il lock o l'unlock mentre operation sarà uno dei seguenti valori:

Quando operation vale LOCK_SH o LOCK_EX, può anche essere un OR bit a bit con LOCK_NB per evitare blocchi su acquisizioni del lock. Se viene usato LOCK_NB ed il lock non può essere acquisito, verrà sollevata una IOError e l'eccezione avra' un attributo errno impostato ad EACCES o EAGAIN (a seconda del sistema operativo; per portabilità, controllate entrambi i valori). Almeno su alcuni sistemi, LOCK_EX può essere utilizzata solo se il descrittore di file si riferisce ad un file aperto in scrittura.

length è il numero di byte su cui effettuare il lock, start è il byte di offset da dove inizia il lock, relativo a whence, mentre whence funziona come con fileobj.seek(), nello specifico:

Il valore predefinito per start è 0, che significa partire dall'inizio del file. Il valore predefinito per lenght è 0 che significa effettuare il lock sino alla fine del file. Anche il valore predefinito per whence è 0.

Esempi (tutti su sistemi compatibili con SVR4):

import struct, fcntl

file = open(...)
rv = fcntl(file, fcntl.F_SETFL, os.O_NDELAY)

lockdata = struct.pack('hhllhh', fcntl.F_WRLCK, 0, 0, 0, 0, 0)
rv = fcntl.fcntl(file, fcntl.F_SETLKW, lockdata)

Notate che nel primo esempio il valore restituito nella variabile rv conterrà un valore intero; nel secondo esempio conterrà un valore stringa. Il lay-out della struttura della variabile lockdata dipende dal sistema -- quindi potrebbe essere meglio utilizzare la chiamata flock().

Vedete anche:

Modulo os:
La funzione os.open supporta le opzioni di locking ed è disponibile per una grande varietà di piattaforme rispetto alle funzioni lockf() e flock(), fornendo una capacità di locking dei file più indipendente dalla piattaforma.
Vedete Circa questo documento... per informazioni su modifiche e suggerimenti.