检测C中fwrite的光盘移除

时间:2013-02-15 21:30:35

标签: 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;
}

2 个答案:

答案 0 :(得分:2)

你需要改变你的方法。

如果你抽出已安装的媒体,你可能会惊慌失措你的内核(因为它会保存代表内存中挂载的文件系统的复杂数据结构),并打破媒体本身。

我已经摧毁了相当多的USB记忆棒 - 它们的内部小逻辑处理分配和磨损均衡不喜欢在中途失去电力,而最便宜的那些似乎没有能够提供的电容器足够的功率使它们保持足够长的运行时间,以确保一致的状态 - 但SD卡和更昂贵的USB记忆棒可能会更好地存活。

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

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

(编辑添加:如果您使用与被测介质设备完全相同的原始分配块大小写入块,则可以避免设备上的读取 - 修改 - 写入周期缓慢。设备仍然会进行磨损均衡,但这只是意味着您编写的块位于闪存芯片中的随机物理位置。本机块大小取决于媒体设备。可以通过观察所需的时间来测量本机块大小。读取和写入不同大小的块,但我认为对于损坏测试,两个足够大的功率应该最好 - 比如256k或262144字节。最好让用户分别为每个设备设置它,并使用其中任何一个制造商提供的信息,或单独的测试程序,以找出正确的值。)

您不希望使用mmap(),因为媒体错误和媒体不可用而导致的SIGBUS信号正确处理非常棘手。在我看来,低级unistd.h I / O最适合这个。

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

答案 1 :(得分:1)

从我的评论中回答:

在你的写作功能中你应该有:

for(j=0; j < KB; j++)
{
    uint32_t bytes_written = fwrite(a, sizeof(int), size, pFile);
    if(bytes_written < size)
    {
        perror("fwrite");
        break;
    }
    stat = fsync(pFile);
    if(stat < 0)
    {
        perror("fysnc");
        break;
    }
}

并在您的阅读功能中:

uint32_t read_bytes_count = fread(a, sizeof(a), 1, pFile);
if(read_bytes_count < sizeof(a))
    break;

此外,如果您有C99编译器,请使用stdint.h中提供的固定大小类型,例如:uint32_t,...