2

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 が私のコードにそれほど大きな影響を与えるのはなぜですか?

どんな助けでも大歓迎です。

よろしく。

4

5 に答える 5

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

于 2010-11-06T13:11:07.687 に答える
1

への呼び出しをprintk()削除すると、コンパイラはループを に最適化しますwhile (1);printk()コンパイラへの呼び出しを追加すると、それdataが変更されていないことがわからないため、ループのたびに値をチェックします。

ループにバリアを挿入できます。これにより、コンパイラdataは各反復で再評価を強制されます。例えば:

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

        barrier();
}
于 2010-11-06T13:43:38.190 に答える
0

ニルス・ピペンブリンクの答えは的を射ている。ポインタをいくつか追加します。

Rustyのカーネルロックに関する信頼性の低いガイド(すべてのカーネルハッカーはこれを読む必要があります)。
さようならセマフォ?ミューテックスAPI(Linuxカーネルがセマフォをミューテックスとして使用する前の2006年初頭に導入された新しいミューテックスAPIに関するlwn.netの記事)。

また、共有データは単純なカウンターであるため、アトミックAPIを使用できます(基本的に、カウンターをatomic_tとして宣言し、atomic_ *関数を使用してアクセスします)。

于 2010-11-06T15:11:17.043 に答える
0

揮発性は必ずしも「悪い考え」ではないかもしれません。揮発性が必要な場合と相互排除メカニズムが必要な場合を区別する必要があります。一方のメカニズムをもう一方のメカニズムに使用または誤用する場合は、最適ではありません。上記の場合。最適な解決策として、両方のメカニズムが必要であることをお勧めします。相互排除を提供するミューテックス、「情報」をハードウェアから新しく読み取る必要があることをコンパイラーに示す揮発性です。そうしないと、状況によっては(最適化-O2、-O3)、コンパイラーが必要なコードを誤って省略してしまう可能性があります。

于 2011-02-11T00:29:46.623 に答える
0

多分データは揮発性と宣言されるべきですか?コンパイラがループ内のデータを取得するためにメモリにアクセスしていない可能性があります。

于 2010-11-06T13:06:03.007 に答える