カーネルモジュールを作成しています。ユーザー空間からバイトを読み書きします。
static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset) {
Node *msg;
int error_count = 0;
// Entering critical section
down(&sem); //wait state
msg = pop(&l, 0);
// No message? No wait!
if(!msg) {
up(&sem);
return -EAGAIN;
}
len = msg->length;
error_count = copy_to_user(buffer, msg->string, msg->length);
if (error_count == 0) {
current_size -= msg->length;
remove_element(&l, 0);
up(&sem);
return 0;
} else {
up(&sem);
printk(KERN_INFO "opsysmem: Failed to send %d characters to the user\n", error_count);
return -EFAULT; // Failed -- return a bad address message (i.e. -14)
}
}
static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset) {
Node *n;
// buffer larger than 2 * 1024 bytes
if(len > MAX_MESSAGE_SIZE || len == 0) {
return -EINVAL;
}
n = kmalloc(sizeof(Node), GFP_KERNEL);
if(!n) {
return -EAGAIN;
}
n->string = (char*) kmalloc(len, GFP_KERNEL);
n->length = len;
copy_from_user(n->string, buffer, len);
// Enter critical section
down(&sem); //wait state
// buffer is larger than the total list memory (2MiB)
if(current_size + len > MAX_LIST_SIZE) {
up(&sem);
return -EAGAIN;
}
current_size += len;
push(&l, n);
up(&sem);
// Exit critical section
return len;
}
リンクリストを解放する必要がある関数を削除します。
static void __exit opsysmem_exit(void) {
// Deallocate the list of messages
down(&sem);
destroy(&l);
up(&sem);
device_destroy(opsysmemClass, MKDEV(majorNumber, 0)); // remove the device
class_unregister(opsysmemClass); // unregister the device class
class_destroy(opsysmemClass); // remove the device class
unregister_chrdev(majorNumber, DEVICE_NAME); // unregister the major number
printk(KERN_INFO "charDeviceDriver: Goodbye from the LKM!\n");
}
私の接続リストと破壊機能は次のとおりです。
static void destroyNode(Node *n) {
if(n) {
destroyNode(n->next);
kfree(n->string);
n->string = NULL;
kfree(n);
n = NULL;
}
}
static void destroy(list *l){
if(l) {
destroyNode(l->node);
}
}
typedef struct Node {
unsigned int length;
char* string;
struct Node *next;
} Node;
typedef struct list{
struct Node *node;
} list;
質問は次のとおりです。
rmmod
私はデバイスドライバを作成し、ドライバとopsysmem_exit
kfree()がすべてのメモリから呼び出されることを望みます。
これは、ノード数が少ない場合に機能します。
多数のノード(1000以上)を実行してrmmodeを使用しようとすると、VMはフリーズします。
この問題を診断するためになぜしなければならないのか、そして何をすべきかを知っていますか?
私の関数はあまりにも多くのレベルの再帰を生成しますか?
2000000個のノードに書き込んで再度読んでみると問題ないようです。 rmmodを実行するときにリストが空の場合、すべてがうまく機能します。
編集1:メモリを解放せずにrmmodを実行すると、カーネルがクラッシュしないことがわかりました。ただし、以下のように割り当てられたメモリはすべて漏れます。ケデル
答え1
今解決しました。マレージョンソンは正しいです。私の核心を殺したのは再帰でした。
これを学ぶのに7時間かかる理由を説明できる人はいますか?実際、Cの最大再帰深度はいくらですか?今朝523756という記事を読んだ。ここで読みました。 Cまで下にスクロールします。。
これが私の割り当て解除者です。ご存知のように、漏れはまったくありません。
static void destroy2(list *l) {
Node *_current = l->node;
Node *_next;
while(_current) {
_next = _current->next;
kfree(_current->string);
kfree(_current);
_current = _next;
}
}
私がメインポストで使用した再帰的なアプローチのもう一つの悪いことは、kfreeing 2〜4ノードをランダムにスキップしたことです。
私の漏れ検査レポートに興味がある人のために:私はgithubで見つけたオープンソースツールを使用しています。https://github.com/euspecter/kedr。保証することはできませんが、非常に役立ちます。カーネルを再コンパイルする必要はありません。