write()返回后立即从文件中读取()是否安全?

我有一个非常具体的应用程序,我需要一个带有永久存储的自动增量variables。

准确地说,我将一个intvariables的十进制表示存储在一个文件中。 为了生成下一个数字,我从文件中read() ,将内容转换回int ,然后将1 write()回到文件中。 我不需要并发访问这些数据。 从一个进程只有一个线程调用函数来检索自动递增数字。 该程序运行在embedded式环境中,没有人可以访问控制台,所以安全性不应该成为问题。 如果重要,它将在MIPS上的Linux 2.6.24上运行。
问题是,我没有得到100%可重现的结果。 有时我得到重复的数字,这是我的申请无法接受的。

我的实现如下。

在开始申请时,我有:

 int fd = open("myfile", O_RDWR|O_CREAT|O_SYNC, S_IRWXU|S_IRWXG|S_IRWXO); 

而自动递增function:

 int get_current(int fd) { char value[SIZE]; lseek(fd, 0, SEEK_SET); read(fd, value, SIZE); return atoi(value); } int get_next(int fd) { char value[SIZE]; int cur = get_current(fd); memset(value, 0, SIZE); sprintf(value, "%d", cur + 1); lseek(fd, 0, SEEK_SET); write(fd, value, SIZE); //fsync(fd); /* Could inserting this be the solution? */ return (cur + 1); } 

为了代码可读性,我故意省略了上面的错误检查。 我有代码来检查所有系统调用的返回值。

代码最初是由另一个人编写的,而现在我已经发现了这个问题,解决这个问题的第一步就是找出可能导致这个问题的原因。 我担心这可能与文件访问的caching方式有关。 我知道我什么时候write()我没有保证数据实际上到达了物理介质,但是可以安全地调用read()而不调用fsync()并且仍然得到可预测的结果吗? 如果是这样,那么我就没有想法了;)

感谢您的阅读。

是的,写作后立即阅读是安全的。 在类Unix系统中,当write()返回时将数据安全地放在内核缓冲池中,并将返回到其他需要读取数据的进程。 当使用O_SYNC,O_DSYNC,O_FSYNC(确保数据写入磁盘)和Windows系统时,应用类似的注释。 显然,当aio_write()调用返回时,异步写入不会完成,但是当完成信号发送时,它将会完成。

但是,您的问题出现了,因为您不能确保您一次只有一个进程或线程访问该文件。 您必须确保您获得串行访问,以便您不会同时从文件中读取两个进程(或线程)。 这是DBMS术语中的“丢失更新”问题。

您需要确保一次只有一个进程可以访问。 如果您的进程合作,您可以使用建议锁定(通过POSIX系统上的fcntl() )。 如果你的流程不合作,或者你不确定,你可能需要去强制锁定,或者完全使用其他技术。

是的,如果你write()到一个文件,然后从它read() ,你应该看到你刚刚写的数据。 如果另一个进程或线程在此期间覆盖了该文件,或者write()实际上失败,则是例外。

一个文件的内容是一个非常糟糕的方式来实现一个原子计数器。 你的计数有多大? 如果它不是很大,一个简单的方法是写一个字节(无所谓)来递增计数器,并使用fstatst_size )来读取计数器。 ftrunc可以将计数器清零。

一个更清晰的方式来实现你想要的是将文件(使用mmap )进行内存映射,不仅存储计数,还存储已初始化为进程共享的pthread_mutex_t ,并在更新计数时将其锁定。

你可以使用mmap另一种方法是如果你有C1x原子类型(_Atomic _Atomic int ),但是你将不得不等待5 – 10年。 :-)或者你可以使用gcc intrinsics或asm来进行原子操作。 这个解决方案迄今为止性能最好(比pthread_mutex_t方法略好一些,比write方法快数百倍)。