1

在我的嵌入式系统上,我想确保在关闭文件时数据被安全写入 - 如果系统报告数据已保存,用户应该能够立即移除电源。

我知道执行此操作的正确方法是fsync()fclose()fsync()在目录上(cfr.this blog entry)。但是,在我的情况下,获取目录的文件描述符有点棘手(我必须通过/proc/self/fd查找文件名并从那里派生目录)。对我来说,只在整个文件系统上做会更简单syncfs()——我知道这是文件系统上唯一打开的文件。

现在我的问题是:

  • 做就够了吗syncfs()
  • 我需要fclose()FILE *一个(目录条目是最新的)吗?还是fflush()足够了?
  • 如果需要关闭,dup()在关闭之前对文件描述符有用吗,以便我可以直接使用它syncfs()
4

2 回答 2

2

首先,不要将标准库<stdio.h>调用(如fprintf(3)or fopen(3))与系统调用(如open(2)orclose(2)sync(2))混为一谈,因为前者是库例程,它们使用进程内缓冲区来存储系统不知道的临时数据,而其他是操作系统接口,使系统从现在开始负责数据维护。您将很容易区分它们,因为前者使用FILE *描述符进行操作,而最后使用int整数描述符进行操作。

因此,如果您使用系统调用来确保您的数据正确同步到磁盘,则绝对有必要fflush(3)sync(2)在执行文件系统或fsync(2)调用 之前首先处理进程的缓冲区数据。

sync(2)保证在时间fclose(3)或什至close(2)准时发生,或者在atexit()您的进程之前执行的回调中发生exit()
出于性能原因,操作系统缓冲区被延迟写入,并且close(2)不是使其触发此类事件的事件。试想一下,许多进程可以同时读取和写入同一个文件,并且每个close(2)触发文件系统刷新都可能很难实现。操作系统会定期、umount(2)系统调用、系统关闭以及对系统调用的特定调用sync(2)触发此类fsync(2)调用。

如果您需要保持FILE *fd描述符打开,只需fflush(fd)对该描述符执行 a 以确保操作系统首先拥有所有用于fwrite(3)d 或fprintf(3)ed 数据的缓冲区。

所以最后,如果您使用<stdio.h>函数,首先对您写入fflush()的所有描述符执行 a ,或者调用以告诉 stdio 在一次调用中同步所有描述符。然后执行or调用以确保您的所有数据物理上都在磁盘上。无需关闭任何东西。FILE *fflush(NULL);sync(2)fsync(2)

FILE *fd;
...
fflush(fd);
fsync(fileno(fd));
/* here you know that up to the last write(2) or fwrite(3)...
 * data is synced to disk */

顺便说一句,您/dev/fd/<number>获取描述符的方法(您以前有过)是错误的,原因有两个:

  • 一旦你关闭你的描述符,/dev/fd/<number>就不再是你想要的描述符了。通常,它甚至不存在。试试这个:

    #include <string.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <errno.h>
    
    int main()
    {
        int fd;
        char fn[] = "/dev/fd/1";
    
        close(1); /* close standard output */
        fd = open(fn, O_RDONLY); /* try to reopen from /dev/fd */
        if (fd < 0) {
            fprintf(stderr,
                    "%s: %s(errno=%d)\n",
                    fn,
                    strerror(errno),
                    errno);
            exit(EXIT_FAILURE);
        }
        exit(EXIT_SUCCESS);
    } /* main */
    
  • 您无法仅使用文件描述符获取打开文件所属的目录。在多链接文件中,可能有数千个目录指向它。inode(或打开的文件结构)上没有任何内容可让您获取用于打开该文件的路径。使用临时文件的一种常见方法是创建它们并立即创建它们unlink(2),因此没有人可以再次打开它。只要您保持文件打开,您就可以访问它,但不再有指向它的路径。

于 2017-02-10T07:49:52.143 回答
1

在文件系统(/etc/fstab)中启用“同步”标志,默认为“异步”(禁用)。启用此标志后,对相应文件系统的所有更改都会立即刷新到磁盘。这会使您的整个文件系统变慢,但根据您的嵌入式系统要求,这可能是一个很好的选择。

于 2017-02-09T12:21:06.757 回答