2017-02-09 102 views
1

在我的嵌入式系统上,我想确保在关闭文件时安全地写入数据 - 如果系统报告数据已保存,则用户应该能够立即切断电源。在调用syncfs()之前是否需要关闭文件

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

现在我的问题是:

  • 是否足以完成syncfs()
  • 我需要fclose()第一个FILE *(目录条目是最新的)吗?或者是fflush()足够?
  • 如果需要关闭,在关闭之前dup()文件描述符有用,所以我可以直接使用syncfs()

回答

1

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

+0

谢谢,但这不是一个选项。它会使每个write()块相对较长的时间,我们承担不起。文件关闭时是同步的好时机。 – Arnout

1

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

所以如果你使用一个系统调用,以确保您的数据正确同步到磁盘,它你做的文件系统sync(2)fsync(2)调用之前是绝对neccessary先fflush(3)你的进程缓冲区中的数据。

没有sync(2)是必要的fclose(3)甚至上close(2)时间发生,或在atexit()回调exit()之前,你的过程一样。
由于性能原因,操作系统缓冲区被写入延迟,而close(2)不是触发此类事件的事件。只要认为许多进程可以同时读取和写入同一个文件,并且每个触发文件系统刷新都可能很难实现。操作系统会定期触发此类调用,系统调用会在umount(2)之间,系统关闭以及对系统调用的sync(2)fsync(2)进行特定的调用。

如果你需要保持FILE *fd描述符开,只是做一个fflush(fd)为描述符,以确保操作系统具有fwrite(3) d或fprintf(3)版数据首次所有缓冲区。

所以最后,如果你正在使用<stdio.h>功能,首先做一个fflush()为你写的所有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(或者在打开的文件结构中)没有任何东西可以让你获得用于打开该文件的路径。使用临时文件的一种常见方式就是创建它们,并立即使用它们,因此没有人可以再次打开它。尽管您保留文件打开,您可以访问它,但没有路径指向它了。

+0

同样要小心,因为在使用闪存的嵌入式系统中强制同步是一个令人头痛的故障SD卡,它可以提前解决故障SD卡的使用寿命。 –

+0

谢谢你的冗长答案,但我询问了syncfs(),而不是sync()或fsync()。对于文件数据没有问题,我可以做fflush()+ fsync()(因为FILE *接口不提供同步操作,所以在这里没有替代混合FILE *和fd的选择)。问题实际上是目录项,在执行sync()时不会同步,并且在关闭()fd(我不确定)时仍然可能会更新。实际上你可以通过/ proc/self/fd来获取目录,当然它还没有被重命名()。 – Arnout

+0

@Arnout,只需阅读manpages,'syncfs(2)'与BSD'fsync(2)'具有相同的功能,可以在辅助存储中同步与打开的文件相关的数据。昨天我在Mac OS X上,它是从BSD派生出来的,并且没有访问Linux系统来检查手册页。 'FILE *'替代提供了一个'fileno(3)'函数,可以访问纯文件系统描述符,因此所有的系统调用都可以在那里使用。无论如何,目录条目永远不会同步(条目始终在文件后同步,以确保文件系统的完整性)并且与您所拥有的文件无关... –

相关问题