原子打开并锁定文件

时间:2017-09-06 11:37:17

标签: c linux file

我有一个文件foo.hex,可由两个进程访问。一个进程具有O_RDONLY访问权限,另一个进程具有O_RDWR访问权限。

第一次启动系统时,读取过程不应在写入过程初始化之前访问该文件。

因此,我写了这样的东西来初始化文件。

fd = open("foo.hex", O_RDWR|O_CREAT, 0666);
flock(fd, LOCK_EX);

init_structures(fd);

flock(fd, LOCK_UN);

仍然有可能让读者进程在初始化之前访问该文件。

我无法以原子方式找到open()flock()的方法。除了互斥体之外,还有其他可能性以尽可能少的开销以优雅的方式实现我的目标(因为它只使用过一次,系统第一次启动时)?

4 个答案:

答案 0 :(得分:6)

让编写器创建一个名为“foo.hex.init”的文件,并在将其重命名为“foo.hex”之前初始化该文件。这样,读者永远不会看到未初始化的文件内容。

答案 1 :(得分:1)

另一种方法是让读者进程稍微睡一觉,然后在发现该文件尚不存在时重试,或者为空。

int open_for_read(const char *fname)
{
    int retries = 0;

    for (;;) {
        int fd = open(fname, O_RDONLY);
        if (fd == -1) {
            if (errno != ENOENT) return -1;
            goto retry;
        } 
        if (flock(fd, LOCK_SH)) {
            close(fd);
            return -1;
        }

        struct stat st;
        if (fstat(fd, &st)) {
            close(fd);
            return -1;
        }
        if (st.st_size == 0) {
            close(fd);
            goto retry;
        }
        return fd;

    retry:
        if (++retries > MAX_RETRIES) return -1;
        sleep(1);
    }
    /* not reached */
}

你需要在写入方面使用类似的代码,这样如果作者输掉了比赛,就不必重新开始。

答案 2 :(得分:1)

另一种方法可能是删除现有文件,重新创建它而没有任何进程访问它的权限,然后在写入后更改文件权限:

unlink("foo.hex");
fd = open("foo.hex", O_RDWR|O_CREAT|O_EXCL, 0);

init_structures(fd);

fchmod(fd, 0666);

如果你以root身份运行,这可能不会起作用。 (无论如何你不应该这样做......)

这会阻止任何进程在进行unlink()调用后使用旧数据。根据您的要求,可能会或可能不值得处理初始化新文件时不存在或可访问的文件所需的额外读者代码。

就个人而言,除非rename( "foo.hex.init", "foo.hex" )占用重要时间,否则我会使用init_structures()解决方案,并且一旦有新数据可用,就有一个真正的硬性要求不使用旧数据。但有时重要的人不习惯使用旧数据,而新数据的任何部分都可用,并且他们并不真正理解,“如果读者进程提前两毫秒开始,它将使用旧数据”。

答案 3 :(得分:0)

inter-process communications有很多方法。

在打开和初始化文件之前,或许使用一个命名的信号量,写入进程会锁定?然后读取过程也可以尝试锁定信号量,如果成功并且文件不存在,则解锁信号量并等待一段时间再重试。

最简单的方法,尤其是每次写入过程都会重新创建文件时,已经在the answer by John Zwinck