Allgemeine Daten zu Linux, Tipps und Tricks und Systemeinstellungen: Unterschied zwischen den Versionen

Aus m-wiki
Zur Navigation springen Zur Suche springen
 
Zeile 229: Zeile 229:


* module_param() -> Zur initialisierung der übergebenen Argumente. Neben dem Namen muss der Typ und die Berechtigungsmaske aus obigen Werten übergeben werden. Das Makro muss ausserhalb jeder Funktion stehen und wird normalerweise direkt unterhalb des Headers platziert. Original definiert ist es in <code>linux/moduleparam.h</code> als <code>module_param(name, type, perm);</code>. Der Aufruf geschieht beispielhaft als <code>module_param(valueETX, int, S_IWUSR|S_IRUSR);</code>. Folgende Typen stehen zur Verfügung: int, long, short, uint, ulong, ushort, charp (Pointer auf char im Speicher für Zeichenkettten), bool, invbool (invertiertes bool)
* module_param() -> Zur initialisierung der übergebenen Argumente. Neben dem Namen muss der Typ und die Berechtigungsmaske aus obigen Werten übergeben werden. Das Makro muss ausserhalb jeder Funktion stehen und wird normalerweise direkt unterhalb des Headers platziert. Original definiert ist es in <code>linux/moduleparam.h</code> als <code>module_param(name, type, perm);</code>. Der Aufruf geschieht beispielhaft als <code>module_param(valueETX, int, S_IWUSR|S_IRUSR);</code>. Folgende Typen stehen zur Verfügung: int, long, short, uint, ulong, ushort, charp (Pointer auf char im Speicher für Zeichenkettten), bool, invbool (invertiertes bool)
* module_param_array() -> Mit idesem Makro übergibt man ein Array mit den Argumenten an den Kernel. <code>module_param_array(name, type, num, perm);</code> "name" ist der Name des Arrays (und der Parameter), "type" ist der Typ der Array Elemente, "num" ist ein (optionaler) integer, der NULL sein kann und "perm" ist die Berechtigung, die oben gezeigt wurde.
* module_param_array() -> Mit diesem Makro übergibt man ein Array mit Argumenten an den Kernel anstatt eines einzelnen Wertes. <code>module_param_array(name, type, num, perm);</code> "name" ist der Name des Arrays (und der Parameter), "type" ist der Typ der Array-Elemente, "num" ist ein (optionaler) integer, der NULL sein kann und "perm" ist die Berechtigung, die oben gezeigt wurde und wie bei einer Variable hier fürs gesamte Array angewendet wird.
* module_param_cb() -> Man kann nicht nur Werte übergeben, sondern auch benachrichtigt werden, wenn ein Wert geändert wurde. Hat man etwa eine Temperaturfunktion, so kann man mit diesem Makro dafür sorgen, dass man benachrichtigt wird, wenn der Wert ändert. Dies geschieht dadurch, dass beim Erstellen des Wertes mit <code>module_param()</code> eine entsprechende Datei unterhalb des Treibers erstellt wird (/sys/module/<modulname>/parameters/<Argumentname>). Durch das Makro wird nun geschaut, wann sich dieser Wert ändert.
* module_param_cb() -> Man kann nicht nur Werte übergeben, sondern auch benachrichtigt werden, wenn ein Wert geändert wurde. Hat man etwa eine Temperaturfunktion, so kann man mit diesem Makro dafür sorgen, dass man benachrichtigt wird, wenn der Wert ändert. Dies geschieht dadurch, dass beim Erstellen des Wertes mit <code>module_param()</code> eine entsprechende Datei unterhalb des Treibers erstellt wird (/sys/module/<modulname>/parameters/<Argumentname>). Durch das Makro wird nun geschaut, wann sich dieser Wert ändert. Es könne nur Variablen kontrolliert werden, die mit diesem Modul erstellt werden. "Normale" Varaiblen aus den anderen Modulen kann man nach der Übergabe nicht mehr "beobachten".
 
Die folgende Datei zeigt eine erweiterte Version, welche die oben besprochenen Parameter implementiert.
 
/***************************************
*  \file      driver2.c
*  \details    Simple hello world driver
*  \author    EmbeTronicX
* *************************************/
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/module.h>
#include<linux/moduleparam.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeTronicX <embetronicx@gmail.com>");
MODULE_DESCRIPTION("A simple hello world driver");
MODULE_VERSION("1.0");
int valueETX, arr_valueETX[4]; // Variable mit int und int-Arry mit 4 Werten
char *nameETX;                  // String-Variable
int cb_valueETX = 0;            // Überwachung der valueETX-Variable
module_param(valueETX, int, S_IRUSR|S_IWUSR);                      // Integer-Wert definieren
module_param(nameETX, charp, S_IRUSR|S_IWUSR);                    // String
module_param_array(arr_valueETX, int, NULL, S_IRUSR|S_IWUSR);      // Integer-Array
/*----------------------Module_param_cb()--------------------------------*/
int notify_param(const char *val, const struct kernel_param *kp) {
        int res = param_set_int(val, kp); // Use helper for write variable
        if(res == 0) {
                printk(KERN_INFO "Call back function called...\n");
                printk(KERN_INFO "New value of cb_valueETX = %d\n", cb_valueETX);
                return 0;
        } // end if
        return -1;
} // end notify_param()
const struct kernel_param_ops my_param_ops = { // dieser Aufruf sorgt dafür, dass auf die
        .set = &notify_param, // Use our setter ...
        .get = &param_get_int, // .. and standard getter
};
module_param_cb(cb_valueETX, &my_param_ops, &cb_valueETX, S_IRUGO|S_IWUSR); // Der Modulaufruf bekommt Leserechte für den Besitzer und die Gruppenbesitzer (siehe oben im Artikel) -> siehe auch https://lwn.net/Articles/696227/
/* Module init function */
static int __init hello_world_init(void) {
        int i;
        printk(KERN_INFO "ValueETX = %d  \n", valueETX);
        printk(KERN_INFO "cb_valueETX = %d  \n", cb_valueETX);
        printk(KERN_INFO "NameETX = %s \n", nameETX);
        for (i = 0; i < (sizeof arr_valueETX / sizeof (int)); i++) {
                printk(KERN_INFO "Arr_value[%d] = %d\n", i, arr_valueETX[i]);
        } // end for
        printk(KERN_INFO "Kernel Module Inserted Successfully...\n");
    return 0;
} // end hello_world_init()
/* Module Exit function */
static void __exit hello_world_exit(void) {
    printk(KERN_INFO "Kernel Module Removed Successfully...\n");
} // end hello_world_exit()
module_init(hello_world_init);
module_exit(hello_world_exit);
 
Der untere Teil besteht wieder aus den Standard-Konstruktoren, während oben nach der Variablendeklaration und deren Übergabe ans Makro noch eine Funktion folgt, welche die Verarbeitung der Callback-Variable übernimmt und hier einfach eine entsprechende Meldung ins Kernellog generiert.
 
Das Modul kann nun mit den Parametern aufgerufen werden: <code>sudo insmod hello_world_module.ko valueETX=14 nameETX="EmbeTronicX" arr_valueETX=100,102,104,106</code> Auch hier liefert <code>dmesg</code> wieder die notwendigen Ausgaben. Diesmal mit allen Parametern. Um den Callback zu testen, wird die Callback-Variable <code>cb_valueETX</code> auf 13 gesetzt mit <code>sudo sh -c "echo 13 > /sys/module/hello_world_module/parameters/cb_valueETX"</code> und ein anschliessendes <code>dmesg</code> zeigt, dass die Überwachungsfunktion aufgerufen wurde und den Wert zurückgegeben hat.


[[Kategorie:Computer]][[Kategorie:Linux]][[Kategorie:Software]][[Kategorie:Hardware]]
[[Kategorie:Computer]][[Kategorie:Linux]][[Kategorie:Software]][[Kategorie:Hardware]]

Aktuelle Version vom 1. September 2022, 12:46 Uhr

Kommandozeile

Versionen herausfinden

Was herausfinden Befehl Ausgabe, Bemerkungen
Betriebsysstem uname -m x86_64 -> 64-Bit-System, i686 -> 32-Bit-Linux
Prozessorinfo cat /proc/cpuinfo
Würde ein 64-Bit System funktionieren? grep lm leer -> funktioniert nicht, 1-2 Zeilen -> würde funktionieren
Grafikkarte /usr/sbin/hwinfo --gfxcard
Kernelversion cat /proc/version Kurzversion: uname -r oder uname -a
PHP Version less Mit dem Parameter -r kann php interactiv direkt in der Shell oder in Scripts verwendet werden.

Wichtige Befehle und Kniffs

Was herausfinden Befehl Ausgabe, Bemerkungen
Dateien suchen locate <Datei> http://www.tippscout.de/linux-dateien-finden_tipp_5.html
Ist auf dem Raspi standardmässig nicht installiert. Installieren mit dem Paketnamen sudo apt-get install mlocate
Da der Befehl in einer internen Datenbank sucht, kann man sich diese mit sudo updatedbaktualisieren lassen.
Dateien suchen find <Verzeichnis> <Parameter> <Datei> Alle Dateien in allen Unterverzeichnissen ab dem aktuellen Verzeichnis finden, welche die mitgegebene Dateiendung haben: find . -name '*.ogg' Man kann dem Befehl auch Optionen wie Dateigrösse oder Bearbeitungszeitpunkt mitgeben. Siehe dazu den Link oben bei locate.
Suche in man Pages man Programm /Suchbegriff man bash /prompt bringt Hilfe zum Programm bash und geht zum ersten Eintrag in dem prompt erwähnt wird. Mit / und Enter geht es zum nächsten Eintrag.
Rechnen in der Kommandozeile Einfach Rechnung direkt eingeben
"unbekannten" Befehl herausfinden apropos Thema Findet Befehle, welche indexiert wurden zum entsprechenden Thema. apropos sql findet Befehle zum Thema und danach kann man sich über man die genaue Verwendung anzeigen lassen.
chown auf ganze Verzeichnisse chown -R Verzeichnis . Nicht den * verwenden, da dieser als "erweiterer" der Shell übernommen wird und sich dann im Hauptverzeichnis falsch auswirkt. Mit dem "." hingegen meint man das aktuelle Verzeichnis und so wirkt der Befehl korrekt.
Log-Dateien live beobachten tail -f Logdatei Abbruch mit Ctrl-c. tail ohne -f zeigt einfach die letzten Zeilen einer Datei an und beendet dann automatisch.
Befehl abbrechen während Eingabe Einfach Ctrl-C drücken
Programm im aktuellen Verzeichnis starten ./Programmname Verhindert, dass falsche Programme unabsichtlich nach einem Hack gestartet werden. Daher müssen Programme in lokalen Verzeichnissen explitit aufgerufen werden, solange dieser nicht in $path gespeichert ist.
Festplattenplatz df -h h steht für "human readable". Dieser Parameter kann vielfach auch bei anderen Programmen verwendet werden.
Prozesse anschauen top
Prozesse beenden killall Programmname Bei hartnäckigen: killall -KILL Programmname
Dateimanager mc Midnight Commander ist evtl. installiert. Funktioniert wie der Norton Commander. Falls nicht farbig -> mc -c

Wichtige Befehle kurz erklärt

Befehl Beispiel Bemerkungen
Befehlstabelle http://blog.addison-wesley.de/wp-content/uploads/2010/2877_Kommandoreferenz-Poster.pdf
alias Alias von einem Befehl: alias wwwneu=´/etc/rc.d/init.d/apache restart´ Definiert einen Namen für einen Befel oder eine Befehlsreihenfolge: http://www.tippscout.de/linux-aliase-sparen-tipparbeit_tipp_736.html oder http://www.tippscout.de/komplizierte-linux-befehle-als-alias-definieren_tipp_3311.html
find Dateien von gestern suchen: find . -name '*.doc' -type f -mtime -1 Findet Dateien: http://www.tippscout.de/linux-dateien-nach-datum-finden_tipp_2047.html
locate Datei suchen: locate... http://www.tippscout.de/linux-dateien-finden_tipp_5.html

Nice to know

Nicht so wichtig, doch manchmal doch nützlich.

Was herausfinden Befehl Ausgabe, Bemerkungen
Wie lange läuft der Rechner schon uptime
Linux schnell herunterfahren halt sonst: shutdown -h now
Linux schnell neu starten reboot sonst: shutdown -r now
Funktionstasten simulieren ESC->Zahl Entspricht der jeweiligen Funktionstaste. Je nach Linuxversion und PC/MAC notwendig. ESC anschliessend 2 -> Entspricht F2.
Liste aller Pakete unter Ubuntu/Debian dpkg http://www.tippscout.de/linux-ubuntu-liste-pakete_tipp_5455.html
Wer ist auf dem System auch noch angemeldet who
Speicher ermitteln free -m zeigt in Megabyte und zusätzlich -t koppelt noch mit Auslagerungsspeicher
Mail von Konsole versenden mail -s"Testmail" foo@foo.bar | kein Abstand nach -s und meist werden solche Mails wegen Spamgefahr vom Provider verworfen.

Netzwerk

ifconfig/iwconfig erledigt das meiste:

Befehl Auswirkung Ausgabe, Bemerkungen
ifconfig -a Anzeige, welche Netzwerkschnittstellen welche IP-Konfiguration haben
Linux schnell herunterfahren halt
ifconfig eth0 192.168.0.2 netmask 255.255.255.0 IP manuell zuweisen
iwconfig Konfiguration des WLANs

Remotezugriff auf Rechner

Einloggen per SSH

ssh benutzername@server
Falls danach das Serverzertifikat kommt (wenn korrekt), bestätigen, Passwort eingeben und schon ist man eingeloggt.

Zugang friert ein

Beim Shell-Zugriff auf einen Linux-Rechner kann es vorkommen, dass der Bildschirm einfriert. Meistens liegt das Problem nur an einer falsche Tastenkombination. Denn drückt man versehentlich [Ctrl-S], stoppt das System jegliche Ausgabe. Erst durch die Tastenkombination [Ctrl-Q] wird dieser Zustand aufgehoben.

KDE

Nützliche Programme:

Einsatz Name Bemerkungen
Systemauslastung anzeigen ksim

32Bit/64Bit

32Bit Programme unter 64Bit System

http://www.tippscout.de/linux-ubuntu-programm-nicht-gefunden_tipp_5406.html

Kernel

Der Kernel ist das Herz von Linux und stellt alle notwendigen Betriebssystem-Funktionen zur Verfügung.

Kernel-Treiber-Grundlagen

Im Kernel-Treiber-Tutorial wird aufgezeigt, wie Kernel-Treiber arbeiten und wie sie aufgebaut sind. Grundsätzlich gibt es den User-Space, wo sich die Programme befinden und den Kernel-Space, in dem auch die Treiber sind. Über die System-Schnittstelle können Programme mit dem Kernel kommunizieren und Funktionen aufrufen, welche dann vom Kernel über die Treiber an die Hardware weitergeleitet werden. LKM (Linux Kernel Modul) sind Module, welche dynamisch nach/während des Systemstarts zum Grundkernel hinzugefügt werden. Dies hat den Vorteil, dass man sie nicht in die Sourcen hinein kompilieren muss. LKM werden (unter anderem) für folgende Bereiche verwendet:

  • Gerätetreiber (Hardware)
  • Dateisystem-Treiber (Festplatten, Memorykarten, Netzwerksysteme wie Samba)
  • Systemaufrufe (Datei lesen, Prozess stoppen, System neu starten)

Folgende Unterschiede bestehen zwischen Benutzerprogrammen und Kernel-Modulen:

  • Unterschiedlicher Adressbereich im Speicher. Dadurch werden Module vor Programmen geschützt.
  • Höhere Ausführungsprivilegien als normale Programme.
  • Die Module werden nicht nacheinander ausgeführt, sondern registrieren sich einfach, damit sie später aufgerufen werden können.
  • Für die Erstellung werden unterschiedliche Header-Dateien verwendet.

Der Unterschied zwischen einem Kernelmodul und einem Gerätetreiber besteht darin, dass das Modul zur Laufzeit mit insmod oder modprobe hinzugefügt werden kann, während Gerätetreiber fix im Kernel einkompiliert sind. Geräte werden in Linux immer als Datei angesprochen. Gerätetreiber lassen sich in folgende Gruppen aufteilen.

Zeichengeräte

Lesen Daten Zeichen für Zeichen ein. Beispiele sind die Tastatur, die Maus oder serielle Drucker. Wird ein Gerät von jemandem benutzt, so kann niemand anders darauf zugreifen. Der gleichzeitige Zugriff ist blockiert. Beim Schreiben auf das Gerät werden die Daten synchronisiert. Für die Kommunikation mit anderen Geräten werden häufig Zeichengerätetreiber verwendet. Diese Geräte kann man entsprechend auch nicht mounten. Character Device

Blockgeräte

Im Gegensatz zu Zeichengeräten werden hier Zeichen blockweise gelesen und geschrieben. Entsprechend ist dieser Typ hilfreich, wenn man massenweise Daten zu verarbeiten hat. Alle Massenmedien wie HDD/SDD, Memorysticks oder CD/DVD sind Blockgeräte. Beim Formatieren wird das Gerät blockweise aufgeteilt. Daten werden meist asynchron geschrieben (mit Puffer) und brauchen die CPU intensiv. Die Geräte können bei Bedarf angeschlossen mount oder abgekoppelt umount werden.

Netzwerkgeräte

Diese Geräte senden und erhalten Pakete in einem Datenstrom. Neben Netzwerkkarten können die Geräte auch in SOftware ausgeführt sein wie das Loopback-Gerät oder VPN-Verbindungen.

Modulaufbau

/********************************************
*  \file       driver.c
*  \details    Simple hello world driver
*  \author     EmbeTronicX
*********************************************/
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/module.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeTronicX <embetronicx@gmail.com>");
MODULE_DESCRIPTION("A simple hello world driver");
MODULE_VERSION("2:1.0");

/* Module Init function */
static int __init hello_world_init(void) {
   printk(KERN_INFO "Welcome to EmbeTronicX\n");
   printk(KERN_INFO "This is the Simple Module\n");
   printk(KERN_INFO "Kernel Module Inserted Successfully...\n");
   return 0;
} // end init

/* Module Exit function */
static void __exit hello_world_exit(void) {
   printk(KERN_INFO "Kernel Module Removed Successfully...\n");
} // end exit

module_init(hello_world_init);
module_exit(hello_world_exit);

Das Modul besitzt zwei Konstruktoren, welche beim Start (insmod modulname) und beim Beenden (rmmod modulname) aufgerufen werden. Die Headerdateien stellen Standardmodule zur Verfügung, welche man verwenden kann, um die Lizenz usw. einzubinden, welche bei modinfo ausgegeben werden.

Wie im Kommentar am Anfang gezeigt, wird diese Datei als "driver.c" gespeichert. Der Name sollte hierbei der Hardware für den Treiber entsprechen, so dass man weiss, was für eine Art Treiber kompiliert wird.

Ausgabe (printk)

Da printf eine Funktion des Userspace ist, kann sie nicht verwendet werden. Mit printk gibt es eine entsprechende Funktion im Kernel. Dort kann mit dem ersten Parameter festgelegt werden, auf welcher Stufe der folgende Ausgabetext erfolgt (KERN_EMERG, KERN_ALERT, KERN_CRIT, KERN_ERR, KERN_WARNING, KERN_NOTICE, KERN_INFO, KERN_DEBUG). printk(KERN_INFO "Willkommen im Treiber"); Die Ausgaben des Kernels kann man mit dmesg anschauen und filtern nach der Ausgabestufe. Neuere Kernelversionen erlauben die Verwendung der APIs anstatt von printk. Der Aufruf wird dann mit pr_info("test info message\n") durchgeführt (pr_debug, pr_info, pr_warn, pr_err, pr_cont). pr_cont führt eine vorhergehende Meldung auf der gleichen Linie im Logbuch weiter.

Modul erstellen und testen

Um das Modul zu kompilieren, erstellt man am besten zuerst ein Makefile.

obj-m += driver.o

ifdef ARCH
 #You can update your Beaglebone path here.
 KDIR = /home/embetronicx/BBG/tmp/lib/modules/5.10.65/build
else
 KDIR = /lib/modules/$(shell uname -r)/build
endif

all:
 make -C $(KDIR)  M=$(shell pwd) modules

clean:
 make -C $(KDIR)  M=$(shell pwd) clean

Wie man sieht, kann man den Pfad für die Kompilierung abhängig von einer Architektur machen und wenn diese nicht gefunden wird, einen Standardpfad nehmen. Die Objektdatei entsprechend dem Treibernamen wählen. Möchte man nicht nur für die Architektur kompilieren auf der man den Vorgang durchführt, so kann man dies nun mitgeben. sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- Sonst genügt ein einfaches sudo make.

Neben der Objektdatei wird auch eine Kernelobjektdatei generiert, welche die Endung .ko hat. Diese kann nun einfach mit sudo insmod modulname.ko geladen werden. Ein anschliessendes lsmod zeigt, dass das Modul (zuoberst) aufgeführt wird und mit dmesg sieht man auch die Nachrichten, die im Modul generiert wurden. Das Laden kann nur im Erstellungsverzeichnis gemacht werden, solange man das Modul nicht öffentlich verfügbar macht, was später gezeigt wird.

Das Entladen mit sudo rmmod modulname (geht ohne Dateiendung) und eine anschliessende Kontrolle mit dmesg sollte zeigen, dass das Entfernen auch sauber funktioniert.

Die Zusatzangaben lassen sich auch sauber mit <codesudo modinfo driver.ko erfragen (auch hier muss das .ko angegeben werden).

Parameter ans Modul übergeben

Wie jedem c-Programm kann man auch einem Kernelmodul Argumente mitgeben. Dies ist notwendig, wenn man den Treiber individualisieren möchte. Wenn es etwa verschiedene Varianten der Hardware gibt und man so einfach angeben kann, welche Variante verwendet wird, wenn die Hardware diese Daten nicht selber liefert. Als Beispiel etwa ein LED-Array, das 8x8 oder 4x16 oder 8x16 usw. LEDs umfassen kann.

Damit die Variablen auch korrekt verarbeitet werden, muss man beachten, welche Berechtigungen es dafür gibt. Diese werden folgendermassen unterschieden: S_IWUSR, S_IRUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP. Wie man sieht, wird hier das Konzept der Dateien integriert mit Lesen(R), Schreiben(W) und Ausführen(X). Dann wird noch zwischen dem Benutzer(USR) und der Gruppe(GRP) unterschieden. Das S_I am Anfang ist ein allgemeiner Header und könnte "secure identity" bedeuten. Die Berechtigungen kombiniert man mit dem Pipe-Zeichen "|", welches für ein "oder" steht.

Für die Verarbeitung stehen folgende drei Makros zur Verfügung:

  • module_param() -> Zur initialisierung der übergebenen Argumente. Neben dem Namen muss der Typ und die Berechtigungsmaske aus obigen Werten übergeben werden. Das Makro muss ausserhalb jeder Funktion stehen und wird normalerweise direkt unterhalb des Headers platziert. Original definiert ist es in linux/moduleparam.h als module_param(name, type, perm);. Der Aufruf geschieht beispielhaft als module_param(valueETX, int, S_IWUSR|S_IRUSR);. Folgende Typen stehen zur Verfügung: int, long, short, uint, ulong, ushort, charp (Pointer auf char im Speicher für Zeichenkettten), bool, invbool (invertiertes bool)
  • module_param_array() -> Mit diesem Makro übergibt man ein Array mit Argumenten an den Kernel anstatt eines einzelnen Wertes. module_param_array(name, type, num, perm); "name" ist der Name des Arrays (und der Parameter), "type" ist der Typ der Array-Elemente, "num" ist ein (optionaler) integer, der NULL sein kann und "perm" ist die Berechtigung, die oben gezeigt wurde und wie bei einer Variable hier fürs gesamte Array angewendet wird.
  • module_param_cb() -> Man kann nicht nur Werte übergeben, sondern auch benachrichtigt werden, wenn ein Wert geändert wurde. Hat man etwa eine Temperaturfunktion, so kann man mit diesem Makro dafür sorgen, dass man benachrichtigt wird, wenn der Wert ändert. Dies geschieht dadurch, dass beim Erstellen des Wertes mit module_param() eine entsprechende Datei unterhalb des Treibers erstellt wird (/sys/module/<modulname>/parameters/<Argumentname>). Durch das Makro wird nun geschaut, wann sich dieser Wert ändert. Es könne nur Variablen kontrolliert werden, die mit diesem Modul erstellt werden. "Normale" Varaiblen aus den anderen Modulen kann man nach der Übergabe nicht mehr "beobachten".

Die folgende Datei zeigt eine erweiterte Version, welche die oben besprochenen Parameter implementiert.

/***************************************
*  \file       driver2.c
*  \details    Simple hello world driver
*  \author     EmbeTronicX
* *************************************/

#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/module.h>
#include<linux/moduleparam.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeTronicX <embetronicx@gmail.com>");
MODULE_DESCRIPTION("A simple hello world driver");
MODULE_VERSION("1.0");

int valueETX, arr_valueETX[4]; // Variable mit int und int-Arry mit 4 Werten
char *nameETX;                  // String-Variable
int cb_valueETX = 0;            // Überwachung der valueETX-Variable

module_param(valueETX, int, S_IRUSR|S_IWUSR);                      // Integer-Wert definieren
module_param(nameETX, charp, S_IRUSR|S_IWUSR);                     // String
module_param_array(arr_valueETX, int, NULL, S_IRUSR|S_IWUSR);      // Integer-Array

/*----------------------Module_param_cb()--------------------------------*/
int notify_param(const char *val, const struct kernel_param *kp) {
       int res = param_set_int(val, kp); // Use helper for write variable
       if(res == 0) {
               printk(KERN_INFO "Call back function called...\n");
               printk(KERN_INFO "New value of cb_valueETX = %d\n", cb_valueETX);
               return 0;
       } // end if
       return -1;
} // end notify_param()

const struct kernel_param_ops my_param_ops = { // dieser Aufruf sorgt dafür, dass auf die 
       .set = &notify_param, // Use our setter ...
       .get = &param_get_int, // .. and standard getter
};

module_param_cb(cb_valueETX, &my_param_ops, &cb_valueETX, S_IRUGO|S_IWUSR); // Der Modulaufruf bekommt Leserechte für den Besitzer und die Gruppenbesitzer (siehe oben im Artikel) -> siehe auch https://lwn.net/Articles/696227/

/* Module init function */
static int __init hello_world_init(void) {
       int i;
       printk(KERN_INFO "ValueETX = %d  \n", valueETX);
       printk(KERN_INFO "cb_valueETX = %d  \n", cb_valueETX);
       printk(KERN_INFO "NameETX = %s \n", nameETX);
       for (i = 0; i < (sizeof arr_valueETX / sizeof (int)); i++) {
               printk(KERN_INFO "Arr_value[%d] = %d\n", i, arr_valueETX[i]);
       } // end for
       printk(KERN_INFO "Kernel Module Inserted Successfully...\n");
   return 0;
} // end hello_world_init()

/* Module Exit function */
static void __exit hello_world_exit(void) {
   printk(KERN_INFO "Kernel Module Removed Successfully...\n");
} // end hello_world_exit()

module_init(hello_world_init);
module_exit(hello_world_exit);

Der untere Teil besteht wieder aus den Standard-Konstruktoren, während oben nach der Variablendeklaration und deren Übergabe ans Makro noch eine Funktion folgt, welche die Verarbeitung der Callback-Variable übernimmt und hier einfach eine entsprechende Meldung ins Kernellog generiert.

Das Modul kann nun mit den Parametern aufgerufen werden: sudo insmod hello_world_module.ko valueETX=14 nameETX="EmbeTronicX" arr_valueETX=100,102,104,106 Auch hier liefert dmesg wieder die notwendigen Ausgaben. Diesmal mit allen Parametern. Um den Callback zu testen, wird die Callback-Variable cb_valueETX auf 13 gesetzt mit sudo sh -c "echo 13 > /sys/module/hello_world_module/parameters/cb_valueETX" und ein anschliessendes dmesg zeigt, dass die Überwachungsfunktion aufgerufen wurde und den Wert zurückgegeben hat.