Strano comportamento di printk in Linux Kernel Module
-
29-09-2019 - |
Domanda
Sto scrivendo un codice per il kernel Linux del modulo e di sperimentare un comportamento strano in esso. Ecco il mio codice:
int data = 0;
void threadfn1()
{
int j;
for( j = 0; j < 10; j++ )
printk(KERN_INFO "I AM THREAD 1 %d\n",j);
data++;
}
void threadfn2()
{
int j;
for( j = 0; j < 10; j++ )
printk(KERN_INFO "I AM THREAD 2 %d\n",j);
data++;
}
static int __init abc_init(void)
{
struct task_struct *t1 = kthread_run(threadfn1, NULL, "thread1");
struct task_struct *t2 = kthread_run(threadfn2, NULL, "thread2");
while( 1 )
{
printk("debug\n"); // runs ok
if( data >= 2 )
{
kthread_stop(t1);
kthread_stop(t2);
break;
}
}
printk(KERN_INFO "HELLO WORLD\n");
}
In fondo stavo cercando di aspettare per i thread per finire e quindi stampare qualcosa dopo.
Il codice di cui sopra fa raggiungere tale obiettivo, ma CON "printk("debug\n");"
non ha commentato. Appena io commento printk("debug\n");
per eseguire il codice senza il debug e caricare il modulo attraverso il comando di insmod, si blocca sul modulo e sembra che si perde nella ricorsione. I dont perché effetti il ??mio codice in un grande modo così printk?
Qualsiasi aiuto sarebbe apprezzato.
saluti.
Soluzione
Con la chiamata printk()
rimosso il compilatore ottimizza il loop in while (1);
. Quando si aggiunge la chiamata a printk()
il compilatore non è sicuro che data
non è cambiato e così controlla il valore di ogni iterazione del ciclo.
È possibile inserire una barriera nel circuito, che costringe il compilatore di rivalutare data
ad ogni iterazione. ad esempio:
while (1) {
if (data >= 2) {
kthread_stop(t1);
kthread_stop(t2);
break;
}
barrier();
}
Altri suggerimenti
Lei non è la sincronizzazione l'accesso alla variabile di dati. Quello che succede è che il compilatore genererà un ciclo infinito. Ecco perché:
while( 1 )
{
if( data >= 2 )
{
kthread_stop(t1);
kthread_stop(t2);
break;
}
}
Il compilatore in grado di rilevare che il valore dei dati non cambia mai all'interno del ciclo while. Pertanto si può muovere completamente il controllo fuori dal giro e vi ritroverete con un semplice
while (1) {}
Se si inserisce printk il compilatore deve assumere che i dati delle variabili globali possono cambiare (dopo tutto - il compilatore ha idea di cosa printk fa in dettaglio), pertanto il codice inizierà a lavorare di nuovo (in una sorta comportamento indefinito di modo ..)
Come risolvere questo problema:
Usa primitive di sincronizzazione corretta filetto. Se si avvolgono l'accesso ai dati in una sezione di codice protetto da un mutex il codice funzionerà. Si potrebbe anche sostituire i dati variabili e utilizzare un contato semaforo posto.
Modifica
Questo legame spiega come il blocco nelle opere linux-kernel:
http://www.linuxgrill.com/ anonimo / incendio / netfilter / kernel-hacking-HOWTO-5.html
Forse i dati dovrebbe essere dichiarato volatili? Potrebbe essere che il compilatore non sta per memoria per ottenere i dati nel circuito.
La risposta di Nils Pipenbrinck è a posto. Io solo aggiungere alcune indicazioni.
di Rusty inaffidabile Guida al Kernel di bloccaggio (ogni kernel hacker, dovrebbe leggere questo).
Addio semafori? , il mutex API ( lwn.net articoli sulla nuova API mutex introdotto nei primi mesi del 2006, prima che il kernel Linux semafori come mutex) utilizzato.
Inoltre, dal momento che i dati condivisi è un semplice contatore, si può semplicemente utilizzare l'API atomica (in pratica, dichiarare il vostro contatore come atomic_t e accedervi utilizzando atomic_ * funzioni).
potrebbe volatile non sempre essere "cattiva idea". Uno ha bisogno di separare il caso di quando volatili è necessario e quando mutua esclusione è necessario meccanismo. E non è ottimale quando si usa o abusi un meccanismo per l'altro. Nel caso di cui sopra. io suggerirei per soluzione ottimale, che entrambi i meccanismi sono necessari: mutex fornire mutua esclusione, volatile per indicare al compilatore "Info" deve essere fresco lettura dall'hardware. In caso contrario, in qualche situazione (ottimizzazione -O2, -O3), compilatori potrebbe inavvertitamente lasciare fuori i codici necessari.