871 lines · plain
1.. include:: ../disclaimer-ita.rst2 3.. note:: Per leggere la documentazione originale in inglese:4 :ref:`Documentation/kernel-hacking/hacking.rst <kernel_hacking_hack>`5 6:Original: :ref:`Documentation/kernel-hacking/hacking.rst <kernel_hacking_hack>`7:Translator: Federico Vaga <federico.vaga@vaga.pv.it>8 9.. _it_kernel_hacking_hack:10 11=================================================12L'inaffidabile guida all'hacking del kernel Linux13=================================================14 15:Author: Rusty Russell16 17Introduzione18============19 20Benvenuto, gentile lettore, alla notevole ed inaffidabile guida all'hacking21del kernel Linux ad opera di Rusty. Questo documento descrive le procedure22più usate ed i concetti necessari per scrivere codice per il kernel: lo scopo23è di fornire ai programmatori C più esperti un manuale di base per sviluppo.24Eviterò dettagli implementativi: per questo abbiamo il codice,25ed ignorerò intere parti di alcune procedure.26 27Prima di leggere questa guida, sappiate che non ho mai voluto scriverla,28essendo esageratamente sotto qualificato, ma ho sempre voluto leggere29qualcosa di simile, e quindi questa era l'unica via. Spero che possa30crescere e diventare un compendio di buone pratiche, punti di partenza31e generiche informazioni.32 33Gli attori34==========35 36In qualsiasi momento ognuna delle CPU di un sistema può essere:37 38- non associata ad alcun processo, servendo un'interruzione hardware;39 40- non associata ad alcun processo, servendo un softirq o tasklet;41 42- in esecuzione nello spazio kernel, associata ad un processo43 (contesto utente);44 45- in esecuzione di un processo nello spazio utente;46 47Esiste un ordine fra questi casi. Gli ultimi due possono avvicendarsi (preempt)48l'un l'altro, ma a parte questo esiste una gerarchia rigida: ognuno di questi49può avvicendarsi solo ad uno di quelli sottostanti. Per esempio, mentre un50softirq è in esecuzione su d'una CPU, nessun altro softirq può avvicendarsi51nell'esecuzione, ma un'interruzione hardware può. Ciò nonostante, le altre CPU52del sistema operano indipendentemente.53 54Più avanti vedremo alcuni modi in cui dal contesto utente è possibile bloccare55le interruzioni, così da impedirne davvero il diritto di prelazione.56 57Contesto utente58---------------59 60Ci si trova nel contesto utente quando si arriva da una chiamata di sistema61od altre eccezioni: come nello spazio utente, altre procedure più importanti,62o le interruzioni, possono far valere il proprio diritto di prelazione sul63vostro processo. Potete sospendere l'esecuzione chiamando :c:func:`schedule()`.64 65.. note::66 67 Si è sempre in contesto utente quando un modulo viene caricato o rimosso,68 e durante le operazioni nello strato dei dispositivi a blocchi69 (*block layer*).70 71Nel contesto utente, il puntatore ``current`` (il quale indica il processo al72momento in esecuzione) è valido, e :c:func:`in_interrupt()`73(``include/linux/preempt.h``) è falsa.74 75.. warning::76 77 Attenzione che se avete la prelazione o i softirq disabilitati (vedere78 di seguito), :c:func:`in_interrupt()` ritornerà un falso positivo.79 80Interruzioni hardware (Hard IRQs)81---------------------------------82 83Temporizzatori, schede di rete e tastiere sono esempi di vero hardware84che possono produrre interruzioni in un qualsiasi momento. Il kernel esegue85i gestori d'interruzione che prestano un servizio all'hardware. Il kernel86garantisce che questi gestori non vengano mai interrotti: se una stessa87interruzione arriva, questa verrà accodata (o scartata).88Dato che durante la loro esecuzione le interruzioni vengono disabilitate,89i gestori d'interruzioni devono essere veloci: spesso si limitano90esclusivamente a notificare la presa in carico dell'interruzione,91programmare una 'interruzione software' per l'esecuzione e quindi terminare.92 93Potete dire d'essere in una interruzione hardware perché in_hardirq()94ritorna vero.95 96.. warning::97 98 Attenzione, questa ritornerà un falso positivo se le interruzioni99 sono disabilitate (vedere di seguito).100 101Contesto d'interruzione software: softirq e tasklet102---------------------------------------------------103 104Quando una chiamata di sistema sta per tornare allo spazio utente,105oppure un gestore d'interruzioni termina, qualsiasi 'interruzione software'106marcata come pendente (solitamente da un'interruzione hardware) viene107eseguita (``kernel/softirq.c``).108 109La maggior parte del lavoro utile alla gestione di un'interruzione avviene qui.110All'inizio della transizione ai sistemi multiprocessore, c'erano solo i111cosiddetti 'bottom half' (BH), i quali non traevano alcun vantaggio da questi112sistemi. Non appena abbandonammo i computer raffazzonati con fiammiferi e113cicche, abbandonammo anche questa limitazione e migrammo alle interruzioni114software 'softirqs'.115 116Il file ``include/linux/interrupt.h`` elenca i differenti tipi di 'softirq'.117Un tipo di softirq molto importante è il timer (``include/linux/timer.h``):118potete programmarlo per far si che esegua funzioni dopo un determinato119periodo di tempo.120 121Dato che i softirq possono essere eseguiti simultaneamente su più di un122processore, spesso diventa estenuante l'averci a che fare. Per questa ragione,123i tasklet (``include/linux/interrupt.h``) vengo usati più di frequente:124possono essere registrati dinamicamente (il che significa che potete averne125quanti ne volete), e garantiscono che un qualsiasi tasklet verrà eseguito126solo su un processore alla volta, sebbene diversi tasklet possono essere127eseguiti simultaneamente.128 129.. warning::130 131 Il nome 'tasklet' è ingannevole: non hanno niente a che fare132 con i 'processi' ('tasks').133 134Potete determinate se siete in un softirq (o tasklet) utilizzando la135macro :c:func:`in_softirq()` (``include/linux/preempt.h``).136 137.. warning::138 139 State attenti che questa macro ritornerà un falso positivo140 se :ref:`bottom half lock <it_local_bh_disable>` è bloccato.141 142Alcune regole basilari143======================144 145Nessuna protezione della memoria146 Se corrompete la memoria, che sia in contesto utente o d'interruzione,147 la macchina si pianterà. Siete sicuri che quello che volete fare148 non possa essere fatto nello spazio utente?149 150Nessun numero in virgola mobile o MMX151 Il contesto della FPU non è salvato; anche se siete in contesto utente152 lo stato dell'FPU probabilmente non corrisponde a quello del processo153 corrente: vi incasinerete con lo stato di qualche altro processo. Se154 volete davvero usare la virgola mobile, allora dovrete salvare e recuperare155 lo stato dell'FPU (ed evitare cambi di contesto). Generalmente è una156 cattiva idea; usate l'aritmetica a virgola fissa.157 158Un limite rigido dello stack159 A seconda della configurazione del kernel lo stack è fra 3K e 6K per la160 maggior parte delle architetture a 32-bit; è di 14K per la maggior161 parte di quelle a 64-bit; e spesso è condiviso con le interruzioni,162 per cui non si può usare.163 Evitare profonde ricorsioni ad enormi array locali nello stack164 (allocateli dinamicamente).165 166Il kernel Linux è portabile167 Quindi mantenetelo tale. Il vostro codice dovrebbe essere a 64-bit ed168 indipendente dall'ordine dei byte (endianess) di un processore. Inoltre,169 dovreste minimizzare il codice specifico per un processore; per esempio170 il codice assembly dovrebbe essere incapsulato in modo pulito e minimizzato171 per facilitarne la migrazione. Generalmente questo codice dovrebbe essere172 limitato alla parte di kernel specifica per un'architettura.173 174ioctl: non scrivere nuove chiamate di sistema175=============================================176 177Una chiamata di sistema, generalmente, è scritta così::178 179 asmlinkage long sys_mycall(int arg)180 {181 return 0;182 }183 184Primo, nella maggior parte dei casi non volete creare nuove chiamate di185sistema.186Create un dispositivo a caratteri ed implementate l'appropriata chiamata ioctl.187Questo meccanismo è molto più flessibile delle chiamate di sistema: esso non188dev'essere dichiarato in tutte le architetture nei file189``include/asm/unistd.h`` e ``arch/kernel/entry.S``; inoltre, è improbabile190che questo venga accettato da Linus.191 192Se tutto quello che il vostro codice fa è leggere o scrivere alcuni parametri,193considerate l'implementazione di un'interfaccia :c:func:`sysfs()`.194 195All'interno di una ioctl vi trovate nel contesto utente di un processo. Quando196avviene un errore dovete ritornare un valore negativo di errno (consultate197``include/uapi/asm-generic/errno-base.h``,198``include/uapi/asm-generic/errno.h`` e ``include/linux/errno.h``), altrimenti199ritornate 0.200 201Dopo aver dormito dovreste verificare se ci sono stati dei segnali: il modo202Unix/Linux di gestire un segnale è di uscire temporaneamente dalla chiamata203di sistema con l'errore ``-ERESTARTSYS``. La chiamata di sistema ritornerà204al contesto utente, eseguirà il gestore del segnale e poi la vostra chiamata205di sistema riprenderà (a meno che l'utente non l'abbia disabilitata). Quindi,206dovreste essere pronti per continuare l'esecuzione, per esempio nel mezzo207della manipolazione di una struttura dati.208 209::210 211 if (signal_pending(current))212 return -ERESTARTSYS;213 214Se dovete eseguire dei calcoli molto lunghi: pensate allo spazio utente.215Se **davvero** volete farlo nel kernel ricordatevi di verificare periodicamente216se dovete *lasciare* il processore (ricordatevi che, per ogni processore, c'è217un sistema multi-processo senza diritto di prelazione).218Esempio::219 220 cond_resched(); /* Will sleep */221 222Una breve nota sulla progettazione delle interfacce: il motto dei sistemi223UNIX è "fornite meccanismi e non politiche"224 225La ricetta per uno stallo226=========================227 228Non è permesso invocare una procedura che potrebbe dormire, fanno eccezione229i seguenti casi:230 231- Siete in un contesto utente.232 233- Non trattenete alcun spinlock.234 235- Avete abilitato le interruzioni (in realtà, Andy Kleen dice che236 lo schedulatore le abiliterà per voi, ma probabilmente questo non è quello237 che volete).238 239Da tener presente che alcune funzioni potrebbero dormire implicitamente:240le più comuni sono quelle per l'accesso allo spazio utente (\*_user) e241quelle per l'allocazione della memoria senza l'opzione ``GFP_ATOMIC``242 243Dovreste sempre compilare il kernel con l'opzione ``CONFIG_DEBUG_ATOMIC_SLEEP``244attiva, questa vi avviserà se infrangete una di queste regole.245Se **infrangete** le regole, allora potreste bloccare il vostro scatolotto.246 247Veramente.248 249Alcune delle procedure più comuni250=================================251 252:c:func:`printk()`253------------------254 255Definita in ``include/linux/printk.h``256 257:c:func:`printk()` fornisce messaggi alla console, dmesg, e al demone syslog.258Essa è utile per il debugging o per la notifica di errori; può essere259utilizzata anche all'interno del contesto d'interruzione, ma usatela con260cautela: una macchina che ha la propria console inondata da messaggi diventa261inutilizzabile. La funzione utilizza un formato stringa quasi compatibile con262la printf ANSI C, e la concatenazione di una stringa C come primo argomento263per indicare la "priorità"::264 265 printk(KERN_INFO "i = %u\n", i);266 267Consultate ``include/linux/kern_levels.h`` per gli altri valori ``KERN_``;268questi sono interpretati da syslog come livelli. Un caso speciale:269per stampare un indirizzo IP usate::270 271 __be32 ipaddress;272 printk(KERN_INFO "my ip: %pI4\n", &ipaddress);273 274 275:c:func:`printk()` utilizza un buffer interno di 1K e non s'accorge di276eventuali sforamenti. Accertatevi che vi basti.277 278.. note::279 280 Saprete di essere un vero hacker del kernel quando inizierete a digitare281 nei vostri programmi utenti le printf come se fossero printk :)282 283.. note::284 285 Un'altra nota a parte: la versione originale di Unix 6 aveva un commento286 sopra alla funzione printf: "Printf non dovrebbe essere usata per il287 chiacchiericcio". Dovreste seguire questo consiglio.288 289:c:func:`copy_to_user()` / :c:func:`copy_from_user()` / :c:func:`get_user()` / :c:func:`put_user()`290---------------------------------------------------------------------------------------------------291 292Definite in ``include/linux/uaccess.h`` / ``asm/uaccess.h``293 294**[DORMONO]**295 296:c:func:`put_user()` e :c:func:`get_user()` sono usate per ricevere ed297impostare singoli valori (come int, char, o long) da e verso lo spazio utente.298Un puntatore nello spazio utente non dovrebbe mai essere dereferenziato: i dati299dovrebbero essere copiati usando suddette procedure. Entrambe ritornano300``-EFAULT`` oppure 0.301 302:c:func:`copy_to_user()` e :c:func:`copy_from_user()` sono più generiche:303esse copiano una quantità arbitraria di dati da e verso lo spazio utente.304 305.. warning::306 307 Al contrario di:c:func:`put_user()` e :c:func:`get_user()`, queste308 funzioni ritornano la quantità di dati copiati (0 è comunque un successo).309 310[Sì, questa interfaccia mi imbarazza. La battaglia torna in auge anno311dopo anno. --RR]312 313Le funzioni potrebbero dormire implicitamente. Queste non dovrebbero mai essere314invocate fuori dal contesto utente (non ha senso), con le interruzioni315disabilitate, o con uno spinlock trattenuto.316 317:c:func:`kmalloc()`/:c:func:`kfree()`318-------------------------------------319 320Definite in ``include/linux/slab.h``321 322**[POTREBBERO DORMIRE: LEGGI SOTTO]**323 324Queste procedure sono utilizzate per la richiesta dinamica di un puntatore ad325un pezzo di memoria allineato, esattamente come malloc e free nello spazio326utente, ma :c:func:`kmalloc()` ha un argomento aggiuntivo per indicare alcune327opzioni. Le opzioni più importanti sono:328 329``GFP_KERNEL``330 Potrebbe dormire per librarare della memoria. L'opzione fornisce il modo331 più affidabile per allocare memoria, ma il suo uso è strettamente limitato332 allo spazio utente.333 334``GFP_ATOMIC``335 Non dorme. Meno affidabile di ``GFP_KERNEL``, ma può essere usata in un336 contesto d'interruzione. Dovreste avere **davvero** una buona strategia337 per la gestione degli errori in caso di mancanza di memoria.338 339``GFP_DMA``340 Alloca memoria per il DMA sul bus ISA nello spazio d'indirizzamento341 inferiore ai 16MB. Se non sapete cos'è allora non vi serve.342 Molto inaffidabile.343 344Se vedete un messaggio d'avviso per una funzione dormiente che viene chiamata345da un contesto errato, allora probabilmente avete usato una funzione346d'allocazione dormiente da un contesto d'interruzione senza ``GFP_ATOMIC``.347Dovreste correggerlo. Sbrigatevi, non cincischiate.348 349Se allocate almeno ``PAGE_SIZE``(``asm/page.h`` o ``asm/page_types.h``) byte,350considerate l'uso di :c:func:`__get_free_pages()` (``include/linux/gfp.h``).351Accetta un argomento che definisce l'ordine (0 per per la dimensione di una352pagine, 1 per una doppia pagina, 2 per quattro pagine, eccetra) e le stesse353opzioni d'allocazione viste precedentemente.354 355Se state allocando un numero di byte notevolemnte superiore ad una pagina356potete usare :c:func:`vmalloc()`. Essa allocherà memoria virtuale all'interno357dello spazio kernel. Questo è un blocco di memoria fisica non contiguo, ma358la MMU vi darà l'impressione che lo sia (quindi, sarà contiguo solo dal punto359di vista dei processori, non dal punto di vista dei driver dei dispositivi360esterni).361Se per qualche strana ragione avete davvero bisogno di una grossa quantità di362memoria fisica contigua, avete un problema: Linux non ha un buon supporto per363questo caso d'uso perché, dopo un po' di tempo, la frammentazione della memoria364rende l'operazione difficile. Il modo migliore per allocare un simile blocco365all'inizio dell'avvio del sistema è attraverso la procedura366:c:func:`alloc_bootmem()`.367 368Prima di inventare la vostra cache per gli oggetti più usati, considerate369l'uso di una cache slab disponibile in ``include/linux/slab.h``.370 371:c:macro:`current`372-------------------373 374Definita in ``include/asm/current.h``375 376Questa variabile globale (in realtà una macro) contiene un puntatore alla377struttura del processo corrente, quindi è valido solo dal contesto utente.378Per esempio, quando un processo esegue una chiamata di sistema, questo379punterà alla struttura dati del processo chiamate.380Nel contesto d'interruzione in suo valore **non è NULL**.381 382:c:func:`mdelay()`/:c:func:`udelay()`383-------------------------------------384 385Definite in ``include/asm/delay.h`` / ``include/linux/delay.h``386 387Le funzioni :c:func:`udelay()` e :c:func:`ndelay()` possono essere utilizzate388per brevi pause. Non usate grandi valori perché rischiate d'avere un389overflow - in questo contesto la funzione :c:func:`mdelay()` è utile,390oppure considerate :c:func:`msleep()`.391 392:c:func:`cpu_to_be32()`/:c:func:`be32_to_cpu()`/:c:func:`cpu_to_le32()`/:c:func:`le32_to_cpu()`393-----------------------------------------------------------------------------------------------394 395Definite in ``include/asm/byteorder.h``396 397La famiglia di funzioni :c:func:`cpu_to_be32()` (dove "32" può essere398sostituito da 64 o 16, e "be" con "le") forniscono un modo generico399per fare conversioni sull'ordine dei byte (endianess): esse ritornano400il valore convertito. Tutte le varianti supportano anche il processo inverso:401:c:func:`be32_to_cpu()`, eccetera.402 403Queste funzioni hanno principalmente due varianti: la variante per404puntatori, come :c:func:`cpu_to_be32p()`, che prende un puntatore405ad un tipo, e ritorna il valore convertito. L'altra variante per406la famiglia di conversioni "in-situ", come :c:func:`cpu_to_be32s()`,407che convertono il valore puntato da un puntatore, e ritornano void.408 409:c:func:`local_irq_save()`/:c:func:`local_irq_restore()`410--------------------------------------------------------411 412Definite in ``include/linux/irqflags.h``413 414Queste funzioni abilitano e disabilitano le interruzioni hardware415sul processore locale. Entrambe sono rientranti; esse salvano lo stato416precedente nel proprio argomento ``unsigned long flags``. Se sapete417che le interruzioni sono abilite, potete semplicemente utilizzare418:c:func:`local_irq_disable()` e :c:func:`local_irq_enable()`.419 420.. _it_local_bh_disable:421 422:c:func:`local_bh_disable()`/:c:func:`local_bh_enable()`423--------------------------------------------------------424 425Definite in ``include/linux/bottom_half.h``426 427 428Queste funzioni abilitano e disabilitano le interruzioni software429sul processore locale. Entrambe sono rientranti; se le interruzioni430software erano già state disabilitate in precedenza, rimarranno431disabilitate anche dopo aver invocato questa coppia di funzioni.432Lo scopo è di prevenire l'esecuzione di softirq e tasklet sul processore433attuale.434 435:c:func:`smp_processor_id()`436----------------------------437 438Definita in ``include/linux/smp.h``439 440:c:func:`get_cpu()` nega il diritto di prelazione (quindi non potete essere441spostati su un altro processore all'improvviso) e ritorna il numero442del processore attuale, fra 0 e ``NR_CPUS``. Da notare che non è detto443che la numerazione dei processori sia continua. Quando avete terminato,444ritornate allo stato precedente con :c:func:`put_cpu()`.445 446Se sapete che non dovete essere interrotti da altri processi (per esempio,447se siete in un contesto d'interruzione, o il diritto di prelazione448è disabilitato) potete utilizzare smp_processor_id().449 450 451``__init``/``__exit``/``__initdata``452------------------------------------453 454Definite in ``include/linux/init.h``455 456Dopo l'avvio, il kernel libera una sezione speciale; le funzioni marcate457con ``__init`` e le strutture dati marcate con ``__initdata`` vengono458eliminate dopo il completamento dell'avvio: in modo simile i moduli eliminano459questa memoria dopo l'inizializzazione. ``__exit`` viene utilizzato per460dichiarare che una funzione verrà utilizzata solo in fase di rimozione:461la detta funzione verrà eliminata quando il file che la contiene non è462compilato come modulo. Guardate l'header file per informazioni. Da notare che463non ha senso avere una funzione marcata come ``__init`` e al tempo stesso464esportata ai moduli utilizzando :c:func:`EXPORT_SYMBOL()` o465:c:func:`EXPORT_SYMBOL_GPL()` - non funzionerà.466 467 468:c:func:`__initcall()`/:c:func:`module_init()`469----------------------------------------------470 471Definite in ``include/linux/init.h`` / ``include/linux/module.h``472 473Molte parti del kernel funzionano bene come moduli (componenti del kernel474caricabili dinamicamente). L'utilizzo delle macro :c:func:`module_init()`475e :c:func:`module_exit()` semplifica la scrittura di codice che può funzionare476sia come modulo, sia come parte del kernel, senza l'ausilio di #ifdef.477 478La macro :c:func:`module_init()` definisce quale funzione dev'essere479chiamata quando il modulo viene inserito (se il file è stato compilato come480tale), o in fase di avvio : se il file non è stato compilato come modulo la481macro :c:func:`module_init()` diventa equivalente a :c:func:`__initcall()`,482la quale, tramite qualche magia del linker, s'assicura che la funzione venga483chiamata durante l'avvio.484 485La funzione può ritornare un numero d'errore negativo per scatenare un486fallimento del caricamento (sfortunatamente, questo non ha effetto se il487modulo è compilato come parte integrante del kernel). Questa funzione è chiamata488in contesto utente con le interruzioni abilitate, quindi potrebbe dormire.489 490 491:c:func:`module_exit()`492-----------------------493 494 495Definita in ``include/linux/module.h``496 497Questa macro definisce la funzione che dev'essere chiamata al momento della498rimozione (o mai, nel caso in cui il file sia parte integrante del kernel).499Essa verrà chiamata solo quando il contatore d'uso del modulo raggiunge lo500zero. Questa funzione può anche dormire, ma non può fallire: tutto dev'essere501ripulito prima che la funzione ritorni.502 503Da notare che questa macro è opzionale: se non presente, il modulo non sarà504removibile (a meno che non usiate 'rmmod -f' ).505 506 507:c:func:`try_module_get()`/:c:func:`module_put()`508-------------------------------------------------509 510Definite in ``include/linux/module.h``511 512Queste funzioni maneggiano il contatore d'uso del modulo per proteggerlo dalla513rimozione (in aggiunta, un modulo non può essere rimosso se un altro modulo514utilizzo uno dei sui simboli esportati: vedere di seguito). Prima di eseguire515codice del modulo, dovreste chiamare :c:func:`try_module_get()` su quel modulo:516se fallisce significa che il modulo è stato rimosso e dovete agire come se517non fosse presente. Altrimenti, potete accedere al modulo in sicurezza, e518chiamare :c:func:`module_put()` quando avete finito.519 520La maggior parte delle strutture registrabili hanno un campo owner521(proprietario), come nella struttura522:c:type:`struct file_operations <file_operations>`.523Impostate questo campo al valore della macro ``THIS_MODULE``.524 525 526Code d'attesa ``include/linux/wait.h``527======================================528 529**[DORMONO]**530 531Una coda d'attesa è usata per aspettare che qualcuno vi attivi quando una532certa condizione s'avvera. Per evitare corse critiche, devono essere usate533con cautela. Dichiarate una :c:type:`wait_queue_head_t`, e poi i processi534che vogliono attendere il verificarsi di quella condizione dichiareranno535una :c:type:`wait_queue_entry_t` facendo riferimento a loro stessi, poi536metteranno questa in coda.537 538Dichiarazione539-------------540 541Potere dichiarare una ``wait_queue_head_t`` utilizzando la macro542:c:func:`DECLARE_WAIT_QUEUE_HEAD()` oppure utilizzando la procedura543:c:func:`init_waitqueue_head()` nel vostro codice d'inizializzazione.544 545Accodamento546-----------547 548Mettersi in una coda d'attesa è piuttosto complesso, perché dovete549mettervi in coda prima di verificare la condizione. Esiste una macro550a questo scopo: :c:func:`wait_event_interruptible()` (``include/linux/wait.h``).551Il primo argomento è la testa della coda d'attesa, e il secondo è552un'espressione che dev'essere valutata; la macro ritorna 0 quando questa553espressione è vera, altrimenti ``-ERESTARTSYS`` se è stato ricevuto un segnale.554La versione :c:func:`wait_event()` ignora i segnali.555 556Svegliare una procedura in coda557-------------------------------558 559Chiamate :c:func:`wake_up()` (``include/linux/wait.h``); questa attiverà tutti560i processi in coda. Ad eccezione se uno di questi è impostato come561``TASK_EXCLUSIVE``, in questo caso i rimanenti non verranno svegliati.562Nello stesso header file esistono altre varianti di questa funzione.563 564Operazioni atomiche565===================566 567Certe operazioni sono garantite come atomiche su tutte le piattaforme.568Il primo gruppo di operazioni utilizza :c:type:`atomic_t`569(``include/asm/atomic.h``); questo contiene un intero con segno (minimo 32bit),570e dovete utilizzare queste funzione per modificare o leggere variabili di tipo571:c:type:`atomic_t`. :c:func:`atomic_read()` e :c:func:`atomic_set()` leggono ed572impostano il contatore, :c:func:`atomic_add()`, :c:func:`atomic_sub()`,573:c:func:`atomic_inc()`, :c:func:`atomic_dec()`, e574:c:func:`atomic_dec_and_test()` (ritorna vero se raggiunge zero dopo essere575stata decrementata).576 577Sì. Ritorna vero (ovvero != 0) se la variabile atomica è zero.578 579Da notare che queste funzioni sono più lente rispetto alla normale aritmetica,580e quindi non dovrebbero essere usate a sproposito.581 582Il secondo gruppo di operazioni atomiche sono definite in583``include/linux/bitops.h`` ed agiscono sui bit d'una variabile di tipo584``unsigned long``. Queste operazioni prendono come argomento un puntatore585alla variabile, e un numero di bit dove 0 è quello meno significativo.586:c:func:`set_bit()`, :c:func:`clear_bit()` e :c:func:`change_bit()`587impostano, cancellano, ed invertono il bit indicato.588:c:func:`test_and_set_bit()`, :c:func:`test_and_clear_bit()` e589:c:func:`test_and_change_bit()` fanno la stessa cosa, ad eccezione che590ritornano vero se il bit era impostato; queste sono particolarmente591utili quando si vuole impostare atomicamente dei flag.592 593Con queste operazioni è possibile utilizzare indici di bit che eccedono594il valore ``BITS_PER_LONG``. Il comportamento è strano sulle piattaforme595big-endian quindi è meglio evitarlo.596 597Simboli598=======599 600All'interno del kernel, si seguono le normali regole del linker (ovvero,601a meno che un simbolo non venga dichiarato con visibilita limitata ad un602file con la parola chiave ``static``, esso può essere utilizzato in qualsiasi603parte del kernel). Nonostante ciò, per i moduli, esiste una tabella dei604simboli esportati che limita i punti di accesso al kernel. Anche i moduli605possono esportare simboli.606 607:c:func:`EXPORT_SYMBOL()`608-------------------------609 610Definita in ``include/linux/export.h``611 612Questo è il classico metodo per esportare un simbolo: i moduli caricati613dinamicamente potranno utilizzare normalmente il simbolo.614 615:c:func:`EXPORT_SYMBOL_GPL()`616-----------------------------617 618Definita in ``include/linux/export.h``619 620Essa è simile a :c:func:`EXPORT_SYMBOL()` ad eccezione del fatto che i621simboli esportati con :c:func:`EXPORT_SYMBOL_GPL()` possono essere622utilizzati solo dai moduli che hanno dichiarato una licenza compatibile623con la GPL attraverso :c:func:`MODULE_LICENSE()`. Questo implica che la624funzione esportata è considerata interna, e non una vera e propria interfaccia.625Alcuni manutentori e sviluppatori potrebbero comunque richiedere626:c:func:`EXPORT_SYMBOL_GPL()` quando si aggiungono nuove funzionalità o627interfacce.628 629:c:func:`EXPORT_SYMBOL_NS()`630----------------------------631 632Definita in ``include/linux/export.h``633 634Questa è una variate di `EXPORT_SYMBOL()` che permette di specificare uno635spazio dei nomi. Lo spazio dei nomi è documentato in636Documentation/translations/it_IT/core-api/symbol-namespaces.rst.637 638:c:func:`EXPORT_SYMBOL_NS_GPL()`639--------------------------------640 641Definita in ``include/linux/export.h``642 643Questa è una variate di `EXPORT_SYMBOL_GPL()` che permette di specificare uno644spazio dei nomi. Lo spazio dei nomi è documentato in645Documentation/translations/it_IT/core-api/symbol-namespaces.rst.646 647Procedure e convenzioni648=======================649 650Liste doppiamente concatenate ``include/linux/list.h``651------------------------------------------------------652 653Un tempo negli header del kernel c'erano tre gruppi di funzioni per654le liste concatenate, ma questa è stata la vincente. Se non avete particolari655necessità per una semplice lista concatenata, allora questa è una buona scelta.656 657In particolare, :c:func:`list_for_each_entry()` è utile.658 659Convenzione dei valori di ritorno660---------------------------------661 662Per codice chiamato in contesto utente, è molto comune sfidare le convenzioni663C e ritornare 0 in caso di successo, ed un codice di errore negativo664(eg. ``-EFAULT``) nei casi fallimentari. Questo potrebbe essere controintuitivo665a prima vista, ma è abbastanza diffuso nel kernel.666 667Utilizzate :c:func:`ERR_PTR()` (``include/linux/err.h``) per codificare668un numero d'errore negativo in un puntatore, e :c:func:`IS_ERR()` e669:c:func:`PTR_ERR()` per recuperarlo di nuovo: così si evita d'avere un670puntatore dedicato per il numero d'errore. Da brividi, ma in senso positivo.671 672Rompere la compilazione673-----------------------674 675Linus e gli altri sviluppatori a volte cambiano i nomi delle funzioni e676delle strutture nei kernel in sviluppo; questo non è solo per tenere677tutti sulle spine: questo riflette cambiamenti fondamentati (eg. la funzione678non può più essere chiamata con le funzioni attive, o fa controlli aggiuntivi,679o non fa più controlli che venivano fatti in precedenza). Solitamente a questo680s'accompagna un'adeguata e completa nota sulla lista di discussone681più adatta; cercate negli archivi. Solitamente eseguire una semplice682sostituzione su tutto un file rendere le cose **peggiori**.683 684Inizializzazione dei campi d'una struttura685------------------------------------------686 687Il metodo preferito per l'inizializzazione delle strutture è quello688di utilizzare gli inizializzatori designati, come definiti nello689standard ISO C99, eg::690 691 static struct block_device_operations opt_fops = {692 .open = opt_open,693 .release = opt_release,694 .ioctl = opt_ioctl,695 .check_media_change = opt_media_change,696 };697 698Questo rende più facile la ricerca con grep, e rende più chiaro quale campo699viene impostato. Dovreste fare così perché si mostra meglio.700 701Estensioni GNU702--------------703 704Le estensioni GNU sono esplicitamente permesse nel kernel Linux. Da notare705che alcune delle più complesse non sono ben supportate, per via dello scarso706sviluppo, ma le seguenti sono da considerarsi la norma (per maggiori dettagli,707leggete la sezione "C Extensions" nella pagina info di GCC - Sì, davvero708la pagina info, la pagina man è solo un breve riassunto delle cose nella709pagina info).710 711- Funzioni inline712 713- Istruzioni in espressioni (ie. il costrutto ({ and }) ).714 715- Dichiarate attributi di una funzione / variabile / tipo716 (__attribute__)717 718- typeof719 720- Array con lunghezza zero721 722- Macro varargs723 724- Aritmentica sui puntatori void725 726- Inizializzatori non costanti727 728- Istruzioni assembler (non al di fuori di 'arch/' e 'include/asm/')729 730- Nomi delle funzioni come stringhe (__func__).731 732- __builtin_constant_p()733 734Siate sospettosi quando utilizzate long long nel kernel, il codice generato735da gcc è orribile ed anche peggio: le divisioni e le moltiplicazioni non736funzionano sulle piattaforme i386 perché le rispettive funzioni di runtime737di GCC non sono incluse nell'ambiente del kernel.738 739C++740---741 742Solitamente utilizzare il C++ nel kernel è una cattiva idea perché743il kernel non fornisce il necessario ambiente di runtime e gli header file744non sono stati verificati. Rimane comunque possibile, ma non consigliato.745Se davvero volete usarlo, almeno evitate le eccezioni.746 747NUMif748-----749 750Viene generalmente considerato più pulito l'uso delle macro negli header file751(o all'inizio dei file .c) per astrarre funzioni piuttosto che utlizzare752l'istruzione di pre-processore \`#if' all'interno del codice sorgente.753 754Mettere le vostre cose nel kernel755=================================756 757Al fine d'avere le vostre cose in ordine per l'inclusione ufficiale, o758anche per avere patch pulite, c'è del lavoro amministrativo da fare:759 760- Trovare chi è responsabile del codice che state modificando. Guardare in cima761 ai file sorgenti, all'interno del file ``MAINTAINERS``, ed alla fine762 di tutti nel file ``CREDITS``. Dovreste coordinarvi con queste persone763 per evitare di duplicare gli sforzi, o provare qualcosa che è già stato764 rigettato.765 766 Assicuratevi di mettere il vostro nome ed indirizzo email in cima a767 tutti i file che create o che maneggiate significativamente. Questo è768 il primo posto dove le persone guarderanno quando troveranno un baco,769 o quando **loro** vorranno fare una modifica.770 771- Solitamente vorrete un'opzione di configurazione per la vostra modifica772 al kernel. Modificate ``Kconfig`` nella cartella giusta. Il linguaggio773 Config è facile con copia ed incolla, e c'è una completa documentazione774 nel file ``Documentation/kbuild/kconfig-language.rst``.775 776 Nella descrizione della vostra opzione, assicuratevi di parlare sia agli777 utenti esperti sia agli utente che non sanno nulla del vostro lavoro.778 Menzionate qui le incompatibilità ed i problemi. Chiaramente la779 descrizione deve terminare con “if in doubt, say N” (se siete in dubbio,780 dite N) (oppure, occasionalmente, \`Y'); questo è per le persone che non781 hanno idea di che cosa voi stiate parlando.782 783- Modificate il file ``Makefile``: le variabili CONFIG sono esportate qui,784 quindi potete solitamente aggiungere una riga come la seguete785 "obj-$(CONFIG_xxx) += xxx.o". La sintassi è documentata nel file786 ``Documentation/kbuild/makefiles.rst``.787 788- Aggiungete voi stessi in ``CREDITS`` se credete di aver fatto qualcosa di789 notevole, solitamente qualcosa che supera il singolo file (comunque il vostro790 nome dovrebbe essere all'inizio dei file sorgenti). ``MAINTAINERS`` significa791 che volete essere consultati quando vengono fatte delle modifiche ad un792 sottosistema, e quando ci sono dei bachi; questo implica molto di più di un793 semplice impegno su una parte del codice.794 795- Infine, non dimenticatevi di leggere796 ``Documentation/process/submitting-patches.rst``.797 798Trucchetti del kernel799=====================800 801Dopo una rapida occhiata al codice, questi sono i preferiti. Sentitevi liberi802di aggiungerne altri.803 804``arch/x86/include/asm/delay.h``::805 806 #define ndelay(n) (__builtin_constant_p(n) ? \807 ((n) > 20000 ? __bad_ndelay() : __const_udelay((n) * 5ul)) : \808 __ndelay(n))809 810 811``include/linux/fs.h``::812 813 /*814 * Kernel pointers have redundant information, so we can use a815 * scheme where we can return either an error code or a dentry816 * pointer with the same return value.817 *818 * This should be a per-architecture thing, to allow different819 * error and pointer decisions.820 */821 #define ERR_PTR(err) ((void *)((long)(err)))822 #define PTR_ERR(ptr) ((long)(ptr))823 #define IS_ERR(ptr) ((unsigned long)(ptr) > (unsigned long)(-1000))824 825``arch/x86/include/asm/uaccess_32.h:``::826 827 #define copy_to_user(to,from,n) \828 (__builtin_constant_p(n) ? \829 __constant_copy_to_user((to),(from),(n)) : \830 __generic_copy_to_user((to),(from),(n)))831 832 833``arch/sparc/kernel/head.S:``::834 835 /*836 * Sun people can't spell worth damn. "compatability" indeed.837 * At least we *know* we can't spell, and use a spell-checker.838 */839 840 /* Uh, actually Linus it is I who cannot spell. Too much murky841 * Sparc assembly will do this to ya.842 */843 C_LABEL(cputypvar):844 .asciz "compatibility"845 846 /* Tested on SS-5, SS-10. Probably someone at Sun applied a spell-checker. */847 .align 4848 C_LABEL(cputypvar_sun4m):849 .asciz "compatible"850 851 852``arch/sparc/lib/checksum.S:``::853 854 /* Sun, you just can't beat me, you just can't. Stop trying,855 * give up. I'm serious, I am going to kick the living shit856 * out of you, game over, lights out.857 */858 859 860Ringraziamenti861==============862 863Ringrazio Andi Kleen per le sue idee, le risposte alle mie domande,864le correzioni dei miei errori, l'aggiunta di contenuti, eccetera.865Philipp Rumpf per l'ortografia e per aver reso più chiaro il testo, e866per alcuni eccellenti punti tutt'altro che ovvi. Werner Almesberger867per avermi fornito un ottimo riassunto di :c:func:`disable_irq()`,868e Jes Sorensen e Andrea Arcangeli per le precisazioni. Michael Elizabeth869Chastain per aver verificato ed aggiunto la sezione configurazione.870Telsa Gwynne per avermi insegnato DocBook.871