内核空间内存释放冻结内核

内核空间内存释放冻结内核

我正在编写一个内核模块。从用户空间读取字节并写回。

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,虚拟机就会冻结。

您知道为什么以及我还应该做什么来诊断这个问题吗?

我的函数是否创建了太多级别的递归?

如果我写入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;
    }
}

我在主帖中使用的递归方法的另一个坏处是,它会随机跳过 kfree-ing 2 到 4 个节点。

对于任何对我的泄漏检查报告感兴趣的人:我正在使用我在 github 上发现的开源工具https://github.com/euspecter/kedr。没有任何保证,但它非常有帮助。您不需要重新编译内核。

相关内容