没有轮询的Linux同步

时间:2016-09-20 17:47:08

标签: c linux asynchronous synchronization glibc

原则上我想要的很简单。

两个可执行文件./read./write分别从资源读取和写入(比如文件)。使用flock(2)可以在任意时间轻松防止./read./write之间的任意调用之间的竞争条件。

要求是./read的每次调用都包含先前调用的资源快照,如果当前资源与快照匹配,./read应等待(休眠),直到调用{ {1}}更改资源。

从我收集的内容来看,每个程序的程序流程应该是这样的:

./write

这种方法的主要问题是标记为//read.c obtain mutex0 read resource is resource same as our snapshot? release mutex0 [1] sleep until ./write says to wake up [2] obtain mutex0 read resource do something with resource release mutex0 //write.c obtain mutex0 change resource in some way tell any sleeping ./read's to wake up release mutex0 [1]的行之间存在明显的延迟。这意味着[2]可以./read释放mutex0[1]的整个调用可以完成,然后执行./write,但会无限期地停止,因为{ {1}}之前已经尝试过唤醒任何睡眠[2]

除了使用完整的单独的完整服务器进程外,还有什么方法可以做我想要的吗?另外,对于那些好奇的我想为CGI中的应用程序执行此操作。

1 个答案:

答案 0 :(得分:1)

不,读者的程序流程不正确。您需要某种锁定机制来防止在一次或多次读取过程中进行写入,以及某种唤醒机制,以便在写入完成时通知读者。

您的作者的程序流程没问题:

    # Initial read of file contents
    Obtain lock
        Read file
    Release lock

    # Whenever wishes to modify file:
    Obtain lock
        Modify file
        Signal readers
    Release lock

读者的程序流程应为:

    # Initial read of file contents
    Obtain lock
        Read file
    Release lock

    # Wait and respond to changes in file
    On signal:
        Obtain lock
            Read file
        Release lock    
        Do something with modified file contents

如果只有一个阅读器,则共享内存中的互斥(pthread_mutex_t)(所有作者和阅读器都可访问)就足够了;否则,我建议使用rwlock(pthread_rwlock_t)代替。为了唤醒任何等待的读者,在条件变量(pthread_cond_t)上广播。当然,困难在于设置共享内存。

咨询文件锁定和fanotify界面也足够了。读者安装fanotify FAN_MODIFY标记,只需等待相应的事件。作家不需要合作,除了使用咨询锁(存在只是为了阻止读者在修改文件时阅读)。

不幸的是,该界面目前需要CAP_SYS_ADMIN功能,您绝对不希望随机CGI程序拥有该功能。

咨询文件锁定和inotify接口就足够了,我认为最合适的是,当读者和编写者为每组操作打开和关闭文件时。这种情况下读者的程序流程是:

Initialize inotify interface
Add inotify watch for IN_CREATE and IN_CLOSE_WRITE for "file"

Open "file" read-only
    Obtain shared/read-lock
        Read contents
    Release lock
Close "file"

Loop:
    Read events from inotify descriptor.
    If IN_CREATE or IN_CLOSE_WRITE for "file":
        Open "file" read-only
            Obtain shared/read-lock
                Read contents
            Release lock
        Close "file"
        Do something with file contents

作家仍然只是

    # Initial read of file contents
    Open "file" for read-only
        Obtain shared/read-lock on "file"
            Read contents
        Release lock
    Close "file"

    # Whenever wishes to modify file:
    Open "file" for read-write
        Obtain exclusive/write-lock
            Modify file
        Release lock
    Close "file"

即使作者没有获得锁定,作者关闭文件时也会通知读者;唯一的风险是读者正在读取文件时写入另一组更改(通过另一个锁定 - 修改修饰符)。

即使修饰符用新文件替换文件,当新的文件准备就绪时,读者也会得到正确的通知(重新命名/链接在旧文件之上,或者新文件创建者关闭文件)。重要的是要注意,如果读者保持文件打开,他们的文件描述符将不会神奇地跳转到新文件,他们只会看到旧的(可能已删除)内容。

如果由于某些原因重要的是读者和作者不关闭文件,读者仍然可以使用inotify,而是使用IN_MODIFY标记,以便在文件被截断或写入时得到通知。在这种情况下,重要的是要记住,如果文件随后被替换(重命名,删除并重新创建),读者和作者将不会看到新文件,但将在旧的,现在看不见的文件上运行-filesystem文件内容。

读者的程序流程:

Initialize inotify interface
Add inotify watch for IN_MODIFY for "file"

Open "file" read-only
    Obtain shared/read-lock
        Read contents
    Release lock

    Loop:
        Read events from inotify descriptor.
        If IN_CREATE or IN_CLOSE_WRITE for "file":
            Obtain shared/read-lock on "file"
                Read contents
            Release lock
            Do something with file contents

作者的程序流程仍然几乎相同:

    # Initial read of file contents
    Open "file" for read-only
        Obtain shared/read-lock on "file"
            Read contents
        Release lock
    Close "file"

    Open "file" for read-write

    # Whenever writer wishes to modify the file:
    Obtain exclusive/write-lock
        Modify file
    Release lock

值得注意的是,inotify事件发生在事实之后。通常存在一些小的延迟,这可能取决于机器上的负载。因此,如果对文件更改的快速响应对于系统正常工作很重要,则可能必须使用共享内存方法中的互斥锁或rwlock和条件变量。

根据我的经验,这些潜伏期往往比典型的人类反应间隔短。因此,我认为 - 我建议你也这样做 - inotify界面在人类时间尺度上足够快速和可靠;不是毫秒和亚毫秒的机器时间尺度。