Linux Device Drivers

download Linux Device Drivers

of 23

Embed Size (px)

description

Uno dei molti vantaggi del Sistema Operativo Linux è che il suo “interno” è aperto a tutti. Il kernel Linux è un corpo grande e complesso di codice. I drivers di periferica, sono distinte “scatole nere” che fanno sì che un particolare pezzo di hardware risponda ad un interfaccia di programmazione ben definita. Le attività dell’utente sono effettuate tramite una serie di chiamate standardizzate indipendenti dal driver specifico. Quindi i driver possono essere costruiti separatamente dal resto del kernel e “inseriti” a runtime quando necessario.

Transcript of Linux Device Drivers

  • iUniversit degli studi di Napoli

    "Parthenope"Facolt di scienze e tecnologieCorso di Laurea in Informatca

    Linux Device Drivers

    Autori:Raffaela DAniello 0124000637Luisa Barbarino 0124000484Daniele Ioviero 0124000459Fabio Nisci 0124000074

    Relatore:Prof. Alfredo Petrosino

    Esame: Sistemi OperativiAnno accademico 2015 - 2016

  • ii

    2015. All rights reserved.No part of this work may be reproduced in any form without prior permission of the

    author.This work is for operating system exam.

    NOT for commercial use.

  • Indice

    Indice iii

    Elenco delle figure iv

    1 Introduzione 11.1 Introduzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.2 Ruolo dei dispositivi di periferica . . . . . . . . . . . . . . . . . . . . 11.3 Kernel Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

    2 I Moduli 42.1 Le Classi di dispositivi e moduli . . . . . . . . . . . . . . . . . . . . . 42.2 Problemi di sicurezza . . . . . . . . . . . . . . . . . . . . . . . . . . . 42.3 Moduli caricabili . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52.4 I moduli di costruzione ed esecuzione . . . . . . . . . . . . . . . . . . 52.5 Compliazione ed esecuzione . . . . . . . . . . . . . . . . . . . . . . . 6

    3 Gestione e comunicazione 83.1 Modulo Kernel VS Applicazioni Utente . . . . . . . . . . . . . . . . . 83.2 Polling VS Interrupt . . . . . . . . . . . . . . . . . . . . . . . . . . . 93.3 Interrupt Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93.4 I/O Bloccante . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103.5 Chiamata di sistema da unapplicazione utente . . . . . . . . . . . . 103.6 Sleeping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113.7 Wait Queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113.8 System call Select/Poll . . . . . . . . . . . . . . . . . . . . . . . . . . 12

    4 Build and run 144.1 Costruiamo un driver . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

    5 Debug 18

    iii

  • ELENCO DELLE FIGURE iv

    5.1 Debugging moduli kernel: KGDB . . . . . . . . . . . . . . . . . . . . 18

    Elenco delle figure

    1.1 Linux Kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

    3.1 device_open() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103.2 device_poll() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

  • Capitolo 1

    Introduzione

    1.1 Introduzione

    Uno dei molti vantaggi del Sistema Operativo Linux che il suo interno aperto a tutti.Il kernel Linux un corpo grande e complesso di codice. I drivers di periferica, sono distintescatole nere che fanno s che un particolare pezzo di hardware risponda ad un interfacciadi programmazione ben definita. Le attivit dellutente sono effettuate tramite una seriedi chiamate standardizzate indipendenti dal driver specifico. Quindi i driver possono esserecostruiti separatamente dal resto del kernel e inseriti a runtime quando necessario.

    1.2 Ruolo dei dispositivi di periferica

    Come programmatore, si in grado di fare le proprie scelte circa il driver, e scegliere un ac-cettabile compromesso tra il tempo di programmazione richiesto e la flessibilit del risultato,ovvero il ruolo di un driver di periferica fornisce il meccanismo, non la politica. La mag-gior parte dei problemi di programmazione possono infatti essere divise in due parti: qualifunzionalit devono essere forniti (il meccanismo) e come possono essere utilizzate quellecapacit (la politica). Esempio, la gestione Unix del display grafico diviso tra il serverX, che conosce lhardware e offre uninterfaccia unificata per programmi utente, e il gestoredelle finestre e di sessione, che implementano una particolare politica senza sapere nullalhardware. Gli utenti possono utilizzare lo stesso gestore di finestre su hardware diversi, eviceversa diversi utenti possono eseguire diverse configurazioni sulla stessa workstation. inoltre possibile guardare il driver da una prospettiva diversa: si tratta di uno strato di soft-ware che si trova tra le applicazioni e il dispositivo vero e proprio. Caratteristiche tipiche:Queste includono il supporto per entrambi funzionamento sincrono e asincrono, la capacitdi essere aperto pi volte, la capacit di sfruttare tutte le funzionalit dellhardware, e lamancanza di strati di software a semplificare le cose o fornire le operazioni legate allapolitica.

    1.3 Kernel Linux

    Gestione dei processi: Il kernel si occupa della creazione e distruzione dei processi,della loro comunicazione.

    1

  • CAPITOLO 1. INTRODUZIONE 2

    Gestione della memoria: Le varie parti del kernel interagiscono con il sottosistema digestione della memoria attraverso una serie di chiamate di funzione, che vanno dalsemplice malloc ad altre pi complesse.

    Filesystem: quasi tutto in Unix pu essere trattato come un file. Il kernel costruisce unfile system strutturato in cima hardware non strutturato, e la conseguente estrazionedi file molto utilizzato in tutto il sistema.

    Controllo dispositivi funzionamento: a quasi tutti i sistemi associato un dispositivofisico. Con leccezione del processore, memoria e poche altre entit, qualsiasi e tutte leoperazioni di controllo del dispositivo vengono eseguite da codice (Driver di periferia)che specifico per il dispositivo. Il kernel deve avere incorporato un device driver perogni periferica presente sul sistema, dal disco rigido alla tastiera e lunit a nastro.

    Networking: deve essere gestita dal sistema operativo, perch la maggior parte delleoperazioni di rete non sono specifici di un processo: i pacchetti in ingresso sono eventiasincroni. I pacchetti devono essere raccolti, identificati e spediti prima che un processosi prenda cura di loro. Inoltre, tutti i problemi di risoluzione di routing e di indirizzosono implementati allinterno del kernel.

  • CAPITOLO 1. INTRODUZIONE 3

    Figura 1.1: Linux Kernel

  • Capitolo 2

    I Moduli

    2.1 Le Classi di dispositivi e moduli

    Character devices Un dispositivo a caratteri (char) pu essere letto come un flusso dibyte (come un file); Ai dispositivi Char si accede per mezzo di nodi del file system.Lunica differenza rilevante tra un dispositivo char e un file normale che si pu semprespostare avanti e indietro nel file regolare, mentre la maggior parte dei dispositivi charsono canali di dati solo, e quindi si pu solo accedere in modo sequenziale. Esistono,tuttavia, i dispositivi char che sembrano aree dati, e ci si pu spostare avanti e indietro.

    I dispositivi a blocchi: Un dispositivo a blocchi un dispositivo (ad esempio, un disco)che pu ospitare un filesystem. Nella maggior parte dei sistemi Unix, un dispositivo diblocco pu gestire solo operazioni I /O che consentono di trasferire uno o pi blocchiinteri, che sono di solito 512 byte di lunghezza. Linux, invece, consente allapplica-zione di leggere e scrivere un dispositivo a blocchi, come un dispositivo char e quindipermette il trasferimento di un qualsiasi numero di byte alla volta.

    Le interfacce di rete: incaricato di inviare e ricevere pacchetti di dati, guidato dalsottosistema di rete del kernel, senza sapere come le singole operazioni mappano ipacchetti da trasmettere. Molte le connessioni di rete (in particolare quelli che utiliz-zano il protocollo TCP) sono stream-oriented, ma i dispositivi di rete sono, in genere,progettati intorno la trasmissione e la ricezione di pacchetti. Un driver di rete non sanulla di singole connessioni; gestisce solo i pacchetti. Il kernel per comunicare con undriver di dispositivo di, chiama le funzioni relative alla trasmissione dei pacchetti.

    USB: il dispositivo stesso si presenta nel sistema come un dispositivo char (una portaseriale USB, per esempio), un dispositivo a blocchi (un lettore di schede di memoriaUSB), o un dispositivo di rete (uninterfaccia Ethernet USB)

    2.2 Problemi di sicurezza

    Nella distribuzione ufficiale del kernel, solo un utente autorizzato pu caricare moduli; lachiamata di sistema init_module controlla se il processo chiamante autorizzato a caricareun modulo nel kernel.

    4

  • CAPITOLO 2. I MODULI 5

    2.3 Moduli caricabili

    Una delle buone caratteristiche di Linux la possibilit di estendere in fase di esecuzionelinsieme delle funzionalit offerte dal kernel. Ci significa che possibile aggiungere fun-zionalit al mentre il sistema attivo e funzionante. Ogni pezzo di codice che pu essereaggiunto al kernel in fase di esecuzione chiamato un modulo.

    2.4 I moduli di costruzione ed esecuzione

    Il driver di periferica progettato, caricato, scaricato come modulo del kernelSviluppo: - Sorgente del kernel - Compilazioni - Controllare la distribuzione Linux su

    come configurare.

    Listing 2.1: "Esempio: modulo HelloWord"

    1 /** "Hello , world !" minimal kernel module

    3 ** Fabio Nisci

    5 **/

    7

    /*9 * The below are header files provided by the kernel which are

    * required for all modules. They include things like the definition11 * of the module_init () macro.

    */13 #include

    #include 15

    /*17 * This is the init function , which is run when the module is first

    * loaded. The __init keyword tells the kernel that this code will19 * only be run once , when the module is loaded.

    */21

    static int __init23 hello_init(void)

    {25 printk("Hello , world !\n");

    return 0;27 }

    29 /** The below macro informs the kernel as to which function to use as

    31 * the init function.*/

    33

    module_init(hello_init);35

    /*37 * Similary , the exit function is run once , upon module unloading , and

    * the module_exit () macro identifies which function is the exit39 * function.

    */41

    static void __exit43 hello_exit(void)

  • CAPITOLO 2. I MODULI 6

    {45 printk("Goodbye , world!\n");

    }47

    module_exit(hello_exit);49

    /*51 * MODULE_LICENSE () informs the kernel what license the module source

    * code is under , which affects which symbols it may access in the53 * main kernel. Certain module licenses will "taint" the kernel ,

    * indicating that non -open or untrusted code has been loaded.55 * Modules licensed under GPLv2 do not taint the kernel and can access

    * all symbols , but declaring it so is a legal statement that the57 * source code to this module is licensed under GPLv2 , and so you must

    * provide the source code if you ship a binary version of the module.59 */

    MODULE_LICENSE("GPL");61 MODULE_DESCRIPTION("\"Hello , world !\" minimal module");

    MODULE_VERSION("printk");

    Spiegazione:Questo modulo definisce due funzioni, una per essere invocata quando il modulo caricato

    nel kernel (hello_init) e una per quando il modulo viene rimosso (hello_exit). Le lineemodule_init e module_exit utilizzano macro speciali del kernel per indicare il ruolo diqueste due funzioni. Unaltra macro speciale (MODULE_LICENSE) utilizzato per direal kernel che questo modulo ha una licenza gratuita; senza una tale dichiarazione, il kernel silamenta quando viene caricato il modulo. La funzione printk definita nel kernel di Linux emesso a disposizione moduli; si comporta in modo simile alla funzione printf della libreria Cstandard. Il kernel ha la propria funzione di stampa perch corre da sola, senza laiuto dellalibreria C. Il modulo pu chiamare printk perch, dopo insmod il modulo legato al kernel epu accedere simboli pubblici del kernel (funzioni e variabili, come dettagliato nella sezionesuccessiva). Il KERN_ALERT indica la priorit del messaggio. Abbiamo specificato lamassima priorit in questo modulo, perch un messaggio con la priorit predefinita potrebbenon presentarsi ovunque utile, a seconda della versione del kernel in esecuzione, la versionedel klogd daemon, e la configurazione. possibile ignorare questo problema, per ora. possibile verificare il modulo con la insmod e rmmod utilities, come illustrato di seguito.Notare che solo il superuser pu caricare e scaricare un modulo.

    2.5 Compliazione ed esecuzione

    Listing 2.2: "Makefile"

    # obj -m is a list of what kernel modules to build. The .o and other2 # objects will be automatically built from the corresponding .c file -

    # no need to list the source files explicitly.4

    obj -m := hello_printk.o6

    # KDIR is the location of the kernel source. The current standard is8 # to link to the associated source tree from the directory containing

    # the compiled modules.10 KDIR := /lib/modules/$(shell uname -r)/build

    12 # PWD is the current working directory and the location of our module# source files.

  • CAPITOLO 2. I MODULI 7

    14 PWD := $(shell pwd)

    16 # default is the default make target. The rule here says to run make# with a working directory of the directory containing the kernel

    18 # source and compile only the modules in the PWD (local) directory.default:

    20 $(MAKE) -C $(KDIR) M=$(PWD) modules

    Spiegazione: Si prega di notare ancora una volta che, per questa sequenza di comandi, necessario disporre di un kernel configurato correttamente e costruita in un luogo doveil makefile in grado di trovarlo (/usr/src/linux2.6.10 nellesempio mostrato). Otteniamonei dettagli di come i moduli sono costruiti nella sezione Compilazione e caricamento. ASeconda del meccanismo che il sistema utilizza per fornire le linee di segnalazione, loutputpotrebbe essere diverso. In particolare, la schermata precedente stata presa da una consoledi testo; se si esegue insmod e rmmod da un emulatore di terminale in esecuzione con ilsistema a finestre, non vedrete nulla sul vostro schermo. Il messaggio va a uno dei filedi registro di sistema, come /var /log /messaggi (il nome del file effettivo varia tra ledistribuzioni Linux. Come potete vedere, la scrittura di un modulo non cos difficile comesi potrebbe aspettare, almeno, fino a quando il modulo non tenuto a fare qualcosa diutile. La parte difficile capire il vostro dispositivo e su come ottimizzare le prestazioni.Andiamo pi a fondo la modularizzazione in questo capitolo e lasciare i problemi specificidel dispositivo per i capitoli successivi.

  • Capitolo 3

    Gestione e comunicazione

    3.1 Modulo Kernel VS Applicazioni Utente

    Compariamo un modulo kernel ad unapplicazione utente.

    I moduli kernel devono essere inizializzati (init) e terminati (exit) in modo attento. Ilcompito della funzione init di preparare, per successive invocazioni, le funzioni delmodulo. un approccio molto simile alla programmazione event-driven anche se condelle differenze; infatti, nei moduli abbiamo la funzione exit che deve de-allocare tuttoquello che stato costruito con la init.

    Nel programma helloword abbiamo visto tali funzioni implementate, ma questo eratutto quello che cera nel codice; normalmente la costruzione di un driver impiega mol-to pi codice. Ad esempio, se abbiamo bisogno di un buffer dove memorizzare dati:allochiamo dinamicamente il size del buffer con una malloc() e non dobbiamo dimenti-care di effettuare una free() altrimenti il programma potrebbe andare in segmentationfault ed uscire. Ma quando costruiamo un modulo kernel ci troviamo appunto in spa-zio kernel e se dal driver di dispositivo andassimo ad accedere ad una locazione noninizializzata avremmo problemi molto pi seri di un segmentation fault perch danneg-geremmo lintegrit del kernel ed anche ad esecuzioni successive potremmo aver resoinconsistente lo stato del sistema operativo. Per questo molto importante effettuarelinizializzazione e la terminazione cos come lallocazione di memoria.

    Non possibile usare printf, esiste solo la printk Bisogna includere i file header del kernel linux (es ) Bug nel driver di dispositivo possono causare il crash del kernel Eseguito nello spazio kernel quindi ho libero accesso a tutte risorse (attenzione) Maggiore concorrenza nei moduli kernel che nelle applicazioni utente quindi bisogna

    tener conto della gestione delle interrupt, timers e nei moderni SO SMP support(supporto symmetric multiprocessing)

    Come risultato il driver di dispositivo deve essere rientrante, gestire la concorrenza edevitare race condition

    8

  • CAPITOLO 3. GESTIONE E COMUNICAZIONE 9

    3.2 Polling VS Interrupt

    Il polling e le interrupt sono due modi per far comunicare il processore ed il dispositivo.Ricordiamo che il polling la verifica ciclica di tutte le unit/periferiche da parte del SO;continuiamo a leggere la porta usando istruzioni continuamente finch non arrivano dati concui lavorare. Questo modo di ricezione dei dati pu essere appropriato solo per dispositiviche hanno un I/O ad alta velocit. Questo perch arrivano molti dati velocemente e ilprocessore deve rimanere al passo. Quando, invece, abbiamo a che fare con dispositivi pilenti o in cui non ci si aspetta che i dati arrivino continuamente, una interrupt sar moltopi appropriata, altrimenti con il polling sprecheremmo molti cicli di CPU controllando laporta inutilmente. Una interrupt asincrona perch non sappiamo quando arriver leventoche la richiamer; basata sul bisogno del dispositivo, cio vogliamo che a prescindere dalflusso dei dati il processore riesca a gestirli tutti Consideriamo unapplicazione utente il cuiscopo quello di suonare un allarme se il sensore che legge i dati in input legge valori in undato range. Diciamo che quando ci troviamo in quel range vogliamo agire in qualche modosui dati, ma non sappiamo quando questa situazione accadr e le operazioni del dispositivosono molto pi lente rispetto a quelle del processore. In questo caso luso di una interrupt pi appropriato cos da non controllare sempre in polling, ma gestire linterrupt solamentequando levento accade.

    3.3 Interrupt Handling

    Una interrupt un segnale che lhardware pu inviare per ottenere lattenzione del pro-cessore. Abbiamo spiegato come le interrupt sono gestite dal processore. Le interrupt delprocessore sono gestite dal IRS (interrupt routine service). Tali routine sono essenzialmenteprogrammi che il processore deve eseguire allaccadere delle interrupt. Quindi allaccade-re dellinterrupt dal dispositivo bisogna eseguire delle azioni. Lammontare di tali azionidipende dal dispositivo. Ci sono delle restrizione dellISR:

    1. esso non eseguito nel contesto di un processo. Sappiamo che il SO pu gestire piprocessi/thread di tipo utente, ma quando ci troviamo in spazio kernel non abbiamotante informazioni sui processi/thread di tipo kernel e il loro contesto rispetto a quelliutente; lISR non eseguito nello spazio utente, ma esso parte del SO quindi nonpossiamo trasferire dati da/a lo spazio utente.

    2. il processore non DEVE dormire quando nellISR. NellISR possiamo avere systemcall, ma queste non possono essere bloccanti, cio non possiamo eseguire chiamatecome la wait_event, bloccare un semaforo/scheduler, etc. perch queste chiamate disistema potrebbero mettere lintero SO in stato sleep. Possiamo lasciare processiutente dormire, ma non una buona idea per processi kernel.

    Il ruolo dellISR:

    da feedback al dispositivo sulla ricezione dellinterrupt (tramite la pulizia di un flag) lettura/scrittura dei dati tipicamente avviene qui pulizia dellINTERRUPT BIT risveglia processi dormienti sul dispositivi in attesa di qualche evento

  • CAPITOLO 3. GESTIONE E COMUNICAZIONE 10

    3.4 I/O Bloccante

    Unapplicazione utente richiede qualcosa da un dispositivo (normalmente tramite una systemcall). Quindi possiamo aprire il file del dispositivo e fare una read e il kernel reindirizzerquella read sulla funzione appropriata del dispositivo. Se al momento della read i dati sonodisponibili nel dispositivo, essi vengono letti; ma se i dati non sono disponibili possiamodecidere di far ritornare la funzione immediatamente (con un 1 per indicare errore) o laltraopzione di bloccare loperazione mettendo il processo utente in sleep. Stesso discorso perla write, se non c il buffer o il dispositivo troppo impegnato possiamo mettere il processoin sleep. Il processo chiamante non si interessa di tali problemi, esegue solamente le systemcall.I programmatori semplicemente invocano read e write e si aspettano che la funzioneritorni dopo aver terminato il lavoro. Se vogliamo usare il blocking I/O dobbiamo far si chesia il driver di dispositivo a gestirlo. Il driver deve:

    bloccare il processo chiamante metterlo a dormire finch la richiesta servita e pu procedere

    3.5 Chiamata di sistema da unapplicazione utente

    Figura 3.1: device_open()

    Questa unillustrazione che mostra cosa accade quando richiamiamo una system call daunapplicazione utente. Il riquadro blu lapplicazione utente (spazio utente); la prima cosada fare aprire il file del mio dispositivo my device assumendo di avere il minus numbere major number quando creiamo il file. Il major number identifica il driver associato al

  • CAPITOLO 3. GESTIONE E COMUNICAZIONE 11

    dispositivo. I moderni kernel di Linux associano molti dispositivi allo stesso major number.Il minor number e; usato dal kernel per determinare esattamente a quale dispositivo ci sista riferendo. In base a come scritto il driver possiamo ottenere o il puntatore diretto aldispositivo dal kernel o usare il minor number come indice in un array di dispositivi. Propriograzie alla coppia minus+major number il kernel sar in grado di identificare il file e linkarloal modulo kernel mydriver. Una read allinterno dellapplicazione utente sar collegata aduna funzione del modulo kernel grazie alla struttura file_operations che fa corrisponderela .read a device_read. A questo punto controlliamo la lunghezza del buffer, se ci sonodati disponibili; se tutto ok faccio la copy_to_user (API del kernel) cosicch i dati suldispositivo vengano inviati al processo utente tramite il buffer. Altrimenti se non abbiamodati in entrata allora mettiamo lapplicazione utente a dormire.

    3.6 Sleeping

    Cosa vuol dire che un processo sta dormendo? Noi sappiamo che sistema operativo usa loscheduler per dare un tipo di ordinamento al susseguirsi dei processi o dei thread, avvalendosidellutilizzo di una coda. Sappiamo che gli stati dei processi sono diversi, tra cui troviamolo stato sleeping. Un processo che si trova nello stato sleeping, vuol dire che viene rimossodalla coda di esecuzione dello scheduler. finch qualche evento futuro non cambia lo stato, ilprocesso non sar in scheludato sulla CPU e, di conseguenza, non verr eseguito. Per far sche un processo vada nello stato sleeping in modo sicuro, bisogna osservare un paio di regoleimportanti. La prima regola che non bisogna mai passare allo stato sleeping quandoci si trova in un contesto atomico (fetch decode execute). Ricordiamo che un contestoatomico si ha quando diverse operazioni devono essere eseguite senza alcun tipo di accessoconcorrente. La seconda regola da osservare che non possibile impostare lo stato sleepingse gli interrupt sono disabilitati. Una cosa da ricordare e che quando si sveglia un processodallo Stato sleeping, non possiamo sapere quanto tempo rimasto fuori dalla CPU e ciche, nel frattempo, pu essere cambiato. Un altro punto rilevante che un processo nonpu dormire finch non si assicurato che qualche altro processo, da qualche altra parte,possa risvegliarsi.

    3.7 Wait Queue

    Abbiamo prima menzionato lutilizzo di una coda. La coda delle attese una strutturakernel, ed una lista dei processi in attesa di uno specifico evento. Con questa struttu-ra delle code di attesa, il kernel ha la possibilit di trovare i processi dormienti nel casoin cui, nel prossimo futuro, decide di risvegliarli. Il driver di un qualsiasi device pu uti-lizzare questa struttura, e pu anche creare una specifica coda ed utilizzarla per i propriscopi. La coda delle attese gestita da una struttura chiamata wait queue head e de-finita nel file . una struttura molto semplice, composta da spinlock ed unalinked list. Questa struttura contiene informazioni relative al processo dormiente ed inol-tre a come si vorrebbe essere svegliati. La wait queue pu essere definita e inizializzatostaticamente con DECLARE_WAIT_QUEUE_HEAD (nome) oppure dinamicamente conwait_queue_head_t nome_coda e init_waitqueue_head (& nome_coda);

  • CAPITOLO 3. GESTIONE E COMUNICAZIONE 12

    3.8 System call Select/Poll

    Le applicazioni che usano lI/O non bloccante, spesso usano le chiamate di sistema poll,select ed poll. Queste chiamate di sistema permettono ad un processo di determinare se possibile leggere o scrivere su uno o pi file aperti senza bloccare nulla. Queste chiamate disistema possono anche bloccare un processo finch un descrittore di file diventa disponibileper la lettura o scrittura. Poll/Select vengono spesso richiamate in applicazioni che devonoutilizzare pi flussi di ingresso o uscita senza restare bloccati su uno di questi. La chiamatadi sistema poll stata aggiunta per ampliare la scala della funzione polling, agendo sumigliaia descrittori di file. Per utilizzare queste chiamate di sistema necessario il supportodel driver del device, coinvolgendo quindi, un descrittore di file associato al driver. Seguardiamo questo codice esempio:

    Figura 3.2: device_poll()

    Spiegazione:Consideriamo il dispositivo mydevice (user space) mappato nel modulo kernel my dri-

    ver (kernel space). La struttura file_operations permette lassociazione delle funzioni ri-chiamate nelluser space a quelle contenute in mydriver in kernel space. Per esempio .readandr ad eseguire la funzione device_read e cos via tutte le altre. Partendo dallapplicazio-ne utente mydevice apriamo il file e chiamiamo la funzione poll che prima di chiamarela system call vera e propria popola la struttura mypollfd con due campi: file handler edeventi. timeout indica quando vogliamo che la poll faccia timeout. Quando la funzionepoll in mydevice verr richiamata il kernel la reindirizzer verso la funzione device_poll.Al suo interno troviamo poll_wait che unAPI del kernel (mai usarla nelluser space)che metter in stato di sleep lapplicazione utente nella waiting queue. Ad un certo puntoavremo bisogno di risvegliare il processo addormentato, il risveglio dipende dal dispositi-

  • CAPITOLO 3. GESTIONE E COMUNICAZIONE 13

    vo, ma nella maggior parte dei casi ci avvaliamo delle interrupt per risvegliare i processi.Il blocco arancione mette in risalto la IRS che risveglia il processo. La wake_up avrbisogno dellelemento della wait queue da risvegliare e il processo riprender lesecuzione.

  • Capitolo 4

    Build and run

    4.1 Costruiamo un driver

    Il driver che costruiremo appartiene alla classe dei character devices. Il dispositivo imple-menta la lettura/scrittura di un carattere. Anche non essendo molto utile ai fini pratici,fornisce un valido esempio sul funzionamento completo di un driver. semplice da im-plementare, in quanto non si interfaccia a nessun dispositivo hardware reale (oltre che alcomputer stesso).

    Listing 4.1: "Modulo Char Device"

    /*2 * chardev.c: Creates a read -only char device that says how many times

    * youve read from the dev file4 * Host: Fabio Nisci

    */6

    #include 8 #include

    #include 10 #include /* for put_user */

    12 /** Prototypes - this would normally go in a .h file

    14 */int init_module(void);

    16 void cleanup_module(void);static int device_open(struct inode *, struct file *);

    18 static int device_release(struct inode *, struct file *);static ssize_t device_read(struct file *, char *, size_t , loff_t *);

    20 static ssize_t device_write(struct file *, const char *, size_t , loff_t *);

    22 #define SUCCESS 0#define DEVICE_NAME "chardev" /* Dev name as it appears in /proc/devices

    */24 #define BUF_LEN 80 /* Max length of the message from the device */

    26 /** Global variables are declared as static , so are global within the file.

    28 */

    30 static int Major; /* Major number assigned to our device driver */static int Device_Open = 0; /* Is device open?

    14

  • CAPITOLO 4. BUILD AND RUN 15

    32 * Used to prevent multiple access to device */static char msg[BUF_LEN ]; /* The msg the device will give when asked */

    34 static char *msg_Ptr;

    36 static struct file_operations fops = {.read = device_read ,

    38 .write = device_write ,.open = device_open ,

    40 .release = device_release};

    42

    /*44 * This function is called when the module is loaded

    */46 int init_module(void)

    {48 Major = register_chrdev (0, DEVICE_NAME , &fops);

    50 if (Major < 0) {printk(KERN_ALERT "Registering char device failed with %d\n", Major);

    52 return Major;}

    54

    printk(KERN_INFO "I was assigned major number %d. To talk to\n", Major);56 printk(KERN_INFO "the driver , create a dev file with\n");

    printk(KERN_INFO "mknod /dev/%s c %d 0.\n", DEVICE_NAME , Major);58 printk(KERN_INFO "Try various minor numbers. Try to cat and echo to\n");

    printk(KERN_INFO "the device file.\n");60 printk(KERN_INFO "Remove the device file and module when done.\n");

    62 return SUCCESS;}

    64

    /*66 * This function is called when the module is unloaded

    */68 void cleanup_module(void)

    {70 /*

    * Unregister the device72 */

    unregister_chrdev(Major , DEVICE_NAME);74 printk(KERN_INFO "Unregister_chrdev");

    }76

    /*78 * Methods

    */80

    /*82 * Called when a process tries to open the device file , like

    * "cat /dev/mycharfile"84 */

    static int device_open(struct inode *inode , struct file *file)86 {

    static int counter = 0;88

    if (Device_Open)90 return -EBUSY;

    92 Device_Open ++;

  • CAPITOLO 4. BUILD AND RUN 16

    sprintf(msg , "I already told you %d times Hello world!\n", counter ++);94 msg_Ptr = msg;

    try_module_get(THIS_MODULE);96

    return SUCCESS;98 }

    100 /** Called when a process closes the device file.

    102 */static int device_release(struct inode *inode , struct file *file)

    104 {Device_Open --; /* Were now ready for our next caller */

    106

    /*108 * Decrement the usage count , or else once you opened the file , youll

    * never get get rid of the module.110 */

    module_put(THIS_MODULE);112

    return 0;114 }

    116 /** Called when a process , which already opened the dev file , attempts to

    118 * read from it.*/

    120 static ssize_t device_read(struct file *filp , /* see include/linux/fs.h*/

    char *buffer , /* buffer to fill with data */122 size_t length , /* length of the buffer */

    loff_t * offset)124 {

    /*126 * Number of bytes actually written to the buffer

    */128 int bytes_read = 0;

    130 /** If were at the end of the message ,

    132 * return 0 signifying end of file*/

    134 if (* msg_Ptr == 0)return 0;

    136

    /*138 * Actually put the data into the buffer

    */140 while (length && *msg_Ptr) {

    142 /** The buffer is in the user data segment , not the kernel

    144 * segment so "*" assignment wont work. We have to use* put_user which copies data from the kernel data segment to

    146 * the user data segment.*/

    148 put_user (*( msg_Ptr ++), buffer ++);

    150 length --;bytes_read ++;

    152 }

  • CAPITOLO 4. BUILD AND RUN 17

    154 /** Most read functions return the number of bytes put into the buffer

    156 */return bytes_read;

    158 }

    160 /** Called when a process writes to dev file: echo "hi" > /dev/hello

    162 */static ssize_t

    164 device_write(struct file *filp , const char *buff , size_t len , loff_t * off){

    166 printk(KERN_ALERT "Sorry , this operation isnt supported .\n");return -EINVAL;

    168 }

  • Capitolo 5

    Debug

    5.1 Debugging moduli kernel: KGDB

    GDB in grado di rilevare quando un modulo viene caricato sul target. Quindi carica ilfile modulo oggetto modulo nella memoria di GDB per ottenere le informazioni di debug. Ilpercorso di ricerca in cui GDB individua file di modulo si trova nella variabile solib-search-path.Sul target carichiamo il modulo:target# insmod my_module.koSullhost carichiamo i simboli del modulo ed impostiamo i breakpoint:host% sh4-linux-gdb vmlinux

    GNU gdb 6.3Copyright 2004 Free Software Foundation, Inc.GDB is free software, covered by the GNU General Public License,and you are welcome to change it and/or distribute copies of itunder certain conditions.Type "show copying" to see the conditions.There is absolutely no warranty for GDB.Type "show warranty" for details.This GDB was configured as "--host=i686-pc-linux-gnu --target=sh4-linux"...0x84031f10 in ?? ()

    Automatically enabled KGDB extensions...(gdb) set solib-search-path /user/my_module_2.6/Reading symbols from /user/my_module_2.6/my_mod.ko...expanding to full symbols...done.Loaded symbols for /user/my_module_2.6/my_mod.ko(gdb) info sharedlibraryFrom To Syms Read Shared Object Library0xc0168000 0xc01680c0 Yes /user/my_module_2.6/my_mod.ko(gdb) b mod_stm_openBreakpoint 1 at 0xc0168002: file /user/my_module_2.6/my_mod.c, line 43.(gdb) cContinuing.

    18

  • CAPITOLO 5. DEBUG 19

    Al fine di individuare i simboli dei moduli caricabili, GDB deve essere collegato alla porta(in cui sono stati caricati i moduli). Ci significa che il comando set solib-search-path /user/my_module_2.6/deve essere eseguito dopo che GDB si sia connesso al target, altrimentiraccoglie i simboli, ma non sa dove si trovano. Questo problema pu essere risolto utilizzandoi punti di interruzione in sospeso. I problemi potrebbero verificarsi anche quando lo scaricodei moduli se GDB ha ancora i punti di interruzione allinterno del modulo. Ad esempio,se si scarica un modulo (utilizzando il comando rmmod),senza rimuovere tutti i punti diinterruzione rilevanti in GDB, qualsiasi tentativo di avviare una nuova sessione di debug(ad esempio fermare lesecuzione del kernel con Ctrl + C) provoca il blocco del sistema .Questo perch KGDB stato chiesto di eseguire azioni su punti di interruzione attivi chenon sono pi accessibili.

    IndiceElenco delle figureIntroduzioneIntroduzioneRuolo dei dispositivi di perifericaKernel Linux

    I ModuliLe Classi di dispositivi e moduliProblemi di sicurezzaModuli caricabiliI moduli di costruzione ed esecuzioneCompliazione ed esecuzione

    Gestione e comunicazioneModulo Kernel VS Applicazioni UtentePolling VS InterruptInterrupt HandlingI/O BloccanteChiamata di sistema da un'applicazione utenteSleepingWait QueueSystem call Select/Poll

    Build and runCostruiamo un driver

    DebugDebugging moduli kernel: KGDB