Hinweise zum erweiterten Zeichengerätetreiber des Linux-Kernel-Gerätetreibers

Hinweise zum erweiterten Zeichengerätetreiber des Linux-Kernel-Gerätetreibers
/******************
 * Erweiterter Zeichengerätetreiber *********************/

(1)ioctl

Neben dem Lesen und Schreiben auf Geräten erfordern die meisten Treiber die Möglichkeit, verschiedene Arten der Hardwaresteuerung über Gerätetreiber durchzuführen. Beispielsweise Medium auswerfen, Baudrate ändern usw. Diese Operationen werden durch die ioctl-Methode unterstützt, die den gleichnamigen Systemaufruf implementiert.

Im Benutzerbereich lautet der Prototyp des ioctl-Systemaufrufs:

  • : Der Befehl fd ist ein logisches Argument, das die Zeichenfolge fd enthält.
  • fd: Gerätedateideskriptor öffnen
  • cmd: Befehl
  • Der dritte Parameter: Kann je nach Befehl eine Ganzzahl, ein Zeiger oder nichts sein.
  • Die Methode "..." wird nur verwendet, um Compilerfehler zu vermeiden.

Die Prototypen der ioctl-Methoden des Treibers unterscheiden sich geringfügig von den Userspace-Versionen:

int (*ioctl) (Struktur inode *inode,
Strukturdatei *filp,
vorzeichenloser int-Befehl,
vorzeichenloses langes Argument);
inode/filp: entspricht dem fd des Benutzerbereichs
cmd: entspricht dem aus dem Benutzerbereich gesendeten cmd
arg: entspricht dem übergebenen cmd-Parameter

Die meisten ioctl-Implementierungen enthalten eine Switch-Anweisung, um die entsprechende Operation basierend auf dem cmd-Parameter auszuwählen. Die Befehlsnummern im Benutzerbereich und im Kernelbereich müssen konsistent sein.

(2) Wählen Sie die ioctl-Befehlsnummer

Bevor Sie Ioctl-Code schreiben, müssen Sie Nummern auswählen, die den verschiedenen Befehlen entsprechen. Sie können die Nummer nicht einfach bei 0 oder 1 beginnend wählen, da Linux erfordert, dass diese Befehlsnummer systemweit eindeutig ist. Der Linux-Kernel verwendet eine Konvention zur Auswahl von Ioctl-Nummern für Treiber. Weitere Informationen finden Sie unter include/asm/ioctl.h und Documentation/ioctl-number.txt.

Eine ioctl-Nummer ist 32 Bit lang. Linux unterteilt sie in vier Teile. Die zur Konstruktion einer ioctl-Nummer benötigten Makros sind in <linux/ioctl.h> definiert:

  • Geben Sie eine 8-Bit-Magic-Zahl ein. Tatsächlich geht es darum, eine Nummer für Ihren Treiber auszuwählen. Siehe ioctl-number.txt
  • Nummer 8-stellige Ordnungszahl.
  • Richtung 2 Bits. Definiert die Richtung der Datenübertragung. Wie _IOC_NONE (keine Datenübertragung), _IOC_READ|_IOC_WRITE (bidirektionale Datenübertragung). Beachten Sie, dass diese Anweisung für den Benutzer gilt. IOC_READ bedeutet also, dass Daten vom Gerät gelesen werden und der Treiber Daten in den Benutzerbereich schreiben sollte.
  • Größe 14 Bit. Die Größe der betroffenen Benutzerdaten.

Sie können die Makros in <linux/ioctl.h> verwenden, um eine ioctl-Nummer zu konstruieren

  • _IO(Typ, Nr)
  • _IOR(Typ,Nr.,Datentyp)
  • _IOW(Typ,Nr,Datentyp)

Rückgabewert

Bei Systemaufrufen werden positive Rückgabewerte zuerst geschützt, während negative Werte als Fehler betrachtet und zum Festlegen der Fehlervariable im Benutzerbereich verwendet werden. Wenn beim Aufruf der ioctl-Methode eine undefinierte ioctl-Nummer übergeben wird, lautet der vom System zurückgegebene Fehlerwert -ENVAL und -ENOTTY.

(3) Blockierende und nicht blockierende Operationen

Bei Vorgängen wie Lesen und Schreiben ist der Standardvorgang das Blockieren. Seine Merkmale sind:

*Wenn ein Prozess „read“ aufruft, aber keine zu lesenden Daten vorhanden sind, muss der Prozess blockiert werden. Wenn Daten eintreffen, wird der Prozess aufgeweckt und die Daten an den Aufrufer zurückgegeben, auch wenn die Datenmenge geringer ist als die durch den Zählparameter angegebene Datenmenge.

*Wenn ein Prozess „Write“ aufruft, aber kein Platz im Puffer vorhanden ist, muss dieser Prozess blockiert werden und in einer anderen Warteschlange schlafen als der Leseprozess. Wenn einige Daten auf das Hardwaregerät geschrieben werden und dadurch ein Teil des Ausgabepuffers freigegeben wird, wird der Prozess aktiviert und der Schreibaufruf ist erfolgreich.

Manchmal möchten wir diese Funktion ändern und sie nicht blockierend machen, sodass die Lese-/Schreibmethode sofort zurückkehrt, unabhängig davon, ob das Gerät Daten zum Lesen oder Schreiben hat.

Wenn Sie eine Datei so einstellen möchten, dass sie nicht blockiert, sollten Sie das Flag O_NONBLOCK von filp->f_flags setzen. Beim Umgang mit nicht blockierenden Dateien müssen Anwendungen beim Aufrufen von stdio-Funktionen sehr vorsichtig sein, da eine nicht blockierende Rückgabe leicht mit EOF verwechselt werden kann. Daher muss „errno“ immer überprüft werden.

(4) Asynchrone Benachrichtigung

a. Die Rolle der asynchronen Benachrichtigung

Meistens kann eine Kombination aus blockierenden und nicht blockierenden Operationen und der Select-Methode das Gerät effektiv abfragen, es gibt jedoch Zeiten, in denen diese Technik nicht effizient ist. In bestimmten zufälligen oder selten auftretenden Situationen (z. B. beim Eintippen von STRG+C auf der Tastatur) ist eine asynchrone Benachrichtigung erforderlich.

b. So starten Sie eine asynchrone Benachrichtigung im User Space-Programm

Um den asynchronen Benachrichtigungsmechanismus für eine Datei zu starten, muss das Benutzerprogramm zwei Schritte ausführen:

  • 01.Geben Sie einen Prozess als „Besitzer“ der Gerätedatei an. Wenn ein Prozess den Befehl F_SETOWN mit dem Systemaufruf fcntl ausführt, wird die Prozess-ID des Eigentümerprozesses in filp->f_owner gespeichert. Dieser Schritt ist notwendig, damit der Kernel weiß, wen er benachrichtigen muss.
  • 02. Um den asynchronen Benachrichtigungsmechanismus tatsächlich zu starten, muss das Anwenderprogramm zusätzlich noch das FASYNC-Flag im Gerät setzen, was über den fchtl-Befehl F_SETFL erfolgt. Nach dem Ausführen dieser beiden Schritte kann die Gerätedatei ein SIGIO-Signal anfordern, wenn neue Daten eintreffen. Das Signal wird an den in file->f_owner gespeicherten Prozess gesendet (oder an die Prozessgruppe, wenn dieser negativ ist). Nicht alle Geräte unterstützen asynchrone Benachrichtigungen und Anwendungen gehen normalerweise davon aus, dass nur Sockets und Terminals über asynchrone Benachrichtigungsfunktionen verfügen.

(5) So implementieren Sie asynchrone Benachrichtigungen im Treiber

a. Entsprechung von Userspace-Operationen im Kernel

  • 01. Wenn Sie F_SETOWN festlegen, weisen Sie file->f_owner einen Wert zu
  • 02. Wenn F_SETFL ausgeführt wird, um FASYNC zu starten, wird die Fasync-Methode des Treibers aufgerufen. Diese Methode wird immer dann aufgerufen, wenn das FASYNC-Flag in filp->f_flags (standardmäßig gelöscht, wenn die Datei geöffnet wird) geändert wird.
  • 03. Wenn Daten eintreffen, sendet der Kernel ein SIGIO-Signal an alle Prozesse, die für die asynchrone Benachrichtigung registriert sind

b. Fügen Sie den fasync_struct-Zeiger zur Gerätestruktur hinzu

Diese Struktur ist in <linux/fs.h> definiert:

Struktur fasync_struct {
int Magie;
int fa_fd;
Struktur fasync_Struktur *fa_next;
Strukturdatei *fa_file;
};

c. Zwei vom Treiber aufzurufende Funktionen

Diese beiden Funktionen sind in <linux/fs.h> deklariert.

Definiert in /fs/fcntl.c.

Der Prototyp sieht wie folgt aus:

  • 01. int fasync_helper(int fd, Strukturdatei *filp, int-Modus, Struktur fasync_Struktur **fa);
  • 02. void kill_fasync(struct fasync_struct **fa, int sig, int band);

Wenn das FASYNC-Flag einer geöffneten Datei geändert wird, wird fasync_helper aufgerufen, um die Datei zur Liste der zugehörigen Prozesse hinzuzufügen oder daraus zu entfernen, und kill_fasync benachrichtigt alle zugehörigen Prozesse, wenn Daten eintreffen.

Beispiel

01. Definieren Sie die dynamische Datenstruktur fasync_struct im Gerätetyp

Struktur meine_pipe {
  struct fasync_struct *async_queue; /* Asynchrone Lesestruktur*/
......
};

02. Die fasync-Funktion im Treiber ruft fasync_helper auf

int my_fasync(fasync_file fd, Strukturdatei *filp, int-Modus)
{
  meine_pipe *dev = filp->private_data;
  gibt fasync_helper zurück (fd, filp, Modus, &dev->async_queue);
}

03. Rufen Sie kill_fasync auf, wenn die Bedingungen für die asynchrone Benachrichtigung erfüllt sind

Die asynchrone Benachrichtigung gilt für einen Lesevorgang, daher sollte kill_fasync per Write gesendet werden.

Rufen Sie kill_fasync auf, um das Signal SIGIO an alle Prozesse zu senden, die in der asynchronen Warteschlange async_queue auf dem Gerät registriert sind.

ssize_t my_write(Strukturdatei *filp, const char *buf, size_t Anzahl,
        loff_t *f_pos)
{
......
wenn (dev->async_queue)
    kill_fasync(&dev->async_queue, SIGIO, POLL_IN); 
    ......
}

04. Die fasync-Methode muss beim Schließen einer Datei aufgerufen werden

Die Methode fasync muss beim Schließen einer Datei aufgerufen werden, um die Datei aus der Liste der aktiven asynchronen Reader zu entfernen.

Aufruf in der Version: scull_p_fasync(-1, filp, 0);

Zusammenfassen

Das Obige ist der vollständige Inhalt dieses Artikels. Ich hoffe, dass der Inhalt dieses Artikels einen gewissen Lernwert für Ihr Studium oder Ihre Arbeit hat. Vielen Dank für Ihre Unterstützung von 123WORDPRESS.COM. Wenn Sie mehr darüber erfahren möchten, schauen Sie sich bitte die folgenden Links an

Das könnte Sie auch interessieren:
  • Eine einfache Möglichkeit, Linux-Code auf Windows zu portieren
  • Hinweise zur Speicherverwaltung von Linux-Kernel-Gerätetreibern
  • Hinweise zur Zeitverwaltung des Linux-Kernel-Gerätetreibers
  • Hinweise zum Zeichengerätetreiber des Linux-Kernel-Gerätetreibers
  • Hinweise zum virtuellen Dateisystem des Linux-Kernel-Gerätetreibers
  • Hinweise zum Systemaufruf des Linux-Kernel-Gerätetreibers
  • Sortierung der technischen Hinweise zum Linux-Kernel-Gerätetreiber-Kernel-Debugging
  • Hinweise zur Verwendung der verknüpften Liste des Linux-Kernel-Gerätetreibers
  • Hinweise zum Proc-Dateisystem des Linux-Kernel-Gerätetreibers
  • Linux-Kernel-Gerätetreiber. Zusammenfassung der Hinweise zum Lademechanismus von Linux-Kernelmodulen.
  • Hinweise zur Adresszuordnung von Gerätetreibern im Linux-Kernel
  • Gerätetreiber des Linux-Kernels – Zusammenfassung der grundlegenden Hinweise zum Linux-Kernel
  • Schritte zum Übertragen des neuen Kernels auf das Linux-System

<<:  Eine kurze Diskussion über allgemeine Operationen von MySQL in cmd und Python

>>:  Detaillierte grafische Erläuterung der MySql5.7.18-Zeichensatzkonfiguration

Artikel empfehlen

Der Unterschied zwischen Datenzeit und Zeitstempel in MySQL

In MySQL gibt es drei Datumstypen: Datum (Jahr-Mo...

Einige Fragen zu Hyperlinks

<br />Ich freue mich sehr, an dieser Folge d...

Vue implementiert die Anzeige und Ausblendung der dreistufigen Navigation

In diesem Artikelbeispiel wird der spezifische Co...

Detaillierter Installationsprozess des NodeJS-Verwaltungstools NVM

keine Ahnung nvm ist für die Verwaltung mehrerer ...

So nummerieren Sie die Ergebnisse von MySQL-Abfragedaten automatisch

Vorwort Tatsächlich bin ich noch nie auf eine Sit...

Eine kurze Diskussion zum Implementierungsprinzip des Vue-Slots

Inhaltsverzeichnis 1. Beispielcode 2. Sehen Sie d...

Zusammenfassung der Vorteile von Vue3 gegenüber Vue2

Inhaltsverzeichnis 1. Warum brauchen wir vue3? 2....

jQuery implementiert Navigationsleisteneffekt mit Erweiterungsanimation

Ich habe eine Navigationsleiste mit einem erweite...

Beispiel für eine HTTPS-Konfigurationsmethode für den Nginx-Server

Linux: Linux-Version 3.10.0-123.9.3.el7.x86_64 Ng...

Zusammenfassung zur Verwendung der Bootstrap-Tabelle

In diesem Artikel erfahren Sie, wie Sie die Boots...

HTML-Lernhinweise – Detaillierte Erklärung der HTML-Syntax (unbedingt lesen)

1. Was ist die HTML-Auszeichnungssprache? HTML is...