4

我有一个嵌入式环境,用户可以在其中插入或移除 USB 闪存驱动器。我想知道驱动器是否已被移除,或者当我尝试写入驱动器时是否存在其他问题。但是,Linux 只是将信息保存在其缓冲区中并返回而没有指示错误。
我使用的计算机带有 2.4.26 内核和 libc 2.3.2。
我以这种方式安装驱动器:
i = mount(MEMORY_DEV_PATH, MEMORY_MNT_PATH, "vfat", MS_SYNCHRONOUS, NULL);
有效:
50:/root # mount
/dev/scsi/host0/bus0/target0/lun0/part1 on /mem type vfat (rw,sync)
50:/root #

后来,我尝试将文件复制到其中:

int ifile, ofile;
ifile = open("/tmp/tmpmidi.mid", O_RDONLY);
if (ifile < 0)
{
    perror("open in");
    break;
}
ofile = open(current_file_name.c_str(), O_WRONLY | O_SYNC);
if (ofile < 0)
{
    perror("open out");
    break;
}
#define BUFSZ 256
char buffer[BUFSZ];
while (1)
{
    i = read(ifile, buffer, BUFSZ);
    if (i < 0)
    {
        perror("read");
        break;
    }
    j = write(ofile, buffer, i);
    if (j < 0)
    {
        perror("write");
        break;
    }
    if (i != j)
    {
        perror("Sizes wrong");
        break;
    }
    if (i < BUFSZ)
    {
        printf("Copy is finished, I hope\n");
        close(ifile);
        close(ofile);
        break;
    }
}

如果这段代码使用写保护的 USB 存储器执行,结果是

Copy is finished, I hope

在来自控制台内核的一连串错误消息中。
我相信如果我只是移除 USB 驱动器(不卸载它),也会发生同样的事情。
我也摆弄过devfs。我想出了如何让它自动安装驱动器(使用 REGISTER 事件),但是当我拉出内存时它似乎永远不会触发 UNREGISTER。
如何在我的程序中确定我是否已成功创建文件?

7 月 4 日更新:我没有检查 close() 的结果是一个愚蠢的疏忽。不幸的是,该文件可以正确关闭。所以这没有帮助。fsync() 呢?这听起来是个好主意,但也没有发现错误。
如果我有这样的东西,/sys 中可能会有一些有趣的信息。我相信直到 2.6 才添加。?
关于我的闪存驱动器质量的评论可能是有道理的。这是较早的之一。事实上,写保护开关现在似乎极为罕见。
我想我必须使用 overkill 选项:创建一个文件,卸载并重新安装驱动器,然后检查文件是否存在。如果这不能解决我的问题,那么事情就真的搞砸了!提醒自己:确保您尝试创建的文件不存在!
顺便说一句,这确实是一个 C++ 程序。您可以通过 .c_str() 来判断,为了简单起见,我打算将其编辑掉。

4

5 回答 5

3

如果您的应用程序想要将一个或多个文件保存到 U 盘,并让用户在小 LED 熄灭后立即拔出 U 盘,您需要

  1. mount()写入文件之前的 U 盘

    您不需要为此安装 SYNC,这有助于使用劣质 USB 记忆棒

  2. 使用正确的代码保存文件

    您对 C 低级 I/O 的尝试是非常不正确的。特别是,任何read()orwrite()都允许返回一个短计数,即介于 1 和请求大小之间的任何正值(包括在内),而不表示任何类型的错误。

  3. fsync()U 盘上的文件

    验证它是否返回成功。如果是这样,那么您就知道数据已进入 USB 记忆棒。

  4. fsync()包含 U 盘上文件的目录

    验证它是否返回成功。如果是这样,那么您知道文件元数据已进入 USB 记忆棒,即使用户拔出 USB 记忆棒,该文件现在也应该可以访问。

  5. umount()U 盘

    这将一直阻塞,直到 USB 记忆棒准备好断开连接,因此您的应用程序应该看起来像是正在保存到文件中,直到umount()返回。

    如果您执行了fsync()上述操作,则umount()应该非常直接。根据文件系统的不同,内核可能希望做一些记账,但无论如何都不应该花很长时间。

其他任何事情都是不可靠的。您可以通过安装具有同步访问的 VFAT 分区来做出某些假设,但这基本上只是手动操作。

如果您不想以 root 权限运行您的应用程序,您始终可以编写一个简单的特权服务来管理挂载和卸载。如果您怀疑在某个时候可能需要多个应用程序,我会非常热情地建议您这样做,因为只有集中式安装程序/卸载程序才能判断媒体何时准备就绪。(如果另一个应用程序同时写入同一个 USB 媒体,它可能会延迟返回“未安装”消息。顺便说一下,当您不需要同步安装 USB 媒体时,这会很好。)我个人会使用 Unix 域数据报套接字/var/run/进行/var/run/mounter.socket进程间通信。

最后,如果您的 Linux 内核配置正确并且已/sys/挂载分区,您可以扫描所有/sys/block/sd?/目录以查找可移动媒体:

  • /sys/block/sd?/removable将包含一个非零十进制数字字符串
  • /sys/block/sd?/size包含以 512 字节为单位的设备大小,作为十进制数字字符串
  • /sys/block/sd?/device/vendor包含供应商名称作为字符串
  • /sys/block/sd?/device/model包含模型名称作为字符串

这些条目是硬件级别的,只要 USB 记忆棒连接并通电,就可以使用这些条目;安装与否都没有关系。如果/当用户拔出 U 盘时,整个设备目录树将立即消失。

于 2012-07-04T04:52:47.963 回答
2

如果要检测所有写入错误,需要检查的不仅仅是返回码write()——还必须调用fsync()(并检查返回值),还要检查close().

于 2012-07-04T05:32:26.887 回答
1

简短的回答:问题出在操作系统和/或 USB 驱动器上。即使设置了写保护开关,Linux 也会(有时?)愉快地将 USB 驱动器挂载为可写。然而,当它实际尝试写入时,驱动器拒绝,这或多或少地被视为有缺陷的驱动器。(我不确定为什么会发生这种情况。我的猜测是闪存驱动器没有向操作系统报告其只读状态。这可能取决于您使用的闪存驱动器的品牌和型号。)

我可以在 3.2.0-26 内核(特别是 Ubuntu 12)上使用(非常旧的)USB 密钥获得相同的行为。我可以毫无问题地安装写保护的 USB 密钥读写,如果我将文件复制到它,'cp' 不会抱怨。该文件甚至可能出现在目录中(由于缓冲),但实际上没有写入任何内容。我确实在系统日志中收到了很多错误消息。

如果我是你,我会尝试实际写入闪存驱动器并确保它成功,然后再假设驱动器实际上是可写的。具体来说,我会:

  1. 将驱动器安装为可写。

  2. 在驱动器上创建一个具有唯一名称的新文件。

  3. 卸载驱动器以清空缓冲区。

  4. 重新安装驱动器。

  5. 检查新文件是否仍然存在并包含您写入的数据。

  6. 删除测试文件。

我对硬件和驱动程序状态的了解还不够,无法告诉您是否有办法从 API 检测不可写的驱动器——可能有,但我不知道。但即使有,这种情况下也会检测到运行不正常的 USB 驱动器。

更新:

我做了更多的研究,结果发现如果设置了写保护开关,对文件句柄执行的 fsync() 将失败。因此,我收回上述建议。相反,这是我的测试程序:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int
main(int argc, char *argv[]) {
    char *path;
    int f;
    size_t stat;
    const char *message = "Hello, world.\n";

    if (argc != 2) {
        printf("Need filename\n");
        exit(1);
    }

    path = argv[1];

    f = open(path, O_CREAT | O_WRONLY | O_SYNC, S_IRWXU);
    if (f < 0) {
        perror("open out");
        exit(1);
    }/* if */

    stat = write(f, message, strlen(message));
    if (stat < 0) {
        perror("write");
        exit(1);
    }/* if */

    stat = fsync(f);
    if (stat) {
        perror("fsync");
        exit(1);
    }

    stat = close(f);
    if (stat) {
        perror("close");
        exit(1);
    }/* if */

    printf("(Apparently) successfully wrote '%s'\n", path);
    return 0;
}

如果设备不可写,这应该在 fsync() 调用中失败。

于 2012-07-04T05:13:40.133 回答
0

这是 POSIX 做事的方式。如果返回值write-1你肯定知道出了什么可怕的错误。但是,如果write返回0,也可能出现问题。检查errno变量以查看它是否与此处显示的预定义写入错误之一匹配:http: //www.kernel.org/doc/man-pages/online/pages/man2/write.2.html

于 2012-07-04T04:12:59.350 回答
0

如果你找不到解决方案,你可以试试这个丑陋的 hack。在写入和关闭输出文件后,您可以简单地尝试以读取模式打开它并检查它的大小是否正确。如果您真的想确保它具有正确的内容,您可以验证它是否具有与您刚刚编写的文件相同的校验和。这假设操作系统将直接从 USB 驱动器读取文件,而不是某种缓存。

于 2012-07-04T04:32:32.200 回答