- oben
- Grundoperationen
- Positionieren: lseek
- Datei duplizieren
- Dateieigenschaften ermitteln
- Dateieigenschaften ändern
- Sperren
- Link erzeugen
- Löschen: unlink
- Umbenennen
- Temporäre Dateien
Universeller Dateibegriff unter UNIX
UNIX hat eine Reihe von Standarddateizugriffe definiert, die mit C auch auf die anderen Plattformen übertragen wurden. Nirgends ist aber der Dateibegriff so universell wie unter UNIX. Mit den gleichen Funktionen können Dateien, Pipes und sogar Netzverbindungen zugegriffen werden.Anwendungsprogramme verwenden fopen
Neben diesen Dateizugriffen, die durch die Aufrufe open(), read(), write() und close() geprägt sind und die einen Integer als Dateihandle zurückgeben, gibt es noch die Dateizugriffe mit fopen(), fread(), fwrite() und fclose(), die als Dateihandle einen Zeiger auf die Struktur FILE zurückgeben. Diese in der Anwendungsprogrammierung weiter verbreitete Variante basiert auf den zuerst genannten Basiszugriffen und erweitert sie um ein Pufferkonzept. Sie gehören auch nicht zu den Systemaufrufen, die vom Betriebssystem behandelt werden, sondern sind Bibliotheksfunktionen. Nähere Informationen dazu finden sich in den Dokumentationen der Compiler oder natürlich unter man 3 fopen.Grundoperationen
Um eine Datei lesen oder schreiben zu können, muss sie zunächst durch den Aufruf von open() geöffnet werden. Vor Ende des Programmes wird sie mit close() wieder geschlossen.Öffnen: open
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(char *filename, int oflag, mode_t mode); |
- filename
- Der Pfad und Dateiname der zu öffnenden Datei.
- oflag
-
Dieser Parameter bestimmt, wie die Datei geöffnet wird.
Es können mehrere Attribute verwendet werden, indem sie mit einander durch
den senkrechten Strich geodert werden.
Konstante Bedeutung O_RDONLY Datei nur zum Lesen öffnen O_WRONLY Datei nur zum Schreiben öffnen O_RDWR Datei zum Lesen und Schreiben öffnen O_APPEND Es wird ans Ende der Datei angehängt O_CREAT Erzeuge die Datei, wenn sie nicht existiert O_EXCL Exklusiver Zugriff - mode
- Hier werden Schreib- und Leserechte angegeben, wie sie vom chmod bekannt sind.
Bei einem Fehler gibt open -1 zurück. Der Fehlercode befindet sich in der globalen Variablen errno. Im Erfolgsfall wird ein Dateihandle vom Typ int zurückgegeben, der von den Dateifunktionen benötigt wird, um die entsprechende Datei weiter zu bearbeiten.
Paralleles Überschreiben vermeiden
Das Flag O_APPEND ist vor allem beim Beschreiben von Protokolldateien wichtig. Da nun ein einfaches write ausreicht, ist die Operation untrennbar. Ein paralleler Prozess, der auch schreiben will, kann also nur davor oder dahinter schreiben, aber nicht an die gleiche Stelle. Dagegen ist der scheinbar gleichwertige Ansatz mit lseek und write ohne O_APPEND im open teilbar, da es zwei Operationen sind. Dann können parallele Prozesse versehentlich in den gleichen Bereich schreiben.Die Kombination O_CREAT | O_EXCL ist wunderbar als Semaphore verwendbar. Da dies nur ein untrennbarer Aufruf ist, kann immer nur ein Prozess die Datei im Zugriff haben. Beispielsweise wird diese Technik vom Drucksystem verwendet, um zu vermeiden, dass zwei Prozesse gleichzeitig einen Eintrag in das Spoolverzeichnis schreiben. Dazu wird vor dem eigentlichen Schreiben versucht, eine Datei namens lock mit den oben angegebenen Parameter zu eröffnen. Der Aufruf misslingt, wenn bereits eine Datei existiert. Derjenige, der also diesen Aufruf erfolgreich durchführen kann, ist also der einzige Prozess im kritischen Bereich. Nach Ende der Arbeiten löscht er einfach wieder die Datei lock und läutet damit die nächste Runde ein.
creat()
Analog zum open() mit der Option O_CREAT gibt es den Befehl creat():int creat(const char *filename, mode_t mode); |
Zum Schließen der Datei wird der Aufruf close() verwendet.
Schließen: close
int close(int fd); |
Dateien schließen
Dass eine geöffnete Datei auch wieder geschlossen werden sollte, sagt bereits der Ordnungssinn. Darüber hinaus ist es einsehbar, dass das System bei übermäßig vielen geöffneten Dateien auch Verwaltungsaufwand hat. Der Hauptgrund, eine Datei möglichst bald wieder zu schließen, ist aber der, dass eine offene Datei immer ein Risiko darstellt, da der Zustand der Datei in der Schwebe ist.Als Parameter nimmt close() das Dateihandle.
Der Rückgabewert ist 0, wenn alles in Ordnung ist und -1, wenn ein Fehler auftritt. In bestimmten Fällen kann eine gepufferte Schreiboperation erst beim Schließen der Datei auffallen. Insofern sollte man auch den Rückgabewert von close prüfen, wenn man eine sichere Information braucht, ob die Dateioperationen geklappt haben.
Lesen: read
#include <unistd.h> int read(int filehandle, void *buffer, size_t length); |
Parameter
Damit aus einer Datei gelesen werden kann, muss sie geöffnet werden. Den Rückgabewert von open() braucht read() als ersten Parameter. Vor dem Lesen muss ein Puffer angelegt werden, in dem die zu lesenden Daten abgelegt werden. Die Adresse dieses Speichers wird als weiterer Parameter benötigt. Schließlich muss angegeben werden, wie groß der Datenblock sein soll, der gelesen wird. Dieser Parameter sollte nicht zu klein gewählt werden. Das byteweise Einlesen einer mittelgroßen Datei kann ein Programm minutenlang beschäftigen.Rückgabewert
Der Rückgabewert gibt an, wieviele Bytes gelesen wurden und er sollte mit dem Parameter length übereinstimmen. Ist er kleiner, sind scheinbar die letzten Byte erreicht worden. Da aber beispielsweise im Netz Verzögerungen auftreten können, sollte man auch in diesem Fall noch einmal lesen, bis der Rückgabewert 0 ist. Ist der Rückgabewert -1, ist ein Fehler aufgetreten. Nähere Informationen zu der Ursache des Fehlers findet man in errno.Schreiben: write
#include <unistd.h> int write(int filehandle, void *buffer, size_t length); |
Parameter
Auch write() braucht das Dateihandle als ersten Parameter. Der zweite Parameter gibt die Adresse des Speichers an, aus dem geschrieben werden soll. Im letzten Parameter wird angegeben, wie groß der zu schreibende Bereich ist.Rückgabewert
Der Rückgabewert ist wie bei read() normalerweise identisch mit dem Paramter length. Im Fehlerfall ist er -1 und die Variable errno gibt über die Ursache Auskunft.Standardein- und ausgabe
Die drei Konstanten stdin, stdout und stderr brauchen nicht geöffnet oder geschlossen zu werden, verhalten sich aber kompatibel zu Dateien, die durch open() geöffnet wurden.Filter
Mit Hilfe dieser Konstanten lassen sich Filter programmieren, die in Pipes gehängt werden können. Das entsprechende Programmgerüst sieht so aus:int zeichen; while (EOF != (zeichen=getc(stdin))) { // verändere die Eingabe in gewünschter Form putc(zeichen, stdout); } |
Dass die Variable zeichen ein int und kein char ist, ist kein Versehen! Eine Variable vom Typ char hat nur Platz für genau ein Zeichen. Der Typ int hat dagegen einen größeren Wertebereich und kann damit zusätzliche Zustände, also auch Fehlerzustände, aufnehmen. Nur mit einem int ist das EOF (End Of File) zu erkennen.
#include <stdio.h> int getc(int DateiHandle); int getchar(void); int putc(int c, int DateiHandle); int putchar(int c); char * gets(char *s); int puts(char *s); |
Die Funktion getchar entspricht der Funktion getc, bezieht sich aber immer auf stdin. Im Beispiel hätte man also auch getchar verwenden können. Gleiches gilt für putchar. Mit gets wird eine Zeile von der Standardeingabe gelesen und puts gibt eine Zeichenkette auf der Standardausgabe aus. Dabei hängt puts noch ein Zeilenvorschubzeichen an die Ausgabe an.
Die Funktion gets liest einen String aus der Standardeingabe bis ein Zeilenendetrenner erscheint. Da gets keinen Parameter hat, der die Länge der Eingabe beschränkt, wird die Funktion auch über das Ende des Speichers hinwegschreiben, wenn genügend Eingabedaten vorliegen. Dieser Designfehler der Funktion wird gern für Angriffe ausgenutzt. In kritischer Umgebung ist diese Funktion also unbedingt zu vermeiden.
Positionieren: lseek
Ein Lesezugriff liest einen Block aus einer bereits geöffneten Datei. Er fängt normalerweise vorn an und setzt an der zuletzt erreichten Stelle wieder auf, um einen weiteren Lesezugriff zu bedienen. Analoges geschieht beim Schreibzugriff. Es gibt also einen Dateipositionszeiger, der immer an die Stelle der letzten Dateioperation zeigt.Will man innerhalb der Datei an einer bestimmten Stelle anfangen zu lesen, kann man mit dem Aufruf lseek() an eine anzugebende Stelle in der Datei springen.
int lseek(int filehandle, off_t offset, int whence); |
Der Parameter offset bestimmt die Position, ab der die nächste Operation beginnt. whence legt fest, von wo der Offset berechnet wird.
Konstante} | Bedeutung |
SEEK_SET | ab Dateianfang |
SEEK_CUR | ab der aktuellen Position |
SEEK_END | ab Dateiende |
Dateien mit Löchern
Mit lseek ist es möglich, Löcher in Dateien entstehen zu lassen. Wird beispielsweise bei einer Datei mit einem Byte Länge an die Position 1000 gesprungen und geschrieben, gibt es ein Loch, das nicht auf der Platte angelegt wird. Für das Programm bleibt das transparent. Liest man später einen Bereich in diesem Loch, erhält man einen mit Nullen aufgefüllten Speicherbereich. Problematischer ist es, wenn man später sukzessive das Loch beschreibt, da die Speicherbereiche auf der Platte dann nicht mehr linear vorliegen und mit Einbußen bei der Performance zu rechnen ist.Datei duplizieren: dup
#include <unistd.h> int dup(int oldfd); |
Diese Funktion gibt ein Dateihandle zurück, der auf die gleiche Datei zeigt. Auf diese Weise ist es beispielsweise möglich an zwei verschiedenen Stellen gleichzeitig in einer Datei zu arbeiten, da jedes Handle seinen eigenen Positionszeiger besitzt. Ist es notwendig, dass das Handle eine bestimmte Nummer hat, benötigt man dup2.
#include <unistd.h> int dup2(int oldfd}, int dupfd); |
Dateieigenschaften ermitteln
Man kann aus dem Programm heraus ermitteln, wie die Zugriffsrechte auf eine Datei sind. Man erhält Informationen über Zeiten des letzten Schreibens und Lesens, kann den Besitzer und den Typ der Datei feststellen.stat() und fstat()
Mit der Funktion stat() und fstat() kann man Informationen über eine Datei ermitteln. Die Ergebnisse werden in einer Variablen vom Typ stat abgelegt. Diese Variable muss der Programmierer anlegen und dessen Adresse der jeweiligen Funktion übergeben. Der Unterschied zwischen beiden Funktionen liegt darin, dass stat() den Dateinamen und fstat() das Dateihandle zur Identifikation der Datei verwendet.#include <sys/types.h> #include <sys/stat.h> int stat(char *path, struct stat *buffer); int fstat(int filehandle, struct stat *buffer); |
Die Ergebnisse finden sich in der Variablen von Typ stat, auf die der Parameter buffer zeigt. Die Definition der Struktur stat lautet:
struct stat { dev_t st_dev /* (P) Device, auf dem die Datei liegt */ ushort st_ino /* (P) Inode-Nummer */ ushort st_mode /* (P) Dateityp */ short st_nlink /* (P) Anzahl der Links der Datei */ ushort st_uid /* (P) Eigner-ID (uid) */ ushort st_gid /* (P) Gruppen-ID (gid) */ dev_t st_rdev /* Major- und Minornumber, falls Device */ off_t st_size /* (P) Größe in Byte */ time_t st_atime /* (P) Zeitpunkt letzter Zugriffs */ time_t st_mtime /* (P) Zeitpunkt letzte Änderung */ time_t st_ctime /* (P) Zeitpunkt letzte Statusänderung */ }; |
Die Bestandteile dieser Struktur können sich je nach System unterscheiden. Die mit (P) gekennzeichneten Elemente sind aber zwingend von POSIX vorgeschrieben.
st_dev und st_ino
st_dev und st_ino beschreiben eindeutig den Ort einer Datei. st_dev ist das Device, bei Festplatten also die Partition. st_ino bezeichnet den Inode, also die Katalognummer, in der auf die Datei verwiesen wird.st_mode
Die rechten 12 Bit beschreiben die Rechtezuordnung der Datei, wie sie von chmod bekannt ist. Zu berücksichtigen ist, dass die Werte oktal sind, Der Modus 755 muss also als 0755 in einem C-Programm dargestellt werden. In den nächsten vier Bit wird kodiert, welchen Typs die Datei ist. Um beides zu trennen, gibt es die Konstante S_IFMT. Mit ihr kann man eine Maske über diese Bits setzen.Normalerweise wird man aber eher direkt die Werte ausmaskieren, die man sucht. Dabei gibt es Konstanten, mit denen man arbeiten sollte.
Konstante} | Dateityp |
S_IFSOCK | Sockets |
S_IFLNK | Symbolische Links |
S_IFREG | reguläre Dateien |
S_IFBLK | Block-Devices |
S_IFDIR | Verzeichnisse |
S_IFCHR | Char-Devices |
S_IFIFO | FIFOs |
Um einen bestimmten Typ abzufragen maskiert man mit einem einfachen & den Inhalt von st_mode aus. Ist der Wert ungleich Null, ist das entsprechende Bit gesetzt.
struct stat s; fstat(filehandle, &s); if (s.st_mode & S_IFREG) /* regulaere Datei? */ |
st_nlink
In st_nlink steht, wieviele harte Links auf die Datei zeigen.st_uid und st_gid
Mit st_uid und st_gid werden der Besitzer und die Besitzergruppe ermittelt. Der Wert ist eine Zahl, nämlich die, welche in /etc/passwd bzw. in /etc/group festgelegt wird.st_rdev
In st_rdev ist die Major- und Minornummer kodiert, sofern es sich bei der Datei um ein Device handelt.st_size
Sofern es sich bei der Datei um eine reguläre Datei handelt, findet sich in st_size die Größe in Bytes.st_atime, st_mtime und st_ctime
Jeder lesende oder schreibende Zugriff auf die Datei aktualisiert den Wert st_atime. Jede Veränderung des Dateiinhalts wird in in st_mtime notiert. Der Zeitpunkt der Änderungen bezüglich der Benutzer, Rechte, Linkzahl oder ähnlichem, also allem, was nicht den Inhalt betrifft, wird in st_ctime festgehalten.Zugriffsrecht ermitteln
access()
Will ein Programm ermitteln, ob es beispielsweise Schreibrecht auf eine Datei hat, könnte es mit stat alle Informationen über die Datei auswerten. Schneller geht es mit der Funktion access(), die nur ermittelt, ob das gewünschte Recht verfügbar ist.#include <unistd.h> int access(const char *pathname}, int mode); |
An den Parameter mode können folgende Konstanten übergeben werden:
Konstante} | Bedeutung |
F_OK | Existenz der Datei |
R_OK | Prozess darf lesen |
W_OK | Prozess darf schreiben |
X_OK | Prozess darf die Datei ausführen |
Der Rückgabewert ist 0, wenn der Zugriff erlaubt ist oder EACCESS, wenn nicht.
Dateieigenschaften ändern
Die Aufrufe chmod, chown und chgrp basieren natürlich auf Systemaufrufen, die sich auch aus einem Programm heraus aufrufen lassen.Zugriffsrechte ändern
Der Aufruf chmod hat sein direktes Gegenstück in der Funktion chmod() bzw. fchmod(). Beide Funktionen unterscheiden sich darin, ob die Datei durch ein Dateihandle oder durch ihren Dateinamen bestimmt wird.#include <sys/stat.h> int chmod(const char *pathname}, mode_t mode); int fchmod(int fd}, mode_t mode); |
chmod()
Die Variable mode erhält die Kodierung der Rechte, wie man sie vom Kommanodo chmod kennt. Dem bekannten Parameter des Kommandozeilenwerkzeug wird eine Null vorangesetzt, damit der Wert oktal übergeben wird. Würde man auf der Kommmandozeile 644 verwenden, würde im Programm chmod(fh, 0644) angegeben.Besitzer und Gruppe ändern
Auch für die Aufrufe chown und chgrp gibt es Gegenstücke in den Systemaufrufen. Allerdings werden beide Funktionalitäten durch die gleiche Funktion chown() bzw. fchown() behandelt. Eine Funktion chgrp() gibt es demzufolge nicht.#include <sys/stat.h> int chown(char *pathname}, uid_t user, gid_t group); int fchown(int fd}, uid_t user, gid_t group); |
chown()
Mit diesen Funktionen kann Benutzer und Gruppe einer Datei geändert werden. Will man nur den Wert des Benutzers oder nur der Gruppe ändern, wird für den jeweils anderen Parameter -1 übergeben.Maske für die Rechte neuer Dateien: umask
Diese Maske legt fest, welche Berechtigungen vom Programm bei der Erzeugung einer Datei oder Verzeichnisses nicht vergeben wird. Die Berechtigungen sind wie bei chmod aufgebaut, allerdings negiert.#include <sys/stat.h> int umask(int mask); |
Der Rückgabewert ist die vormals geltende umask.
Sperren
Wenn mehrere Prozesse in ein und derselben Datei Änderungen vornehmen wollen, wird es notwendig, Bereiche dieser Datei sperren zu müssen. Diese Problematik ist bei den ersten Versionen von UNIX nicht berücksichtigt worden. Erst später beim Einsatz im kommerziellen Umfeld wurde die Notwendigkeit erkannt und in den verschiedenen UNIX-Dialekten unterschiedlich nachgereicht. So gibt es mehrere API-Aufrufe, um einen Dateibereich zu sperren. Relevant ist der Standard unter POSIX, den man bei Wahlmöglichkeit auch einsetzen sollte. Es kann aber durchaus passieren, dass man auf einen der anderen Sperrmechanismen in älteren Programmen stößt, die aus Kompatibilitätsgründen oft noch funktionieren.Dateisystem muss Sperren erlauben
Nicht jedes Dateisystem unterstützt Sperren, insbesondere wenn es sich um ein Netzdateisystem handelt. Aus diesem Grund sollte das Funktionieren der Sperren vor dem Einsatz in der Produktionsumgebung getestet werden.Sperren nach POSIX
Als erstes wird die POSIX-Variante beschrieben. Dort erfolgt das Sperren von Dateiausschnitten über die Funktion fcntl().#include <fcntl.h> #include <unistd.h> #include <sys/types.h> fcntl(int filehandle, int command, struct flock *sperre); |
Der Parameter command gibt die Funktion an und kann folgende Werte annehmen:
Konstante | Bedeutung |
F_GETLK | Ermittle, ob eine Sperre gesetzt ist |
F_SETLK | Eine Sperre setzen oder Fehler zurückgeben |
F_SETLKW | Eine Sperre setzen oder warten |
Die Elemente der Struktur flock geben die näheren Informationen über die Sperre an.
Element} | Wert | Bedeutung |
---|---|---|
l_type | F_WRLCK | exklusiv |
F_RDLCK | geteilt, verhindere Schreibzugriff | |
F_UNLCK | Sperre aufheben | |
l_whence | SEEK_SET | vom Dateianfang |
SEEK_CUR | ab aktueller Dateiposition | |
SEEK_END | vom Dateiende | |
l_start | Offset des Sperrbereichs | |
l_len | Länge des Bereichs, bei 0 bis Dateiende |
Das flock-Element l_pid liefert bei F_GETLK den Prozess, der die Sperre gesetzt hat.
Beispielprogramm one
Als Beispiele dienen zwei Programme, die von zwei Terminalsitzungen aus gestartet werden. Das eine heißt one.c} und das andere two.c. Zentraler Bestandteil der Programme ist die Funktion Sperren()./* Sperren() aus one.c */ #include <unistd.h> #include <stdio.h> int Sperren(char *name) { long data; int fh; struct flock sperre; fh = open(name, O_RDWR, 0644); if (0<fh) { sperre.l_type = F_WRLCK; sperre.l_whence = SEEK_SET; sperre.l_start = 5*sizeof(data); sperre.l_len = sizeof(data); if (0> fcntl(fh, F_SETLK, &sperre)) { perror("Versuche die erste Sperre"); } getchar(); /* nun ist der Satz gesperrt */ sperre.l_type = F_UNLCK; if (0> fcntl(fh, F_SETLK, &sperre)) { perror("Löse die erste Sperre"); } close(fh); } else { perror("Kann Datei nicht öffnen"); } } |
Beispielprogramm two
Das Programm one wird gestartet und bleibt in der Funktion Sperren() an der Stelle stehen, wo es mit getchar() auf die Returntaste wartet. In dieser Zeit wird auf der zweiten Konsole das Programm two gestartet. Dessen Funktion Sperren() sieht nur geringfügig anders aus:int Sperren(char *name) { long data; int fh; struct flock sperre; fh = open(name, O_RDWR, 0644); if (0<fh) { puts("two: vor lock"); sperre.l_type = F_WRLCK; sperre.l_whence = SEEK_SET; sperre.l_start = 5*sizeof(data); sperre.l_len = sizeof(data); if (0> fcntl(fh, F_SETLKW, &sperre)) { perror("Versuche die zweite Sperre"); } puts("two: nach lock"); getchar(); sperre.l_type = F_UNLCK; if (0> fcntl(fh, F_SETLKW, &sperre)) { perror("Löse die zweite Sperre"); } close(fh); } } |
Verhaltenstest
Das Programm two wird noch >>two: vor lock<< melden und dann stehen. Es blockiert also an der von one gesetzten Sperre. Wird das Programm one durch Enter weitergeführt, gibt es die Sperre frei und two meldet sich mit >>two: nach lock<<. Nun hält Programm two die Sperre. Startet man jetzt das Programm one, erhält man die folgende Meldung:Versuche die erste Sperre: Resource temporarily unavailable |
Ganz offensichtlich blockiert one nicht, sondern bekommt eine Fehlerrückgabe durch fcntl().
Wer blockiert hier?
Zuletzt soll bei einer bestehenden Sperre ermittelt werden, welcher Prozess die Sperre setzt. Dazu verwenden wir die Funktion Sniff():int Sniff(char *name) { long data; int fh; struct flock sperre; fh = open(name, O_RDWR, 0644); if (0<fh) { sperre.l_type = F_WRLCK; sperre.l_whence = SEEK_SET; sperre.l_start = 5*sizeof(data); sperre.l_len = sizeof(data); if (0> fcntl(fh, F_GETLK, &sperre)) { perror("Problem bei fcntl"); } printf("Prozess: %d\n", sperre.l_pid); close(fh); } } |
lockf
Die Funktion lockf wurde durch System V eingeführt. Sie wird von manchen Systemen, beispielsweise Linux, als Schnittstelle zum POSIX-Sperren verwendet.#include <sys/file.h> int lockf(int fd, int cmd, off_t len); |
Der Parameter cmd wird mit einer Konstanten besetzt, die die Funktion angibt.
- F_LOCK
- Setzt eine exklusive Sperre. Ist der Bereich bereits gesperrt, blockiert der Prozess. So ist gewährleistet, dass nur ein Prozess eine Sperre setzen kann.
- F_TLOCK
- wie F_LOCK wir ein Bereich exklusiv gesperrt. Bei einer bereits vorliegenden Sperre blockiert der Prozess aber nicht. Statt dessen gibt die Funktion einen Fehler zurück.
- F_ULOCK
- hebt die Sperre auf
- F_TEST
- Prüft, ob eine Sperre auf diesem Bereich liegt. Die Funktion gibt -1, wenn ein anderer Prozess die Sperre gesetzt hat. Der Rückgabewert 0 bedeutet, dass keine Sperre vorliegt oder die Sperre vom eigenen Prozess stammt.
flock
Diese Funktion zum Sperren von ganzen Datei wurde durch BSD 4.2 eingeführt. flock sperrt nicht einen Ausschnitt, sondern die komplette Datei.flock(int filehandle, int operation) |
Der Parameter operation kann folgende Werte annehmen.
- LOCK_SH
- Shared lock. Mehrere Prozesse können parallel sperren. Dies ist eine eher ungewöhnliche Operation und macht nur im Zusammenhang mit mandantory locking (siehe unten) Sinn.
- LOCK_EX
- Exclusive lock. Nur ein Prozess kann sperren.
- LOCK_UN
- Unlock. Die Sperre wird aufgehoben.
- LOCK_NB
- Nonblocking . Die Operation blockiert nicht, sondern gibt eine Fehlermeldung zurück. Dies wird durch Odern einem anderen Kommando hinzugefügt.
Die Funktion gibt im Erfolgsfall 0 ansonsten -1 zurück. Nähere Informationen finden sich dann in der Variablen errno.
locking
Die Funktion locking wurde unter XENIX 31 verwendet.int locking(int fd, int flags, long nbytes); |
Der Parameter flag kann folgende Werte annehmen.
Konstante} | Bedeutung |
LK_LOCK | Eine Sperre setzen |
LK_UNLOCK | Eine Sperre aufheben |
Die Sperre wird ab der aktuellen Dateiposition gesetzt. Als eigenen Parameter kennt locking nur die Länge des zu sperrenden Bereichs. Um die Dateiposition zu ändern, muss man zuvor lseek() aufrufen.
advisory und mandatory
Es gibt zwei Arten des Sperrens: advisory (übersetzt etwa empfohlen) und mandatory (übersetzt zwingend). Beim advisory wird der gesperrte Bereich nur dadurch geschützt, dass alle Programme, die auf die Datei zugreifen, nur über die Sperrmechanismen zugreifen. Greift ein anderer Prozess zu oder hält sich jemand nicht an diese Abmachung, hat die Sperre keinen Wert.mandatory wird dateiweise per chmod geschaltet
Beim Aktivieren des mandatory locking wird die Sperre durch das System überwacht. Alle Dateizugriffe werden geprüft, um die Sperre zu schützen. Mandatory locking wird mit denselben Aufrufen realisiert wie das advisory locking. Der Umstieg erfolgt durch das Setzen bestimmter Dateiattribute. Mit dem chmod wird das Set group id Bit gesetzt und die Ausführbarkeit für die Gruppe gelöscht. Da zu sperrende Dateien Daten enthalten und keine ausführbaren Dateien sind, wäre eine solche Rechtevergabe ansonsten auch unsinnig. Der Befehl, die Datei datendatei auf mandantory locking umzustellen, lautet:chmod 2644 datendatei |
mandatory ist selten sinnvoll
Letztlich ist das mandatory locking nicht so wichtig, wie man vielleicht auf den ersten Blick glaubt. Eine Datendatei wird normalerweise mit Programmen zugegriffen, die >>wissen<<, wie die Daten zu behandeln sind und damit auch darauf eingestellt sind, die Sperren konkurrierender Prozesse zu beachten. Alles andere ist definitiv ein Programmierfehler. Normalerweise wird nur dann mit Fremdprogrammen auf solche Dateien zugegriffen, wenn etwas schief gelaufen ist und eine Administration notwendig ist. Und in diesen Fällen würde ein mandatrory locking die Hilfe der entsprechenden Tools aussperren. Da das mandatory locking zusätzlich die Performance herabsetzt, wird es eher selten verwendet.Link erzeugen: link, symlink
Links können auch aus dem Programm erstellt werden. Der Systemaufruf heißt link():#include <unistd.h> int link(const char *orig}, const char *new); |
Die Funktion link() erzeugt einen harten Link wie der Befehl link. Auch hier gelten die Einschränkungen, wie sie beim Befehl gelten. Die Funktion gibt 0 bei Erfolg und -1 bei einem Fehler zurück.
#include <unistd.h> int symlink(const char *orig}, const char *new); |
Mit der Funktion symlink werden symbolische Links erzeugt.
Löschen: unlink
Die Funktion zum Löschen einer Datei heißt unlink(), was seine Funktion auch besser beschreibt als remove. Tatsächlich entfernt das Löschen eine Datei nur dann, wenn der zu löschende Verzeichniseintrag der letzte Link auf die Datei ist.#include <unistd.h> int unlink(const char *pathname); |
Die Funktion gibt 0 bei Erfolg und -1 bei einem Fehler zurück. Die Fehlerursache steht in der Variablen errno.
Umbenennen: rename
Die Funktion zum Umbenennen von Dateien heißt rename(), obwohl sie wie das Kommando mv wirkt. Es ist also auch möglich, damit Dateien innerhalb des gleichen Dateisystems zu verschieben.#include <unistd.h> int rename(const char *oldpath, const char *newpath); |
Die Funktion gibt 0 bei Erfolg und -1 bei einem Fehler zurück. Die Fehlerursache steht in der Variablen errno.
Temporäre Dateien
Temporäre Dateien werden unter UNIX immer im Verzeichnis /tmp oder /usr/tmp abgelegt. Auf diese Verzeichnisse kann jeder frei zugreifen. Will man sicher sein, dass der Name der temporären Datei nicht auch von einem anderen Programm verwendet wird, lässt man ihn vom System erzeugen. Dies tut die Funktion tmpnam().#include <stdio.h> char *tmpnam(char *s); |
Ist der Parameter s NULL, so liefert die Funktion als Rückgabewert einen Zeiger auf einen internen Namen, der beim nächsten Aufruf von tmpnam() überschrieben wird. Ist s nicht NULL, liefert die Funktion den Namen an dieser Adresse ab.
Es gibt daneben noch die Funktion tmpfile(), die einen Zeiger auf FILE zurückgibt. Hier wird intern mit tmpnam ein Name gewählt und sofort eröffnet. Eine Besonderheit ist, dass diese Datei nach dem Schließen oder bei Programmende automatisch gelöscht wird.
#include <stdio.h> FILE *tmpfile (void); |
1 vgl. Rochkind, Marc J.: UNIX Programmierung für Fortgeschrittene. Hanser, 1988. S. 256-261.
Diese Seite basiert auf Inhalten aus dem Buch Arnold Willemer: Wie werde ich UNIX-Guru |