printk在linux内核模块中的奇怪行为

时间:2010-11-06 12:59:54

标签: multithreading linux-kernel 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");

 }

基本上我试图等待线程完成然后在那之后打印一些东西。 上面的代码确实实现了该目标,但没有注释WITH "printk("debug\n");"。一旦我注释printk("debug\n");运行代码而不调试并通过insmod命令加载模块,模块就会挂起,看起来它在递归时会丢失。我不知道为什么printk会以如此大的方式影响我的代码?

任何帮助都将不胜感激。

问候。

5 个答案:

答案 0 :(得分:4)

您没有同步对数据变量的访问权限。会发生什么,编译器将生成无限循环。原因如下:

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

编译器可以检测到数据的值在while循环中永远不会发生变化。因此,它可以完全将支票移出循环,你最终会得到一个简单的

 while (1) {} 

如果你插入printk,编译器必须假设全局变量数据可能会改变(毕竟 - 编译器根本不知道printk做了什么)因此你的代码将再次开始工作(以未定义的行为方式) ..)

如何解决这个问题:

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

修改

此链接解释了linux-kernel中的锁定如何工作:

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

答案 1 :(得分:1)

删除了对printk()的调用后,编译器正在优化循环到while (1);。当您将调用添加到printk()时,编译器不确定data是否未更改,因此每次循环都会检查该值。

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

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

        barrier();
}

答案 2 :(得分:0)

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

答案 3 :(得分:0)

Nils Pipenbrinck的答案很明显。我只想添加一些指示。

Rusty's Unreliable Guide to Kernel Locking(每个内核黑客都应该阅读此内容)。 的Goodbye semaphores?The mutex APIlwn.net篇关于2006年初推出的新互斥API的文章,之前Linux内核使用信号量作为互斥量。)

此外,由于您的共享数据是一个简单的计数器,您只需使用原子API(基本上,将您的计数器声明为atomic_t并使用atomic_ *函数访问它)。

答案 4 :(得分:0)

易变性可能并不总是“坏主意”。一个人需要分开 需要挥发性的时候和互相排斥时的情况 机制是必要的。当使用或误用时,它是非最佳的 另一种机制。在上面的例子中。我会建议 为了获得最佳解决方案,需要两种机制:互斥到 提供互斥,volatile以指示编译器 必须从硬件中读取“info”。否则,在一些 情况(优化-O2,-O3),编译器可能会无意中发生 遗漏所需的代码。