c++ - 在 C 中的 fwrite 上检测光盘移除

标签 c++ c linux file-io

我正在编写一个应用程序来连续向驱动器(无论是硬盘驱动器还是 SD 卡或其他)写入和读取文件。我正在编写某种模式,然后将其读回作为验证。我想在应用程序失败后立即输出某种明显的错误。基本上,我们正在用辐射撞击硬件,并且需要检测它何时出现故障。到目前为止,我的应用程序可以很好地读取和写入文件,但可以在执行过程中拔出 SD 卡,它会继续运行,就好像它还在那里一样。我真的需要检测 SD 卡被移除的时刻。我看到有人建议使用 libudev。我不能使用它,因为这是在没有它的嵌入式 linux 系统上。这是我到目前为止的代码:

#include <stdio.h>
#include <time.h>

const unsigned long long size = 16ULL*1024;
#define NANOS 1000000000LL
#define KB 1024

long long CreateFile(char* filename)
{
    struct timespec time_start;
    struct timespec time_stop;
    long long start, elapsed, microseconds;
    int timefail = 0;
    size_t stat;

    if(clock_gettime(CLOCK_REALTIME, &time_start) < 0)
        timefail = 1;
    start = time_start.tv_sec*NANOS + time_start.tv_nsec;

    int a[size];
    int i, j;

    for(i=0;i<size;i++)
        a[i] = i;

    FILE* pFile;
    pFile = fopen(filename, "wb");
    if(pFile < 0)
    {
        perror("fopen");
        return -1;
    }
    for(j=0; j < KB; j++)
    {
        stat = fwrite(a, sizeof(int), size, pFile);
        if(stat < 0)
            perror("fwrite");
        stat = fsync(pFile);
        //if(stat)
        //  perror("fysnc");
    }

fclose(pFile);

    if(clock_gettime(CLOCK_REALTIME, &time_stop) < 0)
        timefail = 1;

    elapsed = time_stop.tv_sec*NANOS + time_stop.tv_nsec - start;
    microseconds = elapsed / 1000 + (elapsed % 1000 >= 500);

    if(timefail)
        return -1;

    return microseconds / 1000;
}

long long ReadFile(char* filename)
{
    struct timespec time_start;
    struct timespec time_stop;
    long long start, elapsed, microseconds;
    int timefail = 0;

    if(clock_gettime(CLOCK_REALTIME, &time_start) < 0)
        timefail = 1;
    start = time_start.tv_sec*NANOS + time_start.tv_nsec;

    FILE* pFile;
    pFile = fopen(filename, "rb");

    int a[KB];
    int i=0, j=0;
    for(i=0; i<size; i++)
    {
        if(ferror(pFile) != 0)
        {
            fprintf(stderr, "**********************************************");
            fprintf(stderr, "READ FAILURE\n");
            fclose(pFile);
            return -1;
        }
        fread(a, sizeof(a), 1, pFile);
        for(j=0; j<KB;j++)
        {
            if(a[0] != a[1]-1)
            {
                fprintf(stderr, "**********************************************");
                fprintf(stderr, "DATA FAILURE, %d != %d\n", a[j], a[j+1]-1);
                fclose(pFile);
                return -1;
            }
        }
    }
    fclose(pFile);
    if(clock_gettime(CLOCK_REALTIME, &time_stop) < 0)
            timefail = 1;

    if(timefail)
        return -1;

    elapsed = time_stop.tv_sec*NANOS + time_stop.tv_nsec - start;
    microseconds = elapsed / 1000 + (elapsed % 1000 >= 500);

    return microseconds/1000;
}

int main(int argc, char* argv[])
{
    char* filenamebase = "/tmp/media/mmcblk0p1/test.file";
    char filename[100] = "";
    int i=0;
    long long tmpsec = 0;
    long long totalwritetime = 0;
    int totalreadtime = 0;
    int numfiles = 10;
    int totalwritten = 0;
    int totalread = 0;

    for(i=0;i<numfiles;i++)
    {
        sprintf(filename, "%s%d", filenamebase, i);
        fprintf(stderr, "Writing File: %s ...", filename);
        tmpsec = CreateFile(filename);
        if(tmpsec < 0)
            return 0;
        totalwritetime += tmpsec;
        totalwritten++;
        fprintf(stderr, "completed in %lld seconds\n", tmpsec);
        fprintf(stderr, "Reading File: %s ...", filename);
        tmpsec = ReadFile(filename);
        if(tmpsec < 0)
            return 0;
        totalreadtime += tmpsec;
        totalread++;
        fprintf(stderr, "completed in %lld seconds\n", tmpsec);
    }

    fprintf(stderr, "Test Complete\nTotal Files: %d written, %d read\n", totalwritten, totalread);
    fprintf(stderr, "File Size: %lld KB\n", size);
    fprintf(stderr, "Total KBytes Written: %lld\n", size*totalwritten);
    fprintf(stderr, "Average Write Speed: %0.2f KBps\n", (double)size*totalwritten/(totalwritetime/1000));
    fprintf(stderr, "Total KBytes Read: %lld\n", size*totalread);
    fprintf(stderr, "Average Read Speed: %0.2f KBps\n", (double)size*totalread/(totalreadtime/1000));

    return 0;
}

最佳答案

你需要改变你的方法。

如果您拔出已挂载的媒体,您可能会使内核崩溃(因为它在内存中保留表示已挂载文件系统的复杂数据结构),并破坏媒体本身。

我已经以这种方式毁坏了很多 USB 内存棒——它们处理分配和磨损均衡的内部小逻辑不喜欢在运行中途断电,而且最便宜的似乎没有能够提供的电容器足够的功率让它们运行足够长的时间以确保一致的状态——但 SD 卡和更昂贵的 USB 内存棒可能会存活得更好。

根据所使用的驱动程序,内核可能允许您读取和写入媒体,但只是将更改保留在页面缓存中。 (此外,您的 stdio.h I/O 可能只会进入页面缓存,而不是实际的媒体,具体取决于挂载选项(无论是否直接挂载/同步)。您的方法根本不提供您的行为假设它确实如此。)

相反,您应该使用低级 I/O(unistd.h,参见 man 2 open 和相关调用,没有 stdio.h),使用 O_RDWR|O_DIRECT|O_SYNC 标志来进行确保您的读写命中硬件,并通过 block 设备节点直接访问原始媒体,而不是完全挂载它。您还可以读取/写入设备上的随机位置,希望磨损均衡不会过多影响您的抗辐射检查。

(编辑添加:如果您以 block 的形式写入被测媒体设备的 native 分配 block 的大小,您将避免设备上缓慢的读取-修改-写入周期。设备仍将进行磨损均衡, 但这仅意味着您写入的 block 位于闪存芯片中的随机物理位置。 native block 大小取决于媒体设备。可以通过观察需要多长时间来测量 native block 大小读取和写入不同大小的 block ,但我认为对于损坏测试,足够大的 2 的幂应该是最好的——比如 256k 或 262144 字节。最好让用户分别为每个设备设置它,并使用其中一个制造商提供的信息,或单独的测试程序以找出正确的值。)

您不想为此使用 mmap(),因为由媒体错误和媒体变得不可用引起的 SIGBUS 信号很难正确处理。在我看来,低级 unistd.h I/O 最适合此目的。

我相信,但尚未验证,在读/写到未安装的低级设备的过程中将媒体拉出应该只会产生读/写错误。 (不过,我现在没有任何愿意冒险去检查它的媒体:)

关于c++ - 在 C 中的 fwrite 上检测光盘移除,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14903631/

相关文章:

c++ - Arduino - 将拆分字符串与另一个字符串进行比较

c++ - 带 ACE react 器的 ZeroMQ

c - 包含文件似乎被忽略

c - 'err_quit' 在哪里定义

linux - IRAF fitsparams 任务只删除最左边的点

c++ - 当类型包含具有给定名称和类型的静态变量时启用_if 函数

c++ - 命名空间到底是什么,为什么有必要

c - 从文件中读取字符串并将它们作为 C 中的整数存储在数组中

c - 如何使用 fgetc() 仅捕获字母并将输入存储到数组中

linux - 如何找到信号量未锁定的原因