Open Source im professionellen Einsatz

Newsletter abonnieren
Seite durchsuchen

HEFTARCHIV | NEWS | E-BIBLIOTHEK | VIDEO | BLOGS | WHITEPAPER | EVENTS | ACADEMY | ABO | SHOP

user friendly

  Home  »  Heft & Abo  »  Heftarchiv  »  2004  »  03  »  Kern-Technik  

RSS-Feed der aktuellen News von Linux-Magazin Online Folgen Sie Linux-Magazin Online auf Twitter
Diesen Artikel druckenDiesen Artikel weiterempfehlen Diesen Artikel kommentieren Newsletter abonnieren
Share/Bookmark

Zugriffsrechte vereinfacht

Die Funktion »alloc_disk()« (Listing 1, Zeile 52) reserviert Speicher für die »struct gendisk«. In diese trägt der Treiber die Kapazität der virtuellen Festplatte und die Tabelle mit den Funktionszeigern ein. Da Zugriffsrechte in diesem Beispiel keine besondere Rolle spielen, reicht es bereits aus, in der Tabelle lediglich das Feld ».owner« zu belegen. Dieses Feld muss der Treiber allerdings in jedem Fall ausfüllen, damit der Kernel das Modul erst dann zum Entladen freigibt, wenn keine Instanz mehr darauf zugreift.

Listing 1: Treiber einer
virtuelle Festplatte

01 #include <linux/fs.h>
02 #include <linux/version.h>
03 #include <linux/module.h>
04 #include <linux/init.h>
05 #include <linux/blkdev.h>
06 
07 #define BD_MAJOR 242
08 #define SIZE_IN_KBYTES (8*1024)
09 
10 MODULE_LICENSE("GPL");
11 
12 static struct gendisk *disk;
13 static struct request_queue *bdqueue;
14 static char *DiscSpace;
15 static struct block_device_operations bdops = {
16     .owner           = THIS_MODULE,
17 };
18 
19 static int bdMakeRequest( request_queue_t *q, struct bio *bio )
20 {
21    char *kaddr, *maddr;
22    struct bio_vec *bvec;
23    int segnr, BytesTransferred=0;
24 
25    blk_queue_bounce( q, &bio );
26    bio_for_each_segment( bvec, bio, segnr ) {
27       kaddr = bio_data(bio);
28       maddr = DiscSpace + (512 * bio->bi_sector);
29       BytesTransferred += bio->bi_size;
30       if( bio_data_dir( bio )==READ || bio_data_dir( bio )==READA ) {
31          memcpy( kaddr, maddr, bio->bi_size );
32       } else {
33          memcpy( maddr, kaddr, bio->bi_size );
34       }
35    }
36    bio_endio( bio, BytesTransferred, 0 );
37    return 0;
38 }
39 
40 static int __init ModInit(void)
41 {
42    if( register_blkdev(BD_MAJOR, "bdsample" )) {
43       printk("blockdevice: Majornummer %d not free.", BD_MAJOR);
44       return -EIO;
45    }
46    if( !(DiscSpace=vmalloc(SIZE_IN_KBYTES*1024)) ) {
47       printk("vmalloc failed ...n");
48       goto out_no_mem;
49    }
50    bdqueue = blk_alloc_queue( GFP_KERNEL );
51    blk_queue_make_request( bdqueue, bdMakeRequest );
52    disk = alloc_disk(1);
53    if( !disk ) {
54       printk("alloc_disk failed ...n");
55       goto out;
56    }
57    disk->major = BD_MAJOR;
58    disk->first_minor = 0;
59    disk->fops = &bdops;
60    disk->queue = bdqueue;
61    sprintf(disk->disk_name, "bd0");
62    set_capacity( disk, (SIZE_IN_KBYTES*1024)>>9 ); // in 512 Byte Bloecke
63    add_disk( disk );
64    return 0;
65 out:
66    vfree( DiscSpace );
67 out_no_mem:
68    unregister_blkdev(BD_MAJOR, "bdsample" );
69    return -EIO;
70 }
71 
72 static void __exit ModExit(void)
73 {
74    unregister_blkdev(BD_MAJOR, "bdsample" );
75    del_gendisk(disk);
76    put_disk( disk );
77    blk_cleanup_queue( bdqueue );
78    vfree( DiscSpace );
79 }
80 
81 module_init( ModInit );
82 module_exit( ModExit );

Bevor der Aufruf von »add_disk()« (Zeile 63) das Objekt dem Blockgeräte-Subsystem übergibt, muss die Request-Queue erzeugt werden. Der Treiber für ein virtuelles Gerät reserviert den Speicher für die Request-Queue durch Aufruf von »blk_alloc_queue()« (Zeile 50) und überschreibt die vorgegebene Make-Request-Funktion durch die eigene Variante »bdMakeRequest()«. Dazu ruft der Treiber »blk_queue_make_request()« auf (Zeile 51). Eine gesonderte Request-Funktion ist in diesem Fall nicht nötig. Der noch in Kernel 2.4 notwendige Aufruf der Funktion »register_blkdev()« ist in Kernel 2.6 optional (siehe hierzu Kasten "Portierungshilfe").

Portierungshilfe

Im Kernel 2.6 repräsentiert nicht mehr die Tabelle der Treiber-Einsprungspunkte »struct block_device_operations« das Blockgerät, sondern die Struktur »struct gendisk«. Die Tabelle mit den Treiber-Einsprungspunkten trägt man nun in die »struct gendisk« ein, die darüber hinaus die Kenndaten des Geräts (der Disk) aufnimmt. Die dafür vorgesehenen globalen Variablen aus Kernel 2.4 existieren nicht mehr.

Die Übergabe des Objekts an das Blockgeräte-Subsystem hat sich ebenfalls geändert. Sie findet nicht mehr in »register_blkdev()«, sondern in der Funktion »add_disk()« statt. Außerdem braucht Linux 2.6 die Funktion »register_blkdev()« nicht mehr. Nur wer seinem Treiber dynamisch eine Minor-Nummer zuteilen lassen und ihn im Verzeichnis »/proc/devices« listen möchte, verwendet die Funktion weiterhin.

Lokale Queues

Auch die interne Pufferung hat sich verändert. Statt einer globalen Request-Queue bringt jedes Blockgerät seine eigene Warteschlange und folglich auch seinen eigenen Lock mit. Den Lock »io_request_lock« gibt es nicht mehr.

Der Zugriff auf die Requests erfolgt nicht mehr über das Makro »CURRENT«, sondern über »elv_next_request()«. Die aus Kernel 2.4 bekannten Buffer-Heads ersetzt Version 2.6 durch die BIO-Blöcke mit ihren eigenen Zugriffsfunktionen.

Die Funktion »bdMakeRequest()« bekommt die Request-Queue »q« und den Zeiger auf den BIO-Block des aktuellen Requests »bio« übergeben. Den Parameter »q« verwendet die Funktion nicht, denn der Treiber verarbeitet Anfragen sofort, ohne sie in eine Schlange einzureihen. Bevor der Treiber »bio« auswertet, muss er sicherstellen, dass er auf den Datenbereich des BIO-Blocks zugreifen darf - mit der Funktion »blk_queue_bounce()« (Zeile 25) kein Problem. Um jetzt die Daten zwischen virtueller Festplatte und dem BIO-Block per »memcpy()« zu transferieren, muss der Treiber noch die Quell- und Zieladressen sowie die Anzahl der zu kopierenden Bytes bestimmen.

Adressen berechnen

Über die Startadresse der RAM-Disk und den durch die Sektornummer gegebenen Offset lässt sich die Adresse »maddr« auf der virtuellen Festplatte berechnen. Um an die entsprechenden Adressen im Hauptspeicher zu kommen, durchsucht der Treiber die Tabelle »bio_vec«, in der die Adressangaben des BIO-Blocks stehen. Hierbei hilft das Makro »bio_for _each_segment()«.

Die Makros »bio_data()« und »bio_data _dir()« lesen schließlich die Adresse des zugehörigen Datenbereichs und die Transferrichtung aus. Sie beziehen sich immer auf das aktuelle Segment, also auf den Feldeintrag in »biovec«. Dass der Treiber den Auftrag beendet hat, signalisiert er mit »bio_endio()«. Diese Funktion hat als Parameter einen Zeiger auf den bearbeiteten BIO-Block, die Anzahl der transferierten Bytes und einen möglichen Fehlercode. »BdMakeRequest()« liefert »0« zurück, als Zeichen dafür, dass der Transfer erfolgreich war.

Nach getaner Arbeit muss der Blockgerätetreiber aufräumen. Mit »put_disk()« meldet er die Platte, mit »unregister_blkdev()« sich selbst beim Kernel ab. Dabei gibt er die zu Beginn reservierten Ressourcen für die Platte mit »del_gendisk()« und für die Queue durch »blk_cleanup_queue()« frei. Vor der Freigabe der Queue muss der Treiber »struct gendisk« freigeben.

Mit Hilfe eines passenden Makefile (siehe Listing 2) lässt sich der RAM-Disk-Treiber einfach kompilieren. »insmod« lädt das Modul in den Kernel:

insmod virtdisc.ko

Listing 2: Makefile für die
virtuelle Harddisk

01 ifneq ($(KERNELRELEASE),)
02 obj-m   := virtdisc.o
03 
04 else
05 KDIR    := /lib/modules/$(shell uname -r)/build
06 PWD     := $(shell pwd)
07 
08 default:
09         $(MAKE) -C $(KDIR)      SUBDIRS=$(PWD) modules
10 
11 clean:
12         rm -f *.ko *.o *.mod.c
13 endif

Um die virtuelle Disk zu testen, ist zunächst eine Gerätedatei anzulegen. Vor dem Mounten muss der Root-User auf der virtuellen Platte ein Filesystem erstellen:

mknod virtdiscfile b 242 0
mke2fs virtdiscfile
mount virtdiscfile /mnt

Wer versucht das Blockgerät zu partitionieren, wird wenig Erfolg haben. Der Beispieltreiber hat sich nämlich beim Kern nur für ein einziges Gerät registriert. Der Parameter der dafür verwendeten Funktion »alloc_disk()« gibt die Anzahl der unterstützten Geräte an. Er muss also, soll sich das Gerät partitionieren lassen, einen Wert größer als 1 erhalten. Möchten Programme wie »fdisk« die Geometrie der Platte abfragen, ist im Treiber das IO-Control »HDIO_GETGEO()« zu implementieren (siehe[3]).

Diesen Artikel druckenDiesen Artikel weiterempfehlen Diesen Artikel kommentieren Newsletter abonnieren
Share/Bookmark
Ähnliche Artikel
Einbruch? Spuren und Daten nach Crackversuchen oder Beschlagnahme richtig sichern
Knoppix 6.5 Klaus Knopper über sein neues Linux
Blitzmerker Neue Dateisysteme nehmen Rücksicht auf den begrenzten Lebenszyklus von Flashspeichern
Kern-Technik Kernel- und Treiberprogrammierung mit dem Kernel 2.6 - Folge 28
Kern-Technik Kernel- und Treiberprogrammierung mit dem Kernel 2.6 - Folge 54
Fragwürdige Softskills Kurztest: Mini-Rechner von Transtec mit SLED 10
Whitepaper
Daten Migration - Eine Publikation von Bloor Research

Datenmigrationsprojekte überschreiten häufig das Budget, neigen zu Verzögerung und werden unter Umständen komplett abgebrochen. Bloor Research ist eines der weltweit führenden IT-Forschungs-, Analyse- und Beratungsunternehmen und wird in dem vorliegenden White Paper die wichtigsten Aspekte dieser Problematik näher beleuchten. Ferner werden praktische Empfehlungen für erfolgreiche Migrationsprojekte gegeben, die Sie auf Ihr nächstes Projekt übertragen können.

Download PDF (Registrierung erforderlich)
Open Source Datenintegration in der Praxis: Fallstudien und Anwendungsbeispiele

Über die letzten Jahre hinweg haben sich Open Source Lösungen als fester Bestandteil des gesamten Datenintegrationsmarktes etabliert. Viele Unternehmen haben bereits das Open Source Modell für Ihre Datenintegrationsprojekte aufgegriffen. Das vorliegende White Paper illustriert anhand ausgewählter Fallstudien und Anwendungsbeispiele die Implementierung von Open Source Datenintegration in der Praxis und benennt die daraus resultierenden Vorteile.

Download PDF (Registrierung erforderlich)
Kommentare (0)