fcntl(2) Manipulation von Dateideskriptoren

SYNOPSIS

#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);

DESCRIPTION

fcntl führt eine von vielen unterschiedlichen Operationen auf dem File-Deskriptor fd aus. Die jeweilige Operation wird durch den Parameter cmd angegeben.

Schließen beim Ausführen (close-on-exec)

F_DUPFD
Sucht den verfügbaren File-Deskriptor mit der niedrigsten Nummer größer oder gleich arg und legt in fd eine Kopie davon an. Im Gegensatz dazu verwendet dup2(2) exakt den angegebenen Deskriptor.

Die alten und neuen Deskriptoren können ausgetauscht werden. Sie verwenden beide die gleichen Locks, Positionszeiger und Flags. Wenn beispielsweise die Dateiposition des einen Deskriptors mit lseek geändert wird, dann ist sie gleichzeitig auch beim anderen Deskriptor geändert.

Die beiden Deskriptoren teilen sich jedoch nicht das close-on-exec-Flag. In der Kopie ist das Flag abgeschaltet, so dass der neue Deskriptor beim Ausführen nicht geschlossen wird.

Bei Erfolg wird der neue Deskriptor zurückgegeben.

F_GETFD
Liest das close-on-exec-Flag. Ist das FD_CLOEXEC-Bit 0, so bleibt die Datei bei einem exec geöffnet, ansonsten wird sie geschlossen.
F_SETFD
Setzt das close-on-exec-Flag auf den Wert, des FD_CLOEXEC-Bits in arg.

Die Statusflags einer Datei

Jedem File-Deskriptor sind verschiedene Flags zugeordnet, die beim Aufruf von open(2) initialisiert und später durch fcntl(2) verändert werden können. Kopien eines File-Deskriptors, die mit dup2(2) oder fork(2), etc. erzeugt wurden, besitzen identische Flags.

Die einzelnen Flags und ihre Bedeutung sind in open(2) beschrieben.

F_GETFL
Liest die Flags des Deskriptors.
F_SETFL
Setzt die Flags des Deskriptors, die sich auf den Dateistatus beziehen, auf die in arg angegebenen Werte. Die weiteren Bits für Zugriffsmodus und Dateierzeugung werden ignoriert. Unter Linux lassen sich auf diese Weise nur die Flags O_APPEND, O_NONBLOCK, O_ASYNC und O_DIRECT verändern.

Empfehlendes (advisory) Locking

Man benutzt
F_GETLK, F_SETLK und F_SETLKW, um Locks für bestimmte Segmente in einer Datei anzufordern, aufzugeben oder zu testen. Das dritte Argument lock verweist auf eine Struktur, die zumindest die folgenden Einträge enthält (die Reihenfolge ist nicht festgelegt):

struct flock {
    ...
    short l_type;    /* Typ des Locks: F_RDLCK,
                        F_WRLCK, F_UNLCK */
    short l_whence;  /* Wie ist l_start zu verstehen:
                        SEEK_SET, SEEK_CUR, SEEK_END */
    off_t l_start;   /* Startposition des Locks */
    off_t l_len;     /* Lock für l_len Bytes anfordern */
    pid_t l_pid;     /* PID des Prozesses, der das Lock blockiert
                        (nur F_GETLK) */
    ...
};

Die Einträge l_whence, l_start, und l_len bestimmen den Bereich, für den das Lock angefordert werden soll. l_start gibt den Anfang des Bereichs an, und zwar relativ zum Anfang der Datei (falls l_whence auf SEEK_SET gesetzt ist), relativ zur aktuellen Position in der Datei (falls l_whence auf SEEK_CUR gesetzt ist), bzw. relativ zum Dateiende (falls l_whence auf SEEK_END gesetzt ist). In den beiden zuletzt genannten Fällen kann l_start auch negativ sein, sofern es dadurch nicht über den Beginn der Datei hinaus verweist. Das Lock wird für die in l_len angegebene Anzahl von Bytes angefordert. Sie darf nicht negativ sein. (Man beachte jedoch auch die ANMERKUNGEN unten.) Der Bereich darf über das Dateiende hinausweisen, nicht aber vor den Dateianfang. Der Wert 0 hat eine spezielle Bedeutung: So fordert man ein Lock an, das an der durch l_whence und l_start bestimmten Position beginnt und bis zum Dateiende reicht, gleichgültig auf welche Größe die Datei noch anwächst.

Der Eintrag l_type wird benutzt, um entweder ein Lock für lesenden (F_RDLCK) oder schreibenden (F_WDLCK) Zugriff auf die Datei anzufordern. Beliebig viele Prozesse dürfen ein Lock zum Lesen besitzen (geteiltes Lock), aber nur ein Prozess gleichzeitig darf ein Lock zum Schreiben erhalten (ausschließendes Lock). Ein ausschließendes Lock schließt sämtliche anderen Locks aus, also sowohl lesende, als auch andere schreibende. Ein einzelner Prozess darf für ein bestimmtes Dateisegment nur einen einzigen Typ von Lock besitzen; wird ein neues Lock für einen Bereich angefordert, für den bereits ein anderes Lock existiert, so wird das bestehende Lock auf den Typ des neuen Locks umgestellt. (Stimmen die Bereiche der beiden Locks nicht exakt überein, können sie dadurch aufgeteilt, verkleinert oder mit anderen Locks vereinigt werden.)

F_SETLK
Fordert ein Lock an, falls l_type auf F_RDLCKoderF_WRLCK gesetzt ist. Gibt ein Lock auf, falls l_type auf F_UNLCK gesetzt ist. Der angeforderte Bereich wird durch die Einträge l_whence, l_start und l_len in lock angegeben. Falls ein anderer Prozess ein konkurrierendes Lock hält, gibt dieser Aufruf -1 zurück und setzt errno auf EACCESS oder EAGAIN.
F_SETLKW
Unterscheidet sich nur dann von F_SETLK, wenn ein anderer Prozess ein konkurrierendes Lock hält. In diesem Fall wartet F_SETLKW auf die Freigabe. Der Aufruf kann durch ein Signal unterbrochen werden. Dann wird zunächst der Signalhandler abgearbeitet. Direkt im Anschluss wird fcntl() mit Rückgabewert -1 beendet und errno auf EINTR gesetzt.
F_GETLK
Beim Aufruf beschreibt lock ein zu testendes Lock auf eine Datei. Könnte es erfolgreich gesetzt werden, so setzt fcntl() den Eintrag l_type in lock auf F_UNLCK und lässt die übrigen Felder unverändert. Sind bereits ein oder mehrere konkurrierende Locks gesetzt, werden die Felder l_type, l_whence, l_start und l_len in lock mit Informationen über eines der Locks gefüllt. Die PID des zugehörigen Prozesses wird in l_pid zurückgegeben.

Um ein Lock für Lesezugriff anzufordern, muss der Deskriptor zum Lesen geöffnet sein. Ein Lock für Schreibzugriff erfordert, dass der Deskriptor zum Schreiben geöffnet ist. Ist der Deskriptor zum Lesen und Schreiben geöffnet, können beliebige Locks angefordert werden.

Zusätzlich zum expliziten F_UNLCK werden die Locks auch dann aufgegeben, wenn der Prozess endet oder wenn er einen beliebigen Deskriptor der Datei schließt, auf die die Locks verweisen. Das ist schlecht: Es bedeutet, dass ein Prozess beispielsweise seine Locks auf /etc/passwd oder /etc/mtab verlieren kann, wenn eine Bibliotheksfunktion aus irgendeinem Grund die Datei öffnen und schließen muss.

Die Locks werden nicht über fork(2), wohl aber über execve(2) hinweg weitergereicht. Aufgrund der gepufferten Ein-/Ausgabe in der stdio(3)-Bibliothek sollten ihre Funktionen nicht zusammen mit Locks verwendet werden. Statt dessen sind read(2) und write(2) zu benutzen.

Verpflichtende (mandatory) Locks

(Nicht im POSIX-Standard spezifiziert.) Die oben beschriebenen Locks können sowohl empfehlend oder verpflichtend verwendet werden. Ohne zusätzliche Schritte sind sie empfehlend. Um verpflichtende Locks verwenden zu können, müssen sie für ein Dateisystem mit der Option "-o mand" beim Aufruf von mount(8) zugelassen und zusätzlich für jede Datei aktiviert werden (indem in den Zugriffsrechten für die Gruppe das Ausführungsbit gelöscht und das Set-GID-Bit gesetzt wird).

Empfehlende Locks werden nicht erzwungen, sondern sind nur nützlich zwischen kooperierenden Prozessen. Verpflichtende Locks werden für sämtliche Prozesse erzwungen.

Signalkontrolle

F_GETOWN, F_SETOWN, F_GETSIG und F_SETSIG werden benutzt, um Signale zu kontrollieren, die die Verfügbarkeit von Ein- und Ausgaben anzeigen:
F_GETOWN
Gibt die PID (oder Prozessgruppen-ID) eines Prozesses zurück der im Augenblick ein SIGIO- oder SIGURG-Signal für ein Ereignis auf dem Deskriptor fd erhält. Prozessgruppen werden als negative Werte zurückgegeben.
F_SETOWN
Setzt die PID (oder Prozessgruppen-ID) für einen Prozess, der bei Ereignissen auf dem Deskriptor fd ein SIGIO- oder SIGURG-Signal übermittelt bekommen soll. Prozessgruppen werden als negative Werte angegeben. (Mit Hilfe von F_SETSIG kann auch ein anderes Signal als SIGIO angefordert werden.)

Das SIGIO-Signal wird ausgesandt, falls über den Dateideskriptor neue Eingabedaten gelesen oder weitere Ausgabedaten geschrieben werden können. Als Voraussetzung dafür muss weiterhin das Statusflag O_ASYNC für den Deskriptor gesetzt sein (entweder als Flag beim Aufruf von open(2) oder später über das Kommando F_SETFL von fcntl).

Welcher Prozess oder welche Prozessgruppe das Signal erhält, wird mit Hilfe des Kommandos F_SETOWN von fcntl gesteuert. Handelt es sich bei dem Deskriptor um einen Socket, legt der Aufruf gleichzeitig auch den Empfänger von SIGURG-Signalen fest. Derartige Signale werden ausgesandt, wenn ein Paket außerhalb der normalen Reihenfolge eintrifft. (SIGURG wird immer dann ausgesandt, wenn select(2) anzeigen würde, dass sich der Socket in einem "außergewöhnlichen Zustand" befindet.) Bezeichnet der Deskriptor ein Dateneingabegerät, wird das SIGIO-Signal an die zugehörige Vordergrundprozessgruppe geschickt. geschickt.

F_GETSIG
Gibt die Nummer des Signals zurück, das verwendet wird, um Ein- und Ausgabeereignisse anzuzeigen. Ein Wert von null bedeutet, dass SIGIO benutzt wird. Jeder andere Wert (einschließlich SIGIO) entspricht der verwendeten Signalnummer. In diesem Fall können weitere Informationen über die Signalverarbeitungsroutine abgerufen werden, sofern sie mit SA_SIGINFO installiert wurde.
F_SETSIG
Setzt die Nummer des Signals, das verwendet wird, um Ein- und Ausgabeereignisse anzuzeigen. Ein Wert von null bedeutet, dass SIGIO benutzt wird. Jeder andere Wert (einschließlich SIGIO) bezeichnet die verwendende Signalnummer. In diesem Fall können weitere Informationen über die Signalverarbeitungsroutine abgerufen werden, sofern sie mit SA_SIGINFO installiert wurde.

Durch Kombination von F_SETSIG mit von Null verschiedenen Werten und SA_SIGINFO für die Signalverarbeitungsroutine (siehe sigaction(2)), erhält die Routine in der Struktur siginfo_t Zusatzinformationen über das Ein-/Ausgabeereignis. Zeigt das Feld si_code an, dass die Quelle SI_SIGIO ist, enthält Feld si_fd den zugehörigen Dateideskriptor. Andernfalls stehen keine weiteren Informationen zur Verfügung, an welchem Deskriptor Ein-/Ausgaben anliegen, und man sollte auf die gewöhnlichen Mechanismen (select(2), poll(2), read(2) in Verbindung mit O_NONBLOCK, etc.) zurückgreifen, um sie zu ermitteln.

Wird ein POSIX.1b-Echtzeitsignal (Signalnummer >= SIGRTMIN) ausgewählt, können mehrere Ein-/Ausgabeereignisse unter derselben Signalnummer aufgereiht werden. (Das Aufreihen hängt ab vom verfügbaren Speicher.) Wie oben stehen auch hier weitere Informationen bereit, falls SA_SIGINFO für die Signalverarbeitungsroutine gesetzt ist.

Mit Hilfe dieser Mechanismen ist es möglich, voll asynchrone Ein-/Ausgabe zu implementieren, ohne signifikant auf select(2) oder poll(2) zurückzugreifen.

Die Verwendung von O_ASYNC, F_GETOWN, F_SETOWN ist spezifisch für BSD und Linux. F_GETSIG und F_SETSIG sind Linux-spezifisch. Ähnliches lässt sich im Rahmen des POSIX-Standards durch asynchrone Ein-/Ausgabe und die Struktur aio_sigevent erreichen; sie sind auch unter Linux als Teil der GNU-C-Bibliothek (glibc) verfügbar.

Leases

F_SETLEASE und F_GETLEASE (seit Linux 2.4) werden benutzt, um die aktuellen Einstellungen eines Leases zu setzen beziehungsweise abzufragen, das der aufrufende Prozess auf die durch den Deskriptor fd beschriebene Datei besitzt. Mittels eines Dateileases kann sich ein Prozess (der Lease-Inhaber) durch ein Signal benachrichtigen lassen, falls ein anderer Prozess (der Mitbewerber) versucht, mit open(2) oder truncate(2) auf die Datei zuzugreifen.
F_SETLEASE
Setzt oder entfernt ein Dateilease, abhängig vom Wert in arg:

F_RDLCK
Erwirbt ein lesendes Lease. Der Prozess erhält dann Nachricht, sobald ein anderer Prozess die Datei für Schreibzugriff öffnet oder sie verkürzen will.
F_WRLCK
Erwirbt ein schreibendes Lease. Der Prozess erhält Nachricht, sobald ein anderer Prozess die Datei öffnet (für Schreib- oder Lesezugriff) oder sie verkürzen will. Ein schreibendes Lease kann nur erworben werden, kein anderer Prozess die Datei augenblicklich geöffnet hat.
F_UNLCK
Entfernt das Lease von der Datei.

Ein Prozess kann nur einen Typ von Lease auf eine bestimmte Datei besitzen.

Leases können nur für gewöhnliche Dateien erworben werden. Ein Prozess ohne Sonderprivilegien darf nur Leases auf Dateien erwerben, deren Dateisystem-UID der UID des Prozesses entspricht.

F_GETLEASE
Zeigt den Typ des Leases an für die durch den Deskriptor fd beschriebene Datei. Der Rückgabewert ist F_RDLCK, F_WRLCK oder F_UNLCK, je nachdem, ob der Prozess ein lesendes, schreibendes oder kein Lease auf die Datei besitzt. (Das dritte Argument zu fcntl() wird ausgelassen.)

Wenn der Mitbewerber einen open()- oder truncate()-Aufruf ausführt, der mit dem über F_SETLEASE erworbenen Lease in Konflikt steht, wird der Systemaufruf durch den Kernel angehalten. (Ausgenommen, der hat beim Öffnen der Datei mit open() das Flag O_NONBLOCK angegeben. In diesem Fall kehrt der Aufruf sofort mit dem Fehler EWOULDBLOCK zurück.) Der Kernel benachrichtigt den Lease-Inhaber durch ein Signal (für gewöhnlich SIGIO). Der Lease-Inhaber sollte daraufhin alle nötigen Aufräumarbeiten veranlassen (beispielsweise zwischengespeicherte Daten schreiben), um die Datei auf den Zugriff durch einen anderen Prozess vorzubereiten und anschließend sein Lease entfernen, indem er das Kommando F_SETLEASE mit F_UNLCK in arg ausführt.

Versäumt es der Inhaber, das Lease innerhalb der in /proc/sys/fs/lease-break-time genannten Anzahl von Sekunden zu entfernen, dann bricht der Kernel gewaltsam das Lease. Das gilt nicht, falls der Systemaufruf des Mitbewerbers bereits zuvor abgebrochen worden ist, das heißt, wenn der Mitbewerber die Datei mit O_NONBLOCK geöffnet hatte oder in der Zwischenzeit ein Signal empfangen hat.

Sobald das Lease freiwillig oder gewaltsam entfernt wurde und falls der Systemaufruf des Mitbewerbers nach wie vor blockiert ist, gibt der Kernel den Aufruf nun wieder frei.

SIGIO ist das Standardsignal, mit dem der Lease-Inhaber benachrichtigt wird. Es kann jedoch mit Hilfe des Kommandos F_SETSIG für fcntl() verändert werden. Wird ein F_SETSIG-Kommando ausgeführt (selbst eines, das SIGIO angibt) und wurde die Signalverarbeitungsroutine mit SA_SIGINFO angelegt, dann erhält die Routine als zweites Argument die Struktur siginfo_t übergeben. Deren Eintrag si_fd enthält den Deskriptor auf die geleaste Datei, auf die ein anderer Prozess zugreifen will. (Das ist dann sinnvoll, wenn der Prozess Leases auf mehrere Dateien besitzt.)

Benachrichtigungen über Veränderungen an Dateien und Verzeichnissen

F_NOTIFY
(seit Linux 2.4) Erteilt eine Nachricht, sobald ein durch Deskriptor fd beschriebenes Verzeichnis oder eine der enthaltenen Dateien verändert wird. Die mitzuteilenden Ereignisse sind in arg zu bestimmen, und zwar als Bitmaske, gebildet aus bitweisem Oder von keinem oder beliebig vielen der folgenden Bits:

BitBeschreibung (Ereignis im Verzeichnis)
DN_ACCESS
pread, readv)
DN_MODIFYEine Datei wurde verändert (write,
pwrite, writev, truncate, ftruncate)
DN_CREATEEine Datei wurde erstellt (open, creat,
mknod, mkdir, link, symlink, rename)
DN_DELETEEine Datei wurde entfernt (unlink,
Umbenennen in ein anderes Verzeichnis, rmdir)
DN_RENAMEEine Datei in diesem Verzeichnis wurde
umbenannt (rename)
DN_ATTRIBDie Attribute eine Datei wurden verändert
(chown, chmod, utime[s])

(Um diese Definitionen zu erhalten, muss vor Einbinden von <fcntl.h> das Makro _GNU_SOURCE definiert sein.)

Normalerweise handelt es sich um "Einweg"-Benachrichtigungen, so dass die Anwendung sich für jede weitere Mitteilung stets neu registrieren muss. Alternativ dazu kann DN_MULTISHOT angegeben werden, und die Benachrichtigungen werden solange gesendet, bis sie explizit abbestellt werden.

Eine Serie von F_NOTIFY-Anforderungen ist kumulativ, die Ereignisse in arg werden zu den bereits angeforderten hinzugefügt. Um Benachrichtigungen über sämtliche Ereignisse abzubestellen, ist F_NOTIFY mit 0 als arg auszuführen.

Benachrichtigt wird duch Übermittlung eines Signals. Das Standardsignal ist SIGIO, es kann jedoch durch das F_SETSIG-Kommando zu fcntl() geändert werden. In diesem Fall erhält die Signalverarbeitungsroutine als zweites Argument die Struktur siginfo_t übergeben (sofern die Routine mit SA_SIGINFO angelegt wurde). Deren Eintrag si_fd enthält den Dateideskriptor, der die Benachrichtigung ausgelöst hat (nützlich, falls mehrere Verzeichnisse überwacht werden).

Speziell in Verbindung mit DN_MULTISHOT sollten POSIX.1b-Echtzeitsignale für die Benachrichtigung verwendet werden, so dass mehrere Nachrichten aufgereiht werden können.

RÜCKGABEWERTE

Für einen erfolgreichen Aufruf hängt der Rückgabewert von der ausgeführten Operation ab:
F_DUPFD
Der neue File-Deskriptor.
F_GETFD
Der Inhalt des Flags.
F_GETFL
Der Inhalt der Flags.
F_GETOWN
Der Besitzer des Deskriptors.
F_GETSIG
Wert des Signals zur Anzeige möglicher Ein-/Ausgabe oder null für gewöhnliches SIGIO-Verhalten.
Alle anderen Kommandos
Null.

Bei einem Fehler wird -1 zurückgegeben und errno entsprechend gesetzt.

FEHLER

EACCES or EAGAIN
Aktion ist aufgrund von Locks anderer Prozesse nicht möglich oder weil ein anderer Prozess die Datei in seinen Speicher gespiegelt hat.
EBADF
fd ist kein geöffneter Dateideskriptor oder der Zugriffsmodus stimmt nicht mit dem Typ des angeforderten Locks überein (für F_SETLK und F_SETLKW).
EDEADLK
Es wurde erkannt, dass das angeforderte F_SETLKW-Kommando zu einem Deadlock führen würde.
EFAULT
lock verweist außerhalb des verfügbaren Adressraums.
EINTR
Kommando wurde durch ein Signal unterbrochen (für F_SETLKW). Oder Kommando wurde durch ein Signal unterbrochen, bevor das Lock überprüft und erworben werden konnte (für F_GETLK Und F_SETLK). Tritt vor allem auf, wenn ein Lock auf entfernte Dateien (etwa über NFS) angefordert wird, ist jedoch auch auf lokalen Dateisystemen möglich.
EINVAL
arg ist negativ oder größer als der maximal erlaubte Wert (für F_DUPFD) oder arg ist keine erlaubte Signalnummer (für F_SETSIG).
EMFILE
Der Prozeß hat bereits das Maximum an Dateideskriptoren geöffnet (für F_DUPFD).
ENOLCK
Der Prozeß hat zu viele Locks auf gemeinsame Speichersegmente geöffnet, die Locktabelle ist voll oder es trat ein Fehler auf bei dem Versuch, ein Lock von einem anderen Rechner zu erhalten (etwa über NFS).
EPERM
Es wurde versucht, für eine Datei das Flag O_APPEND zu löschen, deren Zugriffsattribute nur das Anfügen von Daten erlauben.

BEMERKUNGEN

Die Fehler, die von dup2(2) zurückgegeben werden, sind anders als die von F_DUPFD.

Seit Kernelversion 2.0 werden die durch flock(2) und fcntl(2) gesetzten Locks nicht mehr gegeneinander abgeglichen.

POSIX 1003.1-2001 erlaubt negative Längenangaben in l_len. In diesem Fall umfaßt das Lock den Bereich von l_start+l_len bis einschließlich l_start-1. Unter Linux wird das seit den Versionen 2.4.21 beziehungsweise 2.5.49 unterstützt.

Verschiedene Systeme definieren in struct flock weitere Felder wie zum Beispiel l_sysid. Denn offensichtlich ist l_pid nicht übermäßig sinnvoll, falls der Prozeß, der ein Lock hält, auf einer anderen Maschine laufen kann.

ABGESTIMMT MIT

SVr4, SVID, POSIX, X/OPEN, BSD 4.3. In POSIX.1 sind lediglich die Operationen F_DUPFD, F_GETFD, F_SETFD, F_GETFL, F_SETFL, F_GETLK, F_SETLK und F_SETLKW spezifiziert. F_GETOWN und F_SETOWN stammen aus der BSD-Welt und werden in SVr4 nicht unterstützt; F_GETSIG und F_SETSIG sind Linux-spezifisch. Auch F_NOTIFY, F_GETLEASE und F_SETLEASE gibt es nur unter Linux. Um ihre Definitionen zu erhalten, muß zusätzlich noch das Makro _GNU_SOURCE definiert werden, bevor <fcntl.h> eingebunden wird. Die gültigen Flags F_GETFL und F_SETFL entsprechen den von open(2) unterstützten und unterscheiden sich zwischen verschiedenen Systemen; O_APPEND, O_NONBLOCK, O_RDONLY und O_RDWR sind in POSIX.1 festgelegt. SVr4 unterstützt verschiedene weitere Optionen und Flags, die hier nicht aufgeführt sind.

Unter SVr4 sind EIO, ENOLINK und EOVERFLOW als zusätzliche mögliche Fehler dokumentiert.

AUTOREN

Drew Eckhardt, Michael Haardt, Ian Jackson und Martin Schulze. Ins Deutsche übersetzt von Martin Schulze ([email protected]) und Daniel Kobras ([email protected]).