43
contributi
Riga 9: | Riga 9: | ||
===Funzioni disponibili per i moduli=== | ===Funzioni disponibili per i moduli=== | ||
I programmatori usano funzioni che non definiscono tutte le volte. Un primo esempio è <code>printf()</code>. Utilizzi queste funzioni messe a disposizione dalla libreria standard del C, libc. Le definizioni di queste funzioni non vengono inserite nel tuo programma fino al momento del linking, che assicura che il codice (per la <code>printf()</code> ad esempio) è disponibile, e aggiusta le chiamate alla funzione in modo da puntare al codice. | |||
I moduli del kernel sono differenti in questo. Nell'esempio "Ciao Mondo", potresti aver notato che abbiamo usato una funzione, <code>printk()</code>, ma non abbiamo incluso una libreria di I/O. Questo perchè i moduli sono file oggetti i cui simboli sono risolti quando si carica il modulo con <code>insmod</code>. Le definizioni dei simboli arrivano dal kernel stesso; le uniche funzioni esterne che puoi usare sono quelle messe a disposizione dal kernel. Se sei curioso sui simboli che sono esportati dal tuo kernel, dai uno sguardo a <code>/proc/kallsyms</code>. | |||
Un punto da tenere a mente è la differenza tra funzioni di libreria e chiamate di sistema. Le funzioni di libreria sono di alto livello, vengono eseguite ad user space e mettono a disposizione una comoda interfaccia, per il programmatore, alle funzioni che fanno il vero lavoro---le chiamate di sistema (syscall). Le syscall vengono eseguite in kernel mode per conto dell'utente e sono messe a disposizione dal kernel stesso. La funzione di libreria <code>printf()</code> può sembrare una funzione di stampa generica, ma tutto quello che fa è formattare i dati in stringhe e scriverle usando una syscall di basso livello, <code>write()</code>, che poi invia i dati allo standard output. | |||
Vuoi vedere da quali syscall è fatta <code>printf()</code>? E' facile! Compila il seguente programma: | |||
<pre> | |||
#include <stdio.h> | |||
int main(void) | |||
{ printf("hello"); return 0; } | |||
</pre> | |||
con <b>gcc -Wall -o hello hello.c</b>. Esegui il programma con <b>strace ./hello</b>. Sei impressionato? Ogni linea che vedi corrisponde ad una syscall. strace[4] è un comodo programma che ti dà i dettagli su quali syscall il programma chiama, come una chiamata è fatta, quali sono gli argomenti e cosa ritorna. E' uno strumento inestimabile per capire cose come a quali file un programma sta provando ad accedere. Verso la fine, vedrai una linea come <code>write(1, "hello", 5hello)</code>. Questo è. La faccia dietro la maschera <code>printf()</code>. Potresti non essere familiare con <code>write()</code> poichè molte persone usano le funzioni di libreria per le operazioni di I/O su file (come <code>fopen</code>,<code>fputs</code>, <code>fclose</code>). Se questo è il caso, prova a dare uno sguardo a <b>man 2 write</b>. La seconda sezione del man è dedicata alle chiamate di sistema (come <code>kill()</code> e <code>read()</code>). La terza sezione del manuale è dedicata alle chiamate di libreria, alle quali probabilmente siete molto più abituati (come <code>cosh()</code> e <code>random()</code>). | |||
===User Space vs Kernel Space=== | ===User Space vs Kernel Space=== |
contributi