Ein Gerät positionieren

Wir haben den schwierigen Teil des Kapitels hinter uns. Wir werden jetzt noch schnell auf die Methode llseek eingehen, die nützlich und leicht zu implementieren ist.

Die Implementation von llseek

Die Methode llseek implementiert die Systemaufrufe lseek und llseek. Wir haben bereits erwähnt, daß bei fehlender Implementation von lseek im Gerätetreiber der Kernel Positionierungen vom Dateianfang oder von der aktuellen Position an gerechnet selbst durch Modifikation von filp->f_pos erledigt. Beachten Sie, daß die Methoden read und write kooperieren müssen, damit der Systemaufruf lseek korrekt arbeiten kann. Dazu müssen Sie das Offset-Element aktualisieren, das sie als Argument (normalerweise in Form eines Zeigers auf filp->f_pos) bekommen.

Wenn die Positionierungsoperation einer physikalischen Operation Ihres Geräts entspricht oder wenn das Suchen vom Dateiende aus, das von der Default-Methode nicht implementiert ist, sinnvoll ist, müssen Sie möglicherweise eine eigene llseek-Methode implementieren. Ein einfaches Beispiel finden Sie im scull-Treiber.


 

loff_t scull_llseek(struct file *filp, loff_t off, int whence)
{
  Scull_Dev *dev = filp->private_data;
  loff_t newpos;

  switch(whence) {
   case 0: /* SEEK_SET */
    newpos = off;
    break;

   case 1: /* SEEK_CUR */
    newpos = filp->f_pos + off;
    break;

   case 2: /* SEEK_END */
    newpos = dev->size + off;
    break;

   default: /* kann nicht passieren */
    return -EINVAL;
  }
  if (newpos<0) return -EINVAL;
  filp->f_pos = newpos;
  return newpos;
}

Die einzige gerätespezifische Operation, die wir hier verwenden, ist das Abfragen der Dateilänge vom Gerät. Die Implementation von scull kooperiert wie benötigt, wie in “the Section called read und write in Kapitel 3” in Kapitel 3 zu sehen war.

Obwohl die obige Implementation bei scull, wo ein wohldefinierter Datenbereich verwaltet wird, sinnvoll ist, haben die meisten Geräte mit einem Datenstrom anstelle eines Datenbereichs zu tun (denken Sie nur an serielle Ports oder die Tastatur). Dort sind Positionierungen wenig sinnvoll. Wenn dies der Fall ist, dann können Sie nicht einfach darauf verzichten, llseek zu implementieren, weil die Default-Methode ja das Positionieren erlaubt. Statt dessen sollten Sie den folgenden Code verwenden:


 
int scull_p_lseek (struct file *filp, loff_t off, int whence)
{
    return -ESPIPE; /* nicht positionierbar */
}

Die gezeigte Funktion stammt aus dem scullpipe-Gerät, das auch nicht positionierbar ist. Der Fehlercode wird zu “Illegal seek” (illegales Positionieren), auch wenn der symbolische Name “ist eine Pipe” bedeutet. Weil der Positionszeiger filp->f_pos für nicht positionierbare Geräte keine Bedeutung hat, müssen ihn weder read noch write bei der Datenübertragung aktualisieren.

Interessanterweise ist die Geräte-Methode llseek seit dem Hinzufügen von pread und pwrite zur Menge der unterstützten Systemaufrufe nicht mehr die einzige Möglichkeit, mit der ein User-Space-Programm in einer Datei navigieren kann. Eine korrekte Implementation von einem nicht positionierbaren Gerät sollte normale read- und write-Aufrufe erlauben, pread und pwrite aber verbieten. Dies geschieht mit der folgenden Zeile, der ersten in den read- und write-Methoden von scullpipe, die wir bei der Einführung dieser Methoden nicht besprochen haben:

 if (f_pos != &filp->f_pos) return -ESPIPE;