2010-11-06 1 views
2

Я пишу код для модуля ядра Linux и испытываю странное поведение в нем. Вот мой код:Странное поведение printk в модуле ядра linux

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"); 

} 

В основном я пытался ждать нити, чтобы закончить, а затем напечатать что-то после этого. Вышеупомянутый код действительно достигает этой цели, но с "printk("debug\n");" не комментируется. Как только я прокомментирую printk("debug\n");, чтобы запустить код без отладки и загрузить модуль через команду insmod, модуль зависает, и кажется, что он потерялся в рекурсии. Я не понимаю, почему printk влияет на мой код таким большим образом?

Любая помощь будет оценена по достоинству.

Отношения.

ответ

1

С вызовом printk() удалить компилятор оптимизирует цикл в while (1);. Когда вы добавляете вызов в printk(), компилятор не уверен, что data не изменен и поэтому каждый раз проверяет значение в цикле.

Вы можете вставить барьер в цикл, который заставляет компилятор пересматривать data на каждой итерации. например:

while (1) { 
     if (data >= 2) { 
       kthread_stop(t1); 
       kthread_stop(t2); 
       break; 
     } 

     barrier(); 
} 
0

Возможно, данные должны быть объявлены нестабильными? Может случиться так, что компилятор не перейдет в память, чтобы получить данные в цикле.

+1

volatile будет работать, но это всегда плохая идея, потому что вы не можете убедиться, что данные ++ являются атомарной инструкцией. В многопроцессорной системе с несколькими потоками, борющимися за одну и ту же переменную, это даст вам гарантированное условие гонки. –

+0

Отличный пункт. Не используйте волатильность здесь. Вам нужно больше кофе. ;-) –

4

Вы не синхронизируете доступ к переменной данных. Случается, что компилятор будет генерировать бесконечный цикл. Вот почему:

while(1) 
     { 
      if(data >= 2) 
      { 
       kthread_stop(t1); 
       kthread_stop(t2); 
       break; 
      } 
     } 

Компилятор может обнаружить, что значение данных никогда не изменяется в цикле while. Поэтому он может полностью переместить проверку из цикла, и вы будете в конечном итоге с помощью простого

while (1) {} 

Если вставить printk компилятор должен предположить, что глобальные переменные данные могут измениться (в конце концов - компилятор имеет не знаю, что printk делает подробно), поэтому ваш код будет снова начать работать (в неопределенном поведении вид пути ..)

Как это исправить:

Используйте соответствующую нить примитивы синхронизации. Если вы закроете доступ к данным в секцию кода, защищенную мьютексом, код будет работать. Вы также можете заменить переменные данные и вместо этого использовать подсчитанный семафор.

Edit:

Эта ссылка объясняет, как блокировка в Linux-ядро работает:

http://www.linuxgrill.com/anonymous/fire/netfilter/kernel-hacking-HOWTO-5.html

0

Ответ Нильса Пипбенбринка на место. Я просто добавлю несколько указателей.

Rusty's Unreliable Guide to Kernel Locking (каждый хакер должен читать этот).
Goodbye semaphores?, The mutex API (lwn.net статей о новом API-интерфейсе mutex, введенном в начале 2006 года, до этого ядро ​​Linux использовало семафоры как мьютексы).

Кроме того, поскольку ваши общие данные являются простым счетчиком, вы можете просто использовать атомный API (в основном, объявить свой счетчик как atomic_t и получить доступ к нему с помощью функций atom_ *).

0

Летучий не всегда может быть «плохой идеей». Нужно отделить случай, когда требуется летучесть, и когда необходим механизм взаимного исключения . Это не оптимально, если использовать или злоупотреблять один механизм для другого. В приведенном выше случае. Я бы предложил для оптимального решения, что оба механизма необходимы: mutex to обеспечивают взаимное исключение, volatile, чтобы указать компилятору, что «информация» должна быть прочитана свежей с аппаратного обеспечения. В противном случае, в некоторой ситуации (оптимизация -O2, -O3) компиляторы могут непреднамеренно оставить необходимые коды.

 Смежные вопросы

  • Нет связанных вопросов^_^