• RT-Smart 应用开发笔记:fopen 造成文件被清空问题的分析记录


    前言

    • RT-Smart 应用(apps)开发环境,ubuntu 20.04 + win10 VS Code

    • 最近在调试一个问题,需要使用 FILE 的 fopen、fread 等去读取处理一个大文件,为了尽快复现验证问题,随手搜了一下 fopen 等几个 API的用法,调试时闹出来一个【笑话】,程序运行所到之处,把处理过的本地文件清空了。

    • 当时初步的目标只是使用 stat 去获取一个文件的大小,现象就是 0

    获取文件大小

    • 如何在 用户态获取文件大小,RT-Smart 的应用开发与 Linux 的用户应用开发基本类似,Linux 平台上的应用,可以轻松的移植到 RT-Smart 上。

    • fopen 可以在RT-Smart内核态使用,也可以在用户态使用,用户态的使用,一般都是借助 libc 与 系统调用,当前 RT-Smart 使用 musl gcc 工具链

    • 获取文件大小,应该与 fopen、fread 系列无关,代码如下:

    unsigned long get_file_size(const char *path)
    {
        unsigned long filesize = -1;
        struct stat statbuff;
    
        if (stat(path, &statbuff) < 0)
        {
            return filesize;
        }
        else
        {
            filesize = statbuff.st_size;
        }
    
        return filesize;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 这里使用标准的 POSIX 接口 stat,头文件 #include

    测试代码

    • 随手摘抄修改了一个测试代码,想获取到 文件大小,然后分批读取,验证这个过程是否正常,哪想到文件大小获取为0,使用 qemu 调试了多遍,怀疑文件系统BUG,依旧未找到正确的思路

    • 原 BUG 测试代码如下:

    /*  read big file : > 300MB */
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define BUFFER_SIZE     (8 * 1024 * 1024)
    
    unsigned long get_file_size(const char *path)
    {
        unsigned long filesize = -1;
        struct stat statbuff;
    
        if (lstat(path, &statbuff) < 0)
        {
            return filesize;
        }
        else
        {
            filesize = statbuff.st_size;
        }
    
        return filesize;
    }
    
    int main(int argc, char **argv)
    {
        //int errno;
        FILE *fp = NULL;
        unsigned long so_far, signed_len;
        const char *path = "/lib/libc.so";
        char *buffer = NULL;
        size_t size;
        size_t file_size;
        size_t rd_len;
    
        if (argc > 1)
        {
            path = argv[1];
        }
    
        printf("filename path : %s\n", path);
    
        fp = fopen(path, "w+");
        if (!fp)
        {
            printf("fopen failed\n");
            return -1;
        }
    
        buffer = malloc(BUFFER_SIZE);
        if (!buffer)
        {
            printf("buffer alloc failed\n");
            return -1;
        }
    
        file_size = get_file_size(path);
        printf("file size %ld\n", file_size);
    
        signed_len = file_size;
        so_far = 0;
    
        while (so_far < signed_len)
        {
            size = BUFFER_SIZE;
            printf("so far is %lu, signed_len is %lu, size is %lu\n", so_far, signed_len, size);
    
            if (signed_len - so_far < size)
                size = signed_len - so_far;
    
            printf("new size is %lu\n", size);
    
            rd_len = fread(buffer, 1, size, fp);
            printf("fread rd_len = %ld\n", rd_len);
            if ((rd_len != 0) && (rd_len != size))
            {
                printf("fread failed, rd_len = %ld, size = %ld\n", rd_len, size);
                fclose(fp);
                free(buffer);
                return -2;
            }
            so_far += size;
            printf("fread so_far = %ld\n", so_far);
        }
    
        fclose(fp);
        free(buffer);
        printf("fread big file test end!\n");
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 测试结果

    • 文件大小 读取 为 0

    2023-10-20_083936.png

    • 由于可以使用 qemu 来调试,我看看到底为何 大小为0,难道 打印的不对?

    2023-10-20_084236.png

    2023-10-20_084510.png

    2023-10-20_084609.png

    • 内核部分的 sys_stat 后面的调试就不放上来了,说明一个问题:文件获取大小就是 0

    回头看下测试结果

    • 好吧,我发现了文件系统BUG?读取一下状态,文件大小就变为 0 了?

    • 我多试读取了几个文件,文件大小真的变为了0,包括 /lib/libc.so 这个默认文件

    • 我在板子上试了一下,读取了一下文件系统中的核心文件: rtthread.bin,哪想到,板子启动不了,看了固件被【真正】清零0

    2023-10-20_084842.png

    赶快看看神奇的代码

    • 发现 BUG 了,原来在 获取文件状态前,有个 fopen 操作,并且使用了 "w+",就是这个问题

    微信图片_20231019145449.png

    在这里插入图片描述

    在这里插入图片描述

    • 都是C 语言基础薄弱惹的祸,记录一下,以后得好好学习,正确理解,才能致用。

    fopen 的 参数

    • 当前发现 fopen 使用 “a+” 也不能获取文件大小,二进制的文件,需要使用 “rb”

    在这里插入图片描述

    • 正常使用了 fopen 后,文件大小就正常获取了

    在这里插入图片描述

    • 看来测试代码,也需要认真写,测试过程中【看似无关紧要】的代码,也许就是个【地雷】

    小结

    • 以上记录,其实就是 fopen 参数的使用方法的一个记录,写这么多,就是增加【印象】,得到【教训】,防止编写此类BUG 的事情不再重复犯

    • 获取知识途径很多,有时候有些 BUG的出现,会针对性的让你获取更多,嵌入式开发,除了博闻强记,更重要的,就是有个【烂笔头】记录点点滴滴。

  • 相关阅读:
    安装运行dubbo-admin
    C#面:dot net core里面的路径是如何处理的?
    Java List Set Map
    大模型的无限上下文与数据集组合艺术
    【开发日常】insmod: error inserting ‘*.ko‘: -1 Unknown symbol in module原理分析
    cubeIDE开发, stm32的OLED点亮及字符显示设计(基于SPI通信)
    vue实现浏览器禁止复制、禁用F12、禁用右侧菜单
    记一次 .NET 某埋线管理系统 崩溃分析
    python 66 个冷知识 0714
    169. 多数元素 --力扣 --JAVA
  • 原文地址:https://blog.csdn.net/tcjy1000/article/details/133952894