linux内核模块中printk的奇怪行为
-
29-09-2019 - |
题
我正在为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如此重要的方式影响我的代码?
任何帮助,将不胜感激。
问候。
解决方案
呼叫 printk()
删除编译器正在将循环优化到 while (1);
. 。当您将电话添加到 printk()
编译器不确定 data
没有更改,因此每次通过循环检查值。
您可以将屏障插入循环中,这迫使编译器重新评估 data
每次迭代。例如:
while (1) {
if (data >= 2) {
kthread_stop(t1);
kthread_stop(t2);
break;
}
barrier();
}
其他提示
您不会同步对数据变量的访问。发生的事情是,编译器将生成无限的循环。这就是为什么:
while( 1 )
{
if( data >= 2 )
{
kthread_stop(t1);
kthread_stop(t2);
break;
}
}
编译器可以检测到数据的值在当时循环内永远不会变化。因此,它可以完全将签到循环移出,最终会以简单的
while (1) {}
如果您插入PRONTK,编译器必须假设全局变量数据可能会更改(毕竟 - 编译器不知道Printk详细介绍什么),因此您的代码将再次开始工作(以未定义的行为方式类型。
如何解决此问题:
使用适当的线程同步原语。如果将对数据的访问包装到由MUTEX保护的代码部分中,则代码将起作用。您也可以替换变量数据并使用计数信号量。
编辑:
此链接说明了Linux-Kernel中的锁定方式:
http://www.linuxgrill.com/anonymous/fire/netfilter/kernel-hacking-howto-5.html
也许应该声明数据挥发性?可能是编译器不会进入内存以获取循环中的数据。
Nils Pipenbrinck的答案是现实的。我只添加一些指针。
Rusty的内核锁定指南 (每个内核黑客都应该阅读此内容)。
再见信号?, Mutex API (lwn.net 在2006年初推出的新的Mutex API上的文章之前,Linux内核将信号量用作静音词)。
另外,由于您的共享数据是一个简单的计数器,因此您只需使用原子API(基本上,将计数器声明为Atomic_t并使用Atomic_*函数访问它)。
波动可能并不总是“坏主意”。需要分离需要何时需要挥发性以及需要何时需要相互排斥机制的情况。当一个人将一种机制用于另一种机制时,这不是最佳的。在上述情况下。我建议使用最佳解决方案,需要这两种机制:Mutex提供相互排除,挥发性地表明编译器必须从硬件中读取“信息”。否则,在某些情况下(优化-O2,-O3),编译器可能会无意中忽略所需的代码。