NOMBRE¶
fcntl - manipula el descriptor de fichero
SINOPSIS¶
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
DESCRIPCIÓN¶
fcntl realiza una de las diversas y variadas operaciones que se pueden
hacer sobre
fd. La operación en cuestión se determina
mediante
cmd.
Tratamiento de la bandera «cerrar al ejecutar»¶
- F_DUPFD
- Busca el descriptor de fichero disponible de menor número, mayor o
igual que arg, y lo convierte en una copia de fd. Esto es
diferente en dup2(2) que usa exactamente el descriptor
especificado.
Los descriptores antiguo y nuevo pueden usarse indistintamente. Ambos
comparten bloqueos (``locks''), indicadores de posición de ficheros
y opciones o banderas (``flags''); por ejemplo, si la posición del
fichero se modifica usando lseek en uno de los descriptores, la
posición del otro resulta modificada simultáneamente.
Sin embargo, los dos descriptores no comparten la bandera
close-on-exec («cerrar al ejecutar»). La bandera
close-on-exec de la copia está desactivada, lo que significa
que no se cerrará al ejecutar.
En caso de éxito, se devuelve el nuevo descriptor.
- F_GETFD
- Lee la bandera close-on-exec. Si el bit FD_CLOEXEC es 0, el
fichero permanecerá abierto tras exec, en caso contrario se
cerrará el fichero.
- F_SETFD
- Establece la bandera close-on-exec al valor especificado por el bit
FD_CLOEXEC de arg.
Banderas de situación de un fichero¶
Un descriptor de fichero posee ciertas banderas asociadas, inicializadas por
open(2) y posiblemente modificadas por
fcntl(2). Las banderas se
comparten entre copias (hechas con
dup(2),
fork(2), etc.) del
mismo descriptor de fichero.
Las banderas y su semántica están descritas en
open(2).
- F_GETFL
- Lee las banderas de un descriptor de fichero.
- F_SETFL
- De las banderas de un descriptor, establece la parte que se corresponde
con las banderas de situación de un fichero al valor especificado
por arg. Los restantes bits (modo de acceso, banderas de
creación de un fichero) de arg se ignoran. En Linux, esta
orden sólo puede cambiar las banderas O_APPEND, O_NONBLOCK, O_ASYNC
y O_DIRECT.
Bloqueos (candados) consultivos¶
F_GETLK,
F_SETLK y
F_SETLKW se usan para adquirir, liberar
y comprobar la existencia de bloqueos de registros (también conocidos
como bloqueos de segmentos de ficheros o regiones de ficheros). El tercer
argumento
lock es un puntero a una estructura que tiene, al menos, los
siguientes campos (en un orden indeterminado).
struct flock {
...
short l_type; /* Tipo de bloqueo: F_RDLCK,
F_WRLCK, F_UNLCK */
short l_whence; /* Cómo interpretar l_start:
SEEK_SET, SEEK_CUR, SEEK_END */
off_t l_start; /* Dirección de inicio del bloqueo */
off_t l_len; /* Número de bytes a bloquear */
pid_t l_pid; /* PID del proceso que bloquea nuestro candado
(sólo F_GETLK) */
...
};
Los campos
l_whence,
l_start y
l_len de esta estructura
especifican el rango de bytes que deseamos bloquear.
l_start es la
dirección inicial para el bloqueo y se interpreta relativa a: el inicio
del fichero (si
l_whence es
SEEK_SET); la posición actual
dentro del fichero (si
l_whence es
SEEK_CUR) o el fin del
fichero (si
l_whence es
SEEK_END). En los dos últimos
casos,
l_start puede ser un número negativo siempre que la
dirección inicial final no quede antes del principio del fichero.
l_len es un entero no negativo (aunque vea NOTAS más abajo) que
indica el número de bytes a bloquear. Los bytes que se encuentren
más allá del final del fichero se pueden bloquear, pero no los
bytes que se encuentren antes del inicio del fichero. El especificar 0 para
l_len tiene un significado especial: bloquear todos los bytes desde la
posición indicada por
l_whence y
l_start hasta el final
del fichero, sin importar cuánto crezca el fichero.
El campo
l_type se puede usar para colocar un bloqueo de lectura
(
F_RDLCK) o de escritura (
F_WDLCK) sobre un fichero. Varios
procesos pueden tener un bloqueo de lectura (bloqueo compartido) sobre una
región de fichero, pero sólo un proceso puede tener un bloqueo
de escritura (bloqueo exclusivo). Un bloqueo exclusivo excluye a todos los
demás bloqueos, tanto compartidos como exclusivos. Un único
proceso sólo puede tener un tipo de bloqueo sobre una región de
fichero; si se aplica un nuevo bloqueo a una región ya bloqueada, el
bloqueo existente se convierte al nuevo tipo de bloqueo. (Tales conversiones
pueden suponer dividir, reducir o fusionar un bloqueo existente si el rango de
bytes especificado por el nuevo bloqueo no coincide exactamente con el rango
del bloqueo existente).
- F_SETLK
- Adquiere un bloqueo (cuando l_type es F_RDLCK o
F_WRLCK) o libera un bloqueo (cunado l_type es
F_UNLCK) sobre los bytes especificados por los campos
l_whence, l_start y l_len de lock. Si otro
proceso mantiene ya un bloqueo que entra en conflicto con el nuevo, la
llamada devuelve -1 y asigna a errno el código de error
EACCES o EAGAIN.
- F_SETLKW
- Como F_SETLK, pero si ya hay un bloqueo sobre el fichero que entra
en conflicto con el nuevo, entonces espera a que el bloqueo se libere. Si
llega y se captura una señal mientras se está esperando, se
interrumpe la llamada y (tras terminar el manejador de señal)
regresa inmediatamente (devolviendo -1 y asignado a errno el valor
EINTR).
- F_GETLK
- Al entrar a esta llamada, lock describe un bloqueo que nos
gustaría colocar en un fichero. Si el bloqueo se pudiera colocar,
fcntl() realmente no lo colocará, pero devolverá
F_UNLCK en el campo l_type de lock y dejará
los otros campos de la estructura sin cambiar. Si uno o más
bloqueos incompatibles impidieran que este bloqueo se colocara, entonces
fcntl() devolverá detalles sobre uno de estos bloqueos en
los campos l_type, l_whence, l_start y l_len
de lock y asignará a l_pid el PID del proceso que
posee el bloqueo.
Para colocar un bloqueo de lectura, se debe abrir
fd para lectura. Para
colocar un bloqueo de escritura, se debe abrir
fd para escritura. Para
colocar ambos tipos de bloqueo, abra un fichero para lectura y escritura.
Además de ser eliminados por un
F_UNLCK explícito, los
bloqueos de registros se liberan automáticamente cuando un proceso
termina o cierra
cualquier descriptor de fichero que haga referencia a
un fichero sobre el que se mantienen los bloqueos. Esto está mal:
significa que un proceso puede perder los bloqueos sobre un fichero como
/etc/passwd o
/etc/mtab cuando, por alguna razón, una
función de biblioteca decida abrir, leer y cerrar el fichero.
Los bloqueos de registros no los hereda un hijo creado mediante
fork(2),
pero se conservan a través de
execve(2).
Debido al almacenamiento temporal que realiza
stdio(3), se debería
evitar el bloqueo de registros con rutinas de esa biblioteca; en su lugar, use
read(2) y
write(2).
Bloqueos obligatorios¶
(No POSIX.) Los bloqueos de registro anteriores pueden ser o bien consultivos o
bien obligatorios, y son consultivos por omisión. Para usar bloqueos
obligatorios, se debe habilitar dicho tipo de bloqueo en el sistema de
ficheros que contiene el fichero a bloquear (usando la opción "-o
mand" en
mount(8)) y en el propio fichero (deshabilitando el
permiso de ejecución para el grupo y activado el bit de permiso
set-GID).
Los bloqueos consultivos no son de obligado cumplimiento y sólo son
útiles entre procesos que cooperan. Los bloqueos obligatorios son
respetados por todos los procesos.
Manejo de señales¶
F_GETOWN,
F_SETOWN,
F_GETSIG y
F_SETSIG se utilizan
para gestionar las señales de disponibilidad de E/S:
- F_GETOWN
- Obtiene el ID de proceso o el grupo de procesos que actualmente recibe las
señales SIGIO y SIGURG para los eventos sobre el descriptor de
fichero fd.
Los grupos de procesos se devuelven como valores negativos.
- F_SETOWN
- Establece el ID de proceso o el grupo de procesos que recibirá las
señales SIGIO y SIGURG para los eventos sobre el descriptor de
fichero fd.
Los grupos de procesos se especifican mediante valores negativos. (Se puede
usar F_SETSIG para especificar una señal diferente a SIGIO).
Si activa la bandera de estado O_ASYNC sobre un descriptor de fichero
(tanto si proporciona esta bandera con la llamada open(2) como si
usa la orden F_SETFL de fcntl), se enviará una
señal SIGIO cuando sea posible la entrada o la salida sobre ese
descriptor de fichero.
El proceso o el grupo de procesos que recibirá la señal se
puede seleccionar usando la orden F_SETOWN de la función
fcntl. Si el descriptor de fichero es un conector (``socket''),
esto también seleccionará al recipiente de las
señales SIGURG que se entregan cuando llegan datos fuera de orden
(``out-of-band'', OOB) sobre el enchufe. (SIGURG se envía en
cualquier situación en la que select(2) informaría
que el conector tiene una "condición excepcional"). Si el
descriptor de fichero corresponde a un dispositivo de terminal, entonces
las señales SIGIO se envían al grupo de procesos en primer
plano de la terminal.
- F_GETSIG
- Obtiene la señal enviada cuando la entrada o la salida son
posibles. Un valor cero significa que se envía SIGIO. Cualquier
otro valor (incluyendo SIGIO) es la señal enviada en su lugar y en
este caso se dispone de información adicional para el manejador de
señal si éste se instala con SA_SIGINFO.
- F_SETSIG
- Establece la señal enviada cuando la entrada o la salida son
posibles. Un valor cero significa enviar la señal por defecto
SIGIO. Cualquier otro valor (incluyendo SIGIO) es la señal a enviar
en su lugar y en este caso se dispone de información adiciona para
el manejador de señal si éste se instala con SA_SIGINFO.
Usando F_SETSIF con un valor distinto de cero y asignando SA_SIGINFO para el
manejador de señal (vea sigaction(2)), se pasa
información extra sobre los eventos de E/S al manejador en la
estructura siginfo_t. Si el campo si_code indica que la
fuente es SI_SIGIO, el campo si_fd proporciona el descriptor de
fichero asociado con el evento. En caso contrario, no se indican
qué descriptores de ficheros hay pendientes y, para determinar
qué descriptores de fichero están disponibles para E/S,
debería usar los mecanismos usuales (select(2),
poll(2), read(2) con O_NONBLOCK activo, etc.).
Seleccionando una señal de tiempo real POSIX.1b (valor >=
SIGRTMIN), se pueden encolar varios eventos de E/S usando los mismos
números de señal. (El encolamiento depende de la memoria
disponible). Se dispone de información extra si se asigna
SA_SIGINFO al manejador de señal, como antes.
Usando estos mecanismos, un programa puede implementar E/S totalmente
asíncrona, sin usar
select(2) ni
poll(2) la mayor parte
del tiempo.
El uso de
O_ASYNC,
F_GETOWN y
F_SETOWN es específico
de Linux y BSD.
F_GETSIG y
F_SETSIG son específicos de
Linux. POSIX posee E/S asíncrona y la estructura
aio_sigevent
para conseguir cosas similares; éstas también están
disponibles en Linux como parte de la biblioteca de C de GNU (GNU C Library,
Glibc).
Arrendamientos¶
F_SETLEASE y
F_GETLEASE (Linux 2.4 y posteriores) se usan
(respectivamente) para establecer y obtener la configuración actual del
arrendamiento del proceso invocador sobre el fichero referenciado por
fd. Un arrendamiento de fichero proporciona un mecanismo por medio del
cual al proceso que posee el arrendamiento (el "arrendatario") se le
notifica (mediante el envío de una señal) cuándo otro
proceso (el "competidor") intenta abrir (
open(2)) o truncar
(
truncate(2)) ese fichero.
- F_SETLEASE
- Establece o elimina un arrendamiento de fichero según qué
valor de los siguientes se especifique en el entero arg:
- F_RDLCK
- Obtiene un arrendamiento de lectura. Esto hará que se nos informe
cuando otro proceso abra el fichero para escribir en él o cuando lo
trunque.
- F_WRLCK
- Obtiene un arrendamiento de escritura. Esto hará que se nos informe
cuando otro proceso abra el fichero (para leer o escribir) o lo trunque.
Sólo se puede colocar un arrendamiento de escritura en un fichero
cuando ningún otro proceso lo tenga abierto en ese momento.
- F_UNLCK
- Elimina nuestro arrendamiento del fichero.
Un proceso sólo puede tener un tipo de arrendamiento sobre un fichero.
Los arrendamientos sólo se pueden obtener para ficheros regulares. Un
proceso normal sólo puede obtener un arrendamiento sobre un fichero
cuyo UID coincida con el UID de sistema de ficheros del proceso.
- F_GETLEASE
- Indica qué tipo de arrendamiento tenemos sobre el fichero
referenciado por fd devolviendo F_RDLCK, F_WRLCK o
F_UNLCK, lo que indica, respectivamente, que el proceso invocador
posee un arrendamiento de lectura, de escritura o que no posee
arrendamiento alguno sobre el fichero. (El tercer argumento de
fcntl() se omite.)
Cuando el competidor realiza un
open() o
truncate() que entra en
conflicto con un arrendamiento establecido mediante
F_SETLEASE, el
núcleo bloquea la llamada al sistema (a menos que se especifique la
opción
O_NONBLOCK en
open(), en cuyo caso la llamada
regresa inmediatamente con el error
EWOULDBLOCK). El núcleo
entonces notifica al arrendatario enviándole una señal (SIGIO
por omisión). El arrendatario debe responder a la recepción de
esta señal haciendo cualquier limpieza que sea necesaria para preparar
el fichero para que sea accedido por otro proceso (por ejemplo, vaciando los
buffers en caché) y entonces eliminar su arrendamiento ejecuntado una
orden
F_SETLEASE que especifique en
arg el valor
F_UNLCK.
Si el arrendatario no libera el arrendamiento antes del número de
segundos especificado en
/proc/sys/fs/lease-break-time y la llamada al
sistema del competidor sigue bloqueada (es decir, el competidor no
especificó
O_NONBLOCK en su llamada
open() y la llamada
al sistema no ha sido interrumpida por un manejador de señal), entonces
el núcleo pone fin al arrendamiento del arrendatario por la fuerza.
Una vez que se ha eliminado voluntariamente o por la fuerza el arrendamiento, y
suponiendo que el competidor no ha desbloqueado su llamada al sistema, el
núcleo permite continuar a la llamada al sistema del competidor.
La señal por omisión que se usa para informar al arrendatario es
SIGIO, pero se puede cambiar usando la orden
F_SETSIG de
fcntl
(). Si se ejecuta una orden
F_SETSIG (incluso una que especifique
SIGIO) y el manejador de señal se establece usando SA_SIGINFO, el
manejador recibirá una estructura
siginfo_t como su segundo
argumento y el campo
si_fd de este argumento contendrá el
descriptor del fichero arrendado que ha sido accedido por otro proceso. (Esto
es útil si el invocador tiene arrendamientos para varios ficheros).
Notificación de cambios en ficheros y directorios¶
- F_NOTIFY
- (Linux 2.4 y posteriores) Produce una notificación cuando cambia el
directorio referenciado por fd o cualquiera de los ficheros que
contiene. Los eventos a notificar se indican en arg, que es una
máscara de bits que se especifica mediante un O-lógico de
cero o más de los siguientes bits:
| Bit |
Descripción (evento en el directorio) |
|
|
|
|
|
|
| DN_ACCESS |
Se ha accedido a un fichero (read, pread, readv) |
|
|
| DN_MODIFY |
Se ha modificado un fichero (write, pwrite, |
|
|
|
writev, truncate, ftruncate) |
|
|
| DN_CREATE |
Se ha creado un fichero (open, creat, mknod, |
|
|
|
mkdir, link, symlink, rename) |
|
|
| DN_DELETE |
Se ha borrando un fichero (unlink, rename a |
|
|
|
otro directorio, rmdir) |
|
|
| DN_RENAME |
Se ha cambiado el nombre de un fichero de este |
|
|
|
directorio (rename) |
|
|
| DN_ATTRIB |
Se han cambiado los atributos de un fichero |
|
|
|
(chown, chmod, utime[s]) |
|
|
(Para obtener estas definiciones, se debe define la macro _GNU_SOURCE antes
de incluir <fcntl.h>.)
Las notificaciones de directorio normalmente se reciben una única vez
(son de tipo ``one-shot'') y la aplicación debe volver a hacer el
registro para recibir notificaciones adicionales. Otra posibilidad es
incluir DN_MULTISHOT en arg, en cuyo caso la
notificación se producirá hasta que se elimine
explícitamente. Una serie de llamadas que especifican
DN_MULTISHOT es acumulativa, con los eventos en arg
añadiéndose al conjunto ya monitorizado. Para desactivar la
notificación de todos los eventos, haga una llamada F_NOTIFY
especificando 0 en arg.
La notificación se produce mediante el envío de una
señal. La señal por omisión es SIGIO, pero se puede
cambiar usando la orden F_SETSIG de fcntl(). En el segundo
caso, el manejador de señal recibe una estructura siginfo_t
como su segundo argumento (si el manejador se estableció usando
SA_SIGINFO) y el campo si_fd de esta estructura contiene el
descriptor de fichero que produjo la notificación (útil
cuando se establecen notificaciones en varios directorios).
Especialmente cuando se usa DN_MULTISHOT, se debería usar una
señal POSIX.1b de tiempo real para notificación, de tal
manera que se pueden encolar multiples notificaciones.
VALOR DEVUELTO¶
Para una llamada con éxito, el valor devuelto depende de la
operación:
- F_DUPFD
- El nuevo descriptor.
- F_GETFD
- Valor de la bandera.
- F_GETFL
- Valor de las banderas.
- F_GETOWN
- Valor del propietario del descriptor.
- F_GETSIG
- Valor de la señal enviada cuando la lectura o la escritura son
posibles o cero para el comportamiento tradicional con SIGIO.
- Para cualquier otra orden
- Cero.
En caso de error el valor devuelto es -1 y se asigna a
errno un valor
apropiado.
ERRORES¶
- EACCESS o EAGAIN
- Se ha prohibido la operación debido a bloqueos mantenidos por otros
procesos. O se ha prohibido la operación porque el fichero ha sido
proyectado (``mapped'') en memoria por otro proceso.
- EDEADLK
- Se ha detectado que la orden F_SETLKW especificada
provocaría un interbloqueo.
- EFAULT
- lock está fuera de su espacio de direcciones accesible.
- EBADF
- fd no es un descriptor de fichero abierto o la orden era
F_SETLK o F_SETLKW y el modo de apertura del descriptor de
fichero no coincide con el tipo de bloqueo solicitado.
- EINTR
- Para F_SETLKW, la orden ha sido interrumpida por una señal.
Para F_GETLK y F_SETLK, la orden ha sido interrumpida por
una señal antes de que el bloqueo se haya comprobado o adquirido.
Esto ocurre con más probabilidad al poner un bloqueo en un fichero
remoto (por ejemplo, un bloqueo sobre NFS) pero algunas veces puede
ocurrir localmente.
- EINVAL
- Para F_DUPFD, arg es negativo o mayor que el valor
máximo permitido. Para F_SETSIG, arg no es un
número de señal permitido.
- EMFILE
- Para F_DUPFD, el proceso ya ha llegado al número
máximo de descriptores de ficheros abiertos.
- ENOLCK
- Demasiados bloqueos de segmentos abiertos, la tabla de bloqueos
está llena o ha fallado un protocolo de bloqueos remoto (por
ejemplo, un bloqueo sobre NFS).
- EPERM
- Se ha intentado quitar la bandera O_APPEND sobre un fichero que
tiene activo el atributo de ``sólo añadir''
(append-only).
OBSERVACIONES¶
Los errores devueltos por
dup2 son distintos de aquéllos dados por
F_DUPFD.
Desde el núcleo 2.0, no hay interacción entre los tipos de bloqueo
colocados por
flock(2) y
fcntl(2).
POSIX 1003.1-2001 permite que
l_len sea negativo. (Y si lo es, el
intervalo descrito por el bloqueo cubre los bytes desde
l_start+
l_len hasta
l_start-1 inclusive.) Sin embargo,
para los núcleos actuales, la llamada al sistema de Linux devuelve
EINVAL en esta situación.
Otros sistemas tienen más campos en
struct flock como, por
ejemplo,
l_sysid. Evidentemente,
l_pid sólo no va a ser
muy útil si el proceso que posee el bloqueo puede residir en una
máquina diferente.
SVID, AT&T, POSIX, X/OPEN, BSD 4.3. Sólo las operaciones F_DUPFD,
F_GETFD, F_SETFD, F_GETFL, F_SETFL, F_GETLK, F_SETLK y F_SETLKW se especifican
en POSIX.1. F_GETOWN y F_SETOWN son BSD-ismos no aceptados en SVr4; F_GETSIG y
F_SETSIG son específicos de Linux.
F_NOTIFY,
F_GETLEASE y
F_SETLEASE son específicos de Linux. (Defina la macro
_GNU_SOURCE antes de incluir <fcntl.h> para obtener estas definiciones.)
Las banderas legales para F_GETFL/F_SETFL son aquéllas que acepta
open(2) y varían entre estos sistemas; O_APPEND, O_NONBLOCK,
O_RDONLY y O_RDWR son las que se mencionan en POSIX.1. SVr4 admite algunas
otras opciones y banderas no documentadas aquí.
SVr4 documenta las condiciones de error adicionales EIO, ENOLINK y EOVERFLOW.
VÉASE TAMBIÉN¶
dup2(2),
flock(2),
lockf(3),
open(2),
socket(2)
Vea también locks.txt, mandatory.txt y dnotify.txt en
/usr/src/linux/Documentation.