只要write()返回,从文件中读取()是否安全?

时间:2010-12-20 03:04:31

标签: c linux



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

确切地说,我将int变量的十进制表示存储在文件中。要从文件中生成下一个数字read(),请将内容转换回int,将1和write()添加回文件。我不需要并发访问这些数据。一个进程中只有一个线程调用函数来检索自动增量编号。该程序在嵌入式环境中运行,没有人可以访问控制台,因此不应该担心安全问题。如果重要,它将在MIPS上的Linux 2.6.24上运行。
问题是,我没有获得100%可重复的结果。有时我会重复数字,这对我的申请来说是不可接受的。

我的实现如下。

在启动应用程序时,我有:

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

自动增量功能:

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);
}

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

代码最初由另一个人编写,现在我已经检测到了这个问题,解决它的第一步是找出可能导致它的原因。我担心它可能与缓存文件访问的方式有关。我知道当我write()我没有保证数据实际到达物理媒介时,但是在没有调用read()的情况下调用fsync()并且仍能获得可预测的结果是否安全?如果是,那我就没有想法;)

感谢您的阅读。

3 个答案:

答案 0 :(得分:5)

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

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

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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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

实现所需内容的更简洁方法是对文件进行内存映射(使用mmap)并且不仅存储计数,还存储初始化为进程共享的pthread_mutex_t,以及更新计数时锁定它。

另一种可以使用mmap的方法是,如果你有C1x原子类型(_Atomic int),但你必须等待5到10年。 :-)或者您可以使用gcc intrinsics或asm进行原子操作。此解决方案具有迄今为止最佳的性能(略微优于pthread_mutex_t方法,并且比write方法快数百倍。)