在Linux上使用fwrite作为原子进程

时间:2013-06-25 18:16:41

标签: c linux atomic fwrite

我正在Linux环境中开发C代码。我使用fwrite将一些数据写入某些文件。该计划将在经常断电(至少每天一次)的环境中运行。因此,我希望fwrite确保在写入数据时发生断电时不应更新该文件。它应该只在fwrite完成其工作时保存文件。如何使用影响文件的fwrite只有完成写入过程?

编辑:我使用fopen和wb来丢弃文件中的先前信息并写一个新文件,例如

FILE *rtng_p;
rtng_p = fopen("/etc/routing_table", "wb");
fwrite(&user_list, sizeof(struct routing), 40, rtng_p);

并且它是一些非常小的数据,长度为

3 个答案:

答案 0 :(得分:6)

首先将文件写入同一文件系统上的临时路径,如/etc/routing_table.tmp。然后只需将副本重命名为原始文件。重命名是保证原子的。

因此,调用顺序为fopenfwritefcloserename

答案 1 :(得分:2)

除了David Schwartz answer中给出的序列之外,您可以使用咨询锁定,例如flock(2)系统调用(或lockf(3) fcntl(2) Linux Filesystem Hierarchy F_SETLK ....)

这意味着在

之后添加
 FILE * fil = fopen("/etc/routing_table.tmp", "wb");

 if (!fil) 
   { perror("/etc/routing_table.tmp"); exit(EXIT_FAILURE); };
 if (flock(fileno(fil), LOCK_EX)) 
   { perror("flock LOCK_EX"); exit(EXIT_FAILURE); };

最后,你会

 if (fflush(fil)) /* flush the file before unlocking it!!*/
   { perror("fflush"); exit(EXIT_FAILURE); };
 if (flock(fileno(fil), LOCK_UN))
   { perror("flock LOCK_UN"); exit(EXIT_FAILURE); };
 if (fclose (fil))
   { perror("fclose"); exit(EXIT_FAILURE); };;
 if (rename("/etc/routing_table.tmp", "/etc/routing_table"))
   { perror("rename"); exit(EXIT_FAILURE); };

使用这种建议锁定可以确保即使程序的两个进程正在运行,也只有一个程序会写入该文件。

但可能是过度杀伤。

BTW,您似乎在/etc/中编写二进制数据。我认为这违反了习惯或惯例(见Linux Standard BaseAdvanced Linux Programming)。我希望/etc下的文件是文本的。也许您希望自己的文件位于/var/lib

另见{{3}}在线预订。

答案 2 :(得分:1)

在UNIX / Linux社区中有一个很大的争论,即open/write/close/rename模式(如David Schwartz's answer中所述)是否确实是原子的。请注意,此对话是关于write而不是fwrite

EXT4文件系统的主要作者不相信它应该根据POSIX保证,并且早期版本的文件系统不会将其视为原子。最终他投降并使这组操作成为EXT4的默认行为。但是,声称用户程序实际上应该执行open/write/fsync/close/rename

如果没有fsync,其他文件系统可能无法保证原子性,如果EXT4与noauto_da_alloc一起安装,那么该保证也会丢失。因此,如果您想要非常安全,则应在fsync close之后添加rename。我没有尝试使用fwrite,如果您使用fflush可能会有效。

有关详情,请参阅https://www.kernel.org/doc/Documentation/filesystems/ext4.txtauto_da_alloc部分。另请参阅EXT4主要作者撰写的文章:http://thunk.org/tytso/blog/2009/03/12/delayed-allocation-and-the-zero-length-file-problem/