カーネル空間メモリのリリースによりカーネルがフリーズ

カーネル空間メモリのリリースによりカーネルがフリーズ

カーネルモジュールを作成しています。ユーザー空間からバイトを読み書きします。

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_exitkfree()がすべてのメモリから呼び出されることを望みます。

これは、ノード数が少ない場合に機能します。

多数のノード(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。保証することはできませんが、非常に役立ちます。カーネルを再コンパイルする必要はありません。

関連情報