You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多线程模拟du命令程序内存泄漏排查求助

多线程模拟du命令程序的内存泄漏修复

我是编程新手,目前正在编写一个多线程模拟du命令的程序。程序运行正常,但存在内存泄漏问题,不知该如何修复。

以下是出现问题的函数:

void addSubdirectories(const char *basePath) {
    linkedlist *stack = linkedlist_empty();
    if (stack == NULL) {
        return; // 内存分配失败则退出函数
    }

    searching((char *)basePath, &dir_lock);
    linkedlist_insert(linkedlist_first(stack), strdup(basePath)); // 将基准路径插入链表

    while (!linkedlist_isEmpty(stack)) {
        char *currentDir = strdup(linkedlist_inspect(linkedlist_first(stack)));
        if (currentDir == NULL) {
            break; // 内存分配失败则退出循环
        }
        
        linkedlist_remove(linkedlist_first(stack));
        DIR *dir = opendir(currentDir);        
        if (dir == NULL) {
            free(currentDir);
            continue;
        }

        struct dirent *entry;
        while ((entry = readdir(dir)) != NULL) {
            if (entry->d_type == DT_DIR) {
                if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
                    char subDirPath[MAX_PATH_LENGTH];
                    snprintf(subDirPath, sizeof(subDirPath), "%s/%s", currentDir, entry->d_name);
                    // 将子目录加入链表待遍历
                    searching(subDirPath, &dir_lock);
                    linkedlist_insert(linkedlist_first(stack), strdup(subDirPath)); // 将子目录插入栈
                }
            }
        }
        closedir(dir);
        free(currentDir); // 从链表移除后释放currentDir内存
    }
    
    linkedlist_free(stack);
}

我使用Valgrind定位到了泄漏点,但所有释放内存的尝试都失败了,检测结果如下:

10,982 bytes in 500 blocks are definitely lost in loss record 10 of 10
at 0x48407B4: malloc (vg_replace_malloc.c:381)
by 0x49227F9: strdup (strdup.c:42)
by 0x10A25B: addSubdirectories (.c:329)
by 0x109734: main (.c:28)

补充使用的链表实现代码:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct linkedlist_node {
    void *data;
    struct linkedlist_node *next;
    struct linkedlist_node *prev;
} linkedlist_node;

typedef struct {
    linkedlist_node *first;
    linkedlist_node *last;
} linkedlist;

typedef struct {
    linkedlist *list;
    linkedlist_node *node;
} linkedlist_pos;

linkedlist *linkedlist_empty(void) {
    linkedlist *list = malloc(sizeof(linkedlist));
    if (list == NULL) {
        return NULL;
    }
    list->first = NULL;
    list->last = NULL;
    return list;
}

bool linkedlist_isEmpty(const linkedlist *list) {
    return list->first == NULL;
}

linkedlist_pos linkedlist_first(const linkedlist *list) {
    linkedlist_pos pos;
    pos.list = (linkedlist *)list;
    pos.node = list->first;
    return pos;
}

linkedlist_pos linkedlist_last(const linkedlist *list) {
    linkedlist_pos pos;
    pos.list = (linkedlist *)list;
    pos.node = list->last;
    return pos;
}

linkedlist_pos linkedlist_next(linkedlist_pos pos) {
    linkedlist_pos next_pos;
    next_pos.list = pos.list;
    next_pos.node = pos.node->next;
    return next_pos;
}

linkedlist_pos linkedlist_prev(linkedlist_pos pos) {
    linkedlist_pos prev_pos;
    prev_pos.list = pos.list;
    prev_pos.node = pos.node->prev;
    return prev_pos;
}

bool linkedlist_isEnd(linkedlist_pos pos) {
    return pos.node == NULL;
}

void *linkedlist_inspect(linkedlist_pos pos) {
    return pos.node->data;
}

bool linkedlist_insert(linkedlist_pos pos, void *data) {
    linkedlist_node *new_node = malloc(sizeof(linkedlist_node));
    if (new_node == NULL) {
        return false;
    }
    new_node->data = data;
    new_node->next = pos.node;
    new_node->prev = NULL;
    if (pos.node != NULL) {
        new_node->prev = pos.node->prev;
        pos.node->prev = new_node;
    } else {
        new_node->prev = pos.list->last;
        if (pos.list->last != NULL) {
            pos.list->last->next = new_node;
        } else {
            pos.list->first = new_node;
        }
        pos.list->last = new_node;
        return true;
    }
    if (new_node->prev != NULL) {
        new_node->prev->next = new_node;
    } else {
        pos.list->first = new_node;
    }
    return true;
}

void linkedlist_remove(linkedlist_pos pos) {
    linkedlist_node *node = pos.node;
    if (node->prev != NULL) {
        node->prev->next = node->next;
    } else {
        pos.list->first = node->next;
    }
    if (node->next != NULL) {
        node->next->prev = node->prev;
    } else {
        pos.list->last = node->prev;
    }
    free(node);
}

void linkedlist_free(linkedlist *list) {
    while (!linkedlist_isEmpty(list)) {
        linkedlist_remove(linkedlist_first(list));
    }
    free(list);
}

问题原因

Valgrind检测到的泄漏来自你用strdup插入链表的字符串。你的链表实现有个关键问题:linkedlist_remove和linkedlist_free只释放了链表节点本身,完全没处理节点里存储的data(也就是strdup出来的字符串)。每次你调用linkedlist_insert(..., strdup(...))时,都会分配一块新内存,但这些内存从来没被释放,直接导致了泄漏。

另外,你在循环里的操作也加重了泄漏:你先通过strdup(linkedlist_inspect(...))复制了一份字符串到currentDir,然后调用linkedlist_remove删掉节点,但此时节点里的原字符串还没被释放,直接丢了。

修复方案

有两种简单的修复方式,选一种就行:

方案1:修改链表实现,让它自动释放节点数据

直接修改linkedlist_remove函数,在释放节点前先释放存储的字符串:

void linkedlist_remove(linkedlist_pos pos) {
    linkedlist_node *node = pos.node;
    if (node->prev != NULL) {
        node->prev->next = node->next;
    } else {
        pos.list->first = node->next;
    }
    if (node->next != NULL) {
        node->next->prev = node->prev;
    } else {
        pos.list->last = node->prev;
    }
    free(node->data); // 新增这行,释放strdup分配的字符串
    free(node);
}

这个方案适合你的场景,因为你用链表存储的都是strdup分配的动态内存,修改后linkedlist_free会自动遍历所有节点并释放数据和节点本身,不用改业务代码。

方案2:在业务代码中手动释放链表节点的数据

如果不想修改链表实现(比如链表还要用来存储其他不需要释放的数据),就在使用链表的代码里手动处理:

  1. 循环中移除节点前,先取出节点里的字符串并释放:
while (!linkedlist_isEmpty(stack)) {
    // 先取出原字符串指针
    char *originalDir = linkedlist_inspect(linkedlist_first(stack));
    char *currentDir = strdup(originalDir);
    if (currentDir == NULL) {
        // 内存分配失败时,也要先释放原字符串再清理节点
        free(originalDir);
        linkedlist_remove(linkedlist_first(stack));
        break;
    }
    
    // 释放原字符串后再移除节点
    free(originalDir);
    linkedlist_remove(linkedlist_first(stack));
    
    // 后面的代码保持不变...
}
  1. 函数末尾,在调用linkedlist_free前,要释放链表中剩余节点的所有数据(如果循环提前break的话):
// 释放剩余节点的字符串数据
while (!linkedlist_isEmpty(stack)) {
    char *path = linkedlist_inspect(linkedlist_first(stack));
    free(path);
    linkedlist_remove(linkedlist_first(stack));
}
linkedlist_free(stack);

额外优化建议

你在循环里做了一次strdup,其实完全没必要——直接用linkedlist_inspect拿到的原字符串指针就可以操作,不用复制一份,这样还能减少一次内存分配,避免额外的内存管理:

while (!linkedlist_isEmpty(stack)) {
    char *currentDir = linkedlist_inspect(linkedlist_first(stack));
    if (currentDir == NULL) {
        linkedlist_remove(linkedlist_first(stack));
        break;
    }
    
    linkedlist_remove(linkedlist_first(stack));
    DIR *dir = opendir(currentDir);        
    if (dir == NULL) {
        free(currentDir); // 这里直接释放原字符串
        continue;
    }

    // ... 中间代码不变 ...

    closedir(dir);
    free(currentDir); // 释放原字符串
}

这样既减少了内存分配,也简化了内存管理逻辑。


内容的提问来源于stack exchange,提问作者Yohio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 05:37:05