Linuxカーネルモジュールの作成中に、セマフォがロック解除されるのを待つ間に起きることができないkthreadに関する問題に直面しました。これにより、モジュールをアンロードしようとするとスレッドが止まらなくなり、rmmod
がフリーズします。Linuxカーネル - kthreadがセマフォーを待つのを止めるには?
ご注意:注:このモジュールは3.10カーネルで動作します。これを新しいバージョン(3.10カーネルを搭載した在庫CentOS 7で稼動する顧客の要求)に更新する方法はありません。
以下は、モジュールのソースコードから興味深い部分です。シンプルなプロデューサの消費者問題です。リストのサイズに制限はなく(したがって、プロデューサセマフォは不要です)、ミューテックスによって保護されています。リストから何かを取る機能は、プロデューサによってアップされ、消費者が落としたセマフォによって保護されます。プロデューサ関数は、このコードスニペットには表示されていない外部イベント(実際にはcharデバイス)から呼び出され、できるだけ小さくします。モジュールのアンロードを除いて、プロセスは完全に機能します。
フリーズの原因となる部分には、コードスニペットにコメントが付きます。私がkthreadを停止するために知っている唯一の方法は、kthread_stop
を呼び出すことです。この場合は、スリープしているスレッドを目覚めることができないため、失敗します。スレッドが終了するのを待つので、呼び出しは返されず、モジュールはアンロードされません。
セマフォがモジュールを正常にアンロードするのを待つkthreadを起動して停止するにはどうすればよいですか?
List実装:
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/semaphore.h>
static LIST_HEAD(list);
DEFINE_MUTEX(list_lock);
DEFINE_SEMAPHORE(sem_list_consumer);
void add_to_list(struct *some_struct) {
int rv = mutex_lock_interruptible(&list_lock);
if(rv != 0) {
return;
}
list_add(&some_struct->list, &list);
mutex_unlock(&list_lock);
up(&sem_list_consumer);
}
struct some_struct * take_from_list() {
int rv;
some_struct *entry;
/* this is where the kthread will freeze when module is unloaded */
rv = down_interruptible(&sem_list_consumer);
if(rv != 0) {
return NULL;
}
rv = mutex_lock_interruptible(&list_lock);
if(rv != 0) {
up(&sem_list_consumer);
return NULL;
}
if (list_empty(&list)) {
mutex_unlock(&list_lock);
return NULL;
} else {
entry = list_last_entry(&list, struct some_struct, list);
if (entry) {
list_del(&entry->list);
}
}
mutex_unlock(&list_lock);
return entry;
}
消費者kthread実装:
#include <linux/kthread.h>
#include <linux/sched.h>
int consumer_kthread(void *data) {
struct some_struct *entry;
set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
/* Here the function including the semaphore is called */
entry = take_from_list();
if(entry != NULL) {
/* Do something with 'entry' here */
} else {
/* Some handling of returned NULL pointers */
}
set_current_state(TASK_INTERRUPTIBLE);
}
set_current_state(TASK_RUNNING);
return 0;
}
モジュールの実装:
#include <linux/init.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/sched.h>
static struct task_struct *consumer_task;
static int __init initModule(void) {
consumer_task = kthread_run(consumer_kthread, NULL, "list-consumer");
return 0;
}
static void __exit exitModule(void) {
/* this call will cause rmmod to freeze forever */
kthread_stop(consumer_task);
}
module_init(initModule);
module_exit(exitModule);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("My Module");
'take_from_list'関数には、' NULL'を返す場所が3つあります。これらの3つの場所のうちの1つでは、 'sem_list_consumer'セマフォをまだ保持しています。私はこの矛盾があなたの問題と関係していると考えています。さもなければ、呼び出し元はセマフォを解放する必要があるかどうかをどのように伝えることができますか? –
これは後で問題を引き起こす可能性がありますが、プロデューサコードを呼び出すことなくモジュールをロードしたりアンロードしたりするだけで問題が発生します。また、問題が発生するのは、何かがkthreadを中断/起動する場合にのみ発生しますが、これは私が達成できないものです。 – marandus
あなたはすでにプロデューサコードが問題ではないことを確認しているので、エラーはコンシューマコードにあります。あなたの 'take_from_list'関数で、' mutex_unlock(&list_lock);と 'return NULL;'の行の間に 'up(&sem_list_consumer);を呼び出すだけでいいのではないかと思われます。これは、 'take_from_list'が' NULL'を返すときでさえ、 'sem_list_consumer'セマフォーが保持されることを期待しない限りです。この場合、セマフォーを保持せずに' take_from_list'をどういうわけか 'NULL'を返す必要があります。 –