rt-thread icon indicating copy to clipboard operation
rt-thread copied to clipboard

[Bug] dfs_elm.c 中因 open/close 逻辑不一致导致的内存泄漏

Open wdfk-prog opened this issue 1 month ago • 14 comments

RT-Thread Version

Master

Hardware Type/Architectures

STM32F407

Develop Toolchain

RT-Thread Studio

Describe the bug

  • dfs/filesystems/elmfat/dfs_elm.c 文件中的 dfs_elm_opendfs_elm_close 函数在处理文件引用计数 (ref_count) 时的行为不一致,导致了严重的内存泄漏。

问题根源分析:

  1. dfs_elm_open 的行为: 无论一个文件被打开多少次 (ref_count 值是多少),dfs_elm_open 函数总是会为每一次 open 调用执行 rt_malloc 来分配一个新的底层文件句柄 (FILDIR 结构体)。
  2. dfs_elm_close 的行为:open 的行为相反,dfs_elm_close 函数在开始处包含一个检查 if (file->vnode->ref_count > 1)。如果这个条件成立(意味着还有其他文件描述符指向同一个文件),函数会直接返回,并不会调用 rt_free 来释放当初为这个文件描述符在 open 时所分配的内存。

导致的后果: 这种 open (总是分配) 和 close (有条件释放) 之间的逻辑不对称,导致对同一个文件进行多次打开后,除了最后一次关闭操作外,之前所有的关闭操作都会跳过资源释放步骤,造成与关闭次数成正比的内存泄漏。

Steps to reproduce the behavior (复现步骤)

  1. 分析代码发现,尚未测试
  2. 该部分逻辑与littlefs类似,littlefs处理不会导致malloc与free不一致问题,但是会导致第二次open失败

Expected behavior (预期行为)

每一次 close(fd) 调用都应该精确地释放与之对应的 open() 操作所分配的内存资源 (FILDIR 结构体)。在上述测试代码的循环中,initial_memfinal_mem 的值应该保持一致(不考虑系统其他任务的微小波动),不应出现累积性的内存增长。

Add screenshot / media if you have them (截图)

(无)

Other additional context

这个问题的根源在于 dfs_elm.c 文件中 dfs_elm_close 函数的以下代码块:

int dfs_elm_close(struct dfs_file *file)
{
    FRESULT result;

    RT_ASSERT(file->vnode->ref_count > 0);
    if (file->vnode->ref_count > 1)
    {
        return 0; // <--- 此处直接返回,导致内存泄漏
    }
    // ... 后续的释放逻辑 ...
}

建议的修复方案是直接移除这个 if (file->vnode->ref_count > 1) 判断,以确保 openclose 的资源管理行为是对称的。

另外,还有一个相关的逻辑不一致点:在 dfs_elm_open 函数中,对 ref_count 的检查仅在 FF_VOLUMES > 1 的宏条件下存在,而在单卷配置下则没有。虽然这不是导致内存泄漏的直接原因,但建议一并修复,以保证代码在不同配置下行为的统一性。

wdfk-prog avatar Oct 25 '25 05:10 wdfk-prog

我明天看下

illustriousness avatar Oct 26 '25 10:10 illustriousness

  • 文件大小
msh /flash/log>ls
Directory /flash/log:
flash_sys.log        8093   
  • cat文件内存占用
memory heap address:
name    : heap
total   : 95400
used    : 50184
max_used: 52424
heap_ptr: 0x20008b38
lfree   : 0x200097e8
heap_end: 0x2001fff0
  • 第一次cat文件内存占用
msh /flash/log>memtrace

memory heap address:
name    : heap
total   : 95400
used    : 54344
max_used: 54872
heap_ptr: 0x20008b38
lfree   : 0x200097e8
heap_end: 0x2001fff0

  • 第10次cat文件内存占用
msh /flash/log>memtrace

memory heap address:
name    : heap
total   : 95400
used    : 87624
max_used: 88152
heap_ptr: 0x20008b38
lfree   : 0x200097e8
heap_end: 0x2001fff0
  • 解除文件打开状态,内存还是异常占用
Directory /flash/log:
flash_sys.log        8093                     
msh /flash/log>list fd
fd type    ref magic  path
-- ------  --- ----- ------
msh /flash/log>memc
memcheck
msh /flash/log>memt    
memtrace
msh /flash/log>memtrace

memory heap address:
name    : heap
total   : 95400
used    : 87056
max_used: 89248
heap_ptr: 0x20008b38
lfree   : 0x200097e8
heap_end: 0x2001fff0

wdfk-prog avatar Oct 29 '25 03:10 wdfk-prog

使用qemu-a9的BSP测试了下,发现貌似没这个问题?

Image

测试代码:

#include <fcntl.h>
#include <unistd.h>

#define LEAK_TEST_FILE      "/elm_leak_test.txt"
#define LEAK_TEST_PASSES    50

static void dump_heap_usage(const char *tag, rt_uint32_t *used_out)
{
    rt_uint32_t total = 0;
    rt_uint32_t used = 0;
    rt_uint32_t max_used = 0;

    rt_memory_info(&total, &used, &max_used);
    if (used_out)
    {
        *used_out = used;
    }
    rt_kprintf("[%s] heap total=%u used=%u max=%u\n", tag, total, used, max_used);
}

int main(void)
{
    rt_thread_mdelay(1000); /* wait for elm FAT filesystem to be mounted */

    rt_kprintf("elm leak regression test start\n");

    int init_fd = open(LEAK_TEST_FILE, O_CREAT | O_RDWR | O_TRUNC, 0);
    if (init_fd < 0)
    {
        rt_kprintf("failed to create %s, err=%d\n", LEAK_TEST_FILE, init_fd);
        return -1;
    }

    const char *payload = "elm leak repro";
    (void)write(init_fd, payload, rt_strlen(payload));
    close(init_fd);

    rt_uint32_t used_before = 0;
    dump_heap_usage("before", &used_before);

    for (int i = 0; i < LEAK_TEST_PASSES; ++i)
    {
        int fd_a = open(LEAK_TEST_FILE, O_RDONLY);
        if (fd_a < 0)
        {
            rt_kprintf("iteration %d: open A failed, err=%d\n", i, fd_a);
            break;
        }

        int fd_b = open(LEAK_TEST_FILE, O_RDONLY);
        if (fd_b < 0)
        {
            rt_kprintf("iteration %d: open B failed, err=%d\n", i, fd_b);
            close(fd_a);
            break;
        }

        close(fd_b);
        close(fd_a);

        if ((i + 1) % 10 == 0)
        {
            dump_heap_usage("progress", RT_NULL);
        }
    }

    rt_uint32_t used_after = 0;
    dump_heap_usage("after", &used_after);

    rt_kprintf("heap leak delta=%d bytes after %d open/close pairs\n",
               (int)(used_after - used_before), LEAK_TEST_PASSES);

    return 0;
}

Rbb666 avatar Nov 14 '25 07:11 Rbb666

另外测试了下littlefs,发现会有这样的问题:

open同一个文件多次后,在close的第二次会出现断言,问题截图:

Image
static void lfs_multi_open_repro_case(void)
{
	int fd_primary = -1;
	int fd_secondary = -1;

	fd_primary = open(TEST_FILE_PATH, O_RDWR | O_CREAT | O_TRUNC, 0);
	if (fd_primary < 0)
	{
	    rt_kprintf("Failed to prepare littlefs test file '%s' (%d)\n", TEST_FILE_PATH, fd_primary);
		return;
	}

	static const char payload[] = "RT-Thread littlefs multi-open reproduce\r\n";
	int written = write(fd_primary, payload, sizeof(payload) - 1);
	if (written < 0)
	{
	    rt_kprintf("Failed to seed payload for littlefs test (%d)\n", written);
		close(fd_primary);
		unlink(TEST_FILE_PATH);
		return;
	}

	close(fd_primary);

	fd_primary = open(TEST_FILE_PATH, O_RDONLY, 0);
	if (fd_primary < 0)
	{
	    rt_kprintf("Failed to open baseline descriptor (%d)\n", fd_primary);
		unlink(TEST_FILE_PATH);
		return;
	}

	fd_secondary = open(TEST_FILE_PATH, O_RDONLY, 0);
	if (fd_secondary < 0)
	{
	    rt_kprintf("Second open failed unexpectedly (%d)\n", fd_secondary);
		close(fd_primary);
		unlink(TEST_FILE_PATH);
		return;
	}

	rt_kprintf("littlefs multi-open reproduction ready: fd0=%d, fd1=%d\n", fd_primary, fd_secondary);
	rt_kprintf("Closing fd0\n");
	close(fd_primary);

	rt_kprintf("Closing fd1\n");
	close(fd_secondary);

	unlink(TEST_FILE_PATH);
}

Rbb666 avatar Nov 14 '25 07:11 Rbb666

另外测试了下littlefs,发现会有这样的问题:

open同一个文件多次后,在close的第二次会出现断言,问题截图:

Image static void lfs_multi_open_repro_case(void) { int fd_primary = -1; int fd_secondary = -1;

fd_primary = open(TEST_FILE_PATH, O_RDWR | O_CREAT | O_TRUNC, 0); if (fd_primary < 0) { rt_kprintf("Failed to prepare littlefs test file '%s' (%d)\n", TEST_FILE_PATH, fd_primary); return; }

static const char payload[] = "RT-Thread littlefs multi-open reproduce\r\n"; int written = write(fd_primary, payload, sizeof(payload) - 1); if (written < 0) { rt_kprintf("Failed to seed payload for littlefs test (%d)\n", written); close(fd_primary); unlink(TEST_FILE_PATH); return; }

close(fd_primary);

fd_primary = open(TEST_FILE_PATH, O_RDONLY, 0); if (fd_primary < 0) { rt_kprintf("Failed to open baseline descriptor (%d)\n", fd_primary); unlink(TEST_FILE_PATH); return; }

fd_secondary = open(TEST_FILE_PATH, O_RDONLY, 0); if (fd_secondary < 0) { rt_kprintf("Second open failed unexpectedly (%d)\n", fd_secondary); close(fd_primary); unlink(TEST_FILE_PATH); return; }

rt_kprintf("littlefs multi-open reproduction ready: fd0=%d, fd1=%d\n", fd_primary, fd_secondary); rt_kprintf("Closing fd0\n"); close(fd_primary);

rt_kprintf("Closing fd1\n"); close(fd_secondary);

unlink(TEST_FILE_PATH); }

这个修复了吧

  • https://github.com/RT-Thread-packages/littlefs/pull/28

wdfk-prog avatar Nov 14 '25 07:11 wdfk-prog

使用qemu-a9的BSP测试了下,发现貌似没这个问题?

Image 测试代码:

#include <fcntl.h> #include <unistd.h>

#define LEAK_TEST_FILE "/elm_leak_test.txt" #define LEAK_TEST_PASSES 50

static void dump_heap_usage(const char *tag, rt_uint32_t *used_out) { rt_uint32_t total = 0; rt_uint32_t used = 0; rt_uint32_t max_used = 0;

rt_memory_info(&total, &used, &max_used);
if (used_out)
{
    *used_out = used;
}
rt_kprintf("[%s] heap total=%u used=%u max=%u\n", tag, total, used, max_used);

}

int main(void) { rt_thread_mdelay(1000); /* wait for elm FAT filesystem to be mounted */

rt_kprintf("elm leak regression test start\n");

int init_fd = open(LEAK_TEST_FILE, O_CREAT | O_RDWR | O_TRUNC, 0);
if (init_fd < 0)
{
    rt_kprintf("failed to create %s, err=%d\n", LEAK_TEST_FILE, init_fd);
    return -1;
}

const char *payload = "elm leak repro";
(void)write(init_fd, payload, rt_strlen(payload));
close(init_fd);

rt_uint32_t used_before = 0;
dump_heap_usage("before", &used_before);

for (int i = 0; i < LEAK_TEST_PASSES; ++i)
{
    int fd_a = open(LEAK_TEST_FILE, O_RDONLY);
    if (fd_a < 0)
    {
        rt_kprintf("iteration %d: open A failed, err=%d\n", i, fd_a);
        break;
    }

    int fd_b = open(LEAK_TEST_FILE, O_RDONLY);
    if (fd_b < 0)
    {
        rt_kprintf("iteration %d: open B failed, err=%d\n", i, fd_b);
        close(fd_a);
        break;
    }

    close(fd_b);
    close(fd_a);

    if ((i + 1) % 10 == 0)
    {
        dump_heap_usage("progress", RT_NULL);
    }
}

rt_uint32_t used_after = 0;
dump_heap_usage("after", &used_after);

rt_kprintf("heap leak delta=%d bytes after %d open/close pairs\n",
           (int)(used_after - used_before), LEAK_TEST_PASSES);

return 0;

}

  1. 文件打开关闭用的是open与close的话,根据这个问题来看或许是触发不了问题的.https://github.com/RT-Thread/rt-thread/issues/10848
  2. 使用dfs_file_open/dfs_file_close试一下应该可以复现?
  3. 或许先使用finsh的cat多次文件看看👀>?
void cat(const char *filename)
{
    struct dfs_file fd;
    int length = 0;
    char buffer[81];

    fd_init(&fd);
    if (dfs_file_open(&fd, filename, O_RDONLY) < 0)
    {
        rt_kprintf("Open %s failed\n", filename);

        return;
    }

    do
    {
        rt_memset(buffer, 0x0, sizeof(buffer));
        length = dfs_file_read(&fd, (void *)buffer, sizeof(buffer) - 1);
        if (length > 0)
        {
            buffer[length] = '\0';
            rt_device_t out_device = rt_console_get_device();
            rt_device_write(out_device, 0, (void *)buffer, length);
        }
    } while (length > 0);
    rt_kprintf("\n");

    dfs_file_close(&fd);
}
FINSH_FUNCTION_EXPORT(cat, print file);

wdfk-prog avatar Nov 14 '25 07:11 wdfk-prog

另外测试了下littlefs,发现会有这样的问题: open同一个文件多次后,在close的第二次会出现断言,问题截图: Image static void lfs_multi_open_repro_case(void) { int fd_primary = -1; int fd_secondary = -1; fd_primary = open(TEST_FILE_PATH, O_RDWR | O_CREAT | O_TRUNC, 0); if (fd_primary < 0) { rt_kprintf("Failed to prepare littlefs test file '%s' (%d)\n", TEST_FILE_PATH, fd_primary); return; } static const char payload[] = "RT-Thread littlefs multi-open reproduce\r\n"; int written = write(fd_primary, payload, sizeof(payload) - 1); if (written < 0) { rt_kprintf("Failed to seed payload for littlefs test (%d)\n", written); close(fd_primary); unlink(TEST_FILE_PATH); return; } close(fd_primary); fd_primary = open(TEST_FILE_PATH, O_RDONLY, 0); if (fd_primary < 0) { rt_kprintf("Failed to open baseline descriptor (%d)\n", fd_primary); unlink(TEST_FILE_PATH); return; } fd_secondary = open(TEST_FILE_PATH, O_RDONLY, 0); if (fd_secondary < 0) { rt_kprintf("Second open failed unexpectedly (%d)\n", fd_secondary); close(fd_primary); unlink(TEST_FILE_PATH); return; } rt_kprintf("littlefs multi-open reproduction ready: fd0=%d, fd1=%d\n", fd_primary, fd_secondary); rt_kprintf("Closing fd0\n"); close(fd_primary); rt_kprintf("Closing fd1\n"); close(fd_secondary); unlink(TEST_FILE_PATH); }

这个修复了吧

我测测哈,可能是我本地的太老了

Rbb666 avatar Nov 14 '25 07:11 Rbb666

另外测试了下littlefs,发现会有这样的问题: open同一个文件多次后,在close的第二次会出现断言,问题截图: Image static void lfs_multi_open_repro_case(void) { int fd_primary = -1; int fd_secondary = -1; fd_primary = open(TEST_FILE_PATH, O_RDWR | O_CREAT | O_TRUNC, 0); if (fd_primary < 0) { rt_kprintf("Failed to prepare littlefs test file '%s' (%d)\n", TEST_FILE_PATH, fd_primary); return; } static const char payload[] = "RT-Thread littlefs multi-open reproduce\r\n"; int written = write(fd_primary, payload, sizeof(payload) - 1); if (written < 0) { rt_kprintf("Failed to seed payload for littlefs test (%d)\n", written); close(fd_primary); unlink(TEST_FILE_PATH); return; } close(fd_primary); fd_primary = open(TEST_FILE_PATH, O_RDONLY, 0); if (fd_primary < 0) { rt_kprintf("Failed to open baseline descriptor (%d)\n", fd_primary); unlink(TEST_FILE_PATH); return; } fd_secondary = open(TEST_FILE_PATH, O_RDONLY, 0); if (fd_secondary < 0) { rt_kprintf("Second open failed unexpectedly (%d)\n", fd_secondary); close(fd_primary); unlink(TEST_FILE_PATH); return; } rt_kprintf("littlefs multi-open reproduction ready: fd0=%d, fd1=%d\n", fd_primary, fd_secondary); rt_kprintf("Closing fd0\n"); close(fd_primary); rt_kprintf("Closing fd1\n"); close(fd_secondary); unlink(TEST_FILE_PATH); }

这个修复了吧

我测测哈,可能是我本地的太老了

可以的,修复了,我看看上面的问题

Image

Rbb666 avatar Nov 14 '25 07:11 Rbb666

@wdfk-prog 测试了下,还是没能复现

master分支,qemu-a9:

Image

5.1.0分支:

Image

Rbb666 avatar Nov 14 '25 08:11 Rbb666

@wdfk-prog 测试了下,还是没能复现

master分支,qemu-a9:

Image 5.1.0分支: Image
  • 有一个地方open该文件还为close的情况下,进行cat该文件吗?
  • 可以在 if (file->vnode->ref_count > 1)里面加个打印看看是否直接退出了?
int dfs_elm_close(struct dfs_file *file)
{
    FRESULT result;

    RT_ASSERT(file->vnode->ref_count > 0);
    if (file->vnode->ref_count > 1)
    {
        return 0; // <--- 此处直接返回,导致内存泄漏
    }
    // ... 后续的释放逻辑 ...
}
  • 我看你打印的日子ret cnt:1 不知道是啥时候的情况

wdfk-prog avatar Nov 14 '25 13:11 wdfk-prog

@wdfk-prog 测试了下,还是没能复现 master分支,qemu-a9: Image 5.1.0分支: Image

  • 有一个地方open该文件还为close的情况下,进行cat该文件吗?
  • 可以在 if (file->vnode->ref_count > 1)里面加个打印看看是否直接退出了?

int dfs_elm_close(struct dfs_file *file) { FRESULT result;

RT_ASSERT(file->vnode->ref_count > 0);
if (file->vnode->ref_count > 1)
{
    return 0; // <--- 此处直接返回,导致内存泄漏
}
// ... 后续的释放逻辑 ...

}

  • 我看你打印的日子ret cnt:1 不知道是啥时候的情况

我就是在 if (file->vnode->ref_count > 1) 这边加的打印,发现都是1,可以给个稳定复现的测试代码不?

Rbb666 avatar Nov 16 '25 03:11 Rbb666

@wdfk-prog 测试了下,还是没能复现 master分支,qemu-a9: Image 5.1.0分支: Image

  • 有一个地方open该文件还为close的情况下,进行cat该文件吗?
  • 可以在 if (file->vnode->ref_count > 1)里面加个打印看看是否直接退出了?

int dfs_elm_close(struct dfs_file *file) { FRESULT result;

RT_ASSERT(file->vnode->ref_count > 0);
if (file->vnode->ref_count > 1)
{
    return 0; // <--- 此处直接返回,导致内存泄漏
}
// ... 后续的释放逻辑 ...

}

  • 我看你打印的日子ret cnt:1 不知道是啥时候的情况

我就是在 if (file->vnode->ref_count > 1) 这边加的打印,发现都是1,可以给个稳定复现的测试代码不?

  • 我知道了 https://github.com/RT-Thread/rt-thread/blob/6695599afcd365b0fe18e023c830b8260663f3cb/components/dfs/dfs_v1/filesystems/elmfat/dfs_elm.c#L338-L365

  • 应该是FF_VOLUMES配置的问题

  • 你那边走到了 (FF_VOLUMES > 1)里面所以没问题

  • 我这里走的是另一个逻辑 https://github.com/RT-Thread/rt-thread/blob/6695599afcd365b0fe18e023c830b8260663f3cb/components/dfs/dfs_v1/filesystems/elmfat/dfs_elm.c#L366-L368

  • 这一块宏里面的逻辑判断或许应该统一一下,哈哈哈

wdfk-prog avatar Nov 24 '25 06:11 wdfk-prog

@wdfk-prog 测试了下,还是没能复现 master分支,qemu-a9: Image 5.1.0分支: Image

  • 有一个地方open该文件还为close的情况下,进行cat该文件吗?
  • 可以在 if (file->vnode->ref_count > 1)里面加个打印看看是否直接退出了?

int dfs_elm_close(struct dfs_file *file) { FRESULT result;

RT_ASSERT(file->vnode->ref_count > 0);
if (file->vnode->ref_count > 1)
{
    return 0; // <--- 此处直接返回,导致内存泄漏
}
// ... 后续的释放逻辑 ...

}

  • 我看你打印的日子ret cnt:1 不知道是啥时候的情况

我就是在 if (file->vnode->ref_count > 1) 这边加的打印,发现都是1,可以给个稳定复现的测试代码不?

  • 我知道了

      [rt-thread/components/dfs/dfs_v1/filesystems/elmfat/dfs_elm.c](https://github.com/RT-Thread/rt-thread/blob/6695599afcd365b0fe18e023c830b8260663f3cb/components/dfs/dfs_v1/filesystems/elmfat/dfs_elm.c#L338-L365)
    
    
        Lines 338 to 365
      in
      [6695599](/RT-Thread/rt-thread/commit/6695599afcd365b0fe18e023c830b8260663f3cb)
    
    
    
    
    
    
    
           #if (FF_VOLUMES > 1) 
    
    
    
    
               int vol; 
    
    
    
    
               struct dfs_filesystem *fs = file->vnode->fs; 
    
    
    
    
               extern int elm_get_vol(FATFS * fat); 
    
    
    
    
    
    
    
    
    
               RT_ASSERT(file->vnode->ref_count > 0); 
    
    
    
    
               if (file->vnode->ref_count > 1) 
    
    
    
    
               { 
    
    
    
    
                   if (file->vnode->type == FT_DIRECTORY 
    
    
    
    
                           && !(file->flags & O_DIRECTORY)) 
    
    
    
    
                   { 
    
    
    
    
                       return -ENOENT; 
    
    
    
    
                   } 
    
    
    
    
                   file->pos = 0; 
    
    
    
    
               } 
    
    
    
    
    
    
    
    
    
               if (fs == NULL) 
    
    
    
    
                   return -ENOENT; 
    
    
    
    
    
    
    
    
    
               /* add path for ELM FatFS driver support */ 
    
    
    
    
               vol = elm_get_vol((FATFS *)fs->data); 
    
    
    
    
               if (vol < 0) 
    
    
    
    
                   return -ENOENT; 
    
    
    
    
               drivers_fn = (char *)rt_malloc(256); 
    
    
    
    
               if (drivers_fn == RT_NULL) 
    
    
    
    
                   return -ENOMEM; 
    
    
    
    
    
    
    
    
    
               rt_snprintf(drivers_fn, 256, "%d:%s", vol, file->vnode->path);
    
  • 应该是FF_VOLUMES配置的问题

  • 你那边走到了 (FF_VOLUMES > 1)里面所以没问题

  • 我这里走的是另一个逻辑

      [rt-thread/components/dfs/dfs_v1/filesystems/elmfat/dfs_elm.c](https://github.com/RT-Thread/rt-thread/blob/6695599afcd365b0fe18e023c830b8260663f3cb/components/dfs/dfs_v1/filesystems/elmfat/dfs_elm.c#L366-L368)
    
    
        Lines 366 to 368
      in
      [6695599](/RT-Thread/rt-thread/commit/6695599afcd365b0fe18e023c830b8260663f3cb)
    
    
    
    
    
    
    
           #else 
    
    
    
    
               drivers_fn = file->vnode->path; 
    
    
    
    
           #endif
    
  • 这一块宏里面的逻辑判断或许应该统一一下,哈哈哈

RT_DFS_ELM_DRIVES 这个宏我看默认都是2,代表着FatFs 模块同时支持的最大逻辑驱动器(卷)数量,你那边是设置为了1吗?

Rbb666 avatar Nov 26 '25 02:11 Rbb666

@wdfk-prog 测试了下,还是没能复现 master分支,qemu-a9: Image 5.1.0分支: Image

  • 有一个地方open该文件还为close的情况下,进行cat该文件吗?
  • 可以在 if (file->vnode->ref_count > 1)里面加个打印看看是否直接退出了?

int dfs_elm_close(struct dfs_file *file) { FRESULT result;

RT_ASSERT(file->vnode->ref_count > 0);
if (file->vnode->ref_count > 1)
{
    return 0; // <--- 此处直接返回,导致内存泄漏
}
// ... 后续的释放逻辑 ...

}

  • 我看你打印的日子ret cnt:1 不知道是啥时候的情况

我就是在 if (file->vnode->ref_count > 1) 这边加的打印,发现都是1,可以给个稳定复现的测试代码不?

  • 我知道了
      [rt-thread/components/dfs/dfs_v1/filesystems/elmfat/dfs_elm.c](https://github.com/RT-Thread/rt-thread/blob/6695599afcd365b0fe18e023c830b8260663f3cb/components/dfs/dfs_v1/filesystems/elmfat/dfs_elm.c#L338-L365)
    
    
        Lines 338 to 365
      in
      [6695599](/RT-Thread/rt-thread/commit/6695599afcd365b0fe18e023c830b8260663f3cb)
    
    
    
    
    
    
    
           #if (FF_VOLUMES > 1) 
    
    
    
    
               int vol; 
    
    
    
    
               struct dfs_filesystem *fs = file->vnode->fs; 
    
    
    
    
               extern int elm_get_vol(FATFS * fat); 
    
    
    
    
    
    
    
    
    
               RT_ASSERT(file->vnode->ref_count > 0); 
    
    
    
    
               if (file->vnode->ref_count > 1) 
    
    
    
    
               { 
    
    
    
    
                   if (file->vnode->type == FT_DIRECTORY 
    
    
    
    
                           && !(file->flags & O_DIRECTORY)) 
    
    
    
    
                   { 
    
    
    
    
                       return -ENOENT; 
    
    
    
    
                   } 
    
    
    
    
                   file->pos = 0; 
    
    
    
    
               } 
    
    
    
    
    
    
    
    
    
               if (fs == NULL) 
    
    
    
    
                   return -ENOENT; 
    
    
    
    
    
    
    
    
    
               /* add path for ELM FatFS driver support */ 
    
    
    
    
               vol = elm_get_vol((FATFS *)fs->data); 
    
    
    
    
               if (vol < 0) 
    
    
    
    
                   return -ENOENT; 
    
    
    
    
               drivers_fn = (char *)rt_malloc(256); 
    
    
    
    
               if (drivers_fn == RT_NULL) 
    
    
    
    
                   return -ENOMEM; 
    
    
    
    
    
    
    
    
    
               rt_snprintf(drivers_fn, 256, "%d:%s", vol, file->vnode->path);
    
  • 应该是FF_VOLUMES配置的问题
  • 你那边走到了 (FF_VOLUMES > 1)里面所以没问题
  • 我这里走的是另一个逻辑
      [rt-thread/components/dfs/dfs_v1/filesystems/elmfat/dfs_elm.c](https://github.com/RT-Thread/rt-thread/blob/6695599afcd365b0fe18e023c830b8260663f3cb/components/dfs/dfs_v1/filesystems/elmfat/dfs_elm.c#L366-L368)
    
    
        Lines 366 to 368
      in
      [6695599](/RT-Thread/rt-thread/commit/6695599afcd365b0fe18e023c830b8260663f3cb)
    
    
    
    
    
    
    
           #else 
    
    
    
    
               drivers_fn = file->vnode->path; 
    
    
    
    
           #endif
    
  • 这一块宏里面的逻辑判断或许应该统一一下,哈哈哈

RT_DFS_ELM_DRIVES 这个宏我看默认都是2,代表着FatFs 模块同时支持的最大逻辑驱动器(卷)数量,你那边是设置为了1吗?

  • 是的,我修改为1了
#define RT_USING_DFS_ELMFAT

/* elm-chan's FatFs, Generic FAT Filesystem Module */

#define RT_DFS_ELM_CODE_PAGE 437
#define RT_DFS_ELM_WORD_ACCESS
#define RT_DFS_ELM_USE_LFN_3
#define RT_DFS_ELM_USE_LFN 3
#define RT_DFS_ELM_LFN_UNICODE_0
#define RT_DFS_ELM_LFN_UNICODE 0
#define RT_DFS_ELM_MAX_LFN 255
#define RT_DFS_ELM_DRIVES 1
#define RT_DFS_ELM_MAX_SECTOR_SIZE 4096
#define RT_DFS_ELM_REENTRANT
#define RT_DFS_ELM_MUTEX_TIMEOUT 3000
/* end of elm-chan's FatFs, Generic FAT Filesystem Module */

wdfk-prog avatar Nov 27 '25 05:11 wdfk-prog