2010-11-06 79 views
2

我正在为linux内核模块编写代码,并且遇到了一个奇怪的行为。 这里是我的代码:在linux内核模块中printk的奇怪行为

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

} 

基本上我试图等待线程完成,然后打印后的东西。 上述代码确实实现了该目标,但未注释WITH "printk("debug\n");"。只要我注释掉printk("debug\n");以在没有调试的情况下运行代码并通过insmod命令加载模块,模块就会挂起,看起来它在递归中会丢失。我不明白为什么printk会以这么大的方式影响我的代码?

任何帮助,将不胜感激。

关于。

回答

1

由于删除了编译器优化循环到while (1);调用printk()。当您将呼叫添加到printk()时,编译器不确定data未更改,因此每次都通过循环检查值。

您可以在循环中插入一个障碍,这会强制编译器在每次迭代中重新评估data。例如:

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

     barrier(); 
} 
0

也许数据应该被声明为volatile?这可能是编译器不会去内存中获取循环中的数据。

+1

volatile会有些作用,但它总是一个坏主意,因为你不能确保data ++是一个原子指令。在多线程争取同一个变量的多处理器系统上,这会给你一个有保证的竞争条件。 – 2010-11-06 13:18:14

+0

伟大的一点。这里不要使用volatile。需要更多的咖啡。 ;-) – 2010-11-06 13:22:29

4

您不同步对数据变量的访问。会发生什么是编译器会产生一个无限循环。原因如下:

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

编译器可以检测到数据的值在while循环内永远不会改变。因此,它可以完全移动退房循环,你会用一个简单的

while (1) {} 

最终如果你插入printk编译器必须假定全局变量的数据可能会改变(毕竟 - 编译器不知道具体做什么的printk),因此您的代码将再次开始工作(在不确定的行为的一种方式。)

如何解决这个问题:

使用正确的线程同步原语。如果将对数据的访问包装到由互斥锁保护的代码段中,代码将起作用。您也可以替换可变数据并使用计数的信号量。

编辑:

此链接介绍如何在Linux内核锁定的工作方式:

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

0

Nils Pipenbrinck的回答是现货。我只是添加一些指针。

Rusty's Unreliable Guide to Kernel Locking(每个内核黑客都应该读这个)。
Goodbye semaphores?The mutex APIlwn.net关于新的互斥API的文章在2006年初推出,之前Linux内核使用信号量作为互斥体)。另外,由于您的共享数据是一个简单的计数器,因此您可以使用原子API(基本上,将计数器声明为atomic_t并使用atomic_ *函数访问它)。

0

挥发性可能并不总是“坏主意”。需要分离出当需要易失性时和需要互斥机制时的情况。当一个人使用或误用另一个机制时,它不是最佳的。在上述情况下。我建议 最佳的解决方案,这两个机制是必要的:互斥到 提供互斥,易失性向编译器表明 “信息”必须从硬件新鲜读取。否则,在某些 的情况下(优化-O2,-O3),编译器可能会在不经意间 遗漏所需的代码。