← Documents Documentation/translations/it_IT/kernel-hacking/hacking.rst GitHub 원문 ↗

Linux 6.18.37 · Translations

신뢰하기 어려운 Linux 커널 해킹 안내서

커널 실행 컨텍스트, 수면·메모리·IRQ 규칙, 모듈 생명주기, 대기 큐, 원자 연산, 심볼과 상류 제출 관례를 설명합니다.

Source pathDocumentation/translations/it_IT/kernel-hacking/hacking.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약·해설

hacking.rst:1-870

이 안내서는 CPU 실행 컨텍스트와 선점 계층을 출발점으로, 잠들 수 있는 함수의 제약, 사용자 메모리 접근, 할당 플래그, IRQ·bottom half 제어, 모듈 생명주기와 동기화 기본기를 연결합니다.

API 이름과 일부 권고는 역사적 문서 특성을 갖지만, 로컬 Linux v6.18.37 이탈리아어 원문의 870줄을 변경하지 않고 번역했습니다. 실제 코드에서는 현재 하위 시스템 문서와 구현을 함께 확인해야 합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
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 <[email protected]>
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é in_hardirq()
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').
133
134 Potete determinate se siete in un softirq (o tasklet) utilizzando la
135 macro :c:func:`in_softirq()` (``include/linux/preempt.h``).
136
137 .. warning::
138
139 State attenti che questa macro ritornerà un falso positivo
140 se :ref:`bottom half lock <it_local_bh_disable>` è bloccato.
141
142 Alcune regole basilari
143 ======================
144
145 Nessuna protezione della memoria
146 Se corrompete la memoria, che sia in contesto utente o d'interruzione,
147 la macchina si pianterà. Siete sicuri che quello che volete fare
148 non possa essere fatto nello spazio utente?
149
150 Nessun numero in virgola mobile o MMX
151 Il contesto della FPU non è salvato; anche se siete in contesto utente
152 lo stato dell'FPU probabilmente non corrisponde a quello del processo
153 corrente: vi incasinerete con lo stato di qualche altro processo. Se
154 volete davvero usare la virgola mobile, allora dovrete salvare e recuperare
155 lo stato dell'FPU (ed evitare cambi di contesto). Generalmente è una
156 cattiva idea; usate l'aritmetica a virgola fissa.
157
158 Un limite rigido dello stack
159 A seconda della configurazione del kernel lo stack è fra 3K e 6K per la
160 maggior parte delle architetture a 32-bit; è di 14K per la maggior
161 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 stack
164 (allocateli dinamicamente).
165
166 Il kernel Linux è portabile
167 Quindi mantenetelo tale. Il vostro codice dovrebbe essere a 64-bit ed
168 indipendente dall'ordine dei byte (endianess) di un processore. Inoltre,
169 dovreste minimizzare il codice specifico per un processore; per esempio
170 il codice assembly dovrebbe essere incapsulato in modo pulito e minimizzato
171 per facilitarne la migrazione. Generalmente questo codice dovrebbe essere
172 limitato alla parte di kernel specifica per un'architettura.
173
174 ioctl: non scrivere nuove chiamate di sistema
175 =============================================
176
177 Una chiamata di sistema, generalmente, è scritta così::
178
179 asmlinkage long sys_mycall(int arg)
180 {
181 return 0;
182 }
183
184 Primo, nella maggior parte dei casi non volete creare nuove chiamate di
185 sistema.
186 Create un dispositivo a caratteri ed implementate l'appropriata chiamata ioctl.
187 Questo meccanismo è molto più flessibile delle chiamate di sistema: esso non
188 dev'essere dichiarato in tutte le architetture nei file
189 ``include/asm/unistd.h`` e ``arch/kernel/entry.S``; inoltre, è improbabile
190 che questo venga accettato da Linus.
191
192 Se tutto quello che il vostro codice fa è leggere o scrivere alcuni parametri,
193 considerate l'implementazione di un'interfaccia :c:func:`sysfs()`.
194
195 All'interno di una ioctl vi trovate nel contesto utente di un processo. Quando
196 avviene un errore dovete ritornare un valore negativo di errno (consultate
197 ``include/uapi/asm-generic/errno-base.h``,
198 ``include/uapi/asm-generic/errno.h`` e ``include/linux/errno.h``), altrimenti
199 ritornate 0.
200
201 Dopo aver dormito dovreste verificare se ci sono stati dei segnali: il modo
202 Unix/Linux di gestire un segnale è di uscire temporaneamente dalla chiamata
203 di sistema con l'errore ``-ERESTARTSYS``. La chiamata di sistema ritornerà
204 al contesto utente, eseguirà il gestore del segnale e poi la vostra chiamata
205 di sistema riprenderà (a meno che l'utente non l'abbia disabilitata). Quindi,
206 dovreste essere pronti per continuare l'esecuzione, per esempio nel mezzo
207 della manipolazione di una struttura dati.
208
209 ::
210
211 if (signal_pending(current))
212 return -ERESTARTSYS;
213
214 Se dovete eseguire dei calcoli molto lunghi: pensate allo spazio utente.
215 Se **davvero** volete farlo nel kernel ricordatevi di verificare periodicamente
216 se dovete *lasciare* il processore (ricordatevi che, per ogni processore, c'è
217 un sistema multi-processo senza diritto di prelazione).
218 Esempio::
219
220 cond_resched(); /* Will sleep */
221
222 Una breve nota sulla progettazione delle interfacce: il motto dei sistemi
223 UNIX è "fornite meccanismi e non politiche"
224
225 La ricetta per uno stallo
226 =========================
227
228 Non è permesso invocare una procedura che potrebbe dormire, fanno eccezione
229 i 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 che
236 lo schedulatore le abiliterà per voi, ma probabilmente questo non è quello
237 che volete).
238
239 Da tener presente che alcune funzioni potrebbero dormire implicitamente:
240 le più comuni sono quelle per l'accesso allo spazio utente (\*_user) e
241 quelle per l'allocazione della memoria senza l'opzione ``GFP_ATOMIC``
242
243 Dovreste sempre compilare il kernel con l'opzione ``CONFIG_DEBUG_ATOMIC_SLEEP``
244 attiva, questa vi avviserà se infrangete una di queste regole.
245 Se **infrangete** le regole, allora potreste bloccare il vostro scatolotto.
246
247 Veramente.
248
249 Alcune delle procedure più comuni
250 =================================
251
252 :c:func:`printk()`
253 ------------------
254
255 Definita in ``include/linux/printk.h``
256
257 :c:func:`printk()` fornisce messaggi alla console, dmesg, e al demone syslog.
258 Essa è utile per il debugging o per la notifica di errori; può essere
259 utilizzata anche all'interno del contesto d'interruzione, ma usatela con
260 cautela: una macchina che ha la propria console inondata da messaggi diventa
261 inutilizzabile. La funzione utilizza un formato stringa quasi compatibile con
262 la printf ANSI C, e la concatenazione di una stringa C come primo argomento
263 per indicare la "priorità"::
264
265 printk(KERN_INFO "i = %u\n", i);
266
267 Consultate ``include/linux/kern_levels.h`` per gli altri valori ``KERN_``;
268 questi sono interpretati da syslog come livelli. Un caso speciale:
269 per 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 di
276 eventuali sforamenti. Accertatevi che vi basti.
277
278 .. note::
279
280 Saprete di essere un vero hacker del kernel quando inizierete a digitare
281 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 commento
286 sopra alla funzione printf: "Printf non dovrebbe essere usata per il
287 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
292 Definite 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 ed
297 impostare singoli valori (come int, char, o long) da e verso lo spazio utente.
298 Un puntatore nello spazio utente non dovrebbe mai essere dereferenziato: i dati
299 dovrebbero essere copiati usando suddette procedure. Entrambe ritornano
300 ``-EFAULT`` oppure 0.
301
302 :c:func:`copy_to_user()` e :c:func:`copy_from_user()` sono più generiche:
303 esse 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()`, queste
308 funzioni ritornano la quantità di dati copiati (0 è comunque un successo).
309
310 [Sì, questa interfaccia mi imbarazza. La battaglia torna in auge anno
311 dopo anno. --RR]
312
313 Le funzioni potrebbero dormire implicitamente. Queste non dovrebbero mai essere
314 invocate fuori dal contesto utente (non ha senso), con le interruzioni
315 disabilitate, o con uno spinlock trattenuto.
316
317 :c:func:`kmalloc()`/:c:func:`kfree()`
318 -------------------------------------
319
320 Definite in ``include/linux/slab.h``
321
322 **[POTREBBERO DORMIRE: LEGGI SOTTO]**
323
324 Queste procedure sono utilizzate per la richiesta dinamica di un puntatore ad
325 un pezzo di memoria allineato, esattamente come malloc e free nello spazio
326 utente, ma :c:func:`kmalloc()` ha un argomento aggiuntivo per indicare alcune
327 opzioni. Le opzioni più importanti sono:
328
329 ``GFP_KERNEL``
330 Potrebbe dormire per librarare della memoria. L'opzione fornisce il modo
331 più affidabile per allocare memoria, ma il suo uso è strettamente limitato
332 allo spazio utente.
333
334 ``GFP_ATOMIC``
335 Non dorme. Meno affidabile di ``GFP_KERNEL``, ma può essere usata in un
336 contesto d'interruzione. Dovreste avere **davvero** una buona strategia
337 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'indirizzamento
341 inferiore ai 16MB. Se non sapete cos'è allora non vi serve.
342 Molto inaffidabile.
343
344 Se vedete un messaggio d'avviso per una funzione dormiente che viene chiamata
345 da un contesto errato, allora probabilmente avete usato una funzione
346 d'allocazione dormiente da un contesto d'interruzione senza ``GFP_ATOMIC``.
347 Dovreste correggerlo. Sbrigatevi, non cincischiate.
348
349 Se allocate almeno ``PAGE_SIZE``(``asm/page.h`` o ``asm/page_types.h``) byte,
350 considerate l'uso di :c:func:`__get_free_pages()` (``include/linux/gfp.h``).
351 Accetta un argomento che definisce l'ordine (0 per per la dimensione di una
352 pagine, 1 per una doppia pagina, 2 per quattro pagine, eccetra) e le stesse
353 opzioni d'allocazione viste precedentemente.
354
355 Se state allocando un numero di byte notevolemnte superiore ad una pagina
356 potete usare :c:func:`vmalloc()`. Essa allocherà memoria virtuale all'interno
357 dello spazio kernel. Questo è un blocco di memoria fisica non contiguo, ma
358 la MMU vi darà l'impressione che lo sia (quindi, sarà contiguo solo dal punto
359 di vista dei processori, non dal punto di vista dei driver dei dispositivi
360 esterni).
361 Se per qualche strana ragione avete davvero bisogno di una grossa quantità di
362 memoria fisica contigua, avete un problema: Linux non ha un buon supporto per
363 questo caso d'uso perché, dopo un po' di tempo, la frammentazione della memoria
364 rende l'operazione difficile. Il modo migliore per allocare un simile blocco
365 all'inizio dell'avvio del sistema è attraverso la procedura
366 :c:func:`alloc_bootmem()`.
367
368 Prima di inventare la vostra cache per gli oggetti più usati, considerate
369 l'uso di una cache slab disponibile in ``include/linux/slab.h``.
370
371 :c:macro:`current`
372 -------------------
373
374 Definita in ``include/asm/current.h``
375
376 Questa variabile globale (in realtà una macro) contiene un puntatore alla
377 struttura del processo corrente, quindi è valido solo dal contesto utente.
378 Per esempio, quando un processo esegue una chiamata di sistema, questo
379 punterà alla struttura dati del processo chiamate.
380 Nel contesto d'interruzione in suo valore **non è NULL**.
381
382 :c:func:`mdelay()`/:c:func:`udelay()`
383 -------------------------------------
384
385 Definite in ``include/asm/delay.h`` / ``include/linux/delay.h``
386
387 Le funzioni :c:func:`udelay()` e :c:func:`ndelay()` possono essere utilizzate
388 per brevi pause. Non usate grandi valori perché rischiate d'avere un
389 overflow - in questo contesto la funzione :c:func:`mdelay()` è utile,
390 oppure 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
395 Definite in ``include/asm/byteorder.h``
396
397 La famiglia di funzioni :c:func:`cpu_to_be32()` (dove "32" può essere
398 sostituito da 64 o 16, e "be" con "le") forniscono un modo generico
399 per fare conversioni sull'ordine dei byte (endianess): esse ritornano
400 il valore convertito. Tutte le varianti supportano anche il processo inverso:
401 :c:func:`be32_to_cpu()`, eccetera.
402
403 Queste funzioni hanno principalmente due varianti: la variante per
404 puntatori, come :c:func:`cpu_to_be32p()`, che prende un puntatore
405 ad un tipo, e ritorna il valore convertito. L'altra variante per
406 la famiglia di conversioni "in-situ", come :c:func:`cpu_to_be32s()`,
407 che convertono il valore puntato da un puntatore, e ritornano void.
408
409 :c:func:`local_irq_save()`/:c:func:`local_irq_restore()`
410 --------------------------------------------------------
411
412 Definite in ``include/linux/irqflags.h``
413
414 Queste funzioni abilitano e disabilitano le interruzioni hardware
415 sul processore locale. Entrambe sono rientranti; esse salvano lo stato
416 precedente nel proprio argomento ``unsigned long flags``. Se sapete
417 che le interruzioni sono abilite, potete semplicemente utilizzare
418 :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
425 Definite in ``include/linux/bottom_half.h``
426
427
428 Queste funzioni abilitano e disabilitano le interruzioni software
429 sul processore locale. Entrambe sono rientranti; se le interruzioni
430 software erano già state disabilitate in precedenza, rimarranno
431 disabilitate anche dopo aver invocato questa coppia di funzioni.
432 Lo scopo è di prevenire l'esecuzione di softirq e tasklet sul processore
433 attuale.
434
435 :c:func:`smp_processor_id()`
436 ----------------------------
437
438 Definita in ``include/linux/smp.h``
439
440 :c:func:`get_cpu()` nega il diritto di prelazione (quindi non potete essere
441 spostati su un altro processore all'improvviso) e ritorna il numero
442 del processore attuale, fra 0 e ``NR_CPUS``. Da notare che non è detto
443 che la numerazione dei processori sia continua. Quando avete terminato,
444 ritornate allo stato precedente con :c:func:`put_cpu()`.
445
446 Se sapete che non dovete essere interrotti da altri processi (per esempio,
447 se siete in un contesto d'interruzione, o il diritto di prelazione
448 è disabilitato) potete utilizzare smp_processor_id().
449
450
451 ``__init``/``__exit``/``__initdata``
452 ------------------------------------
453
454 Definite in ``include/linux/init.h``
455
456 Dopo l'avvio, il kernel libera una sezione speciale; le funzioni marcate
457 con ``__init`` e le strutture dati marcate con ``__initdata`` vengono
458 eliminate dopo il completamento dell'avvio: in modo simile i moduli eliminano
459 questa memoria dopo l'inizializzazione. ``__exit`` viene utilizzato per
460 dichiarare che una funzione verrà utilizzata solo in fase di rimozione:
461 la detta funzione verrà eliminata quando il file che la contiene non è
462 compilato come modulo. Guardate l'header file per informazioni. Da notare che
463 non ha senso avere una funzione marcata come ``__init`` e al tempo stesso
464 esportata ai moduli utilizzando :c:func:`EXPORT_SYMBOL()` o
465 :c:func:`EXPORT_SYMBOL_GPL()` - non funzionerà.
466
467
468 :c:func:`__initcall()`/:c:func:`module_init()`
469 ----------------------------------------------
470
471 Definite in ``include/linux/init.h`` / ``include/linux/module.h``
472
473 Molte parti del kernel funzionano bene come moduli (componenti del kernel
474 caricabili dinamicamente). L'utilizzo delle macro :c:func:`module_init()`
475 e :c:func:`module_exit()` semplifica la scrittura di codice che può funzionare
476 sia come modulo, sia come parte del kernel, senza l'ausilio di #ifdef.
477
478 La macro :c:func:`module_init()` definisce quale funzione dev'essere
479 chiamata quando il modulo viene inserito (se il file è stato compilato come
480 tale), o in fase di avvio : se il file non è stato compilato come modulo la
481 macro :c:func:`module_init()` diventa equivalente a :c:func:`__initcall()`,
482 la quale, tramite qualche magia del linker, s'assicura che la funzione venga
483 chiamata durante l'avvio.
484
485 La funzione può ritornare un numero d'errore negativo per scatenare un
486 fallimento del caricamento (sfortunatamente, questo non ha effetto se il
487 modulo è compilato come parte integrante del kernel). Questa funzione è chiamata
488 in contesto utente con le interruzioni abilitate, quindi potrebbe dormire.
489
490
491 :c:func:`module_exit()`
492 -----------------------
493
494
495 Definita in ``include/linux/module.h``
496
497 Questa macro definisce la funzione che dev'essere chiamata al momento della
498 rimozione (o mai, nel caso in cui il file sia parte integrante del kernel).
499 Essa verrà chiamata solo quando il contatore d'uso del modulo raggiunge lo
500 zero. Questa funzione può anche dormire, ma non può fallire: tutto dev'essere
501 ripulito prima che la funzione ritorni.
502
503 Da notare che questa macro è opzionale: se non presente, il modulo non sarà
504 removibile (a meno che non usiate 'rmmod -f' ).
505
506
507 :c:func:`try_module_get()`/:c:func:`module_put()`
508 -------------------------------------------------
509
510 Definite in ``include/linux/module.h``
511
512 Queste funzioni maneggiano il contatore d'uso del modulo per proteggerlo dalla
513 rimozione (in aggiunta, un modulo non può essere rimosso se un altro modulo
514 utilizzo uno dei sui simboli esportati: vedere di seguito). Prima di eseguire
515 codice del modulo, dovreste chiamare :c:func:`try_module_get()` su quel modulo:
516 se fallisce significa che il modulo è stato rimosso e dovete agire come se
517 non fosse presente. Altrimenti, potete accedere al modulo in sicurezza, e
518 chiamare :c:func:`module_put()` quando avete finito.
519
520 La maggior parte delle strutture registrabili hanno un campo owner
521 (proprietario), come nella struttura
522 :c:type:`struct file_operations <file_operations>`.
523 Impostate questo campo al valore della macro ``THIS_MODULE``.
524
525
526 Code d'attesa ``include/linux/wait.h``
527 ======================================
528
529 **[DORMONO]**
530
531 Una coda d'attesa è usata per aspettare che qualcuno vi attivi quando una
532 certa condizione s'avvera. Per evitare corse critiche, devono essere usate
533 con cautela. Dichiarate una :c:type:`wait_queue_head_t`, e poi i processi
534 che vogliono attendere il verificarsi di quella condizione dichiareranno
535 una :c:type:`wait_queue_entry_t` facendo riferimento a loro stessi, poi
536 metteranno questa in coda.
537
538 Dichiarazione
539 -------------
540
541 Potere dichiarare una ``wait_queue_head_t`` utilizzando la macro
542 :c:func:`DECLARE_WAIT_QUEUE_HEAD()` oppure utilizzando la procedura
543 :c:func:`init_waitqueue_head()` nel vostro codice d'inizializzazione.
544
545 Accodamento
546 -----------
547
548 Mettersi in una coda d'attesa è piuttosto complesso, perché dovete
549 mettervi in coda prima di verificare la condizione. Esiste una macro
550 a questo scopo: :c:func:`wait_event_interruptible()` (``include/linux/wait.h``).
551 Il primo argomento è la testa della coda d'attesa, e il secondo è
552 un'espressione che dev'essere valutata; la macro ritorna 0 quando questa
553 espressione è vera, altrimenti ``-ERESTARTSYS`` se è stato ricevuto un segnale.
554 La versione :c:func:`wait_event()` ignora i segnali.
555
556 Svegliare una procedura in coda
557 -------------------------------
558
559 Chiamate :c:func:`wake_up()` (``include/linux/wait.h``); questa attiverà tutti
560 i processi in coda. Ad eccezione se uno di questi è impostato come
561 ``TASK_EXCLUSIVE``, in questo caso i rimanenti non verranno svegliati.
562 Nello stesso header file esistono altre varianti di questa funzione.
563
564 Operazioni atomiche
565 ===================
566
567 Certe operazioni sono garantite come atomiche su tutte le piattaforme.
568 Il primo gruppo di operazioni utilizza :c:type:`atomic_t`
569 (``include/asm/atomic.h``); questo contiene un intero con segno (minimo 32bit),
570 e dovete utilizzare queste funzione per modificare o leggere variabili di tipo
571 :c:type:`atomic_t`. :c:func:`atomic_read()` e :c:func:`atomic_set()` leggono ed
572 impostano il contatore, :c:func:`atomic_add()`, :c:func:`atomic_sub()`,
573 :c:func:`atomic_inc()`, :c:func:`atomic_dec()`, e
574 :c:func:`atomic_dec_and_test()` (ritorna vero se raggiunge zero dopo essere
575 stata decrementata).
576
577 Sì. Ritorna vero (ovvero != 0) se la variabile atomica è zero.
578
579 Da notare che queste funzioni sono più lente rispetto alla normale aritmetica,
580 e quindi non dovrebbero essere usate a sproposito.
581
582 Il secondo gruppo di operazioni atomiche sono definite in
583 ``include/linux/bitops.h`` ed agiscono sui bit d'una variabile di tipo
584 ``unsigned long``. Queste operazioni prendono come argomento un puntatore
585 alla 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()`
587 impostano, cancellano, ed invertono il bit indicato.
588 :c:func:`test_and_set_bit()`, :c:func:`test_and_clear_bit()` e
589 :c:func:`test_and_change_bit()` fanno la stessa cosa, ad eccezione che
590 ritornano vero se il bit era impostato; queste sono particolarmente
591 utili quando si vuole impostare atomicamente dei flag.
592
593 Con queste operazioni è possibile utilizzare indici di bit che eccedono
594 il valore ``BITS_PER_LONG``. Il comportamento è strano sulle piattaforme
595 big-endian quindi è meglio evitarlo.
596
597 Simboli
598 =======
599
600 All'interno del kernel, si seguono le normali regole del linker (ovvero,
601 a meno che un simbolo non venga dichiarato con visibilita limitata ad un
602 file con la parola chiave ``static``, esso può essere utilizzato in qualsiasi
603 parte del kernel). Nonostante ciò, per i moduli, esiste una tabella dei
604 simboli esportati che limita i punti di accesso al kernel. Anche i moduli
605 possono esportare simboli.
606
607 :c:func:`EXPORT_SYMBOL()`
608 -------------------------
609
610 Definita in ``include/linux/export.h``
611
612 Questo è il classico metodo per esportare un simbolo: i moduli caricati
613 dinamicamente potranno utilizzare normalmente il simbolo.
614
615 :c:func:`EXPORT_SYMBOL_GPL()`
616 -----------------------------
617
618 Definita in ``include/linux/export.h``
619
620 Essa è simile a :c:func:`EXPORT_SYMBOL()` ad eccezione del fatto che i
621 simboli esportati con :c:func:`EXPORT_SYMBOL_GPL()` possono essere
622 utilizzati solo dai moduli che hanno dichiarato una licenza compatibile
623 con la GPL attraverso :c:func:`MODULE_LICENSE()`. Questo implica che la
624 funzione esportata è considerata interna, e non una vera e propria interfaccia.
625 Alcuni manutentori e sviluppatori potrebbero comunque richiedere
626 :c:func:`EXPORT_SYMBOL_GPL()` quando si aggiungono nuove funzionalità o
627 interfacce.
628
629 :c:func:`EXPORT_SYMBOL_NS()`
630 ----------------------------
631
632 Definita in ``include/linux/export.h``
633
634 Questa è una variate di `EXPORT_SYMBOL()` che permette di specificare uno
635 spazio dei nomi. Lo spazio dei nomi è documentato in
636 Documentation/translations/it_IT/core-api/symbol-namespaces.rst.
637
638 :c:func:`EXPORT_SYMBOL_NS_GPL()`
639 --------------------------------
640
641 Definita in ``include/linux/export.h``
642
643 Questa è una variate di `EXPORT_SYMBOL_GPL()` che permette di specificare uno
644 spazio dei nomi. Lo spazio dei nomi è documentato in
645 Documentation/translations/it_IT/core-api/symbol-namespaces.rst.
646
647 Procedure e convenzioni
648 =======================
649
650 Liste doppiamente concatenate ``include/linux/list.h``
651 ------------------------------------------------------
652
653 Un tempo negli header del kernel c'erano tre gruppi di funzioni per
654 le liste concatenate, ma questa è stata la vincente. Se non avete particolari
655 necessità per una semplice lista concatenata, allora questa è una buona scelta.
656
657 In particolare, :c:func:`list_for_each_entry()` è utile.
658
659 Convenzione dei valori di ritorno
660 ---------------------------------
661
662 Per codice chiamato in contesto utente, è molto comune sfidare le convenzioni
663 C e ritornare 0 in caso di successo, ed un codice di errore negativo
664 (eg. ``-EFAULT``) nei casi fallimentari. Questo potrebbe essere controintuitivo
665 a prima vista, ma è abbastanza diffuso nel kernel.
666
667 Utilizzate :c:func:`ERR_PTR()` (``include/linux/err.h``) per codificare
668 un numero d'errore negativo in un puntatore, e :c:func:`IS_ERR()` e
669 :c:func:`PTR_ERR()` per recuperarlo di nuovo: così si evita d'avere un
670 puntatore dedicato per il numero d'errore. Da brividi, ma in senso positivo.
671
672 Rompere la compilazione
673 -----------------------
674
675 Linus e gli altri sviluppatori a volte cambiano i nomi delle funzioni e
676 delle strutture nei kernel in sviluppo; questo non è solo per tenere
677 tutti sulle spine: questo riflette cambiamenti fondamentati (eg. la funzione
678 non può più essere chiamata con le funzioni attive, o fa controlli aggiuntivi,
679 o non fa più controlli che venivano fatti in precedenza). Solitamente a questo
680 s'accompagna un'adeguata e completa nota sulla lista di discussone
681 più adatta; cercate negli archivi. Solitamente eseguire una semplice
682 sostituzione su tutto un file rendere le cose **peggiori**.
683
684 Inizializzazione dei campi d'una struttura
685 ------------------------------------------
686
687 Il metodo preferito per l'inizializzazione delle strutture è quello
688 di utilizzare gli inizializzatori designati, come definiti nello
689 standard 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
698 Questo rende più facile la ricerca con grep, e rende più chiaro quale campo
699 viene impostato. Dovreste fare così perché si mostra meglio.
700
701 Estensioni GNU
702 --------------
703
704 Le estensioni GNU sono esplicitamente permesse nel kernel Linux. Da notare
705 che alcune delle più complesse non sono ben supportate, per via dello scarso
706 sviluppo, ma le seguenti sono da considerarsi la norma (per maggiori dettagli,
707 leggete la sezione "C Extensions" nella pagina info di GCC - Sì, davvero
708 la pagina info, la pagina man è solo un breve riassunto delle cose nella
709 pagina info).
710
711 - Funzioni inline
712
713 - Istruzioni in espressioni (ie. il costrutto ({ and }) ).
714
715 - Dichiarate attributi di una funzione / variabile / tipo
716 (__attribute__)
717
718 - typeof
719
720 - Array con lunghezza zero
721
722 - Macro varargs
723
724 - Aritmentica sui puntatori void
725
726 - Inizializzatori non costanti
727
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
734 Siate sospettosi quando utilizzate long long nel kernel, il codice generato
735 da gcc è orribile ed anche peggio: le divisioni e le moltiplicazioni non
736 funzionano sulle piattaforme i386 perché le rispettive funzioni di runtime
737 di GCC non sono incluse nell'ambiente del kernel.
738
739 C++
740 ---
741
742 Solitamente utilizzare il C++ nel kernel è una cattiva idea perché
743 il kernel non fornisce il necessario ambiente di runtime e gli header file
744 non sono stati verificati. Rimane comunque possibile, ma non consigliato.
745 Se davvero volete usarlo, almeno evitate le eccezioni.
746
747 NUMif
748 -----
749
750 Viene generalmente considerato più pulito l'uso delle macro negli header file
751 (o all'inizio dei file .c) per astrarre funzioni piuttosto che utlizzare
752 l'istruzione di pre-processore \`#if' all'interno del codice sorgente.
753
754 Mettere le vostre cose nel kernel
755 =================================
756
757 Al fine d'avere le vostre cose in ordine per l'inclusione ufficiale, o
758 anche per avere patch pulite, c'è del lavoro amministrativo da fare:
759
760 - Trovare chi è responsabile del codice che state modificando. Guardare in cima
761 ai file sorgenti, all'interno del file ``MAINTAINERS``, ed alla fine
762 di tutti nel file ``CREDITS``. Dovreste coordinarvi con queste persone
763 per evitare di duplicare gli sforzi, o provare qualcosa che è già stato
764 rigettato.
765
766 Assicuratevi di mettere il vostro nome ed indirizzo email in cima a
767 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 modifica
772 al kernel. Modificate ``Kconfig`` nella cartella giusta. Il linguaggio
773 Config è facile con copia ed incolla, e c'è una completa documentazione
774 nel file ``Documentation/kbuild/kconfig-language.rst``.
775
776 Nella descrizione della vostra opzione, assicuratevi di parlare sia agli
777 utenti esperti sia agli utente che non sanno nulla del vostro lavoro.
778 Menzionate qui le incompatibilità ed i problemi. Chiaramente la
779 descrizione deve terminare con “if in doubt, say N” (se siete in dubbio,
780 dite N) (oppure, occasionalmente, \`Y'); questo è per le persone che non
781 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 seguete
785 "obj-$(CONFIG_xxx) += xxx.o". La sintassi è documentata nel file
786 ``Documentation/kbuild/makefiles.rst``.
787
788 - Aggiungete voi stessi in ``CREDITS`` se credete di aver fatto qualcosa di
789 notevole, solitamente qualcosa che supera il singolo file (comunque il vostro
790 nome dovrebbe essere all'inizio dei file sorgenti). ``MAINTAINERS`` significa
791 che volete essere consultati quando vengono fatte delle modifiche ad un
792 sottosistema, e quando ci sono dei bachi; questo implica molto di più di un
793 semplice impegno su una parte del codice.
794
795 - Infine, non dimenticatevi di leggere
796 ``Documentation/process/submitting-patches.rst``.
797
798 Trucchetti del kernel
799 =====================
800
801 Dopo una rapida occhiata al codice, questi sono i preferiti. Sentitevi liberi
802 di 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 a
815 * scheme where we can return either an error code or a dentry
816 * pointer with the same return value.
817 *
818 * This should be a per-architecture thing, to allow different
819 * 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 murky
841 * 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 4
848 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 shit
856 * out of you, game over, lights out.
857 */
858
859
860 Ringraziamenti
861 ==============
862
863 Ringrazio Andi Kleen per le sue idee, le risposte alle mie domande,
864 le correzioni dei miei errori, l'aggiunta di contenuti, eccetera.
865 Philipp Rumpf per l'ortografia e per aver reso più chiaro il testo, e
866 per alcuni eccellenti punti tutt'altro che ovvi. Werner Almesberger
867 per avermi fornito un ottimo riassunto di :c:func:`disable_irq()`,
868 e Jes Sorensen e Andrea Arcangeli per le precisazioni. Michael Elizabeth
869 Chastain per aver verificato ed aggiunto la sezione configurazione.
870 Telsa Gwynne per avermi insegnato DocBook.
871

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

번역 정보와 안내서의 목적

1-31

이 이탈리아어 번역은 공통 면책 고지 `../disclaimer-ita.rst`를 포함하며, 공식 영어 원문 `Documentation/kernel-hacking/hacking.rst`의 `kernel_hacking_hack` 참조를 명시합니다. 번역자는 Federico Vaga이고 원 저자는 Rusty Russell입니다.

`_it_kernel_hacking_hack`은 이탈리아어 페이지의 앵커입니다. 제목은 ‘신뢰하기 어려운 Linux 커널 해킹 안내서’로, 표현은 유머러스하지만 내용은 커널 코드 작성에 필요한 일반 절차와 요구 사항을 설명합니다.

대상 독자는 경험 있는 C 프로그래머입니다. 목표는 Linux 커널 개발의 입문서를 제공하는 것이며, 세부 구현은 실제 소스 코드에 맡기고 유용한 절차 전체를 망라하려 하지는 않습니다.

저자는 자신이 충분한 자격을 갖추지 못했다고 농담하면서도 이런 문서를 읽고 싶었기 때문에 직접 썼다고 설명합니다. 문서가 모범 사례, 공통 출발점, 여러 실용 정보를 모은 개요서로 성장하기를 바랍니다.

문서 식별 정보
항목
공식 원문Documentation/kernel-hacking/hacking.rst
원문 anchorkernel_hacking_hack
이탈리아어 anchorit_kernel_hacking_hack
원 저자Rusty Russell
번역자Federico Vaga

번역본과 공식 원문의 관계 및 작성자를 정리합니다.

안내서의 범위
Experienced C programmerKernel development primer
Common routinesGeneral requirements
Implementation detailsRead the source code

경험 있는 C 개발자가 구현 세부로 들어가기 전에 공통 원칙을 익히도록 구성됩니다.

.. include:: ../disclaimer-ita.rst

.. note:: Per leggere la documentazione originale in inglese:
	  :ref:`Documentation/kernel-hacking/hacking.rst <kernel_hacking_hack>`

:Original: :ref:`Documentation/kernel-hacking/hacking.rst <kernel_hacking_hack>`
:Translator: Federico Vaga <[email protected]>

.. _it_kernel_hacking_hack:

=================================================
L'inaffidabile guida all'hacking del kernel Linux
=================================================

:Author: Rusty Russell

Introduzione
============

Benvenuto, gentile lettore, alla notevole ed inaffidabile guida all'hacking
del kernel Linux ad opera di Rusty. Questo documento descrive le procedure
più usate ed i concetti necessari per scrivere codice per il kernel: lo scopo
è di fornire ai programmatori C più esperti un manuale di base per sviluppo.
Eviterò dettagli implementativi: per questo abbiamo il codice,
ed ignorerò intere parti di alcune procedure.

Prima di leggere questa guida, sappiate che non ho mai voluto scriverla,
essendo esageratamente sotto qualificato, ma ho sempre voluto leggere
qualcosa di simile, e quindi questa era l'unica via. Spero che possa
crescere e diventare un compendio di buone pratiche, punti di partenza
e generiche informazioni.

CPU 실행 컨텍스트와 선점 계층

32-141

어느 순간이든 시스템의 각 CPU는 네 상태 중 하나에 있을 수 있습니다. 프로세스와 무관하게 하드웨어 인터럽트를 처리하거나, 프로세스와 무관하게 softirq 또는 tasklet을 처리하거나, 프로세스에 연결된 채 커널 공간에서 실행하거나, 사용자 공간 프로세스를 실행합니다.

이 상태에는 선점 계층이 있습니다. 사용자 공간 실행과 프로세스에 연결된 커널 실행은 서로 전환될 수 있지만, 그 위에서는 아래 단계가 위 단계에 의해서만 선점되는 엄격한 순서가 적용됩니다.

예를 들어 한 CPU에서 softirq가 실행되는 동안 다른 softirq가 그것을 선점하지는 않지만 하드웨어 인터럽트는 선점할 수 있습니다. 다른 CPU들은 이 CPU와 독립적으로 실행되므로 SMP 동시성은 별도로 고려해야 합니다.

사용자 컨텍스트는 시스템 호출이나 다른 트랩에서 커널로 들어온 상태입니다. 더 중요한 작업이나 인터럽트에 선점될 수 있고, `schedule()`을 호출해 잠들 수 있습니다.

모듈을 로드하거나 제거할 때와 블록 장치 계층에서 작업할 때는 항상 사용자 컨텍스트입니다. 이 컨텍스트에서는 현재 실행 프로세스를 가리키는 `current`가 유효하고 `in_interrupt()`는 거짓입니다.

다만 선점이나 softirq를 비활성화한 상태에서는 `in_interrupt()`가 거짓 양성으로 참을 반환할 수 있으므로, 이 값 하나만으로 호출 가능 문맥을 단정해서는 안 됩니다.

타이머, 네트워크 카드, 키보드 같은 실제 하드웨어는 언제든 인터럽트를 만들 수 있습니다. 커널은 하드웨어를 서비스하는 인터럽트 핸들러를 실행하며, 같은 인터럽트가 다시 오면 큐에 넣거나 버려서 동일 핸들러가 스스로 선점되지 않도록 보장합니다.

핸들러 실행 중에는 인터럽트가 비활성화되므로 핸들러는 빨라야 합니다. 보통 인터럽트를 인수했다는 사실만 기록하고 소프트웨어 인터럽트 처리를 예약한 뒤 즉시 끝냅니다.

하드웨어 인터럽트 컨텍스트는 `in_hardirq()`가 참인지로 확인할 수 있습니다. 그러나 인터럽트를 수동으로 비활성화했을 때도 거짓 양성이 생길 수 있다는 경고가 붙습니다.

시스템 호출이 사용자 공간으로 돌아가기 직전이나 인터럽트 핸들러가 끝날 때, 대기 중으로 표시된 소프트웨어 인터럽트가 `kernel/softirq.c`에서 실행됩니다. 실제 인터럽트 처리 작업의 많은 부분이 여기서 이루어집니다.

과거의 bottom half(BH)는 멀티프로세서 장점을 활용하지 못했습니다. 이후 softirq로 옮겨 갔으며, 종류는 `include/linux/interrupt.h`에 정의됩니다.

중요한 softirq 종류 중 하나는 `include/linux/timer.h`의 타이머입니다. 일정 시간이 지난 뒤 함수를 실행하도록 예약할 수 있습니다.

softirq는 여러 프로세서에서 동시에 실행될 수 있어 다루기 어렵습니다. 그래서 동적으로 등록할 수 있고 동일 tasklet 하나가 한 번에 한 CPU에서만 실행되도록 보장하는 tasklet을 흔히 사용합니다. 서로 다른 tasklet은 동시에 실행될 수 있습니다.

`tasklet`이라는 이름은 misleading하며 프로세스인 task와는 관련이 없습니다. `in_softirq()`로 softirq 또는 tasklet 컨텍스트를 확인할 수 있지만, `local_bh_disable()`로 bottom half를 막았을 때는 거짓 양성이 생길 수 있습니다.

CPU 실행 상태의 선점 관계
우선순위상태프로세스 연결
가장 높음하드웨어 IRQ없음
높음softirq 또는 tasklet없음
보통프로세스의 커널 공간 실행있음
보통사용자 공간 프로세스있음

아래쪽 상태는 위쪽 상태에 의해 선점될 수 있으며 다른 CPU는 독립적으로 동작합니다.

컨텍스트 판별 도우미의 주의점
도우미일반 의미거짓 양성 조건
in_interrupt()인터럽트 문맥 여부선점 또는 softirq 비활성화
in_hardirq()하드 IRQ 문맥 여부하드웨어 인터럽트 비활성화
in_softirq()softirq/tasklet 문맥 여부bottom half 비활성화

상태 비활성화 때문에 참이 되면 실제 IRQ 실행과 구분해야 합니다.

컨텍스트별 수면 가능성
컨텍스트수면 가능current 의미
하드웨어 IRQ불가프로세스 문맥으로 사용 불가
softirq/tasklet불가프로세스 문맥으로 사용 불가
프로세스의 커널 실행조건부 가능유효
사용자 공간시스템 호출로 진입 후 가능진입한 프로세스

프로세스에 연결된 사용자 컨텍스트에서만 일반적인 수면 호출을 사용할 수 있습니다.

하드 IRQ에서 지연 처리로
Hardware interruptFast IRQ handler
Acknowledge and schedulePending softirq
Return boundarysoftirq/tasklet work

핸들러는 최소 작업만 하고 나머지를 softirq 또는 tasklet로 넘깁니다.

동일 tasklet의 실행 보장
Tasklet ACPU 0 또는 CPU 1 중 한 곳
Tasklet B다른 CPU에서 동시 실행 가능

한 tasklet 인스턴스는 직렬화되지만 서로 다른 tasklet은 병렬 실행될 수 있습니다.


Gli attori
==========

In qualsiasi momento ognuna delle CPU di un sistema può essere:

-  non associata ad alcun processo, servendo un'interruzione hardware;

-  non associata ad alcun processo, servendo un softirq o tasklet;

-  in esecuzione nello spazio kernel, associata ad un processo
   (contesto utente);

-  in esecuzione di un processo nello spazio utente;

Esiste un ordine fra questi casi. Gli ultimi due possono avvicendarsi (preempt)
l'un l'altro, ma a parte questo esiste una gerarchia rigida: ognuno di questi
può avvicendarsi solo ad uno di quelli sottostanti. Per esempio, mentre un
softirq è in esecuzione su d'una CPU, nessun altro softirq può avvicendarsi
nell'esecuzione, ma un'interruzione hardware può. Ciò nonostante, le altre CPU
del sistema operano indipendentemente.

Più avanti vedremo alcuni modi in cui dal contesto utente è possibile bloccare
le interruzioni, così da impedirne davvero il diritto di prelazione.

Contesto utente
---------------

Ci si trova nel contesto utente quando si arriva da una chiamata di sistema
od altre eccezioni: come nello spazio utente, altre procedure più importanti,
o le interruzioni, possono far valere il proprio diritto di prelazione sul
vostro processo. Potete sospendere l'esecuzione chiamando :c:func:`schedule()`.

.. note::

    Si è sempre in contesto utente quando un modulo viene caricato o rimosso,
    e durante le operazioni nello strato dei dispositivi a blocchi
    (*block layer*).

Nel contesto utente, il puntatore ``current`` (il quale indica il processo al
momento in esecuzione) è valido, e :c:func:`in_interrupt()`
(``include/linux/preempt.h``) è falsa.

.. warning::

    Attenzione che se avete la prelazione o i softirq disabilitati (vedere
    di seguito), :c:func:`in_interrupt()` ritornerà un falso positivo.

Interruzioni hardware (Hard IRQs)
---------------------------------

Temporizzatori, schede di rete e tastiere sono esempi di vero hardware
che possono produrre interruzioni in un qualsiasi momento. Il kernel esegue
i gestori d'interruzione che prestano un servizio all'hardware. Il kernel
garantisce che questi gestori non vengano mai interrotti: se una stessa
interruzione arriva, questa verrà accodata (o scartata).
Dato che durante la loro esecuzione le interruzioni vengono disabilitate,
i gestori d'interruzioni devono essere veloci: spesso si limitano
esclusivamente a notificare la presa in carico dell'interruzione,
programmare una 'interruzione software' per l'esecuzione e quindi terminare.

Potete dire d'essere in una interruzione hardware perché in_hardirq()
ritorna vero.

.. warning::

    Attenzione, questa ritornerà un falso positivo se le interruzioni
    sono disabilitate (vedere di seguito).

Contesto d'interruzione software: softirq e tasklet
---------------------------------------------------

Quando una chiamata di sistema sta per tornare allo spazio utente,
oppure un gestore d'interruzioni termina, qualsiasi 'interruzione software'
marcata come pendente (solitamente da un'interruzione hardware) viene
eseguita (``kernel/softirq.c``).

La maggior parte del lavoro utile alla gestione di un'interruzione avviene qui.
All'inizio della transizione ai sistemi multiprocessore, c'erano solo i
cosiddetti 'bottom half' (BH), i quali non traevano alcun vantaggio da questi
sistemi. Non appena abbandonammo i computer raffazzonati con fiammiferi e
cicche, abbandonammo anche questa limitazione e migrammo alle interruzioni
software 'softirqs'.

Il file ``include/linux/interrupt.h`` elenca i differenti tipi di 'softirq'.
Un tipo di softirq molto importante è il timer (``include/linux/timer.h``):
potete programmarlo per far si che esegua funzioni dopo un determinato
periodo di tempo.

Dato che i softirq possono essere eseguiti simultaneamente su più di un
processore, spesso diventa estenuante l'averci a che fare. Per questa ragione,
i tasklet (``include/linux/interrupt.h``) vengo usati più di frequente:
possono essere registrati dinamicamente (il che significa che potete averne
quanti ne volete), e garantiscono che un qualsiasi tasklet verrà eseguito
solo su un processore alla volta, sebbene diversi tasklet possono essere
eseguiti simultaneamente.

.. warning::

    Il nome 'tasklet' è ingannevole: non hanno niente a che fare
    con i 'processi' ('tasks').

Potete determinate se siete in un softirq (o tasklet) utilizzando la
macro :c:func:`in_softirq()` (``include/linux/preempt.h``).

.. warning::

    State attenti che questa macro ritornerà un falso positivo
    se :ref:`bottom half lock <it_local_bh_disable>` è bloccato.

기본 제약과 ioctl 인터페이스

142-224

커널에는 사용자 공간과 같은 메모리 보호가 없습니다. 사용자 컨텍스트든 인터럽트 컨텍스트든 메모리를 손상시키면 시스템 전체가 멈출 수 있으므로, 작업을 사용자 공간에서 수행할 수 없는지 먼저 검토해야 합니다.

FPU 컨텍스트는 저장되지 않으며 현재 FPU 상태가 현재 프로세스의 것이라고 보장할 수 없습니다. 부동소수점이나 MMX를 함부로 사용하면 다른 프로세스 상태를 망칠 수 있으므로 일반적으로 고정소수점 연산을 사용합니다.

정말 부동소수점이 필요하면 FPU 상태 저장·복원과 컨텍스트 전환 방지를 직접 책임져야 하므로 좋은 선택이 아닙니다.

커널 스택은 엄격히 제한됩니다. 원문은 구성에 따라 대부분의 32비트 아키텍처에서 3K~6K, 대부분의 64비트 아키텍처에서 14K라고 설명하며 인터럽트와 공유될 수도 있다고 경고합니다.

깊은 재귀와 큰 지역 배열은 피하고 필요한 메모리는 동적으로 할당해야 합니다. 스택에 여유가 있다고 가정하는 설계는 이식성과 안정성을 해칩니다.

Linux 커널은 이식 가능해야 합니다. 코드는 64비트와 바이트 순서에 독립적이어야 하고, 프로세서별 코드와 어셈블리는 최소화해 깔끔하게 감싸며 보통 아키텍처 전용 영역에 둡니다.

원문은 일반적인 시스템 호출 골격으로 `asmlinkage long sys_mycall(int arg)` 예제를 제시하지만, 대부분의 경우 새 시스템 호출을 만들지 말라고 권합니다.

Una chiamata di sistema, generalmente, è scritta così::

    asmlinkage long sys_mycall(int arg)
    {
            return 0;
    }

대신 문자 장치를 만들고 적절한 `ioctl`을 구현하는 편이 더 유연합니다. 새 시스템 호출은 모든 아키텍처의 `include/asm/unistd.h`와 `arch/kernel/entry.S`에 선언해야 하며 상류 수용 가능성도 낮습니다.

코드가 몇 개의 매개변수를 읽거나 쓰기만 한다면 `sysfs()` 인터페이스도 고려합니다.

ioctl 안에서는 프로세스의 사용자 컨텍스트에 있습니다. 오류 시 `include/uapi/asm-generic/errno-base.h`, `include/uapi/asm-generic/errno.h`, `include/linux/errno.h`에 정의된 음수 errno를 반환하고 성공 시 0을 반환합니다.

잠든 뒤에는 대기 중 신호를 확인해야 합니다. Unix/Linux 방식은 `-ERESTARTSYS`로 시스템 호출을 잠시 빠져나가 사용자 공간의 신호 핸들러를 실행한 다음, 사용자가 재시작을 막지 않았다면 호출을 다시 시작하는 것입니다.

따라서 자료구조 조작 중간에서도 안전하게 재개할 수 있도록 상태를 설계해야 합니다. 원문은 `signal_pending(current)`가 참이면 `-ERESTARTSYS`를 반환하는 예를 제공합니다.

::

    if (signal_pending(current))
            return -ERESTARTSYS;

아주 긴 계산은 사용자 공간에서 하는 것이 좋습니다. 정말 커널에서 해야 한다면 CPU를 양보해야 하는지 주기적으로 확인하고 `cond_resched()` 같은 지점에서 잠들 수 있게 해야 합니다.

Esempio::

    cond_resched(); /* Will sleep */

인터페이스 설계의 짧은 원칙은 UNIX의 표어인 ‘정책이 아니라 메커니즘을 제공하라’입니다. 커널 인터페이스는 특정 사용자 공간 정책을 강제하기보다 필요한 기본 기능을 제공해야 합니다.

커널 코드의 기본 제약
제약권장 대응
메모리 보호 없음사용자 공간 구현 가능성 우선 검토
FPU 상태 불안정고정소수점 사용
작은 커널 스택깊은 재귀·큰 지역 배열 금지
아키텍처 다양성64비트·endian 독립 코드

위반 시 시스템 전체에 영향을 주는 핵심 제한입니다.

시스템 호출·ioctl 반환 규칙
상황반환
성공0
일반 오류음수 errno
신호로 중단 후 재시작 가능-ERESTARTSYS

사용자 컨텍스트 인터페이스의 대표 반환값입니다.

새 인터페이스 선택
Need kernel interfaceRead/write a few parameters?
Yessysfs
Device operationcharacter device + ioctl
Only if truly universal ABInew syscall review

새 시스템 호출은 마지막 선택지이며 단순 설정은 더 작은 인터페이스로 표현합니다.

Alcune regole basilari
======================

Nessuna protezione della memoria
    Se corrompete la memoria, che sia in contesto utente o d'interruzione,
    la macchina si pianterà. Siete sicuri che quello che volete fare
    non possa essere fatto nello spazio utente?

Nessun numero in virgola mobile o MMX
    Il contesto della FPU non è salvato; anche se siete in contesto utente
    lo stato dell'FPU probabilmente non corrisponde a quello del processo
    corrente: vi incasinerete con lo stato di qualche altro processo. Se
    volete davvero usare la virgola mobile, allora dovrete salvare e recuperare
    lo stato dell'FPU (ed evitare cambi di contesto). Generalmente è una
    cattiva idea; usate l'aritmetica a virgola fissa.

Un limite rigido dello stack
    A seconda della configurazione del kernel lo stack è fra 3K e 6K per la
    maggior parte delle architetture a 32-bit; è di 14K per la maggior
    parte di quelle a 64-bit; e spesso è condiviso con le interruzioni,
    per cui non si può usare.
    Evitare profonde ricorsioni ad enormi array locali nello stack
    (allocateli dinamicamente).

Il kernel Linux è portabile
    Quindi mantenetelo tale. Il vostro codice dovrebbe essere a 64-bit ed
    indipendente dall'ordine dei byte (endianess) di un processore. Inoltre,
    dovreste minimizzare il codice specifico per un processore; per esempio
    il codice assembly dovrebbe essere incapsulato in modo pulito e minimizzato
    per facilitarne la migrazione. Generalmente questo codice dovrebbe essere
    limitato alla parte di kernel specifica per un'architettura.

ioctl: non scrivere nuove chiamate di sistema
=============================================

Una chiamata di sistema, generalmente, è scritta così::

    asmlinkage long sys_mycall(int arg)
    {
            return 0;
    }

Primo, nella maggior parte dei casi non volete creare nuove chiamate di
sistema.
Create un dispositivo a caratteri ed implementate l'appropriata chiamata ioctl.
Questo meccanismo è molto più flessibile delle chiamate di sistema: esso non
dev'essere dichiarato in tutte le architetture nei file
``include/asm/unistd.h`` e ``arch/kernel/entry.S``; inoltre, è improbabile
che questo venga accettato da Linus.

Se tutto quello che il vostro codice fa è leggere o scrivere alcuni parametri,
considerate l'implementazione di un'interfaccia :c:func:`sysfs()`.

All'interno di una ioctl vi trovate nel contesto utente di un processo. Quando
avviene un errore dovete ritornare un valore negativo di errno (consultate
``include/uapi/asm-generic/errno-base.h``,
``include/uapi/asm-generic/errno.h`` e ``include/linux/errno.h``), altrimenti
ritornate 0.

Dopo aver dormito dovreste verificare se ci sono stati dei segnali: il modo
Unix/Linux di gestire un segnale è di uscire temporaneamente dalla chiamata
di sistema con l'errore ``-ERESTARTSYS``. La chiamata di sistema ritornerà
al contesto utente, eseguirà il gestore del segnale e poi la vostra chiamata
di sistema riprenderà (a meno che l'utente non l'abbia disabilitata). Quindi,
dovreste essere pronti per continuare l'esecuzione, per esempio nel mezzo
della manipolazione di una struttura dati.

::

    if (signal_pending(current))
            return -ERESTARTSYS;

Se dovete eseguire dei calcoli molto lunghi: pensate allo spazio utente.
Se **davvero** volete farlo nel kernel ricordatevi di verificare periodicamente
se dovete *lasciare* il processore (ricordatevi che, per ogni processore, c'è
un sistema multi-processo senza diritto di prelazione).
Esempio::

    cond_resched(); /* Will sleep */

Una breve nota sulla progettazione delle interfacce: il motto dei sistemi
UNIX è "fornite meccanismi e non politiche"

수면 규칙, printk, 사용자 메모리 접근

225-316

잠들 수 있는 루틴은 사용자 컨텍스트이고, spinlock을 보유하지 않았고, 인터럽트가 활성화된 경우에만 호출할 수 있습니다. 이 세 조건을 어기면 교착이나 시스템 정지가 발생할 수 있습니다.

일부 함수는 암묵적으로 잠듭니다. 대표적으로 `*_user` 사용자 공간 접근 함수와 `GFP_ATOMIC` 없이 수행하는 메모리 할당이 있습니다.

`CONFIG_DEBUG_ATOMIC_SLEEP`을 활성화해 잘못된 컨텍스트에서 잠드는 호출을 탐지해야 합니다. 원문은 이 규칙을 어기면 실제로 시스템을 멈출 수 있다고 거듭 강조합니다.

`printk()`는 `include/linux/printk.h`에 정의되며 콘솔, dmesg, syslog 데몬에 메시지를 제공합니다. 디버깅과 오류 알림에 유용하고 인터럽트 컨텍스트에서도 쓸 수 있지만, 콘솔을 메시지로 범람시키면 시스템이 사실상 사용할 수 없게 됩니다.

형식 문자열은 ANSI C `printf`와 거의 호환되며 첫 문자열에 `KERN_INFO` 같은 우선순위를 연결합니다. 다른 `KERN_` 값은 `include/linux/kern_levels.h`에 있고 syslog가 수준으로 해석합니다.

per indicare la "priorità"::

    printk(KERN_INFO "i = %u\n", i);

Consultate ``include/linux/kern_levels.h`` per gli altri valori ``KERN_``;
questi sono interpretati da syslog come livelli. Un caso speciale:
per stampare un indirizzo IP usate::

    __be32 ipaddress;
    printk(KERN_INFO "my ip: %pI4\n", &ipaddress);

IPv4 주소는 `__be32` 값을 `%pI4`와 주소 포인터로 출력합니다. 원문은 `printk()` 내부 버퍼가 1K이고 넘침을 알아채지 못한다고 하므로 메시지 크기를 제한해야 한다고 경고합니다.

`put_user()`와 `get_user()`는 `include/linux/uaccess.h` 또는 `asm/uaccess.h`의 사용자 접근 함수이며 잠들 수 있습니다. int, char, long 같은 단일 값을 사용자 공간에서 읽거나 사용자 공간에 씁니다.

사용자 공간 포인터를 직접 역참조해서는 안 됩니다. 이 루틴을 통해 데이터를 복사해야 하며 `put_user()`와 `get_user()`는 `-EFAULT` 또는 0을 반환합니다.

`copy_to_user()`와 `copy_from_user()`는 임의 길이 데이터를 복사합니다. 단일 값 함수와 달리 복사하지 못한 데이터 양을 반환하므로 0이 성공입니다.

이 사용자 접근 함수는 암묵적으로 잠들 수 있으므로 사용자 컨텍스트 밖, 인터럽트 비활성 상태, spinlock 보유 상태에서 호출해서는 안 됩니다.

잠들 수 있는 호출의 전제
조건필수 상태
실행 컨텍스트사용자 컨텍스트
spinlock보유하지 않음
인터럽트활성화됨

세 조건이 모두 충족되어야 수면 가능 루틴을 호출할 수 있습니다.

사용자 공간 접근 반환값
함수성공오류
get_user / put_user0-EFAULT
copy_to_user / copy_from_user복사 못 한 바이트 0복사 못 한 바이트 수

함수 집합마다 성공 판정 방식이 다릅니다.

하드 IRQ 작업 분리
Interrupt contextMinimal diagnostics
Schedule deferred workReturn quickly

printk도 사용할 수 있지만 핸들러에서는 빠른 기록과 지연 처리를 우선합니다.

La ricetta per uno stallo
=========================

Non è permesso invocare una procedura che potrebbe dormire, fanno eccezione
i seguenti casi:

-  Siete in un contesto utente.

-  Non trattenete alcun spinlock.

-  Avete abilitato le interruzioni (in realtà, Andy Kleen dice che
   lo schedulatore le abiliterà per voi, ma probabilmente questo non è quello
   che volete).

Da tener presente che alcune funzioni potrebbero dormire implicitamente:
le più comuni sono quelle per l'accesso allo spazio utente (\*_user) e
quelle per l'allocazione della memoria senza l'opzione ``GFP_ATOMIC``

Dovreste sempre compilare il kernel con l'opzione ``CONFIG_DEBUG_ATOMIC_SLEEP``
attiva, questa vi avviserà se infrangete una di queste regole.
Se **infrangete** le regole, allora potreste bloccare il vostro scatolotto.

Veramente.

Alcune delle procedure più comuni
=================================

:c:func:`printk()`
------------------

Definita in ``include/linux/printk.h``

:c:func:`printk()` fornisce messaggi alla console, dmesg, e al demone syslog.
Essa è utile per il debugging o per la notifica di errori; può essere
utilizzata anche all'interno del contesto d'interruzione, ma usatela con
cautela: una macchina che ha la propria console inondata da messaggi diventa
inutilizzabile. La funzione utilizza un formato stringa quasi compatibile con
la printf ANSI C, e la concatenazione di una stringa C come primo argomento
per indicare la "priorità"::

    printk(KERN_INFO "i = %u\n", i);

Consultate ``include/linux/kern_levels.h`` per gli altri valori ``KERN_``;
questi sono interpretati da syslog come livelli. Un caso speciale:
per stampare un indirizzo IP usate::

    __be32 ipaddress;
    printk(KERN_INFO "my ip: %pI4\n", &ipaddress);


:c:func:`printk()` utilizza un buffer interno di 1K e non s'accorge di
eventuali sforamenti. Accertatevi che vi basti.

.. note::

    Saprete di essere un vero hacker del kernel quando inizierete a digitare
    nei vostri programmi utenti le printf come se fossero printk :)

.. note::

    Un'altra nota a parte: la versione originale di Unix 6 aveva un commento
    sopra alla funzione printf: "Printf non dovrebbe essere usata per il
    chiacchiericcio". Dovreste seguire questo consiglio.

:c:func:`copy_to_user()` / :c:func:`copy_from_user()` / :c:func:`get_user()` / :c:func:`put_user()`
---------------------------------------------------------------------------------------------------

Definite in ``include/linux/uaccess.h`` / ``asm/uaccess.h``

**[DORMONO]**

:c:func:`put_user()` e :c:func:`get_user()` sono usate per ricevere ed
impostare singoli valori (come int, char, o long) da e verso lo spazio utente.
Un puntatore nello spazio utente non dovrebbe mai essere dereferenziato: i dati
dovrebbero essere copiati usando suddette procedure. Entrambe ritornano
``-EFAULT`` oppure 0.

:c:func:`copy_to_user()` e :c:func:`copy_from_user()` sono più generiche:
esse copiano una quantità arbitraria di dati da e verso lo spazio utente.

.. warning::

    Al contrario di:c:func:`put_user()` e :c:func:`get_user()`, queste
    funzioni ritornano la quantità di dati copiati (0 è comunque un successo).

[Sì, questa interfaccia mi imbarazza. La battaglia torna in auge anno
dopo anno. --RR]

Le funzioni potrebbero dormire implicitamente. Queste non dovrebbero mai essere
invocate fuori dal contesto utente (non ha senso), con le interruzioni
disabilitate, o con uno spinlock trattenuto.

메모리 할당, 지연, 바이트 순서, IRQ와 CPU

317-450

`kmalloc()`과 `kfree()`는 `include/linux/slab.h`에 정의되며 사용자 공간의 malloc/free처럼 정렬된 동적 메모리 블록을 다룹니다. `kmalloc()`에는 할당 동작을 정하는 추가 플래그가 있습니다.

`GFP_KERNEL`은 메모리를 확보하기 위해 잠들 수 있고 가장 신뢰도 높은 일반 할당이지만 사용자 컨텍스트에서만 사용할 수 있습니다.

`GFP_ATOMIC`은 잠들지 않아 인터럽트 컨텍스트에서 사용할 수 있지만 `GFP_KERNEL`보다 실패하기 쉽습니다. 메모리 부족 때의 확실한 오류 처리 전략이 필요합니다.

`GFP_DMA`는 ISA 버스 DMA용으로 16MB 아래 주소 공간에서 메모리를 할당하며 매우 신뢰하기 어렵습니다. 필요성을 정확히 아는 장치 코드에서만 사용합니다.

잘못된 컨텍스트의 수면 함수 경고가 나오면 인터럽트 컨텍스트에서 `GFP_ATOMIC` 없이 수면 가능한 할당을 호출했는지 확인하고 즉시 수정해야 합니다.

`PAGE_SIZE` 이상을 할당할 때는 `include/linux/gfp.h`의 `__get_free_pages()`를 고려합니다. order 0은 한 페이지, 1은 두 페이지, 2는 네 페이지처럼 2의 거듭제곱 페이지 수를 뜻하며 같은 할당 플래그를 받습니다.

한 페이지보다 훨씬 큰 메모리는 `vmalloc()`로 커널 가상 주소 공간에 할당할 수 있습니다. 물리적으로는 비연속이지만 MMU가 CPU에 연속으로 보이게 하므로 외부 장치 DMA 관점에서는 연속 메모리가 아닙니다.

큰 물리 연속 메모리는 시스템 가동 후 단편화 때문에 확보하기 어렵습니다. 원문은 부팅 초기에 `alloc_bootmem()`을 사용하는 방법을 언급하며, 이 오래된 API 이름도 로컬 원문 그대로 보존합니다.

자주 쓰는 객체를 위해 자체 캐시를 만들기 전에 `include/linux/slab.h`의 slab 캐시 사용을 고려합니다.

`current`는 `include/asm/current.h`에 정의된 매크로로 현재 프로세스 구조체를 가리킵니다. 사용자 컨텍스트에서만 의미가 유효하며 인터럽트 컨텍스트에서도 값 자체가 NULL이 되는 것은 아닙니다.

`udelay()`와 `ndelay()`는 짧은 지연에 사용합니다. 큰 값은 오버플로 위험이 있으므로 `mdelay()`를 쓰거나 잠들 수 있는 상황이면 `msleep()`을 고려합니다.

`cpu_to_be32()` 계열은 `include/asm/byteorder.h`에서 CPU 바이트 순서와 big-endian 또는 little-endian 값 사이를 변환합니다. 32 대신 16·64, be 대신 le를 쓰는 변형과 `be32_to_cpu()` 같은 역변환이 있습니다.

포인터 변형인 `cpu_to_be32p()`는 포인터가 가리키는 값을 읽어 변환값을 반환합니다. in-place 변형인 `cpu_to_be32s()`는 포인터 대상 자체를 변환하고 void를 반환합니다.

`local_irq_save()`와 `local_irq_restore()`는 `include/linux/irqflags.h`에서 현재 CPU의 하드웨어 인터럽트를 비활성화·복원합니다. 재진입 가능하며 `unsigned long flags`에 이전 상태를 저장합니다.

인터럽트가 활성화되어 있다는 것을 확실히 알면 `local_irq_disable()`과 `local_irq_enable()`을 사용할 수 있습니다.

`local_bh_disable()`과 `local_bh_enable()`은 `include/linux/bottom_half.h`에서 현재 CPU의 softirq와 tasklet 실행을 막고 복원합니다. 중첩 가능하며 이전부터 비활성 상태였다면 쌍을 호출한 뒤에도 비활성 상태를 유지합니다.

`get_cpu()`는 선점을 막아 다른 CPU로 이동하지 못하게 하고 현재 CPU 번호를 반환합니다. 번호 범위는 0부터 `NR_CPUS` 사이지만 연속이라는 보장은 없으며 끝나면 `put_cpu()`로 이전 상태를 복원합니다.

이미 인터럽트 컨텍스트이거나 선점이 비활성화되어 다른 프로세스가 선점할 수 없다는 사실을 알면 `smp_processor_id()`를 직접 사용할 수 있습니다.

메모리 할당 선택
방법잠들 수 있음특징
kmalloc GFP_KERNEL일반적이고 신뢰도 높음
kmalloc GFP_ATOMIC아니요인터럽트용, 실패 가능성 높음
__get_free_pages플래그에 따름2^order 물리 페이지
vmalloc가상 연속, 물리 비연속

호출 컨텍스트와 연속성 요구에 따라 할당 방법을 고릅니다.

지연 함수 선택
함수용도
ndelay / udelay아주 짧은 busy wait
mdelay더 큰 busy wait 값
msleep잠들 수 있는 컨텍스트의 밀리초 지연

busy wait와 수면 지연을 구분합니다.

현재 CPU 식별
상황방법마무리
일반 프로세스 문맥get_cpu()put_cpu()
이미 비선점 문맥smp_processor_id()별도 선점 상태 변경 없음

선점 상태를 안전하게 관리해야 CPU 번호가 유효합니다.

바이트 순서 변환 변형
CPU valuecpu_to_be32 / cpu_to_le32
Pointer inputcpu_to_be32p
Modify pointed valuecpu_to_be32s

값, 포인터, in-place API를 목적에 맞게 구분합니다.

:c:func:`kmalloc()`/:c:func:`kfree()`
-------------------------------------

Definite in ``include/linux/slab.h``

**[POTREBBERO DORMIRE: LEGGI SOTTO]**

Queste procedure sono utilizzate per la richiesta dinamica di un puntatore ad
un pezzo di memoria allineato, esattamente come malloc e free nello spazio
utente, ma :c:func:`kmalloc()` ha un argomento aggiuntivo per indicare alcune
opzioni. Le opzioni più importanti sono:

``GFP_KERNEL``
    Potrebbe dormire per librarare della memoria. L'opzione fornisce il modo
    più affidabile per allocare memoria, ma il suo uso è strettamente limitato
    allo spazio utente.

``GFP_ATOMIC``
    Non dorme. Meno affidabile di ``GFP_KERNEL``, ma può essere usata in un
    contesto d'interruzione. Dovreste avere **davvero** una buona strategia
    per la gestione degli errori in caso di mancanza di memoria.

``GFP_DMA``
    Alloca memoria per il DMA sul bus ISA nello spazio d'indirizzamento
    inferiore ai 16MB. Se non sapete cos'è allora non vi serve.
    Molto inaffidabile.

Se vedete un messaggio d'avviso per una funzione dormiente che viene chiamata
da un contesto errato, allora probabilmente avete usato una funzione
d'allocazione dormiente da un contesto d'interruzione senza ``GFP_ATOMIC``.
Dovreste correggerlo. Sbrigatevi, non cincischiate.

Se allocate almeno ``PAGE_SIZE``(``asm/page.h`` o ``asm/page_types.h``) byte,
considerate l'uso di :c:func:`__get_free_pages()` (``include/linux/gfp.h``).
Accetta un argomento che definisce l'ordine (0 per per la dimensione di una
pagine, 1 per una doppia pagina, 2 per quattro pagine, eccetra) e le stesse
opzioni d'allocazione viste precedentemente.

Se state allocando un numero di byte notevolemnte superiore ad una pagina
potete usare :c:func:`vmalloc()`. Essa allocherà memoria virtuale all'interno
dello spazio kernel. Questo è un blocco di memoria fisica non contiguo, ma
la MMU vi darà l'impressione che lo sia (quindi, sarà contiguo solo dal punto
di vista dei processori, non dal punto di vista dei driver dei dispositivi
esterni).
Se per qualche strana ragione avete davvero bisogno di una grossa quantità di
memoria fisica contigua, avete un problema: Linux non ha un buon supporto per
questo caso d'uso perché, dopo un po' di tempo, la frammentazione della memoria
rende l'operazione difficile. Il modo migliore per allocare un simile blocco
all'inizio dell'avvio del sistema è attraverso la procedura
:c:func:`alloc_bootmem()`.

Prima di inventare la vostra cache per gli oggetti più usati, considerate
l'uso di una cache slab disponibile in ``include/linux/slab.h``.

:c:macro:`current`
-------------------

Definita in ``include/asm/current.h``

Questa variabile globale (in realtà una macro) contiene un puntatore alla
struttura del processo corrente, quindi è valido solo dal contesto utente.
Per esempio, quando un processo esegue una chiamata di sistema, questo
punterà alla struttura dati del processo chiamate.
Nel contesto d'interruzione in suo valore **non è NULL**.

:c:func:`mdelay()`/:c:func:`udelay()`
-------------------------------------

Definite in ``include/asm/delay.h`` / ``include/linux/delay.h``

Le funzioni :c:func:`udelay()` e :c:func:`ndelay()` possono essere utilizzate
per brevi pause. Non usate grandi valori perché rischiate d'avere un
overflow - in questo contesto la funzione :c:func:`mdelay()` è utile,
oppure considerate :c:func:`msleep()`.

:c:func:`cpu_to_be32()`/:c:func:`be32_to_cpu()`/:c:func:`cpu_to_le32()`/:c:func:`le32_to_cpu()`
-----------------------------------------------------------------------------------------------

Definite in ``include/asm/byteorder.h``

La famiglia di funzioni :c:func:`cpu_to_be32()` (dove "32" può essere
sostituito da 64 o 16, e "be" con "le") forniscono un modo generico
per fare conversioni sull'ordine dei byte (endianess): esse ritornano
il valore convertito. Tutte le varianti supportano anche il processo inverso:
:c:func:`be32_to_cpu()`, eccetera.

Queste funzioni hanno principalmente due varianti: la variante per
puntatori, come :c:func:`cpu_to_be32p()`, che prende un puntatore
ad un tipo, e ritorna il valore convertito. L'altra variante per
la famiglia di conversioni "in-situ", come :c:func:`cpu_to_be32s()`,
che convertono il valore puntato da un puntatore, e ritornano void.

:c:func:`local_irq_save()`/:c:func:`local_irq_restore()`
--------------------------------------------------------

Definite in ``include/linux/irqflags.h``

Queste funzioni abilitano e disabilitano le interruzioni hardware
sul processore locale. Entrambe sono rientranti; esse salvano lo stato
precedente nel proprio argomento ``unsigned long flags``. Se sapete
che le interruzioni sono abilite, potete semplicemente utilizzare
:c:func:`local_irq_disable()` e :c:func:`local_irq_enable()`.

.. _it_local_bh_disable:

:c:func:`local_bh_disable()`/:c:func:`local_bh_enable()`
--------------------------------------------------------

Definite in ``include/linux/bottom_half.h``


Queste funzioni abilitano e disabilitano le interruzioni software
sul processore locale. Entrambe sono rientranti; se le interruzioni
software erano già state disabilitate in precedenza, rimarranno
disabilitate anche dopo aver invocato questa coppia di funzioni.
Lo scopo è di prevenire l'esecuzione di softirq e tasklet sul processore
attuale.

:c:func:`smp_processor_id()`
----------------------------

Definita in ``include/linux/smp.h``

:c:func:`get_cpu()` nega il diritto di prelazione (quindi non potete essere
spostati su un altro processore all'improvviso) e ritorna il numero
del processore attuale, fra 0 e ``NR_CPUS``. Da notare che non è detto
che la numerazione dei processori sia continua. Quando avete terminato,
ritornate allo stato precedente con :c:func:`put_cpu()`.

Se sapete che non dovete essere interrotti da altri processi (per esempio,
se siete in un contesto d'interruzione, o il diritto di prelazione
è disabilitato) potete utilizzare smp_processor_id().

초기화 섹션과 모듈 생명주기

451-525

`__init`, `__exit`, `__initdata`는 `include/linux/init.h`에 정의됩니다. 부팅이 끝나면 커널은 특별 초기화 섹션을 해제하므로 `__init` 함수와 `__initdata` 자료구조는 더 이상 남지 않습니다.

모듈도 초기화가 끝난 뒤 같은 종류의 메모리를 해제합니다. `__exit`는 제거 때만 사용하는 함수임을 나타내며, 파일이 모듈로 빌드되지 않으면 그 함수를 버릴 수 있게 합니다.

`__init`로 표시한 함수를 `EXPORT_SYMBOL()` 또는 `EXPORT_SYMBOL_GPL()`로 모듈에 내보내는 것은 의미가 없습니다. 초기화 뒤 함수 메모리가 사라지므로 동작하지 않습니다.

`module_init()`과 `module_exit()`는 `include/linux/init.h` 및 `include/linux/module.h`에서 같은 코드를 동적 모듈과 내장 커널 구성 양쪽에 사용할 수 있게 합니다.

`module_init()`은 모듈 삽입 때 호출할 함수를 지정합니다. 내장 빌드에서는 `__initcall()`과 같아져 링커를 통해 부팅 중 해당 함수를 호출합니다.

초기화 함수는 음수 오류를 반환해 모듈 로드를 실패시킬 수 있지만 내장 커널 구성에서는 같은 효과가 없습니다. 사용자 컨텍스트, 인터럽트 활성 상태에서 호출되므로 잠들 수 있습니다.

`module_exit()`는 모듈 제거 때 호출할 함수를 지정하며 내장 구성에서는 호출되지 않습니다. 사용 횟수가 0일 때만 실행되고 잠들 수 있지만 실패할 수는 없으므로 반환 전에 모든 자원을 정리해야 합니다.

`module_exit()`가 없으면 일반적으로 모듈을 제거할 수 없습니다. 원문은 강제 제거 `rmmod -f`를 예외로 언급합니다.

`try_module_get()`과 `module_put()`은 모듈 사용 카운터를 관리해 실행 중 제거를 막습니다. 모듈 코드를 실행하기 전에 `try_module_get()`이 성공해야 하며, 끝나면 `module_put()`을 호출합니다.

다른 모듈이 내보낸 심볼을 사용 중인 경우에도 모듈은 제거되지 않습니다. 등록 가능한 많은 구조체에는 `struct file_operations`처럼 `owner` 필드가 있으며 `THIS_MODULE`로 설정해야 합니다.

초기화·종료 표식
표식의미
__init초기화 뒤 해제할 함수
__initdata초기화 뒤 해제할 데이터
__exit모듈 제거 때만 사용할 함수

코드와 데이터가 필요한 생명주기를 빌드 시스템에 알립니다.

모듈 콜백의 실행 특성
콜백호출 시점수면실패
module_init삽입 또는 내장 부팅가능모듈 로드 시 가능
module_exit제거가능불가

초기화와 제거의 오류·수면 규칙이 다릅니다.

모듈 참조 보호
try_module_get(module)Success?
YesExecute module code
Donemodule_put(module)
NoTreat module as absent

참조를 획득한 동안 모듈 제거를 막습니다.

``__init``/``__exit``/``__initdata``
------------------------------------

Definite in  ``include/linux/init.h``

Dopo l'avvio, il kernel libera una sezione speciale; le funzioni marcate
con ``__init`` e le strutture dati marcate con ``__initdata`` vengono
eliminate dopo il completamento dell'avvio: in modo simile i moduli eliminano
questa memoria dopo l'inizializzazione. ``__exit`` viene utilizzato per
dichiarare che una funzione verrà utilizzata solo in fase di rimozione:
la detta funzione verrà eliminata quando il file che la contiene non è
compilato come modulo. Guardate l'header file per informazioni. Da notare che
non ha senso avere una funzione marcata come ``__init`` e al tempo stesso
esportata ai moduli utilizzando :c:func:`EXPORT_SYMBOL()` o
:c:func:`EXPORT_SYMBOL_GPL()` - non funzionerà.


:c:func:`__initcall()`/:c:func:`module_init()`
----------------------------------------------

Definite in  ``include/linux/init.h`` / ``include/linux/module.h``

Molte parti del kernel funzionano bene come moduli (componenti del kernel
caricabili dinamicamente). L'utilizzo delle macro :c:func:`module_init()`
e :c:func:`module_exit()` semplifica la scrittura di codice che può funzionare
sia come modulo, sia come parte del kernel, senza l'ausilio di #ifdef.

La macro :c:func:`module_init()` definisce quale funzione dev'essere
chiamata quando il modulo viene inserito (se il file è stato compilato come
tale), o in fase di avvio : se il file non è stato compilato come modulo la
macro :c:func:`module_init()` diventa equivalente a :c:func:`__initcall()`,
la quale, tramite qualche magia del linker, s'assicura che la funzione venga
chiamata durante l'avvio.

La funzione può ritornare un numero d'errore negativo per scatenare un
fallimento del caricamento (sfortunatamente, questo non ha effetto se il
modulo è compilato come parte integrante del kernel). Questa funzione è chiamata
in contesto utente con le interruzioni abilitate, quindi potrebbe dormire.


:c:func:`module_exit()`
-----------------------


Definita in  ``include/linux/module.h``

Questa macro definisce la funzione che dev'essere chiamata al momento della
rimozione (o mai, nel caso in cui il file sia parte integrante del kernel).
Essa verrà chiamata solo quando il contatore d'uso del modulo raggiunge lo
zero. Questa funzione può anche dormire, ma non può fallire: tutto dev'essere
ripulito prima che la funzione ritorni.

Da notare che questa macro è opzionale: se non presente, il modulo non sarà
removibile (a meno che non usiate 'rmmod -f' ).


:c:func:`try_module_get()`/:c:func:`module_put()`
-------------------------------------------------

Definite in ``include/linux/module.h``

Queste funzioni maneggiano il contatore d'uso del modulo per proteggerlo dalla
rimozione (in aggiunta, un modulo non può essere rimosso se un altro modulo
utilizzo uno dei sui simboli esportati: vedere di seguito). Prima di eseguire
codice del modulo, dovreste chiamare :c:func:`try_module_get()` su quel modulo:
se fallisce significa che il modulo è stato rimosso e dovete agire come se
non fosse presente. Altrimenti, potete accedere al modulo in sicurezza, e
chiamare :c:func:`module_put()` quando avete finito.

La maggior parte delle strutture registrabili hanno un campo owner
(proprietario), come nella struttura
:c:type:`struct file_operations <file_operations>`.
Impostate questo campo al valore della macro ``THIS_MODULE``.

대기 큐와 원자 연산

526-596

`include/linux/wait.h`의 대기 큐는 특정 조건이 참이 될 때 누군가 깨워 주기를 기다리는 데 사용하며 잠드는 기능입니다. 경쟁 상태를 피하도록 순서를 주의해야 합니다.

먼저 `wait_queue_head_t`를 선언합니다. 기다리는 프로세스는 자신을 가리키는 `wait_queue_entry_t`를 준비해 큐에 넣습니다.

대기 큐 머리는 `DECLARE_WAIT_QUEUE_HEAD()`로 정적으로 선언하거나 초기화 코드에서 `init_waitqueue_head()`로 설정합니다.

큐에 들어가는 과정에서는 조건을 검사하기 전에 먼저 자신을 큐에 넣어야 합니다. `wait_event_interruptible()`은 첫 인자로 큐 머리, 둘째 인자로 평가할 조건식을 받습니다.

조건식이 참이면 0을 반환하고, 기다리는 동안 신호를 받으면 `-ERESTARTSYS`를 반환합니다. `wait_event()` 변형은 신호를 무시합니다.

조건을 만든 쪽은 `wake_up()`을 호출해 큐의 프로세스를 깨웁니다. 일반적으로 모두 깨우지만 `TASK_EXCLUSIVE` 항목이 있으면 그 뒤의 나머지는 깨우지 않습니다. 같은 헤더에 여러 변형이 있습니다.

원자 연산의 첫 집합은 `include/asm/atomic.h`의 `atomic_t`를 사용합니다. 최소 32비트 부호 있는 정수를 담으며 반드시 전용 함수를 통해 읽고 수정합니다.

`atomic_read()`, `atomic_set()`은 읽기와 설정, `atomic_add()`, `atomic_sub()`, `atomic_inc()`, `atomic_dec()`은 산술 연산을 수행합니다. `atomic_dec_and_test()`는 감소 후 0이 되면 참을 반환합니다.

원자 함수는 일반 산술보다 느리므로 필요하지 않은 곳에 남용하면 안 됩니다.

두 번째 원자 연산 집합은 `include/linux/bitops.h`에 있고 `unsigned long`의 비트를 다룹니다. 변수 포인터와 최하위 비트를 0으로 하는 비트 번호를 받습니다.

`set_bit()`, `clear_bit()`, `change_bit()`는 지정 비트를 설정·해제·반전합니다. `test_and_set_bit()`, `test_and_clear_bit()`, `test_and_change_bit()`은 같은 동작을 하면서 이전 비트가 설정되어 있었는지 반환해 원자적 플래그 조작에 유용합니다.

`BITS_PER_LONG`을 넘는 비트 인덱스도 기술적으로 쓸 수 있지만 big-endian 플랫폼에서 동작이 이상할 수 있으므로 피해야 합니다.

대기 큐 절차
순서동작
1wait_queue_head_t 준비
2자신을 큐에 등록
3조건 평가 후 수면
4조건 변경 측에서 wake_up

등록을 조건 검사보다 먼저 해야 깨우기 이벤트를 놓치지 않습니다.

대기 매크로 반환
함수조건 참신호 수신
wait_event_interruptible0-ERESTARTSYS
wait_event0신호 무시

신호 처리 여부에 따라 사용할 변형을 고릅니다.

원자 비트 연산
종류함수
변경만set_bit / clear_bit / change_bit
이전 상태 반환test_and_set_bit / test_and_clear_bit / test_and_change_bit

test_and 계열은 변경 전 상태도 반환합니다.

조건 대기와 깨우기
Queue waiterwait_event_interruptible(condition)
Condition falseSleep
Producer changes statewake_up
Condition trueReturn 0

대기자는 조건식과 신호를 구분하고 생산자는 상태 변경 뒤 깨웁니다.

Code d'attesa ``include/linux/wait.h``
======================================

**[DORMONO]**

Una coda d'attesa è usata per aspettare che qualcuno vi attivi quando una
certa condizione s'avvera. Per evitare corse critiche, devono essere usate
con cautela. Dichiarate una :c:type:`wait_queue_head_t`, e poi i processi
che vogliono attendere il verificarsi di quella condizione dichiareranno
una :c:type:`wait_queue_entry_t` facendo riferimento a loro stessi, poi
metteranno questa in coda.

Dichiarazione
-------------

Potere dichiarare una ``wait_queue_head_t`` utilizzando la macro
:c:func:`DECLARE_WAIT_QUEUE_HEAD()` oppure utilizzando la procedura
:c:func:`init_waitqueue_head()` nel vostro codice d'inizializzazione.

Accodamento
-----------

Mettersi in una coda d'attesa è piuttosto complesso, perché dovete
mettervi in coda prima di verificare la condizione. Esiste una macro
a questo scopo: :c:func:`wait_event_interruptible()` (``include/linux/wait.h``).
Il primo argomento è la testa della coda d'attesa, e il secondo è
un'espressione che dev'essere valutata; la macro ritorna 0 quando questa
espressione è vera, altrimenti ``-ERESTARTSYS`` se è stato ricevuto un segnale.
La versione :c:func:`wait_event()` ignora i segnali.

Svegliare una procedura in coda
-------------------------------

Chiamate :c:func:`wake_up()` (``include/linux/wait.h``); questa attiverà tutti
i processi in coda. Ad eccezione se uno di questi è impostato come
``TASK_EXCLUSIVE``, in questo caso i rimanenti non verranno svegliati.
Nello stesso header file esistono altre varianti di questa funzione.

Operazioni atomiche
===================

Certe operazioni sono garantite come atomiche su tutte le piattaforme.
Il primo gruppo di operazioni utilizza :c:type:`atomic_t`
(``include/asm/atomic.h``); questo contiene un intero con segno (minimo 32bit),
e dovete utilizzare queste funzione per modificare o leggere variabili di tipo
:c:type:`atomic_t`. :c:func:`atomic_read()` e :c:func:`atomic_set()` leggono ed
impostano il contatore, :c:func:`atomic_add()`, :c:func:`atomic_sub()`,
:c:func:`atomic_inc()`, :c:func:`atomic_dec()`, e
:c:func:`atomic_dec_and_test()` (ritorna vero se raggiunge zero dopo essere
stata decrementata).

Sì. Ritorna vero (ovvero != 0) se la variabile atomica è zero.

Da notare che queste funzioni sono più lente rispetto alla normale aritmetica,
e quindi non dovrebbero essere usate a sproposito.

Il secondo gruppo di operazioni atomiche sono definite in
``include/linux/bitops.h`` ed agiscono sui bit d'una variabile di tipo
``unsigned long``. Queste operazioni prendono come argomento un puntatore
alla variabile, e un numero di bit dove 0 è quello meno significativo.
:c:func:`set_bit()`, :c:func:`clear_bit()` e :c:func:`change_bit()`
impostano, cancellano, ed invertono il bit indicato.
:c:func:`test_and_set_bit()`, :c:func:`test_and_clear_bit()` e
:c:func:`test_and_change_bit()` fanno la stessa cosa, ad eccezione che
ritornano vero se il bit era impostato; queste sono particolarmente
utili quando si vuole impostare atomicamente dei flag.

Con queste operazioni è possibile utilizzare indici di bit che eccedono
il valore ``BITS_PER_LONG``. Il comportamento è strano sulle piattaforme
big-endian quindi è meglio evitarlo.

커널과 모듈의 심볼 내보내기

597-646

커널 내부에서는 일반 링커 규칙을 따릅니다. `static`으로 파일 내부 가시성을 제한하지 않은 심볼은 커널 다른 부분에서 사용할 수 있습니다.

동적 모듈에는 별도의 내보낸 심볼 테이블이 있어 커널 진입점을 제한합니다. 모듈 자신도 심볼을 내보낼 수 있습니다.

`EXPORT_SYMBOL()`은 `include/linux/export.h`에 정의된 일반 내보내기 방법이며 로드된 모듈이 해당 심볼을 사용할 수 있게 합니다.

`EXPORT_SYMBOL_GPL()`은 `MODULE_LICENSE()`로 GPL 호환 라이선스를 선언한 모듈만 심볼을 사용할 수 있게 합니다. 이런 심볼은 안정된 외부 인터페이스라기보다 내부 함수로 간주됩니다.

일부 유지관리자와 개발자는 새 기능이나 인터페이스를 추가할 때도 `EXPORT_SYMBOL_GPL()` 사용을 요구할 수 있습니다.

`EXPORT_SYMBOL_NS()`는 `EXPORT_SYMBOL()`에 네임스페이스를 지정하는 변형이고, `EXPORT_SYMBOL_NS_GPL()`은 GPL 제한 변형에 네임스페이스를 지정합니다.

심볼 네임스페이스는 `Documentation/translations/it_IT/core-api/symbol-namespaces.rst`에 설명되어 있습니다. 원문의 이 경로를 그대로 보존합니다.

심볼 내보내기 매크로
매크로GPL 호환 제한네임스페이스
EXPORT_SYMBOL없음없음
EXPORT_SYMBOL_GPL있음없음
EXPORT_SYMBOL_NS없음있음
EXPORT_SYMBOL_NS_GPL있음있음

라이선스 제한과 네임스페이스 사용 여부를 구분합니다.

모듈의 심볼 사용 가능성
Exported symbolGPL-restricted?
NoAny compatible module may resolve
YesCheck MODULE_LICENSE
GPL-compatibleResolve symbol

내보내기 종류와 모듈 라이선스 선언을 함께 확인합니다.

Simboli
=======

All'interno del kernel, si seguono le normali regole del linker (ovvero,
a meno che un simbolo non venga dichiarato con visibilita limitata ad un
file con la parola chiave ``static``, esso può essere utilizzato in qualsiasi
parte del kernel). Nonostante ciò, per i moduli, esiste una tabella dei
simboli esportati che limita i punti di accesso al kernel. Anche i moduli
possono esportare simboli.

:c:func:`EXPORT_SYMBOL()`
-------------------------

Definita in ``include/linux/export.h``

Questo è il classico metodo per esportare un simbolo: i moduli caricati
dinamicamente potranno utilizzare normalmente il simbolo.

:c:func:`EXPORT_SYMBOL_GPL()`
-----------------------------

Definita in ``include/linux/export.h``

Essa è simile a :c:func:`EXPORT_SYMBOL()` ad eccezione del fatto che i
simboli esportati con :c:func:`EXPORT_SYMBOL_GPL()` possono essere
utilizzati solo dai moduli che hanno dichiarato una licenza compatibile
con la GPL attraverso :c:func:`MODULE_LICENSE()`. Questo implica che la
funzione esportata è considerata interna, e non una vera e propria interfaccia.
Alcuni manutentori e sviluppatori potrebbero comunque richiedere
:c:func:`EXPORT_SYMBOL_GPL()` quando si aggiungono nuove funzionalità o
interfacce.

:c:func:`EXPORT_SYMBOL_NS()`
----------------------------

Definita in ``include/linux/export.h``

Questa è una variate di `EXPORT_SYMBOL()` che permette di specificare uno
spazio dei nomi. Lo spazio dei nomi è documentato in
Documentation/translations/it_IT/core-api/symbol-namespaces.rst.

:c:func:`EXPORT_SYMBOL_NS_GPL()`
--------------------------------

Definita in ``include/linux/export.h``

Questa è una variate di `EXPORT_SYMBOL_GPL()` che permette di specificare uno
spazio dei nomi. Lo spazio dei nomi è documentato in
Documentation/translations/it_IT/core-api/symbol-namespaces.rst.

자료구조와 코딩 관례

647-753

단순한 이중 연결 리스트가 필요하면 `include/linux/list.h`의 공통 리스트 구현을 사용합니다. 특별한 요구가 없다면 자체 구현보다 이 선택이 적절하며 `list_for_each_entry()`가 특히 유용합니다.

사용자 컨텍스트에서 호출되는 커널 코드는 C의 흔한 관례와 달리 성공 시 0, 실패 시 `-EFAULT` 같은 음수 오류 코드를 반환하는 경우가 많습니다.

`include/linux/err.h`의 `ERR_PTR()`는 음수 오류를 포인터 값으로 인코딩합니다. `IS_ERR()`로 오류 포인터인지 확인하고 `PTR_ERR()`로 오류 번호를 되찾아 별도 오류 출력 포인터를 피할 수 있습니다.

개발 중인 커널에서는 함수나 구조체 이름이 바뀔 수 있습니다. 이는 호출 가능 컨텍스트, 추가 검사, 제거된 검사처럼 근본 동작이 바뀌었음을 반영하는 경우가 많습니다.

관련 메일링 리스트와 아카이브에서 변경 설명을 찾아야 합니다. 파일 전체에 단순 문자열 치환을 적용하면 의미 변화를 놓쳐 상황을 더 나쁘게 만들 수 있습니다.

구조체는 ISO C99 지정 초기화자를 사용하는 것이 권장됩니다. 필드 이름을 명시하면 grep으로 찾기 쉽고 어떤 필드를 설정하는지 분명합니다.

Il metodo preferito per l'inizializzazione delle strutture è quello
di utilizzare gli inizializzatori designati, come definiti nello
standard ISO C99, eg::

    static struct block_device_operations opt_fops = {
            .open               = opt_open,
            .release            = opt_release,
            .ioctl              = opt_ioctl,
            .check_media_change = opt_media_change,
    };

Linux 커널은 GNU C 확장을 명시적으로 허용합니다. 자세한 내용은 GCC info 페이지의 ‘C Extensions’를 참조하며, man 페이지는 info 내용의 짧은 요약일 뿐입니다.

일반적으로 쓰이는 확장에는 inline 함수, statement expression `({ ... })`, `__attribute__`, `typeof`, 길이 0 배열, 가변 인자 매크로, void 포인터 산술, 비상수 초기화자, `__func__`, `__builtin_constant_p()`가 있습니다.

어셈블러 문장은 허용되지만 원문은 `arch/`와 `include/asm/` 밖에서 사용하지 말라고 제한합니다.

커널에서 `long long` 사용은 주의해야 합니다. GCC 생성 코드가 좋지 않을 수 있고 i386에서는 필요한 런타임 함수가 커널 환경에 없어 나눗셈과 곱셈이 동작하지 않을 수 있다고 원문이 경고합니다.

C++는 필요한 런타임 환경이 없고 커널 헤더도 검증되지 않아 일반적으로 나쁜 선택입니다. 기술적으로 가능하더라도 권장하지 않으며, 꼭 쓴다면 최소한 예외를 피해야 합니다.

`NUMif` 절은 소스 코드 중간에 전처리기 `#if`를 흩뿌리기보다 헤더나 `.c` 파일 시작 부분의 매크로로 기능 차이를 추상화하는 편이 더 깔끔하다고 권합니다.

커널 반환 관례
형태성공실패
정수 반환0음수 errno
포인터 반환정상 포인터ERR_PTR(음수 errno)

값과 포인터 양쪽에서 오류를 음수 errno로 표현합니다.

오류 포인터 도우미
도우미역할
ERR_PTR(error)음수 오류를 포인터로 인코딩
IS_ERR(ptr)오류 포인터 여부 확인
PTR_ERR(ptr)포인터에서 오류 번호 복구

오류를 포인터 반환 하나에 함께 싣는 절차입니다.

허용되는 대표 GNU C 확장
범주
형식·타입typeof, __attribute__, __func__
표현식·매크로statement expression, varargs macro
컴파일 상수__builtin_constant_p
저수준 코드arch/ 또는 include/asm/의 assembler

커널에서 흔히 보는 확장을 목적별로 묶습니다.

API 이름 변경 대응
Build breaks after renameFind mailing-list explanation
Understand semantic changeAudit each call site
Update behaviorRebuild and test

컴파일 오류를 기계적 치환으로 숨기지 않고 의미 변화를 조사합니다.

Procedure e convenzioni
=======================

Liste doppiamente concatenate ``include/linux/list.h``
------------------------------------------------------

Un tempo negli header del kernel c'erano tre gruppi di funzioni per
le liste concatenate, ma questa è stata la vincente. Se non avete particolari
necessità per una semplice lista concatenata, allora questa è una buona scelta.

In particolare, :c:func:`list_for_each_entry()` è utile.

Convenzione dei valori di ritorno
---------------------------------

Per codice chiamato in contesto utente, è molto comune sfidare le convenzioni
C e ritornare 0 in caso di successo, ed un codice di errore negativo
(eg. ``-EFAULT``) nei casi fallimentari. Questo potrebbe essere controintuitivo
a prima vista, ma è abbastanza diffuso nel kernel.

Utilizzate :c:func:`ERR_PTR()` (``include/linux/err.h``) per codificare
un numero d'errore negativo in un puntatore, e :c:func:`IS_ERR()` e
:c:func:`PTR_ERR()` per recuperarlo di nuovo: così si evita d'avere un
puntatore dedicato per il numero d'errore. Da brividi, ma in senso positivo.

Rompere la compilazione
-----------------------

Linus e gli altri sviluppatori a volte cambiano i nomi delle funzioni e
delle strutture nei kernel in sviluppo; questo non è solo per tenere
tutti sulle spine: questo riflette cambiamenti fondamentati (eg. la funzione
non può più essere chiamata con le funzioni attive, o fa controlli aggiuntivi,
o non fa più controlli che venivano fatti in precedenza). Solitamente a questo
s'accompagna un'adeguata e completa nota sulla lista di discussone
più adatta; cercate negli archivi. Solitamente eseguire una semplice
sostituzione su tutto un file rendere le cose **peggiori**.

Inizializzazione dei campi d'una struttura
------------------------------------------

Il metodo preferito per l'inizializzazione delle strutture è quello
di utilizzare gli inizializzatori designati, come definiti nello
standard ISO C99, eg::

    static struct block_device_operations opt_fops = {
            .open               = opt_open,
            .release            = opt_release,
            .ioctl              = opt_ioctl,
            .check_media_change = opt_media_change,
    };

Questo rende più facile la ricerca con grep, e rende più chiaro quale campo
viene impostato. Dovreste fare così perché si mostra meglio.

Estensioni GNU
--------------

Le estensioni GNU sono esplicitamente permesse nel kernel Linux. Da notare
che alcune delle più complesse non sono ben supportate, per via dello scarso
sviluppo, ma le seguenti sono da considerarsi la norma (per maggiori dettagli,
leggete la sezione "C Extensions" nella pagina info di GCC - Sì, davvero
la pagina info, la pagina man è solo un breve riassunto delle cose nella
pagina info).

-  Funzioni inline

-  Istruzioni in espressioni (ie. il costrutto ({ and }) ).

-  Dichiarate attributi di una funzione / variabile / tipo
   (__attribute__)

-  typeof

-  Array con lunghezza zero

-  Macro varargs

-  Aritmentica sui puntatori void

-  Inizializzatori non costanti

-  Istruzioni assembler (non al di fuori di 'arch/' e 'include/asm/')

-  Nomi delle funzioni come stringhe (__func__).

-  __builtin_constant_p()

Siate sospettosi quando utilizzate long long nel kernel, il codice generato
da gcc è orribile ed anche peggio: le divisioni e le moltiplicazioni non
funzionano sulle piattaforme i386 perché le rispettive funzioni di runtime
di GCC non sono incluse nell'ambiente del kernel.

C++
---

Solitamente utilizzare il C++ nel kernel è una cattiva idea perché
il kernel non fornisce il necessario ambiente di runtime e gli header file
non sono stati verificati. Rimane comunque possibile, ma non consigliato.
Se davvero volete usarlo, almeno evitate le eccezioni.

NUMif
-----

Viene generalmente considerato più pulito l'uso delle macro negli header file
(o all'inizio dei file .c) per astrarre funzioni piuttosto che utlizzare
l'istruzione di pre-processore \`#if' all'interno del codice sorgente.

커널에 변경을 넣기 위한 준비

754-797

공식 포함을 목표로 하거나 깔끔한 패치를 만들려면 코드 작성 외의 관리 작업도 필요합니다.

먼저 수정하는 코드의 책임자를 찾습니다. 소스 파일 상단, `MAINTAINERS`, 마지막 수단으로 `CREDITS`를 확인하고, 노력 중복이나 과거에 거절된 접근을 피하도록 관련자와 조율합니다.

새로 만들거나 크게 다룬 파일 상단에는 이름과 이메일 주소를 적습니다. 버그를 발견하거나 후속 변경을 하려는 사람이 가장 먼저 보는 위치입니다.

커널 변경에는 보통 구성 옵션이 필요합니다. 알맞은 디렉터리의 `Kconfig`를 수정하고 `Documentation/kbuild/kconfig-language.rst`의 언어 문서를 참조합니다.

구성 옵션 설명은 전문 사용자와 해당 기능을 모르는 사용자 모두에게 말해야 합니다. 호환성 문제와 위험을 언급하고, 선택을 모르는 사람을 위해 일반적으로 ‘if in doubt, say N’ 또는 드물게 Y로 끝냅니다.

`Makefile`도 수정합니다. CONFIG 변수가 내보내지므로 보통 `obj-$(CONFIG_xxx) += xxx.o` 같은 줄을 추가하며 문법은 `Documentation/kbuild/makefiles.rst`에 설명되어 있습니다.

한 파일을 넘는 주목할 만한 기여라면 `CREDITS` 추가를 고려합니다. `MAINTAINERS` 등록은 단순 공로 표시보다 훨씬 큰 약속으로, 하위 시스템 변경과 버그 때 연락받고 책임지겠다는 뜻입니다.

마지막으로 `Documentation/process/submitting-patches.rst`를 읽어 패치 제출 규칙을 따라야 합니다.

상류 제출 준비 목록
작업파일 또는 문서
책임자 확인MAINTAINERS, CREDITS, 소스 상단
구성 옵션Kconfig, kconfig-language.rst
빌드 연결Makefile, makefiles.rst
제출 형식process/submitting-patches.rst

변경을 패치로 내기 전에 확인할 관리 파일과 문서입니다.

패치 준비 순서
Find maintainerCoordinate approach
Add Kconfig and MakefileDocument user-visible option
Build and testRead submitting-patches
Send patchReview

기술 구현과 유지관리 책임을 함께 준비합니다.

Mettere le vostre cose nel kernel
=================================

Al fine d'avere le vostre cose in ordine per l'inclusione ufficiale, o
anche per avere patch pulite, c'è del lavoro amministrativo da fare:

-  Trovare chi è responsabile del codice che state modificando. Guardare in cima
   ai file sorgenti, all'interno del file ``MAINTAINERS``, ed alla fine
   di tutti nel file ``CREDITS``. Dovreste coordinarvi con queste persone
   per evitare di duplicare gli sforzi, o provare qualcosa che è già stato
   rigettato.

   Assicuratevi di mettere il vostro nome ed indirizzo email in cima a
   tutti i file che create o che maneggiate significativamente. Questo è
   il primo posto dove le persone guarderanno quando troveranno un baco,
   o quando **loro** vorranno fare una modifica.

-  Solitamente vorrete un'opzione di configurazione per la vostra modifica
   al kernel. Modificate ``Kconfig`` nella cartella giusta. Il linguaggio
   Config è facile con copia ed incolla, e c'è una completa documentazione
   nel file ``Documentation/kbuild/kconfig-language.rst``.

   Nella descrizione della vostra opzione, assicuratevi di parlare sia agli
   utenti esperti sia agli utente che non sanno nulla del vostro lavoro.
   Menzionate qui le incompatibilità ed i problemi. Chiaramente la
   descrizione deve terminare con “if in doubt, say N” (se siete in dubbio,
   dite N) (oppure, occasionalmente, \`Y'); questo è per le persone che non
   hanno idea di che cosa voi stiate parlando.

-  Modificate il file ``Makefile``: le variabili CONFIG sono esportate qui,
   quindi potete solitamente aggiungere una riga come la seguete
   "obj-$(CONFIG_xxx) += xxx.o". La sintassi è documentata nel file
   ``Documentation/kbuild/makefiles.rst``.

-  Aggiungete voi stessi in ``CREDITS`` se credete di aver fatto qualcosa di
   notevole, solitamente qualcosa che supera il singolo file (comunque il vostro
   nome dovrebbe essere all'inizio dei file sorgenti). ``MAINTAINERS`` significa
   che volete essere consultati quando vengono fatte delle modifiche ad un
   sottosistema, e quando ci sono dei bachi; questo implica molto di più di un
   semplice impegno su una parte del codice.

-  Infine, non dimenticatevi di leggere
   ``Documentation/process/submitting-patches.rst``.

커널 코드 예제 모음

798-859

‘커널의 요령’ 절은 소스에서 발견한 흥미로운 예를 모으며 다른 예의 추가도 환영합니다. 주석과 매크로는 역사적 원문이므로 표현을 정제하지 않고 코드 그대로 보존합니다.

`arch/x86/include/asm/delay.h`의 `ndelay(n)`은 `__builtin_constant_p(n)`으로 상수 여부를 컴파일 시 판별합니다. 상수이면 범위를 검사해 `__const_udelay()`를 사용하고, 아니면 `__ndelay(n)`로 보냅니다.

``arch/x86/include/asm/delay.h``::

    #define ndelay(n) (__builtin_constant_p(n) ? \
            ((n) > 20000 ? __bad_ndelay() : __const_udelay((n) * 5ul)) : \
            __ndelay(n))

`include/linux/fs.h`의 예는 커널 포인터 주소 공간의 여유를 이용해 오류 코드와 dentry 포인터를 같은 반환값에 담는 `ERR_PTR`, `PTR_ERR`, `IS_ERR` 매크로를 보여 줍니다.

``include/linux/fs.h``::

    /*
     * Kernel pointers have redundant information, so we can use a
     * scheme where we can return either an error code or a dentry
     * pointer with the same return value.
     *
     * This should be a per-architecture thing, to allow different
     * error and pointer decisions.
     */
     #define ERR_PTR(err)    ((void *)((long)(err)))
     #define PTR_ERR(ptr)    ((long)(ptr))
     #define IS_ERR(ptr)     ((unsigned long)(ptr) > (unsigned long)(-1000))

`arch/x86/include/asm/uaccess_32.h`의 `copy_to_user` 매크로도 복사 길이가 컴파일 상수인지 검사해 상수 전용 구현과 일반 구현 중 하나를 선택합니다.

``arch/x86/include/asm/uaccess_32.h:``::

    #define copy_to_user(to,from,n)                         \
            (__builtin_constant_p(n) ?                      \
             __constant_copy_to_user((to),(from),(n)) :     \
             __generic_copy_to_user((to),(from),(n)))

`arch/sparc/kernel/head.S`와 `arch/sparc/lib/checksum.S` 예는 개발자 사이의 유머러스한 주석을 보여 줍니다. 이 주석은 API 지침이 아니라 역사적 코드 예시이며 원문 전체를 그대로 유지합니다.

``arch/sparc/kernel/head.S:``::

    /*
     * Sun people can't spell worth damn. "compatability" indeed.
     * At least we *know* we can't spell, and use a spell-checker.
     */

    /* Uh, actually Linus it is I who cannot spell. Too much murky
     * Sparc assembly will do this to ya.
     */
    C_LABEL(cputypvar):
            .asciz "compatibility"

    /* Tested on SS-5, SS-10. Probably someone at Sun applied a spell-checker. */
            .align 4
    C_LABEL(cputypvar_sun4m):
            .asciz "compatible"


``arch/sparc/lib/checksum.S:``::

            /* Sun, you just can't beat me, you just can't.  Stop trying,
             * give up.  I'm serious, I am going to kick the living shit
             * out of you, game over, lights out.
             */
코드 예제의 기법
경로핵심 기법
arch/x86/include/asm/delay.h컴파일 상수에 따른 구현 선택
include/linux/fs.h오류 번호를 포인터 값으로 인코딩
arch/x86/include/asm/uaccess_32.h상수 길이 복사 최적화
arch/sparc/...역사적 어셈블리 주석

각 소스 조각이 보여 주는 커널 구현 패턴입니다.

__builtin_constant_p 최적화
Argument n__builtin_constant_p(n)?
Yesconstant-specialized implementation
Nogeneric implementation

컴파일러가 상수임을 알 때 전용 경로를 선택합니다.

Trucchetti del kernel
=====================

Dopo una rapida occhiata al codice, questi sono i preferiti. Sentitevi liberi
di aggiungerne altri.

``arch/x86/include/asm/delay.h``::

    #define ndelay(n) (__builtin_constant_p(n) ? \
            ((n) > 20000 ? __bad_ndelay() : __const_udelay((n) * 5ul)) : \
            __ndelay(n))


``include/linux/fs.h``::

    /*
     * Kernel pointers have redundant information, so we can use a
     * scheme where we can return either an error code or a dentry
     * pointer with the same return value.
     *
     * This should be a per-architecture thing, to allow different
     * error and pointer decisions.
     */
     #define ERR_PTR(err)    ((void *)((long)(err)))
     #define PTR_ERR(ptr)    ((long)(ptr))
     #define IS_ERR(ptr)     ((unsigned long)(ptr) > (unsigned long)(-1000))

``arch/x86/include/asm/uaccess_32.h:``::

    #define copy_to_user(to,from,n)                         \
            (__builtin_constant_p(n) ?                      \
             __constant_copy_to_user((to),(from),(n)) :     \
             __generic_copy_to_user((to),(from),(n)))


``arch/sparc/kernel/head.S:``::

    /*
     * Sun people can't spell worth damn. "compatability" indeed.
     * At least we *know* we can't spell, and use a spell-checker.
     */

    /* Uh, actually Linus it is I who cannot spell. Too much murky
     * Sparc assembly will do this to ya.
     */
    C_LABEL(cputypvar):
            .asciz "compatibility"

    /* Tested on SS-5, SS-10. Probably someone at Sun applied a spell-checker. */
            .align 4
    C_LABEL(cputypvar_sun4m):
            .asciz "compatible"


``arch/sparc/lib/checksum.S:``::

            /* Sun, you just can't beat me, you just can't.  Stop trying,
             * give up.  I'm serious, I am going to kick the living shit
             * out of you, game over, lights out.
             */

감사의 글

860-870

저자는 아이디어, 질문 답변, 오류 수정, 내용 추가에 기여한 Andi Kleen에게 감사합니다.

Philipp Rumpf는 맞춤법과 문장 명료성을 개선하고 쉽게 떠올리기 어려운 훌륭한 지적을 제공했습니다.

Werner Almesberger는 `disable_irq()`에 관한 좋은 요약을 제공했고, Jes Sorensen과 Andrea Arcangeli는 세부 사항을 명확히 했습니다.

Michael Elizabeth Chastain은 구성 섹션을 검증하고 내용을 추가했으며, Telsa Gwynne은 저자에게 DocBook을 가르쳤습니다.

기여자와 기여 내용
기여자기여
Andi Kleen아이디어, 답변, 수정, 내용
Philipp Rumpf맞춤법, 명료성, 핵심 지적
Werner Almesbergerdisable_irq 요약
Jes Sorensen, Andrea Arcangeli세부 설명
Michael Elizabeth Chastain구성 섹션 검증·추가
Telsa GwynneDocBook 안내

감사의 글에 기록된 도움을 정리합니다.

Ringraziamenti
==============

Ringrazio Andi Kleen per le sue idee, le risposte alle mie domande,
le correzioni dei miei errori, l'aggiunta di contenuti, eccetera.
Philipp Rumpf per l'ortografia e per aver reso più chiaro il testo, e
per alcuni eccellenti punti tutt'altro che ovvi. Werner Almesberger
per avermi fornito un ottimo riassunto di :c:func:`disable_irq()`,
e Jes Sorensen e Andrea Arcangeli per le precisazioni. Michael Elizabeth
Chastain per aver verificato ed aggiunto la sezione configurazione.
Telsa Gwynne per avermi insegnato DocBook.