Federico Vaga | 1497624 | 2018-07-07 00:05:17 +0200 | [diff] [blame] | 1 | .. include:: ../disclaimer-ita.rst |
| 2 | |
| 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 | ================================================= |
| 12 | L'inaffidabile guida all'hacking del kernel Linux |
| 13 | ================================================= |
| 14 | |
| 15 | :Author: Rusty Russell |
| 16 | |
| 17 | Introduzione |
| 18 | ============ |
| 19 | |
| 20 | Benvenuto, gentile lettore, alla notevole ed inaffidabile guida all'hacking |
| 21 | del kernel Linux ad opera di Rusty. Questo documento descrive le procedure |
| 22 | più usate ed i concetti necessari per scrivere codice per il kernel: lo scopo |
| 23 | è di fornire ai programmatori C più esperti un manuale di base per sviluppo. |
| 24 | Eviterò dettagli implementativi: per questo abbiamo il codice, |
| 25 | ed ignorerò intere parti di alcune procedure. |
| 26 | |
| 27 | Prima di leggere questa guida, sappiate che non ho mai voluto scriverla, |
| 28 | essendo esageratamente sotto qualificato, ma ho sempre voluto leggere |
| 29 | qualcosa di simile, e quindi questa era l'unica via. Spero che possa |
| 30 | crescere e diventare un compendio di buone pratiche, punti di partenza |
| 31 | e generiche informazioni. |
| 32 | |
| 33 | Gli attori |
| 34 | ========== |
| 35 | |
| 36 | In 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 processo |
| 43 | (contesto utente); |
| 44 | |
| 45 | - in esecuzione di un processo nello spazio utente; |
| 46 | |
| 47 | Esiste un ordine fra questi casi. Gli ultimi due possono avvicendarsi (preempt) |
| 48 | l'un l'altro, ma a parte questo esiste una gerarchia rigida: ognuno di questi |
| 49 | può avvicendarsi solo ad uno di quelli sottostanti. Per esempio, mentre un |
| 50 | softirq è in esecuzione su d'una CPU, nessun altro softirq può avvicendarsi |
| 51 | nell'esecuzione, ma un'interruzione hardware può. Ciò nonostante, le altre CPU |
| 52 | del sistema operano indipendentemente. |
| 53 | |
| 54 | Più avanti vedremo alcuni modi in cui dal contesto utente è possibile bloccare |
| 55 | le interruzioni, così da impedirne davvero il diritto di prelazione. |
| 56 | |
| 57 | Contesto utente |
| 58 | --------------- |
| 59 | |
| 60 | Ci si trova nel contesto utente quando si arriva da una chiamata di sistema |
| 61 | od altre eccezioni: come nello spazio utente, altre procedure più importanti, |
| 62 | o le interruzioni, possono far valere il proprio diritto di prelazione sul |
| 63 | vostro 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 blocchi |
| 69 | (*block layer*). |
| 70 | |
| 71 | Nel contesto utente, il puntatore ``current`` (il quale indica il processo al |
| 72 | momento 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 (vedere |
| 78 | di seguito), :c:func:`in_interrupt()` ritornerà un falso positivo. |
| 79 | |
| 80 | Interruzioni hardware (Hard IRQs) |
| 81 | --------------------------------- |
| 82 | |
| 83 | Temporizzatori, schede di rete e tastiere sono esempi di vero hardware |
| 84 | che possono produrre interruzioni in un qualsiasi momento. Il kernel esegue |
| 85 | i gestori d'interruzione che prestano un servizio all'hardware. Il kernel |
| 86 | garantisce che questi gestori non vengano mai interrotti: se una stessa |
| 87 | interruzione arriva, questa verrà accodata (o scartata). |
| 88 | Dato che durante la loro esecuzione le interruzioni vengono disabilitate, |
| 89 | i gestori d'interruzioni devono essere veloci: spesso si limitano |
| 90 | esclusivamente a notificare la presa in carico dell'interruzione, |
| 91 | programmare una 'interruzione software' per l'esecuzione e quindi terminare. |
| 92 | |
| 93 | Potete dire d'essere in una interruzione hardware perché :c:func:`in_irq()` |
| 94 | ritorna vero. |
| 95 | |
| 96 | .. warning:: |
| 97 | |
| 98 | Attenzione, questa ritornerà un falso positivo se le interruzioni |
| 99 | sono disabilitate (vedere di seguito). |
| 100 | |
| 101 | Contesto d'interruzione software: softirq e tasklet |
| 102 | --------------------------------------------------- |
| 103 | |
| 104 | Quando una chiamata di sistema sta per tornare allo spazio utente, |
| 105 | oppure un gestore d'interruzioni termina, qualsiasi 'interruzione software' |
| 106 | marcata come pendente (solitamente da un'interruzione hardware) viene |
| 107 | eseguita (``kernel/softirq.c``). |
| 108 | |
| 109 | La maggior parte del lavoro utile alla gestione di un'interruzione avviene qui. |
| 110 | All'inizio della transizione ai sistemi multiprocessore, c'erano solo i |
| 111 | cosiddetti 'bottom half' (BH), i quali non traevano alcun vantaggio da questi |
| 112 | sistemi. Non appena abbandonammo i computer raffazzonati con fiammiferi e |
| 113 | cicche, abbandonammo anche questa limitazione e migrammo alle interruzioni |
| 114 | software 'softirqs'. |
| 115 | |
| 116 | Il file ``include/linux/interrupt.h`` elenca i differenti tipi di 'softirq'. |
| 117 | Un tipo di softirq molto importante è il timer (``include/linux/timer.h``): |
| 118 | potete programmarlo per far si che esegua funzioni dopo un determinato |
| 119 | periodo di tempo. |
| 120 | |
| 121 | Dato che i softirq possono essere eseguiti simultaneamente su più di un |
| 122 | processore, spesso diventa estenuante l'averci a che fare. Per questa ragione, |
| 123 | i tasklet (``include/linux/interrupt.h``) vengo usati più di frequente: |
| 124 | possono essere registrati dinamicamente (il che significa che potete averne |
| 125 | quanti ne volete), e garantiscono che un qualsiasi tasklet verrà eseguito |
| 126 | solo su un processore alla volta, sebbene diversi tasklet possono essere |
| 127 | eseguiti simultaneamente. |
| 128 | |
| 129 | .. warning:: |
| 130 | |
| 131 | Il nome 'tasklet' è ingannevole: non hanno niente a che fare |
| 132 | con i 'processi' ('tasks'), e probabilmente hanno più a che vedere |
| 133 | con qualche pessima vodka che Alexey Kuznetsov si fece a quel tempo. |
| 134 | |
| 135 | Potete determinate se siete in un softirq (o tasklet) utilizzando la |
| 136 | macro :c:func:`in_softirq()` (``include/linux/preempt.h``). |
| 137 | |
| 138 | .. warning:: |
| 139 | |
| 140 | State attenti che questa macro ritornerà un falso positivo |
| 141 | se :ref:`botton half lock <it_local_bh_disable>` è bloccato. |
| 142 | |
| 143 | Alcune regole basilari |
| 144 | ====================== |
| 145 | |
| 146 | Nessuna protezione della memoria |
| 147 | Se corrompete la memoria, che sia in contesto utente o d'interruzione, |
| 148 | la macchina si pianterà. Siete sicuri che quello che volete fare |
| 149 | non possa essere fatto nello spazio utente? |
| 150 | |
| 151 | Nessun numero in virgola mobile o MMX |
| 152 | Il contesto della FPU non è salvato; anche se siete in contesto utente |
| 153 | lo stato dell'FPU probabilmente non corrisponde a quello del processo |
| 154 | corrente: vi incasinerete con lo stato di qualche altro processo. Se |
| 155 | volete davvero usare la virgola mobile, allora dovrete salvare e recuperare |
| 156 | lo stato dell'FPU (ed evitare cambi di contesto). Generalmente è una |
| 157 | cattiva idea; usate l'aritmetica a virgola fissa. |
| 158 | |
| 159 | Un limite rigido dello stack |
| 160 | A seconda della configurazione del kernel lo stack è fra 3K e 6K per la |
| 161 | maggior parte delle architetture a 32-bit; è di 14K per la maggior |
| 162 | parte di quelle a 64-bit; e spesso è condiviso con le interruzioni, |
| 163 | per cui non si può usare. |
| 164 | Evitare profonde ricorsioni ad enormi array locali nello stack |
| 165 | (allocateli dinamicamente). |
| 166 | |
| 167 | Il kernel Linux è portabile |
| 168 | Quindi mantenetelo tale. Il vostro codice dovrebbe essere a 64-bit ed |
| 169 | indipendente dall'ordine dei byte (endianess) di un processore. Inoltre, |
| 170 | dovreste minimizzare il codice specifico per un processore; per esempio |
| 171 | il codice assembly dovrebbe essere incapsulato in modo pulito e minimizzato |
| 172 | per facilitarne la migrazione. Generalmente questo codice dovrebbe essere |
| 173 | limitato alla parte di kernel specifica per un'architettura. |
| 174 | |
| 175 | ioctl: non scrivere nuove chiamate di sistema |
| 176 | ============================================= |
| 177 | |
| 178 | Una chiamata di sistema, generalmente, è scritta così:: |
| 179 | |
| 180 | asmlinkage long sys_mycall(int arg) |
| 181 | { |
| 182 | return 0; |
| 183 | } |
| 184 | |
| 185 | Primo, nella maggior parte dei casi non volete creare nuove chiamate di |
| 186 | sistema. |
| 187 | Create un dispositivo a caratteri ed implementate l'appropriata chiamata ioctl. |
| 188 | Questo meccanismo è molto più flessibile delle chiamate di sistema: esso non |
| 189 | dev'essere dichiarato in tutte le architetture nei file |
| 190 | ``include/asm/unistd.h`` e ``arch/kernel/entry.S``; inoltre, è improbabile |
| 191 | che questo venga accettato da Linus. |
| 192 | |
| 193 | Se tutto quello che il vostro codice fa è leggere o scrivere alcuni parametri, |
| 194 | considerate l'implementazione di un'interfaccia :c:func:`sysfs()`. |
| 195 | |
| 196 | All'interno di una ioctl vi trovate nel contesto utente di un processo. Quando |
| 197 | avviene un errore dovete ritornare un valore negativo di errno (consultate |
| 198 | ``include/uapi/asm-generic/errno-base.h``, |
| 199 | ``include/uapi/asm-generic/errno.h`` e ``include/linux/errno.h``), altrimenti |
| 200 | ritornate 0. |
| 201 | |
| 202 | Dopo aver dormito dovreste verificare se ci sono stati dei segnali: il modo |
| 203 | Unix/Linux di gestire un segnale è di uscire temporaneamente dalla chiamata |
| 204 | di sistema con l'errore ``-ERESTARTSYS``. La chiamata di sistema ritornerà |
| 205 | al contesto utente, eseguirà il gestore del segnale e poi la vostra chiamata |
| 206 | di sistema riprenderà (a meno che l'utente non l'abbia disabilitata). Quindi, |
| 207 | dovreste essere pronti per continuare l'esecuzione, per esempio nel mezzo |
| 208 | della manipolazione di una struttura dati. |
| 209 | |
| 210 | :: |
| 211 | |
| 212 | if (signal_pending(current)) |
| 213 | return -ERESTARTSYS; |
| 214 | |
| 215 | Se dovete eseguire dei calcoli molto lunghi: pensate allo spazio utente. |
| 216 | Se **davvero** volete farlo nel kernel ricordatevi di verificare periodicamente |
| 217 | se dovete *lasciare* il processore (ricordatevi che, per ogni processore, c'è |
| 218 | un sistema multi-processo senza diritto di prelazione). |
| 219 | Esempio:: |
| 220 | |
| 221 | cond_resched(); /* Will sleep */ |
| 222 | |
| 223 | Una breve nota sulla progettazione delle interfacce: il motto dei sistemi |
| 224 | UNIX è "fornite meccanismi e non politiche" |
| 225 | |
| 226 | La ricetta per uno stallo |
| 227 | ========================= |
| 228 | |
| 229 | Non è permesso invocare una procedura che potrebbe dormire, fanno eccezione |
| 230 | i seguenti casi: |
| 231 | |
| 232 | - Siete in un contesto utente. |
| 233 | |
| 234 | - Non trattenete alcun spinlock. |
| 235 | |
| 236 | - Avete abilitato le interruzioni (in realtà, Andy Kleen dice che |
| 237 | lo schedulatore le abiliterà per voi, ma probabilmente questo non è quello |
| 238 | che volete). |
| 239 | |
| 240 | Da tener presente che alcune funzioni potrebbero dormire implicitamente: |
| 241 | le più comuni sono quelle per l'accesso allo spazio utente (\*_user) e |
| 242 | quelle per l'allocazione della memoria senza l'opzione ``GFP_ATOMIC`` |
| 243 | |
| 244 | Dovreste sempre compilare il kernel con l'opzione ``CONFIG_DEBUG_ATOMIC_SLEEP`` |
| 245 | attiva, questa vi avviserà se infrangete una di queste regole. |
| 246 | Se **infrangete** le regole, allora potreste bloccare il vostro scatolotto. |
| 247 | |
| 248 | Veramente. |
| 249 | |
| 250 | Alcune delle procedure più comuni |
| 251 | ================================= |
| 252 | |
| 253 | :c:func:`printk()` |
| 254 | ------------------ |
| 255 | |
| 256 | Definita in ``include/linux/printk.h`` |
| 257 | |
| 258 | :c:func:`printk()` fornisce messaggi alla console, dmesg, e al demone syslog. |
| 259 | Essa è utile per il debugging o per la notifica di errori; può essere |
| 260 | utilizzata anche all'interno del contesto d'interruzione, ma usatela con |
| 261 | cautela: una macchina che ha la propria console inondata da messaggi diventa |
| 262 | inutilizzabile. La funzione utilizza un formato stringa quasi compatibile con |
| 263 | la printf ANSI C, e la concatenazione di una stringa C come primo argomento |
| 264 | per indicare la "priorità":: |
| 265 | |
| 266 | printk(KERN_INFO "i = %u\n", i); |
| 267 | |
| 268 | Consultate ``include/linux/kern_levels.h`` per gli altri valori ``KERN_``; |
| 269 | questi sono interpretati da syslog come livelli. Un caso speciale: |
| 270 | per stampare un indirizzo IP usate:: |
| 271 | |
| 272 | __be32 ipaddress; |
| 273 | printk(KERN_INFO "my ip: %pI4\n", &ipaddress); |
| 274 | |
| 275 | |
| 276 | :c:func:`printk()` utilizza un buffer interno di 1K e non s'accorge di |
| 277 | eventuali sforamenti. Accertatevi che vi basti. |
| 278 | |
| 279 | .. note:: |
| 280 | |
| 281 | Saprete di essere un vero hacker del kernel quando inizierete a digitare |
| 282 | nei vostri programmi utenti le printf come se fossero printk :) |
| 283 | |
| 284 | .. note:: |
| 285 | |
| 286 | Un'altra nota a parte: la versione originale di Unix 6 aveva un commento |
| 287 | sopra alla funzione printf: "Printf non dovrebbe essere usata per il |
| 288 | chiacchiericcio". Dovreste seguire questo consiglio. |
| 289 | |
| 290 | :c:func:`copy_to_user()` / :c:func:`copy_from_user()` / :c:func:`get_user()` / :c:func:`put_user()` |
| 291 | --------------------------------------------------------------------------------------------------- |
| 292 | |
| 293 | Definite in ``include/linux/uaccess.h`` / ``asm/uaccess.h`` |
| 294 | |
| 295 | **[DORMONO]** |
| 296 | |
| 297 | :c:func:`put_user()` e :c:func:`get_user()` sono usate per ricevere ed |
| 298 | impostare singoli valori (come int, char, o long) da e verso lo spazio utente. |
| 299 | Un puntatore nello spazio utente non dovrebbe mai essere dereferenziato: i dati |
| 300 | dovrebbero essere copiati usando suddette procedure. Entrambe ritornano |
| 301 | ``-EFAULT`` oppure 0. |
| 302 | |
| 303 | :c:func:`copy_to_user()` e :c:func:`copy_from_user()` sono più generiche: |
| 304 | esse copiano una quantità arbitraria di dati da e verso lo spazio utente. |
| 305 | |
| 306 | .. warning:: |
| 307 | |
| 308 | Al contrario di:c:func:`put_user()` e :c:func:`get_user()`, queste |
| 309 | funzioni ritornano la quantità di dati copiati (0 è comunque un successo). |
| 310 | |
| 311 | [Sì, questa stupida interfaccia mi imbarazza. La battaglia torna in auge anno |
| 312 | dopo anno. --RR] |
| 313 | |
| 314 | Le funzioni potrebbero dormire implicitamente. Queste non dovrebbero mai essere |
| 315 | invocate fuori dal contesto utente (non ha senso), con le interruzioni |
| 316 | disabilitate, o con uno spinlock trattenuto. |
| 317 | |
| 318 | :c:func:`kmalloc()`/:c:func:`kfree()` |
| 319 | ------------------------------------- |
| 320 | |
| 321 | Definite in ``include/linux/slab.h`` |
| 322 | |
| 323 | **[POTREBBERO DORMIRE: LEGGI SOTTO]** |
| 324 | |
| 325 | Queste procedure sono utilizzate per la richiesta dinamica di un puntatore ad |
| 326 | un pezzo di memoria allineato, esattamente come malloc e free nello spazio |
| 327 | utente, ma :c:func:`kmalloc()` ha un argomento aggiuntivo per indicare alcune |
| 328 | opzioni. Le opzioni più importanti sono: |
| 329 | |
| 330 | ``GFP_KERNEL`` |
| 331 | Potrebbe dormire per librarare della memoria. L'opzione fornisce il modo |
| 332 | più affidabile per allocare memoria, ma il suo uso è strettamente limitato |
| 333 | allo spazio utente. |
| 334 | |
| 335 | ``GFP_ATOMIC`` |
| 336 | Non dorme. Meno affidabile di ``GFP_KERNEL``, ma può essere usata in un |
| 337 | contesto d'interruzione. Dovreste avere **davvero** una buona strategia |
| 338 | per la gestione degli errori in caso di mancanza di memoria. |
| 339 | |
| 340 | ``GFP_DMA`` |
| 341 | Alloca memoria per il DMA sul bus ISA nello spazio d'indirizzamento |
| 342 | inferiore ai 16MB. Se non sapete cos'è allora non vi serve. |
| 343 | Molto inaffidabile. |
| 344 | |
| 345 | Se vedete un messaggio d'avviso per una funzione dormiente che viene chiamata |
| 346 | da un contesto errato, allora probabilmente avete usato una funzione |
| 347 | d'allocazione dormiente da un contesto d'interruzione senza ``GFP_ATOMIC``. |
| 348 | Dovreste correggerlo. Sbrigatevi, non cincischiate. |
| 349 | |
| 350 | Se allocate almeno ``PAGE_SIZE``(``asm/page.h`` o ``asm/page_types.h``) byte, |
| 351 | considerate l'uso di :c:func:`__get_free_pages()` (``include/linux/gfp.h``). |
| 352 | Accetta un argomento che definisce l'ordine (0 per per la dimensione di una |
| 353 | pagine, 1 per una doppia pagina, 2 per quattro pagine, eccetra) e le stesse |
| 354 | opzioni d'allocazione viste precedentemente. |
| 355 | |
| 356 | Se state allocando un numero di byte notevolemnte superiore ad una pagina |
| 357 | potete usare :c:func:`vmalloc()`. Essa allocherà memoria virtuale all'interno |
| 358 | dello spazio kernel. Questo è un blocco di memoria fisica non contiguo, ma |
| 359 | la MMU vi darà l'impressione che lo sia (quindi, sarà contiguo solo dal punto |
| 360 | di vista dei processori, non dal punto di vista dei driver dei dispositivi |
| 361 | esterni). |
| 362 | Se per qualche strana ragione avete davvero bisogno di una grossa quantità di |
| 363 | memoria fisica contigua, avete un problema: Linux non ha un buon supporto per |
| 364 | questo caso d'uso perché, dopo un po' di tempo, la frammentazione della memoria |
| 365 | rende l'operazione difficile. Il modo migliore per allocare un simile blocco |
| 366 | all'inizio dell'avvio del sistema è attraverso la procedura |
| 367 | :c:func:`alloc_bootmem()`. |
| 368 | |
| 369 | Prima di inventare la vostra cache per gli oggetti più usati, considerate |
| 370 | l'uso di una cache slab disponibile in ``include/linux/slab.h``. |
| 371 | |
| 372 | :c:func:`current()` |
| 373 | ------------------- |
| 374 | |
| 375 | Definita in ``include/asm/current.h`` |
| 376 | |
| 377 | Questa variabile globale (in realtà una macro) contiene un puntatore alla |
| 378 | struttura del processo corrente, quindi è valido solo dal contesto utente. |
| 379 | Per esempio, quando un processo esegue una chiamata di sistema, questo |
| 380 | punterà alla struttura dati del processo chiamate. |
| 381 | Nel contesto d'interruzione in suo valore **non è NULL**. |
| 382 | |
| 383 | :c:func:`mdelay()`/:c:func:`udelay()` |
| 384 | ------------------------------------- |
| 385 | |
| 386 | Definite in ``include/asm/delay.h`` / ``include/linux/delay.h`` |
| 387 | |
| 388 | Le funzioni :c:func:`udelay()` e :c:func:`ndelay()` possono essere utilizzate |
| 389 | per brevi pause. Non usate grandi valori perché rischiate d'avere un |
| 390 | overflow - in questo contesto la funzione :c:func:`mdelay()` è utile, |
| 391 | oppure considerate :c:func:`msleep()`. |
| 392 | |
| 393 | :c:func:`cpu_to_be32()`/:c:func:`be32_to_cpu()`/:c:func:`cpu_to_le32()`/:c:func:`le32_to_cpu()` |
| 394 | ----------------------------------------------------------------------------------------------- |
| 395 | |
| 396 | Definite in ``include/asm/byteorder.h`` |
| 397 | |
| 398 | La famiglia di funzioni :c:func:`cpu_to_be32()` (dove "32" può essere |
| 399 | sostituito da 64 o 16, e "be" con "le") forniscono un modo generico |
| 400 | per fare conversioni sull'ordine dei byte (endianess): esse ritornano |
| 401 | il valore convertito. Tutte le varianti supportano anche il processo inverso: |
| 402 | :c:func:`be32_to_cpu()`, eccetera. |
| 403 | |
| 404 | Queste funzioni hanno principalmente due varianti: la variante per |
| 405 | puntatori, come :c:func:`cpu_to_be32p(), che prende un puntatore |
| 406 | ad un tipo, e ritorna il valore convertito. L'altra variante per |
| 407 | la famiglia di conversioni "in-situ", come :c:func:`cpu_to_be32s()`, |
| 408 | che convertono il valore puntato da un puntatore, e ritornano void. |
| 409 | |
| 410 | :c:func:`local_irq_save()`/:c:func:`local_irq_restore()` |
| 411 | -------------------------------------------------------- |
| 412 | |
| 413 | Definite in ``include/linux/irqflags.h`` |
| 414 | |
| 415 | Queste funzioni abilitano e disabilitano le interruzioni hardware |
| 416 | sul processore locale. Entrambe sono rientranti; esse salvano lo stato |
| 417 | precedente nel proprio argomento ``unsigned long flags``. Se sapete |
| 418 | che le interruzioni sono abilite, potete semplicemente utilizzare |
| 419 | :c:func:`local_irq_disable()` e :c:func:`local_irq_enable()`. |
| 420 | |
| 421 | .. _it_local_bh_disable: |
| 422 | |
| 423 | :c:func:`local_bh_disable()`/:c:func:`local_bh_enable()` |
| 424 | -------------------------------------------------------- |
| 425 | |
| 426 | Definite in ``include/linux/bottom_half.h`` |
| 427 | |
| 428 | |
| 429 | Queste funzioni abilitano e disabilitano le interruzioni software |
| 430 | sul processore locale. Entrambe sono rientranti; se le interruzioni |
| 431 | software erano già state disabilitate in precedenza, rimarranno |
| 432 | disabilitate anche dopo aver invocato questa coppia di funzioni. |
| 433 | Lo scopo è di prevenire l'esecuzione di softirq e tasklet sul processore |
| 434 | attuale. |
| 435 | |
| 436 | :c:func:`smp_processor_id()` |
| 437 | ---------------------------- |
| 438 | |
| 439 | Definita in ``include/linux/smp.h`` |
| 440 | |
| 441 | :c:func:`get_cpu()` nega il diritto di prelazione (quindi non potete essere |
| 442 | spostati su un altro processore all'improvviso) e ritorna il numero |
| 443 | del processore attuale, fra 0 e ``NR_CPUS``. Da notare che non è detto |
| 444 | che la numerazione dei processori sia continua. Quando avete terminato, |
| 445 | ritornate allo stato precedente con :c:func:`put_cpu()`. |
| 446 | |
| 447 | Se sapete che non dovete essere interrotti da altri processi (per esempio, |
| 448 | se siete in un contesto d'interruzione, o il diritto di prelazione |
| 449 | è disabilitato) potete utilizzare smp_processor_id(). |
| 450 | |
| 451 | |
| 452 | ``__init``/``__exit``/``__initdata`` |
| 453 | ------------------------------------ |
| 454 | |
| 455 | Definite in ``include/linux/init.h`` |
| 456 | |
| 457 | Dopo l'avvio, il kernel libera una sezione speciale; le funzioni marcate |
| 458 | con ``__init`` e le strutture dati marcate con ``__initdata`` vengono |
| 459 | eliminate dopo il completamento dell'avvio: in modo simile i moduli eliminano |
| 460 | questa memoria dopo l'inizializzazione. ``__exit`` viene utilizzato per |
| 461 | dichiarare che una funzione verrà utilizzata solo in fase di rimozione: |
| 462 | la detta funzione verrà eliminata quando il file che la contiene non è |
| 463 | compilato come modulo. Guardate l'header file per informazioni. Da notare che |
| 464 | non ha senso avere una funzione marcata come ``__init`` e al tempo stesso |
| 465 | esportata ai moduli utilizzando :c:func:`EXPORT_SYMBOL()` o |
| 466 | :c:func:`EXPORT_SYMBOL_GPL()` - non funzionerà. |
| 467 | |
| 468 | |
| 469 | :c:func:`__initcall()`/:c:func:`module_init()` |
| 470 | ---------------------------------------------- |
| 471 | |
| 472 | Definite in ``include/linux/init.h`` / ``include/linux/module.h`` |
| 473 | |
| 474 | Molte parti del kernel funzionano bene come moduli (componenti del kernel |
| 475 | caricabili dinamicamente). L'utilizzo delle macro :c:func:`module_init()` |
| 476 | e :c:func:`module_exit()` semplifica la scrittura di codice che può funzionare |
| 477 | sia come modulo, sia come parte del kernel, senza l'ausilio di #ifdef. |
| 478 | |
| 479 | La macro :c:func:`module_init()` definisce quale funzione dev'essere |
| 480 | chiamata quando il modulo viene inserito (se il file è stato compilato come |
| 481 | tale), o in fase di avvio : se il file non è stato compilato come modulo la |
| 482 | macro :c:func:`module_init()` diventa equivalente a :c:func:`__initcall()`, |
| 483 | la quale, tramite qualche magia del linker, s'assicura che la funzione venga |
| 484 | chiamata durante l'avvio. |
| 485 | |
| 486 | La funzione può ritornare un numero d'errore negativo per scatenare un |
| 487 | fallimento del caricamento (sfortunatamente, questo non ha effetto se il |
| 488 | modulo è compilato come parte integrante del kernel). Questa funzione è chiamata |
| 489 | in contesto utente con le interruzioni abilitate, quindi potrebbe dormire. |
| 490 | |
| 491 | |
| 492 | :c:func:`module_exit()` |
| 493 | ----------------------- |
| 494 | |
| 495 | |
| 496 | Definita in ``include/linux/module.h`` |
| 497 | |
| 498 | Questa macro definisce la funzione che dev'essere chiamata al momento della |
| 499 | rimozione (o mai, nel caso in cui il file sia parte integrante del kernel). |
| 500 | Essa verrà chiamata solo quando il contatore d'uso del modulo raggiunge lo |
| 501 | zero. Questa funzione può anche dormire, ma non può fallire: tutto dev'essere |
| 502 | ripulito prima che la funzione ritorni. |
| 503 | |
| 504 | Da notare che questa macro è opzionale: se non presente, il modulo non sarà |
| 505 | removibile (a meno che non usiate 'rmmod -f' ). |
| 506 | |
| 507 | |
| 508 | :c:func:`try_module_get()`/:c:func:`module_put()` |
| 509 | ------------------------------------------------- |
| 510 | |
| 511 | Definite in ``include/linux/module.h`` |
| 512 | |
| 513 | Queste funzioni maneggiano il contatore d'uso del modulo per proteggerlo dalla |
| 514 | rimozione (in aggiunta, un modulo non può essere rimosso se un altro modulo |
| 515 | utilizzo uno dei sui simboli esportati: vedere di seguito). Prima di eseguire |
| 516 | codice del modulo, dovreste chiamare :c:func:`try_module_get()` su quel modulo: |
| 517 | se fallisce significa che il modulo è stato rimosso e dovete agire come se |
| 518 | non fosse presente. Altrimenti, potete accedere al modulo in sicurezza, e |
| 519 | chiamare :c:func:`module_put()` quando avete finito. |
| 520 | |
| 521 | La maggior parte delle strutture registrabili hanno un campo owner |
| 522 | (proprietario), come nella struttura |
| 523 | :c:type:`struct file_operations <file_operations>`. |
| 524 | Impostate questo campo al valore della macro ``THIS_MODULE``. |
| 525 | |
| 526 | |
| 527 | Code d'attesa ``include/linux/wait.h`` |
| 528 | ====================================== |
| 529 | |
| 530 | **[DORMONO]** |
| 531 | |
| 532 | Una coda d'attesa è usata per aspettare che qualcuno vi attivi quando una |
| 533 | certa condizione s'avvera. Per evitare corse critiche, devono essere usate |
| 534 | con cautela. Dichiarate una :c:type:`wait_queue_head_t`, e poi i processi |
| 535 | che vogliono attendere il verificarsi di quella condizione dichiareranno |
| 536 | una :c:type:`wait_queue_entry_t` facendo riferimento a loro stessi, poi |
| 537 | metteranno questa in coda. |
| 538 | |
| 539 | Dichiarazione |
| 540 | ------------- |
| 541 | |
| 542 | Potere dichiarare una ``wait_queue_head_t`` utilizzando la macro |
| 543 | :c:func:`DECLARE_WAIT_QUEUE_HEAD()` oppure utilizzando la procedura |
| 544 | :c:func:`init_waitqueue_head()` nel vostro codice d'inizializzazione. |
| 545 | |
| 546 | Accodamento |
| 547 | ----------- |
| 548 | |
| 549 | Mettersi in una coda d'attesa è piuttosto complesso, perché dovete |
| 550 | mettervi in coda prima di verificare la condizione. Esiste una macro |
| 551 | a questo scopo: :c:func:`wait_event_interruptible()` (``include/linux/wait.h``). |
| 552 | Il primo argomento è la testa della coda d'attesa, e il secondo è |
| 553 | un'espressione che dev'essere valutata; la macro ritorna 0 quando questa |
| 554 | espressione è vera, altrimenti ``-ERESTARTSYS`` se è stato ricevuto un segnale. |
| 555 | La versione :c:func:`wait_event()` ignora i segnali. |
| 556 | |
| 557 | Svegliare una procedura in coda |
| 558 | ------------------------------- |
| 559 | |
| 560 | Chiamate :c:func:`wake_up()` (``include/linux/wait.h``); questa attiverà tutti |
| 561 | i processi in coda. Ad eccezione se uno di questi è impostato come |
| 562 | ``TASK_EXCLUSIVE``, in questo caso i rimanenti non verranno svegliati. |
| 563 | Nello stesso header file esistono altre varianti di questa funzione. |
| 564 | |
| 565 | Operazioni atomiche |
| 566 | =================== |
| 567 | |
| 568 | Certe operazioni sono garantite come atomiche su tutte le piattaforme. |
| 569 | Il primo gruppo di operazioni utilizza :c:type:`atomic_t` |
| 570 | (``include/asm/atomic.h``); questo contiene un intero con segno (minimo 32bit), |
| 571 | e dovete utilizzare queste funzione per modificare o leggere variabili di tipo |
| 572 | :c:type:`atomic_t`. :c:func:`atomic_read()` e :c:func:`atomic_set()` leggono ed |
| 573 | impostano il contatore, :c:func:`atomic_add()`, :c:func:`atomic_sub()`, |
| 574 | :c:func:`atomic_inc()`, :c:func:`atomic_dec()`, e |
| 575 | :c:func:`atomic_dec_and_test()` (ritorna vero se raggiunge zero dopo essere |
| 576 | stata decrementata). |
| 577 | |
| 578 | Sì. Ritorna vero (ovvero != 0) se la variabile atomica è zero. |
| 579 | |
| 580 | Da notare che queste funzioni sono più lente rispetto alla normale aritmetica, |
| 581 | e quindi non dovrebbero essere usate a sproposito. |
| 582 | |
| 583 | Il secondo gruppo di operazioni atomiche sono definite in |
| 584 | ``include/linux/bitops.h`` ed agiscono sui bit d'una variabile di tipo |
| 585 | ``unsigned long``. Queste operazioni prendono come argomento un puntatore |
| 586 | alla variabile, e un numero di bit dove 0 è quello meno significativo. |
| 587 | :c:func:`set_bit()`, :c:func:`clear_bit()` e :c:func:`change_bit()` |
| 588 | impostano, cancellano, ed invertono il bit indicato. |
| 589 | :c:func:`test_and_set_bit()`, :c:func:`test_and_clear_bit()` e |
| 590 | :c:func:`test_and_change_bit()` fanno la stessa cosa, ad eccezione che |
| 591 | ritornano vero se il bit era impostato; queste sono particolarmente |
| 592 | utili quando si vuole impostare atomicamente dei flag. |
| 593 | |
| 594 | Con queste operazioni è possibile utilizzare indici di bit che eccedono |
| 595 | il valore ``BITS_PER_LONG``. Il comportamento è strano sulle piattaforme |
| 596 | big-endian quindi è meglio evitarlo. |
| 597 | |
| 598 | Simboli |
| 599 | ======= |
| 600 | |
| 601 | All'interno del kernel, si seguono le normali regole del linker (ovvero, |
| 602 | a meno che un simbolo non venga dichiarato con visibilita limitata ad un |
| 603 | file con la parola chiave ``static``, esso può essere utilizzato in qualsiasi |
| 604 | parte del kernel). Nonostante ciò, per i moduli, esiste una tabella dei |
| 605 | simboli esportati che limita i punti di accesso al kernel. Anche i moduli |
| 606 | possono esportare simboli. |
| 607 | |
| 608 | :c:func:`EXPORT_SYMBOL()` |
| 609 | ------------------------- |
| 610 | |
| 611 | Definita in ``include/linux/export.h`` |
| 612 | |
| 613 | Questo è il classico metodo per esportare un simbolo: i moduli caricati |
| 614 | dinamicamente potranno utilizzare normalmente il simbolo. |
| 615 | |
| 616 | :c:func:`EXPORT_SYMBOL_GPL()` |
| 617 | ----------------------------- |
| 618 | |
| 619 | Definita in ``include/linux/export.h`` |
| 620 | |
| 621 | Essa è simile a :c:func:`EXPORT_SYMBOL()` ad eccezione del fatto che i |
| 622 | simboli esportati con :c:func:`EXPORT_SYMBOL_GPL()` possono essere |
| 623 | utilizzati solo dai moduli che hanno dichiarato una licenza compatibile |
| 624 | con la GPL attraverso :c:func:`MODULE_LICENSE()`. Questo implica che la |
| 625 | funzione esportata è considerata interna, e non una vera e propria interfaccia. |
| 626 | Alcuni manutentori e sviluppatori potrebbero comunque richiedere |
| 627 | :c:func:`EXPORT_SYMBOL_GPL()` quando si aggiungono nuove funzionalità o |
| 628 | interfacce. |
| 629 | |
| 630 | Procedure e convenzioni |
| 631 | ======================= |
| 632 | |
| 633 | Liste doppiamente concatenate ``include/linux/list.h`` |
| 634 | ------------------------------------------------------ |
| 635 | |
| 636 | Un tempo negli header del kernel c'erano tre gruppi di funzioni per |
| 637 | le liste concatenate, ma questa è stata la vincente. Se non avete particolari |
| 638 | necessità per una semplice lista concatenata, allora questa è una buona scelta. |
| 639 | |
| 640 | In particolare, :c:func:`list_for_each_entry()` è utile. |
| 641 | |
| 642 | Convenzione dei valori di ritorno |
| 643 | --------------------------------- |
| 644 | |
| 645 | Per codice chiamato in contesto utente, è molto comune sfidare le convenzioni |
| 646 | C e ritornare 0 in caso di successo, ed un codice di errore negativo |
| 647 | (eg. ``-EFAULT``) nei casi fallimentari. Questo potrebbe essere controintuitivo |
| 648 | a prima vista, ma è abbastanza diffuso nel kernel. |
| 649 | |
| 650 | Utilizzate :c:func:`ERR_PTR()` (``include/linux/err.h``) per codificare |
| 651 | un numero d'errore negativo in un puntatore, e :c:func:`IS_ERR()` e |
| 652 | :c:func:`PTR_ERR()` per recuperarlo di nuovo: così si evita d'avere un |
| 653 | puntatore dedicato per il numero d'errore. Da brividi, ma in senso positivo. |
| 654 | |
| 655 | Rompere la compilazione |
| 656 | ----------------------- |
| 657 | |
| 658 | Linus e gli altri sviluppatori a volte cambiano i nomi delle funzioni e |
| 659 | delle strutture nei kernel in sviluppo; questo non è solo per tenere |
| 660 | tutti sulle spine: questo riflette cambiamenti fondamentati (eg. la funzione |
| 661 | non può più essere chiamata con le funzioni attive, o fa controlli aggiuntivi, |
| 662 | o non fa più controlli che venivano fatti in precedenza). Solitamente a questo |
| 663 | s'accompagna un'adeguata e completa nota sulla lista di discussone |
| 664 | linux-kernel; cercate negli archivi. |
| 665 | Solitamente eseguire una semplice sostituzione su tutto un file rendere |
| 666 | le cose **peggiori**. |
| 667 | |
| 668 | Inizializzazione dei campi d'una struttura |
| 669 | ------------------------------------------ |
| 670 | |
| 671 | Il metodo preferito per l'inizializzazione delle strutture è quello |
| 672 | di utilizzare gli inizializzatori designati, come definiti nello |
| 673 | standard ISO C99, eg:: |
| 674 | |
| 675 | static struct block_device_operations opt_fops = { |
| 676 | .open = opt_open, |
| 677 | .release = opt_release, |
| 678 | .ioctl = opt_ioctl, |
| 679 | .check_media_change = opt_media_change, |
| 680 | }; |
| 681 | |
| 682 | Questo rende più facile la ricerca con grep, e rende più chiaro quale campo |
| 683 | viene impostato. Dovreste fare così perché si mostra meglio. |
| 684 | |
| 685 | Estensioni GNU |
| 686 | -------------- |
| 687 | |
| 688 | Le estensioni GNU sono esplicitamente permesse nel kernel Linux. Da notare |
| 689 | che alcune delle più complesse non sono ben supportate, per via dello scarso |
| 690 | sviluppo, ma le seguenti sono da considerarsi la norma (per maggiori dettagli, |
| 691 | leggete la sezione "C Extensions" nella pagina info di GCC - Sì, davvero |
| 692 | la pagina info, la pagina man è solo un breve riassunto delle cose nella |
| 693 | pagina info). |
| 694 | |
| 695 | - Funzioni inline |
| 696 | |
| 697 | - Istruzioni in espressioni (ie. il costrutto ({ and }) ). |
| 698 | |
| 699 | - Dichiarate attributi di una funzione / variabile / tipo |
| 700 | (__attribute__) |
| 701 | |
| 702 | - typeof |
| 703 | |
| 704 | - Array con lunghezza zero |
| 705 | |
| 706 | - Macro varargs |
| 707 | |
| 708 | - Aritmentica sui puntatori void |
| 709 | |
| 710 | - Inizializzatori non costanti |
| 711 | |
| 712 | - Istruzioni assembler (non al di fuori di 'arch/' e 'include/asm/') |
| 713 | |
| 714 | - Nomi delle funzioni come stringhe (__func__). |
| 715 | |
| 716 | - __builtin_constant_p() |
| 717 | |
| 718 | Siate sospettosi quando utilizzate long long nel kernel, il codice generato |
| 719 | da gcc è orribile ed anche peggio: le divisioni e le moltiplicazioni non |
| 720 | funzionano sulle piattaforme i386 perché le rispettive funzioni di runtime |
| 721 | di GCC non sono incluse nell'ambiente del kernel. |
| 722 | |
| 723 | C++ |
| 724 | --- |
| 725 | |
| 726 | Solitamente utilizzare il C++ nel kernel è una cattiva idea perché |
| 727 | il kernel non fornisce il necessario ambiente di runtime e gli header file |
| 728 | non sono stati verificati. Rimane comunque possibile, ma non consigliato. |
| 729 | Se davvero volete usarlo, almeno evitate le eccezioni. |
| 730 | |
| 731 | NUMif |
| 732 | ----- |
| 733 | |
| 734 | Viene generalmente considerato più pulito l'uso delle macro negli header file |
| 735 | (o all'inizio dei file .c) per astrarre funzioni piuttosto che utlizzare |
| 736 | l'istruzione di pre-processore \`#if' all'interno del codice sorgente. |
| 737 | |
| 738 | Mettere le vostre cose nel kernel |
| 739 | ================================= |
| 740 | |
| 741 | Al fine d'avere le vostre cose in ordine per l'inclusione ufficiale, o |
| 742 | anche per avere patch pulite, c'è del lavoro amministrativo da fare: |
| 743 | |
| 744 | - Trovare di chi è lo stagno in cui state pisciando. Guardare in cima |
| 745 | ai file sorgenti, all'interno del file ``MAINTAINERS``, ed alla fine |
| 746 | di tutti nel file ``CREDITS``. Dovreste coordinarvi con queste persone |
| 747 | per evitare di duplicare gli sforzi, o provare qualcosa che è già stato |
| 748 | rigettato. |
| 749 | |
| 750 | Assicuratevi di mettere il vostro nome ed indirizzo email in cima a |
| 751 | tutti i file che create o che mangeggiate significativamente. Questo è |
| 752 | il primo posto dove le persone guarderanno quando troveranno un baco, |
| 753 | o quando **loro** vorranno fare una modifica. |
| 754 | |
| 755 | - Solitamente vorrete un'opzione di configurazione per la vostra modifica |
| 756 | al kernel. Modificate ``Kconfig`` nella cartella giusta. Il linguaggio |
| 757 | Config è facile con copia ed incolla, e c'è una completa documentazione |
| 758 | nel file ``Documentation/kbuild/kconfig-language.txt``. |
| 759 | |
| 760 | Nella descrizione della vostra opzione, assicuratevi di parlare sia agli |
| 761 | utenti esperti sia agli utente che non sanno nulla del vostro lavoro. |
| 762 | Menzionate qui le incompatibilità ed i problemi. Chiaramente la |
| 763 | descrizione deve terminare con “if in doubt, say N” (se siete in dubbio, |
| 764 | dite N) (oppure, occasionalmente, \`Y'); questo è per le persone che non |
| 765 | hanno idea di che cosa voi stiate parlando. |
| 766 | |
| 767 | - Modificate il file ``Makefile``: le variabili CONFIG sono esportate qui, |
| 768 | quindi potete solitamente aggiungere una riga come la seguete |
| 769 | "obj-$(CONFIG_xxx) += xxx.o". La sintassi è documentata nel file |
| 770 | ``Documentation/kbuild/makefiles.txt``. |
| 771 | |
| 772 | - Aggiungete voi stessi in ``CREDITS`` se avete fatto qualcosa di notevole, |
| 773 | solitamente qualcosa che supera il singolo file (comunque il vostro nome |
| 774 | dovrebbe essere all'inizio dei file sorgenti). ``MAINTAINERS`` significa |
| 775 | che volete essere consultati quando vengono fatte delle modifiche ad un |
| 776 | sottosistema, e quando ci sono dei bachi; questo implica molto di più |
| 777 | di un semplice impegno su una parte del codice. |
| 778 | |
| 779 | - Infine, non dimenticatevi di leggere |
| 780 | ``Documentation/process/submitting-patches.rst`` e possibilmente anche |
| 781 | ``Documentation/process/submitting-drivers.rst``. |
| 782 | |
| 783 | Trucchetti del kernel |
| 784 | ===================== |
| 785 | |
| 786 | Dopo una rapida occhiata al codice, questi sono i preferiti. Sentitevi liberi |
| 787 | di aggiungerne altri. |
| 788 | |
| 789 | ``arch/x86/include/asm/delay.h``:: |
| 790 | |
| 791 | #define ndelay(n) (__builtin_constant_p(n) ? \ |
| 792 | ((n) > 20000 ? __bad_ndelay() : __const_udelay((n) * 5ul)) : \ |
| 793 | __ndelay(n)) |
| 794 | |
| 795 | |
| 796 | ``include/linux/fs.h``:: |
| 797 | |
| 798 | /* |
| 799 | * Kernel pointers have redundant information, so we can use a |
| 800 | * scheme where we can return either an error code or a dentry |
| 801 | * pointer with the same return value. |
| 802 | * |
| 803 | * This should be a per-architecture thing, to allow different |
| 804 | * error and pointer decisions. |
| 805 | */ |
| 806 | #define ERR_PTR(err) ((void *)((long)(err))) |
| 807 | #define PTR_ERR(ptr) ((long)(ptr)) |
| 808 | #define IS_ERR(ptr) ((unsigned long)(ptr) > (unsigned long)(-1000)) |
| 809 | |
| 810 | ``arch/x86/include/asm/uaccess_32.h:``:: |
| 811 | |
| 812 | #define copy_to_user(to,from,n) \ |
| 813 | (__builtin_constant_p(n) ? \ |
| 814 | __constant_copy_to_user((to),(from),(n)) : \ |
| 815 | __generic_copy_to_user((to),(from),(n))) |
| 816 | |
| 817 | |
| 818 | ``arch/sparc/kernel/head.S:``:: |
| 819 | |
| 820 | /* |
| 821 | * Sun people can't spell worth damn. "compatability" indeed. |
| 822 | * At least we *know* we can't spell, and use a spell-checker. |
| 823 | */ |
| 824 | |
| 825 | /* Uh, actually Linus it is I who cannot spell. Too much murky |
| 826 | * Sparc assembly will do this to ya. |
| 827 | */ |
| 828 | C_LABEL(cputypvar): |
| 829 | .asciz "compatibility" |
| 830 | |
| 831 | /* Tested on SS-5, SS-10. Probably someone at Sun applied a spell-checker. */ |
| 832 | .align 4 |
| 833 | C_LABEL(cputypvar_sun4m): |
| 834 | .asciz "compatible" |
| 835 | |
| 836 | |
| 837 | ``arch/sparc/lib/checksum.S:``:: |
| 838 | |
| 839 | /* Sun, you just can't beat me, you just can't. Stop trying, |
| 840 | * give up. I'm serious, I am going to kick the living shit |
| 841 | * out of you, game over, lights out. |
| 842 | */ |
| 843 | |
| 844 | |
| 845 | Ringraziamenti |
| 846 | ============== |
| 847 | |
| 848 | Ringrazio Andi Kleen per le sue idee, le risposte alle mie domande, |
| 849 | le correzioni dei miei errori, l'aggiunta di contenuti, eccetera. |
| 850 | Philipp Rumpf per l'ortografia e per aver reso più chiaro il testo, e |
| 851 | per alcuni eccellenti punti tutt'altro che ovvi. Werner Almesberger |
| 852 | per avermi fornito un ottimo riassunto di :c:func:`disable_irq()`, |
| 853 | e Jes Sorensen e Andrea Arcangeli per le precisazioni. Michael Elizabeth |
| 854 | Chastain per aver verificato ed aggiunto la sezione configurazione. |
| 855 | Telsa Gwynne per avermi insegnato DocBook. |