Sehr Umfangreiche Webseite zum Programmieren in C Perl CGI, Skripting, Linux, Systemprogrammierung C C++ C/C++ ANSI C Linux Linuxsystemprogrammierung Blockieren und Sperren von Dateien Deadlocks Linuxsystemprogrammieren Blockieren und Sperren von Dateien Deadlocks 7. Blockieren und Sperren von Dateien

7.1. Dateisperren (record locking)           zurück Ein Kapitel tiefer zum Inhaltsverzeichnis

Wenn zwei oder mehrere Prozesse gleichzeitig auf eine Datei zugreifen und mindestens einer davon will darauf Schreiben, kann dies zu Problemen führen. Es könnte ein sogenannter Datensalat entstehen. Einfaches Programmbeispiel ...

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

int main()
{
 int fd;
 int x,y;
 pid_t pid;
 unlink("/tmp/file"); /*Fall vorhanden weg damit*/
 fd=open("/tmp/file", O_WRONLY|O_CREAT, 0777);
 if(fd==-1)
  {
    perror("open : ");
    exit(0);
  }

 if((pid=fork()) == -1)
  {
    perror("fork :");
    exit(0);
  }
 else if(pid)
  { /*Elternprozess*/
    while(1)
     {
      for(x=0; x<10; x++){
        sleep(1);
        write(fd,(char *)"x",1);
       }
      break;
     }
  }
 else
  { /*Kindprozess*/
    while(1)
     {
      for(y=0; y<10; y++){
        sleep(1);
        write(fd,(char *)"X",1);
       }
      break;
     }
  }
 return 0;
}

Hier haben wir mal schnell zwei Prozesse gestartet, die beide in die Datei /tmp/file schreiben. Schauen Sie sich nun den Inhalt von "/tmp/file" an und Sie finden ein gutes Durcheinander wie etwa ...

XxxXXxxXXxxXXxxXXxx

Stellen Sie sich nun vor, dass wären Daten in einer Datenbank gewesen. Mit Sperren sollte es also möglich sein, immer nur einen Prozess in diese Datei schreiben zu lassen. Der richtige Inhalt der Datei "/tmp/file" müsste also lauten ...

xxxxxxxxxxXXXXXXXXXX

Zum Sperren von Dateien steht uns die Funktion fcntl zur Verfügung ...

int fcntl(int fd, int kommando,.../*struct flock *flockzeiger*/);

Sehen wir uns zuerst die Struktur flock und deren Inhalt an ...

struct flock{
        short l_type;     /*3 Möglichkeiten F_RDLCK (Lesesperre)
                                           F_WRLCK (Schreibsperre)
                                           F_UNLCK (Sperre aufheben)*/
        off_t l_start;    /*relatives offset in Byte, abhängig von l_whence*/
        short l_whence;   /*SEEK_SET;SEEK_CUR;SEEK_END*/
        off_t l_len;      /*Grösse der Speicherung in Bytes,0=Sperren bis Dateiende*/
        pid_t l_pid;      /*wird bei F_GETLK zurückgegeben*/
             };

Wenn Sie eine ganze Datei zum lesen bzw. schreiben sperren wollen, müssen sie l_start und l_whence an dem Dateianfang setzen. Also ...

flockzeiger.l_start=0;
flockzeiger.l_whence=SEEK_SET;

Falls Sie eine Datei immer wieder vom aktuellen Dateiende sichern wollen, übergeben sie an l_len den Wert 0 ...

flockzeiger.l_len=0;

Damit sind Sie immer sicher, dass die Datei bis zum Dateiende gesperrt ist. Egal ob Sie jetzt einen neuen Inhalt ans Ende anfügen wollen. Folgende Angaben können Sie bei Kommando machen ...

F_GETLK
Hiermit können Sie abfragen, ob und welche Sperre für eine Datei bereits vorhanden ist ...

struct flock sperre;

fd=open(datei, O_CREAT|O_WRONLY);

fcntl(fd, F_GETLK, &sperre);
if(sperre.l_type==F_UNLCK)
    {/*Datei nicht gesperrt*/}
else if(sperre.l_type==F_RDLCK)
    {/*Lesesperre*/}
else if(sperre.l_type==F_WRLCK)
    {/*Schreibsperre*/}

F_SETLK
Hiermit können Sie ein Sperre einrichten. Ist es nicht möglich eine Sperre einzurichten, beendet die Funktion fcntl sich und setzt die Variable errno auf EACCES oder EAGAIN. Mit F_SETLK wird auch die gesetzte Sperre wieder entfernt (F_UNLCK) ...

sperre.l_start=0;
sperre.l_whence=SEEK_SET;
sperre.l_len=0;
sperre.l_type=F_RDLCK;
if((fcntl(fd, F_SETLK, &sperre)!=-1)
      {/*Lesesperre gesetzt*/}

sperre.l_start=0;
sperre.l_whence=SEEK_SET;
sperre.l_len=0;
sperre.l_type=F_WRLCK);
if((fcntl(fd, F_SETLK, &sperre)!=-1)
      {/*Schreibsperre gesetzt*/}

sperre.l_type=F_UNLCK;
if((fcntl(fd, F_SETLK, &sperre)!=-1)
      {/*Sperre aufgehoben*/}

F_SETLKW
Mit diesem Kommando wird der Prozess suspendiert, bis er die geforderte Sperre einrichten kann.

WICHTIG: Wenn sie eine Datei mit einer Schreibsperre (F_WRLCK) versehen wollen, muss die Datei auch zum schreiben geöffnet (O_WRONLY) werden. Und umgekehrt (F_RDLCK) wenn sie einer Datei eine Lesesperre anhängen wollen muss diese auch im Lesemodus (O_RDONLY) geöffnet werden. Das bedeutet auch das sie niemals Beide gleichzeitig auf ein bestimmtes Byte festlegen können. Hierzu nun ein Beispiel ...

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>

#define FNAME "locki.lck"

void status(struct flock *lock)
{
  printf("Status: ");
  switch(lock->l_type)
   {
     case F_UNLCK:  printf("F_UNLCK (Sperre aufgehoben)\n");
                    break;
     case F_RDLCK:  printf("F_RDLCK (pid: %d) (Lesesperre)\n", lock->l_pid);
                    break;
     case F_WRLCK:  printf("F_WRLCK (pid: %d) (Schreibsperre)\n", lock->l_pid);
                    break;
     default     :  break;
    }
}


int main(int argc, char **argv)
{
  struct flock lock;
  int fd;
  char puffer[100];

  fd = open(FNAME, O_WRONLY|O_CREAT|O_APPEND, S_IRWXU);

  memset(&lock, 0, sizeof(struct flock));
  fcntl(fd, F_GETLK, &lock);

  if(lock.l_type==F_WRLCK || lock.l_type==F_RDLCK)
    {
      status(&lock);
      memset(&lock,0,sizeof(struct flock));
      lock.l_type = F_UNLCK;
      if (fcntl(fd, F_SETLK, &lock) < 0)
        printf("Fehler : fcntl(fd, F_SETLK, F_UNLCK)  (%s)\n",strerror(errno));
      else
        printf("Erfolgreich gesetzt : fcntl(fd, F_SETLK, F_UNLCK)\n");
    }

  status(&lock);
  write(STDOUT_FILENO,"\nEingabe machen : ", sizeof("\nEingabe machen : "));
  while((read(1,puffer,100))>1)
  write(fd,puffer,100);


  memset(&lock, 0, sizeof(struct flock));
  lock.l_type = F_WRLCK;
  lock.l_start=0;
  lock.l_whence = SEEK_SET;
  lock.l_len=0;
  lock.l_pid = getpid();
  if (fcntl(fd, F_SETLK, &lock) < 0)
     printf("Fehler bei : fcntl(fd, F_SETLK, F_WRLCK)(%s)\n",strerror(errno));
  else
     printf("Erfolgreich mit : fcntl(fd, F_SETLK, F_WRLCK)\n");
  status(&lock);

  switch(fork())
    {
      case -1 :  exit(0);
      case 0  :  if(lock.l_type == F_WRLCK)
                  {
                   printf("Kind:Kann nicht in die Datei schreiben (F_WRLCK)\n");
                   exit(0);
                  }
                 else
                   printf("Kindprozess ist bereit zum schreiben in die Datei\n") ;
                 exit(0);
     default  :  wait(NULL);  break;
    }

  close(fd);
  return 0;
}

Wenn Sie dieses Programm ausführen, fragen Sie zuerst mittels fcntl(fd, F_GETLK, &lock); ...

fcntl(fd, F_GETLK, &lock);
if(lock.l_type==F_WRLCK || lock.l_type==F_RDLCK)

...ab, ob die Datei locki.lck einen Lese.-bzw-Schreibsperre hat. Falls ja heben Sie dies mit ...

lock.l_type = F_UNLCK;
if (fcntl(fd, F_SETLK, &lock) < 0)

...entsprechender Meldung, ob Erfolgreich oder nicht, auf. Anschließend werden Sie Aufgefordert eine Eingabe zu machen, so lange bis eine Zeile erfolgt ohne Eingabezeichen und nur mit Enter. Nach der Eingabe verhängen wir der Datei locki.lck eine Schreibsperre mit ...

lock.l_type = F_WRLCK;
lock.l_start=0;
lock.l_whence = SEEK_SET;
lock.l_len=0;
lock.l_pid = getpid();
if (fcntl(fd, F_SETLK, &lock) < 0)

Anschließend erzeugen wir einen Kindprozess mittels fork(). Unser Kindprozess überprüft ob Sie in der Datei locki.lck schreiben können oder nicht. Was normalerweise nicht möglich sein sollte.

Nun wollen wir das Anhand unserem Beispiels vom Anfang dieses Kapitels anwenden ...

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>

#define FNAME "/tmp/file"

void status(struct flock *lock)
{
  printf("Status: ");
  switch(lock->l_type)
   {
     case F_UNLCK:  printf("F_UNLCK (pid: %d) (Sperre aufgehoben)\n",lock->l_pid);
                    break;
     case F_RDLCK:  printf("F_RDLCK (pid: %d) (Lesesperre)\n", lock->l_pid);
                    break;
     case F_WRLCK:  printf("F_WRLCK (pid: %d) (Schreibsperre)\n", lock->l_pid);
                    break;
     default     :  break;
    }
}


int main(int argc, char **argv)
{
  struct flock lock;
  int fd;
  char puffer[100];
  int x,y;
  pid_t pid;
  unlink(FNAME);
  fd = open(FNAME, O_WRONLY|O_CREAT, 0777);

  memset(&lock, 0, sizeof(struct flock));
  fcntl(fd, F_GETLK, &lock);

  if(lock.l_type==F_WRLCK || lock.l_type==F_RDLCK)
    {
      status(&lock);
      memset(&lock,0,sizeof(struct flock));
      lock.l_type = F_UNLCK;
      if (fcntl(fd, F_SETLK, &lock) < 0)
        printf("Fehler : fcntl(fd, F_SETLK, F_UNLCK)  (%s)\n",strerror(errno));
      else
        printf("Erfolgreich gesetzt : fcntl(fd, F_SETLK, F_UNLCK)\n");
    }


  memset(&lock, 0, sizeof(struct flock));
  lock.l_type = F_WRLCK;
  lock.l_start=0;
  lock.l_whence = SEEK_SET;
  lock.l_len=0;
  lock.l_pid = getpid();
  if (fcntl(fd, F_SETLK, &lock) < 0)
     printf("Fehler bei : fcntl(fd, F_SETLK, F_WRLCK)(%s)\n",strerror(errno));
  else
     printf("Erfolgreich mit : fcntl(fd, F_SETLK, F_WRLCK)\n");
  status(&lock);

  switch(pid=fork())
    {
      case -1  :  exit(0);
      case 0   :  while(1) /*Kindprozess*/
                  {
                    fcntl(fd, F_GETLK, &lock);
                    status(&lock);
                    if(lock.l_type == F_WRLCK)
                      {
                        sleep(1);
                       }
                      else
                        {
                          for(y=0; y<10; y++){
                             sleep(1);
                             write(fd,(char *)"x",1);
                            }
                          exit(0);
                        }
                      }


        default  :   while(1)   /*Eltern*/
                      {
                        for(x=0; x<10; x++){
                          sleep(1);
                           write(fd,(char *)"X",1);
                          }
                       memset(&lock, 0, sizeof(struct flock));
                       lock.l_type = F_UNLCK;

                       if (fcntl(fd, F_SETLK, &lock) < 0)
                          printf("Fehler : fcntl(fd, F_SETLK, F_UNLCK)"
                                               "(%s)\n",strerror(errno));
                       else
                           printf("Erfolgreich gesetzt : "
                                  " fcntl(fd, F_SETLK, F_UNLCK)\n");
                        status(&lock);
                        wait(NULL);
                        break;
                       }
    }
  close(fd);
  return 0;
}

Zugegeben, wir hätten Sperre ein und Sperre aus auch in eine Funktion packen können (wäre sogar besser), nur finde ich es hier leichter zu durchschauen was geschieht. Natürlich müssten sie nachdem der Elternprozess fertig ist mit dem Schreiben auf die Datei, im Kindprozess eine Sperre einrichten und am Ende wieder aufheben. Ich denke mal soweit dürfte das Programm recht einfach verständlich sein.

Mit diesem Beispiel haben wir den Prozesse serialisiert. Nun manch einer von Ihnen wird es jetzt trotzdem probiert haben, read und write auf den Kindprozess zu testen und dabei bemerkt haben, dass es doch möglich ist, trotz F_WRLCK, in die Datei locki.lck zu schreiben. Das liegt daran das advisory locking (wahlfreie Sperren) dazu verwendet wurden, auch genannt schwache Sperren. Bei diesen schwachen Sperren findet keine Überprüfung statt ob die Systemfunktionen open, read und write bei Ihrer Ausführung durch eine Sperre verhindert werden sollten. Also gilt für die advisory locking, dass Sie selber dafür Verantwortlich sind zu überprüfen, ob den nun Sperren vorliegen oder nicht (wie im Programm oben geschehen im Kindprozess). Einfach gesagt, es ist nicht möglich ALLEN Prozessen den Zugriff auf eine Datei zu verweigern. Nur Prozessen die Sperren mit fcntl abfragen, werden bei existierenden Sperren blockiert (nicht immer).

Für andere Fälle gibt es sogenannte Starke Sperren (mandatory locking). Starke Sperren werden nicht von POSIX.1 vorgeschrieben, werden aber von SVR4 unterstützt. Daher ist auch nicht garantiert, dass dies funktioniert. Starke Sperren hindern einen Prozess, der Versucht mit read oder write auf einen anderen Prozess zuzugreifen, der zuvor mit fcntl gesperrt wurde, darauf zuzugreifen.

Starke Sperren können Sie Einrichten in dem Sie das Set-Group-ID-Bit einschalten und das Group-Execute-Bit ausschalten. Dies könnte etwa so aussehen ...

int mandatory_lock(int fd)
{
  struct stat statpuffer;

  if(fstat(fd, &statpuffer) < 0)
    {
      fprintf(stderr, "Fehler bei fstat.......\n");
      return 0;
    }

  if(fchmod(fd (statpuffer.st_mode & ~S_IXGRP) | S_ISGID) < 0)
    {
      fprintf(stderr, "Konnte Starke Sperre nicht einrichten.........\n");
      return 0;
    }
  return 1;
}

Ansonsten gilt das selbe wie schon oben gelernt. Wie gesagt, Starke Sperren sind Systemabhängig. mandatory locking kann aber z.B. nicht das Löschen einer Datei mit unlink verhindern.

Wenn mit der Funktion open() versucht wird eine Datei mit den Flags O_TRUNC und O_CREAT zu öffnen und für diese Datei ist eine Starke Sperre eingerichtet, so wird der Fehler errno=EAGAIN zurückgegeben.

Das selbe erreichen sie übrigens auch in einem Terminal mit ...

[pronix@localhost de]$ chmod g+s-x adressen.txt
[pronix@localhost de]$ ls -l adressen.txt
-rw-rwSr-- 1 de de 92160 Jul 9 09:51 adressen.txt
[pronix@localhost de]$

Das große S im Feld, für die Ausführungsrechte der Gruppe zeigt, dass die Datei nicht ausführbar und das SGID-Bit gesetzt ist. Rückgängig können Sie dies wieder machen mit ...

chmod g-s+x adressen.txt

Mehr zu chmod finden sie unter man chmod.

Sperren bis Dateiende
Wenn Sie die Datei immer aktuell bis zum Dateiende Sperren wollen, müssen Sie eine Kleinigkeit beachten. Wenn Sie eine Sperre einrichten, gilt diese momentan vom Anfang (l_start) bis zum Ende (l_len, abhängig von l_whence (SEEK_CUR oder SEEK_END) und vom relativen Offset des Schreib.-und-Lesezeigers. Werden jetzt neue Dateien am Ende hinzugefügt, hat sich somit das Datei-Ende auch wieder verschoben ...

struct adressen adress[100];
struct flock lock;
...........
...........
fd=open("adressen.dat",O_RDWR|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
..........
/*Schreibsperre einrichten vom aktuellen EOF bis EOF*/
memset(&lock, 0, sizeof(struct flock));
lock.l_type = F_WRLCK;
lock.l_start=0;
lock.l_whence = SEEK_END;
lock.l_len=0;
fcntl(fd, F_SETLK, &lock);

/*Hier steht der Code für Eingabe der Struktur adressen*/
..........
..........
/*Wir schreiben in adressen.dat*/
write(fd , &adress, sizeof(adress));

/*Wir heben die Sperre wieder auf*/
memset(&lock, 0, sizeof(struct flock));
lock.l_type = F_UNLCK;
lock.l_start=0;
lock.l_whence = SEEK_END;
lock.l_len=0;
fcntl(fd, F_SETLK, &lock);

Auf dem ersten Blick scheint nichts falsches an dem Code-Snippet.

Sperren bis zum Dateiende

Nun wenn Sie sich das Bild betrachten, fällt ihnen gleich auf, dass beim Schreiben der zweiten Struktur die erste immer noch gesperrt ist, obwohl Sie diese Sperre eigentlich freigegeben haben. Das Problem liegt in diesem Fall daran, dass auf den meisten Systemen SEEK_END in das absolute offset umgerechnet wird. Genau heißt dies, dass die Sperre exakt am Ende (EOF) wieder freigegeben wird.

Da Sie die Größe der Struktur kennen, müssen Sie in einem solchem Fall einfach das offset von l_start einen negativen Wert von der Größe unserer Struktur angeben. Dies heißt, dass beim Aufheben der Sperre anstatt ...

lock.l_start=0;

... folgendes geschrieben werden muss ...

lock.l_start = -(sizeof(struct adressen));

Bitte beachten Sie das, wenn sie vorhaben immer bis zum Dateiende zu Sperren!

7.2. Sperren von Dateiteilen und Deadlocks           zurück Ein Kapitel tiefer zum Inhaltsverzeichnis

Kommen wir nun zum Blockieren von Prozessen, durch gegenseitiges Aussperren (Deadlock). Dies kann dadurch entstehen in dem zum Beispiel Prozess Nummer 1 versucht in einer Datei, nennen wir diese dead.txt, bei Byte 10 eine Sperre einrichtet.

Ein weiterer Prozess, wir geben ihm die Nummer 2, richtet nun in der selben Datei (dead.txt) bei Byte 20 eine Sperre ein. Soweit ist alles noch OK. Nun will Prozess 1 eine weitere Sperre einrichten bei Byte 20, wo bereits der Prozess Nummer 2 eine Sperre eingerichtet hat. Wir benutzen dabei das Kommando F_SETLKW. Damit wird unser Prozess Nummer 1 solange suspendiert, bis der Prozess mit der Nummer 2 seinerseits diese Sperre bei Byte 20 wieder freigibt. Bis hierhin ist immer noch alles in bester Ordnung. Jetzt richtet der Prozess Nummer 2 bei Byte 10, wo Prozess Nummer 1 bereits eine Sperre eingerichtet hat, ebenfalls eine Sperre mit dem Kommando F_SETLKW ein und wird ebenso suspendiert und wartet bis Prozess Nummer 1 die Sperre wieder aufhebt. Jetzt da beide Prozesse, Nummer 1 und Nummer 2, suspendiert sind und beide darauf warten (F_SETLKW) auf die Freigabe der Sperre eines anderen haben wir ein Deadlock. Keiner der Prozesse wird hier jemals mehr aufgeweckt.

Aus Grund das diese Situation passieren kann, so gering ist, hat man unter UNIX keine Mechanismen dazu geschrieben, die eine Deadlock erkennen bzw. verhindern könnten. Solch ein Erkennungs- und Vermeidungsverfahren würde nur unnötig die Performance des Betriebssystem beeinträchtigen. Das heißt also wieder, dass Sie selbst dafür Verantwortlich sind.

Hierzu nun ein Programmbeispiel wie Sie ein Deadlock aufspüren können ...

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>

extern int errno;

void status(struct flock *lock)
{
  printf("Status: ");
  switch(lock->l_type)
   {
     case F_UNLCK:  printf("F_UNLCK\n"); break;
     case F_RDLCK:  printf("F_RDLCK (pid: %d)\n", lock->l_pid); break;
     case F_WRLCK:  printf("F_WRLCK (pid: %d)\n", lock->l_pid); break;
     default               :  break;
   }
}


void schreibsperre(char *prozess, int fd, off_t von, off_t bis)
{
  struct flock lock;

  lock.l_type = F_WRLCK;
  lock.l_start=von;
  lock.l_whence = SEEK_SET;
  lock.l_len=bis;
  lock.l_pid = getpid();
  if (fcntl(fd, F_SETLKW, &lock) < 0)
    {
     printf("%s : fcntl(fd, F_SETLKW, F_WRLCK) failed (%s)\n",
         prozess,strerror(errno));
     printf("\nDEADLOCK erkannt (%s - prozess)!!!!!!!!!\n\n",prozess);
     exit(0);
     }
  else
     printf("%s : fcntl(fd, F_SETLKW, F_WRLCK) succeeded\n",prozess);

  status(&lock);
}


int main()
{
  int fd, i;
  pid_t pid;

  if(( fd=creat("dead.txt", S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0)
   {
     fprintf(stderr, "Fehler bei creat......\n");
     exit(0);
    }

/*Beschreiben dead.txt mit 50 Bytes mit dem Buchstaben X*/
  for(i=0; i<50; i++)
     write(fd, "X", 1);

  if((pid = fork()) < 0)
    {
      fprintf(stderr, "Fehler bei fork()......\n");
      exit(0);
    }
  else if(pid == 0) //Kindprozess
    {
      schreibsperre("Kind ", fd, 20, 0);
      sleep(3);
      schreibsperre("Kind" , fd, 0, 20);
    }
  else //Elternprozess
    {
      schreibsperre("Eltern ", fd, 0, 20);
      sleep(1);
      schreibsperre("Eltern " , fd, 20, 0);
    }
  exit(0);
}

In diesem Programmbeispiel wird mit Absicht ein Deadlock erzeugt. Es wird eine Datei dead.txt erstellt und beschreiben mit 50 Buchstaben (X).

Dann richtet der Elternprozess eine Sperre von Byte 0 bis Byte 19 ein und unser Kindprozess errichtet eine Sperre von Byte 20 bis zum Ende (EOF). Wir legen den Kindprozess für 3 sec. zum Schlafen damit richtet nun der Elternprozess eine Sperre von Byte 20 bis Byte EOF ein und wird dabei suspendiert, da Byte 20 bis EOF momentan noch vom Kindprozess gesperrt wird und wir das Kommando F_SETLKW benutzen. Zum Schluss versucht unser Kindprozess eine Schreibsperre von Byte 0 bis Byte 19 einzurichten wobei dieser ebenfalls suspendiert wird, da in diesem Bereich bereits eine Sperre von dem Elternprozess vorliegt und wir das Kommando F_SETLKW benutzt haben. Hier haben Sie nun ein Deadlock, was uns die Ausgabe auch bestätigen wird. Der Fehlercode für errno ist EDEADLK (Resource deadlock avoided). Deadlock können also nur Auftreten wenn sie das Kommando F_SETLKW benutzen. Sie können wenn Sie wollen das Programm mal mit dem Kommando F_SETLK testen. In diesem Fall ist der Fehlercode für errno EAGAIN (Resource temporarily unavailable).

Exkurs: Deadlock
Deadlock ist ein Thema von den man sagt, es passiert mir sowieso nie, wozu soll ich mich darum kümmern. Deadlocks können in unterschiedlichen Arten von Computersystemen auftreten, wie Beispielsweise in Betriebssystemen, in Datenbanken oder Rechnernetzwerken. Egal wo sie auftreten, im Aufbau sind sich diese alle ähnlich.

Deadlock sind klassische Synchronisationsprobleme, bei denen eine Situation auftritt, in der sich mehrer Prozesse um die selben Ressourcen streiten und dabei nicht mehr weiterkommen. Ein Beispiel und Problemlösung haben Sie ja bereits oben kennen gelernt.

Ein weitere Möglichkeit Deadlocks zu verhindern wäre es Ressourcen durchzunummerieren und in Aufsteigender Form anzufordern. Ist eine Ressource nicht freigegeben müssen alle anderen bisher angeforderten Ressourcen ebenfalls wieder freigegeben werden. Wenn sich alle Prozesse daran halten, kann kein Deadlock entstehen.

Wann könnten Deadlocks auftreten?
Ob es sich überhaupt lohnt, sich Gedanken darüber zu machen, klären die folgenden vier Punkte auf. Trifft einer der vier Punkte zu, kann theoretisch ein Deadlock auf Ihrem System auftreten.

Natürlich treten Deadlock häufiger auf Systemen auf, bei denen mehr Prozesse in einem System aktiv sind als normal und auf dem weniger Ressourcen für Betriebsmittel vorhanden sind.

Verhindern von Deadlocks
Außer den vorhin genannten Möglichkeiten, können Sie folgende Möglichkeiten durchdenken um Deadlocks zu verhindern ...

Exkurs: Livelocks
Livelocks sind Prozesse die in einer ständigen Aktivität gefangen sind. Sie kommen Beispielsweise nur zur Ruhe, wenn alle anderen Prozesse ebenfalls zur Ruhe kommen. Und natürlich andersherum genauso. Ein Beispiel wäre zwei Prozesse die immer wieder Daten hin-und-herschicken ohne das dabei etwas herauskommt. Eine Art Endlosschleife zwischen zwei Prozessen.

Weiter mit 8.1. Unterscheidung zwischen Threads und Prozessen