3

ローダブル モジュールに 2 つの Linux カーネル スレッドを作成し、それらをデュアル コア Android デバイスで実行される個別の CPU コアにバインドします。これを数回実行した後、HW ウォッチドッグ タイマーがリセットされてデバイスが再起動することに気付きました。私は一貫して問題にぶつかりました。デッドロックの原因は何ですか?

基本的に、私がする必要があるのは、両方のスレッドが do_something() を別のコアで同時に実行することであり、誰も CPU サイクルを盗むことはありません (つまり、割り込みが無効になっています)。これにはスピンロックと揮発性変数を使用しています。親スレッドが子スレッドを待機するためのセマフォもあります。

#define CPU_COUNT 2

/* Globals */
spinlock_t lock;
struct semaphore sem;
volatile unsigned long count;

/* Thread util function for binding the thread to CPU*/
struct task_struct* thread_init(kthread_fn fn, void* data, int cpu)
{
    struct task_struct *ts;

    ts=kthread_create(fn, data, "per_cpu_thread");
    kthread_bind(ts, cpu);
    if (!IS_ERR(ts)) {
        wake_up_process(ts);
    }
    else {
        ERR("Failed to bind thread to CPU %d\n", cpu);
    }
    return ts;
}

/* Sync both threads */
void thread_sync()
{   
    spin_lock(&lock);
    ++count;
    spin_unlock(&lock); 

    while (count != CPU_COUNT);
}

void do_something()
{
}

/* Child thread */
int per_cpu_thread_fn(void* data)
{
    int i = 0;
    unsigned long flags = 0;
    int cpu = smp_processor_id();

    DBG("per_cpu_thread entering (cpu:%d)...\n", cpu);

    /* Disable local interrupts */
    local_irq_save(flags);

    /* sync threads */
    thread_sync();

    /* Do something */
    do_something();

    /* Enable interrupts */
    local_irq_restore(flags);

    /* Notify parent about exit */
    up(&sem);
    DBG("per_cpu_thread exiting (cpu:%d)...\n", cpu);
    return value;
}

/* Main thread */
int main_thread()
{
    int cpuB;
    int cpu = smp_processor_id();
    unsigned long flags = 0;

    DBG("main thread running (cpu:%d)...\n", cpu);

    /* Init globals*/
    sema_init(&sem, 0);
    spin_lock_init(&lock);
    count = 0;

    /* Launch child thread and bind to the other CPU core */
    if (cpu == 0) cpuB = 1; else cpuB = 0;        
    thread_init(per_cpu_thread_fn, NULL, cpuB);

    /* Disable local interrupts */
    local_irq_save(flags);

    /* thread sync */
    thread_sync();

    /* Do something here */
    do_something();

    /* Enable interrupts */
    local_irq_restore(flags);

    /* Wait for child to join */
    DBG("main thread waiting for all child threads to finish ...\n");
    down_interruptible(&sem);
}
4

1 に答える 1

0

よくわかりませんが、これが本当の理由ですが、コードに重大なエラーが含まれています。

最初while (count != CPU_COUNT);. 読み取りがアトミックでない限り、ロックを保持せずに共有変数を読み取ってはなりません。でcountあるとは限りません。

count読み取りをロックで保護する必要があります。while ループを次のように置き換えることができます。

unsigned long local_count;
do {
    spin_lock(&lock);
    local_count = count;
    spin_unlock(&lock);
} while (local_count != CPU_COUNT);

または、アトミック型を使用することもできます。ロックの不在に注意してください

atomic_t count = ATOMIC_INIT(0);

...

void thread_sync() {
    atomic_inc(&count);
    while (atomic_read(&count) != CPU_COUNT);
}

割り込みに関する2 番目の問題。あなたは自分が何をしているのか理解していないと思います。

local_irq_save()割り込みを保存して無効にします。次に、 で再び割り込みを無効にしlocal_irq_disable()ます。いくつかの作業の後、 で以前の状態を復元しlocal_irq_restore()、 で割り込みを有効にしlocal_irq_enable()ます。この有効化は完全に間違っています。以前の状態に関係なく、割り込みを有効にします。

3番目の問題。メイン スレッドが CPU にバインドされていsmp_processor_id()ない場合は、CPU 番号を取得した直後にカーネルが再スケジュールしないことが確実でない限り、使用しないでください。を使用することをお勧めしget_cpu()ます。これは、カーネルのプリエンプションを無効にしてから、CPU ID を返します。完了したら、 を呼び出しますput_cpu()

ただし、 を呼び出すとget_cpu()、これは他のスレッドを作成して実行するためのバグです。そのため、メイン スレッドのアフィニティを設定する必要があります。

4番目local_irq_save()local_irq_restore()のポインターではなく、変数を取るマクロunsigned long。(ポインタを渡すときにエラーといくつかの警告が表示されました。コードをどのようにコンパイルしたのだろうか)。参照を削除

最終的なコードは、http: //pastebin.com/Ven6wqWfから入手できます。

于 2013-08-03T01:07:01.550 に答える