/dev
. Beim Aufruf von ls -l
sind sie in der Ausgabe durch ein 'c' in der ersten Spalte gekennzeichnet, Blockgerätetreiber analog durch 'b'. Vor dem Datum der letzten Änderung befinden sich zwei durch Komma getrennte Zahlen, die major und die minor device number.
crw-rw-rw-1 root root 1,3 Feb 23 1999 null
crw-------1 root root 10,1 Feb 23 1999 psaux
crw-------1 rubini tty 4,1 Aug 16 22:22 tty1
crw-rw-rw-1 root dialout 4,64 Jun 30 11:19 ttyS0
crw-rw-rw-1 root dialout 4,65 Aug 16 00:00 ttyS1
Die major number identifiziert den Treiber, die minor number wird vom Treiber genutzt, um die Geräte aueinanderzuhalten. Wird das DevFS verwendet, gilt das nicht.<linux/fs.h>
definierten Funktionint register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
major
auf 0 gesetzt ist. Der (positive) Rückgabewert ist die zugeteilte Nummer, wird 0 zurückgegeben, wurde ein Gerät mit einer festen Major-Nummer registriert!/proc/devices
die vom System vergebene Major-Nummer ausliest und ein Gerätefile mit dieser Major-Nummer anlegt. In eingebetteten Systemen läßt man oftmals, um Speicherplatz zu sparen, das /proc-Device weg. In diesem Fall gibt man die Major-Nummer einfach fest vor.
#!/bin/sh
module="my_test"
device="my_test"
mode="664"
#invoke insmod with all arguments we were passed
#and use a pathname,as newer modutils don ’t look in .by default
/sbin/insmod -f ./$module.o $*||exit 1
#remove stale nodes
rm -f /dev/${device}[0 ]
major=‘awk "\\$2==\"$module \"{print \\$1}"/proc/devices ‘
mknod /dev/${device}0 c $major 0
#give appropriate group/permissions,and change the group.
#Not all distributions have staff;some have "wheel"instead.
group="staff"
grep ’ˆstaff:’ //etc/group >/dev/null ||group="wheel"
chgrp $group /dev/${device}[0 ]
chmod $mode /dev/${device}[0 ]
mknod
erzeugt den Knoten im Dateisystem.int unregister_chrdev(unsigned int major, const char *name);
open
müssen durch ein close
abgeschlossen worden sein. Dazu wird die Anzahl der open
-Aufrufe gezählt. Linux benutzt dazu die Makros MOD_INC_USE_COUNT
bzw. MOD_DEC_USE_COUNT
, die durch open
bzw. release
aktualisiert werden müssen. Bei der Entwicklung des Treibers kann es manchmal Sinn machen, diese Makros auszukommentieren, da sich sonst bei einem Fehler im Treiber das Modul nicht mehr entladen läßt und das System neu gebootet werden muß.
i_rdev
der Struktur inode
. Einige Treiberfunktionen erhalten als erstes Argument einen Zeiger auf diese Struktur, so dass über inode->i_rdev
auf die Gerätenummern zugegriffen werden kann.kdev_t
gespeichert, der für Kernelfunktionen eine black box ist. Folgende Makros und Funktionen gibt es für den Zugriff:MAJOR(kdev_t dev);
: liefert die Major-NummerMINOR(kdev_t dev);
: liefert die Minor-NummerMKDEV(int ma,int mi);
: erzeugt ein neues kdev_t
mit Major- und Minornummerkdev_t_to_nr(kdev_t dev);
: Konvertierung in eine Zahlto_kdev_t(int dev);
: Konvertierung einer Zahl in ein kdev_t
file_operations
zu.loff_t (*llseek)(struct file *,loff_t,int);
: Ändern der read/write-Position in einer Datei, Rückgabewert ist die neue Position.ssize_t (*read)(struct file *,char *,size_t,loff_t *);
: Lesen der Daten vom Gerätssize_t (*write)(struct file *,const char *,size_t,loff_t *);
: Schreiben auf ein Gerätint (*readdir)(struct file *,void *,filldir_t);
: Lesen von Verzeichnissen, sollte bei Device files NULL seinunsigned int (*poll)(struct file *,struct poll_table_struct *);
poll und select fragen den Status eines Gerätes abint (*ioctl)(struct inode *,struct file *,unsigned int,unsigned long);
: ermöglicht gerätespezifische Befehle wie das Formatieren einer Disketteint (*mmap)(struct file *,struct vm_area_struct *);
: Mapping von Gerätespeicher auf eine Prozessadresseint (*open)(struct inode *,struct file *);
: Gerät öffnenint (*flush)(struct file *);
: wird aufgerufen, wenn ein Prozess seine Kopie eines File-Deskriptors for ein Gerät schließt, z.Z. nur für Network File System Code.int (*release)(struct inode *,struct file *);
Freigabe der Dateistrukturread
durchgeführt, wird vom Betriebssystem die Funktion aufgerufen, die bei der Initialisierung von file_operations
an zweiter Stelle aufgeführt wurde. Nicht implementierte Funktionen werden durch NULL gekennzeichnet. Alternativ kann auch das sogenannte tagged format bei der Deklaration der Struktur verwendet werden:
struct file_operations my_fops ={
llseek:my_llseek,
read:my_read,
write:my_write,
ioctl:my_ioctl,
open:my_open,
release:my_release,
};
Dadurch wird der Code lesbarer und er ist weniger anfällig gegenüber Änderungen in der Strukturdefinition.owner
in der Struktur gesetzt werden. Häufig wird es mit dem Rest der Struktur initialisiert. Im tagged format sieht da so aus:owner:THIS_MODULE
.<linux/module.h>
definiert ist:SET_MODULE_OWNER(&my_fops);
file
zugegriffen. Dabei handelt es sich um eine Struktur, die (ganz allgemein, nicht nur treiberbezogen) offene Dateien repräsentiert. Sie ist nicht zu verwechseln mit FILE
aus der C-Bibliothek oder einer disk file, die durch inode
dargestellt wird.open
durch den Kernel erzeugt und bis zum letzten close
an jede Funktion weitergegeben, die auf der Datei operiert. Wenn alle Instanzen der Datei geschlossen sind, wird die Struktur vom Kernel freigegeben.file_operations
./* vim: set ts=4: */ /* Minitreiber */ #include <linux/fs.h> #include <linux/version.h> #include <linux/module.h> #include <linux/init.h> #define MINI_MAJOR 240 static char *Version = ''$Id: treiber3.htm,v 1.1 2001/11/15 16:41:34 si Exp $''; static struct file_operations MiniFops = { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) THIS_MODULE, #endif NULL, /* llseek */ NULL, /* read */ NULL, /* write */ NULL, /* readdir */ NULL, /* poll */ NULL, /* ioctl */ NULL, /* mmap */ NULL, /* open */ NULL, /* flush */ NULL, /* release */ NULL, /* fsync */ NULL, /* fasync */ NULL, /* lock */ }; static int __init MiniInit(void) { if(register_chrdev(MINI_MAJOR, "Mini-Driver", &MiniFops) == 0) { printk("%s\n", Version ); return 0; }; printk("mini: unable to get major %d\n",MINI_MAJOR); return( -EIO ); } static void __exit MiniExit(void) { printk("cleanup_module called\n"); unregister_chrdev(MINI_MAJOR,"Mini-Driver"); } module_init( MiniInit ); module_exit( MiniExit );
printk
, um Nachrichten auf die laufende Konsole auszugeben. Als Parameter kann man diverse Makros übergeben, um die Nachrichten zu klassifizieren, für Debugging z.B. KERN_DEBUG
. create_proc_entry
und remove_proc_entry
. Erstere erzeugt eine Struktur vom Type proc_dir_entry
, die durch die zweite Funktion wieder freigegeben wird.read_proc
vom Typ read_proc_t
auf, wird darauf geschrieben, wird write_proc
vom Typ write_proc_t
aufgerufen.create_proc_entry
in init_module
aufgerufen werden, in cleanup_module
analog remove_proc_entry
. Der Treiber muß außerdem eine Lesefunktoin read_proc
zur Verfügung stellen und im Struct proc_dir_entry
mit create_proc_read_entry
eintragen.int read_proc(char *buffer, char **start, off_t offset, int len, int *eof, void *private);
buffer
übergeben, der eine Speicherseitengrösse len
(mind. 4096 Byte) hat. In diesen Puffer werden die über das Proc-Device auszugebenden Informationen geschrieben. Ist vom Kernel ein Offset angegeben, ab dem er erst die Informationen erwartet, kann read_proc
entweder die Daten erst ab dem Offset an den Anfang des Buffers kopieren, oder aber alle Daten schreiben und dem Kernel den Beginn der eigentlichen Daten über den Parameter start
mitteilen. Wenn alle Daten erfolgreich geschrieben wurden, setzt der Treiber eof
. private
wird bei der Initialisierung angegeben, wenn die Lesefunktion mehrfach verwendet werden kann. der Rückgabewert ist die Anzahl der geschriebenen Bytes.ioctl
-Kommandos benutzen. Manchmal hilft auch schon die einfache Beobachtung des Applikationsverhaltens.