2013-04-17 10 views
5

Ich programmiere einen einfachen Linux-Zeichengerätetreiber, um Daten über I/O-Ports an eine Hardware auszugeben. Ich habe eine Funktion, die Fließkommaoperationen ausführt, um die korrekte Ausgabe für die Hardware zu berechnen; Das bedeutet leider, dass ich diese Funktion im Userspace behalten muss, da der Linux-Kernel keine Fließkommaoperationen sehr gut beherrscht.Aufruf einer Userspace-Funktion innerhalb eines Linux-Kernel-Moduls

Hier ist eine Pseudo-Darstellung des Setup (beachten Sie, dass dieser Code nicht etwas Bestimmtes tut, es zeigt nur die relative Layout meines Codes):

Userspace-Funktion:

char calculate_output(char x){ 
    double y = 2.5*x; 
    double z = sqrt(y); 

    char output = 0xA3; 

    if(z > 35.67){ 
     output = 0xC0; 
    } 

    return output; 
} 

Kernelcode:

unsigned i; 
for(i = 0; i < 300; i++){ 
    if(inb(INPUT_PORT) & NEED_DATA){ 
     char seed = inb(SEED_PORT); 
     char output = calculate_output(seed); 
     outb(output, OUTPUT_PORT); 
    } 

    /* do some random stuff here */ 
} 

ich dachte über 01 mit, um die Daten von der Userspace-Funktion zu übergeben, aber ich bin mir nicht sicher, wie die Tatsache behandelt wird, dass der Funktionsaufruf in einer Schleife ist und mehr Code ausgeführt wird, bevor der nächste Aufruf an calculate_output auftritt.

Die Art, wie ich diese Arbeit vorstellen, ist:

  1. Haupt User-Space-Programm wird den Kernel- Code (vielleicht über ioctl)
  2. Userspace-Programm Blöcke und wartet auf Kernel- Code
    • Kernel- Programm starten fragt Userspace-Programm für Ausgabedaten und Blöcke warten
    • Anwenderseite Programm entblockiert, berechnet und sendet Daten (ioctl?), Dann Blöcke wieder
    • Kernel- Programm und setzt
    • Anwenderseite Anwenderseite entblockiert
  3. Kernel- Programm beendet und benachrichtigtentsperrt und fährt mit der nächsten Aufgabe fort

Also, wie habe ich die Kommunikation zwischen Kernelspace und Userspace, und auch Blockierung, so dass ich den Userspace nicht ständig eine Gerätedatei abfragen, um zu sehen, ob es Daten senden muss?


Ein Nachteil: Während Festkommaarithmetik würde in meinem Beispiel Code recht gut funktionieren, es ist keine Option in dem realen Code; Ich benötige den großen Bereich, den der Gleitkomma-Punkt bietet, und - selbst wenn nicht - ich befürchte, den Code neu schreiben zu müssen, um Festkommaarithmetik zu verwenden, würde den Algorithmus für zukünftige Betreuer verdecken.

+0

Jede Möglichkeit, Sie können fast alles im Benutzerbereich haben, und nur eine dünne Schnittstelle Enabler im Kernel? –

+0

@ChrisStratton Das versuche ich zu erreichen. Das Zeug, das innerhalb der for-Schleife auftritt, ist mehr verschiedene Hardware-Ausgabe (einfache Outb-Aufrufe und nichts anderes). –

+0

@ChrisStratta Oh, warte, meinst du die 'for' -Schleife außerhalb des Kernels verschieben? –

Antwort

4

Ich denke, die einfachste Lösung wäre, ein Zeichengerät in Ihrem Kernel-Treiber mit eigenen Dateioperationen für eine virtuelle Datei zu erstellen. Dann kann der Benutzerbereich dieses Gerät öffnen O_RDWR. Sie haben zwei Hauptdateioperationen zu implementieren:

  • read - das ist, wie der Kernel-Daten bis zu User-Space geht zurück. Diese Funktion wird im Kontext des Userspace-Threads ausgeführt, der den Systemaufruf read() aufruft, und in Ihrem Fall sollte er so lange blockiert werden, bis der Kernel einen anderen Startwert hat, für den er die Ausgabe kennen muss.

  • write - so gibt der Userspace Daten an den Kernel weiter. In Ihrem Fall würde der Kernel einfach die Antwort auf den vorherigen Lesevorgang übernehmen und sie an die Hardware weitergeben.

Dann beenden Sie mit einer einfachen Schleife im User-Space up:

while (1) { 
    read(fd, buf, sizeof buf); 
    calculate_output(buf, output); 
    write(fd, output, sizeof output); 
} 

und keine Schleife überhaupt in der Kernel - alles läuft im Rahmen des User-Space Prozesses, Dinge treiben, und Der Kernel-Treiber ist nur dafür verantwortlich, die Daten zur/von der Hardware zu bewegen.

Je nachdem, was Sie hier auf der Kernel-Seite tun, ist es vielleicht nicht ganz so einfach. Wenn Sie wirklich die Kernel-Schleife benötigen, müssen Sie einen Kernel-Thread erstellen, um diese Schleife auszuführen, und dann einige Variablen in den Zeilen input_data, input_ready, output_ready, zusammen mit ein paar Wartezeiten und was für Sperren Sie benötigen.

Wenn der Kernel-Thread Daten liest, stellen Sie die Daten in input_ready und stellen die input_ready Flag und das Signal des Eingangs waitqueue und dann wait_event(<output_ready is set>) tun. Die Dateioperation read würde eine wait_event(<input_ready is set>) tun und die Daten an den Benutzerbereich zurückgeben, wenn es fertig ist. In ähnlicher Weise würde die write Dateioperation die Daten, die sie aus dem Benutzerbereich erhält, in output_data setzen und output_ready setzen und die Ausgabewarteschlange signalisieren.

Ein anderer (hässlicher, weniger portabler) Weg ist, etwas wie ioperm, iopl oder /dev/port zu verwenden, um alles komplett im Benutzerbereich zu tun, einschließlich des Low-Level-Hardwarezugriffs.

+2

Ich habe im Wesentlichen implementiert, was hier beschrieben wird, obwohl ich 'read' /' write' auf einem ** sysfs ** Attribut anstelle eines Zeichen-Geräte-Knotens verwende. –

0

Ich würde vorschlagen, dass Sie den Code, der alle "Heavy Lifting" in den Benutzermodus macht - das heißt, berechnen Sie alle 300 Werte auf einmal, und diese an den Kernel übergeben.

Ich bin nicht einmal sicher, dass Sie einen beliebigen Code den Benutzermodus vom Kernel aufrufen lassen können. Ich bin mir sicher, dass es möglich ist, denn das ist zum Beispiel "Signal", aber ich bin weit davon entfernt, davon überzeugt zu sein, dass man es "beliebig" machen kann (und mit ziemlicher Sicherheit gibt es Einschränkungen, z. B. was) Sie können in dieser Funktion tun). Es scheint sicherlich keine gute Idee zu sein, und es wäre definitiv ziemlich langsam, viele Male zum Usermode zurückzurufen.

+0

Leider kann die Ausführungssequenz nicht geändert werden, was das Verschieben der Schleife in den Benutzerbereich erschwert. Ich habe meinen Beispielcode so angepasst, dass er die sequenzielle Abhängigkeit wiedergibt, der ich gegenüberstehe (d. H. Frühere Hardware-Ausgaben können die an die Funktion gesendete Eingabe beeinflussen). –

+1

Dann würde ich vorschlagen, dass Sie einen Weg finden, Ihre Berechnung (mit Fixpunkt oder ähnlichem) im Kernel durchzuführen - das Zurückrufen für jedes Byte in usermode ist einfach keine gute Idee - es dauert 10-100x länger als die Berechnungen, die Sie zeigen Hier. –

Verwandte Themen