【问题标题】:Linux synchronization without pollingLinux 同步无轮询
【发布时间】:2016-09-20 17:47:08
【问题描述】:

原则上我想要的很简单。

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

要求./read 的每次调用都包含来自先前调用的资源快照,如果当前资源与快照匹配,./read 应等待(休眠)直到调用 ./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 可以在[1] 处释放mutex0,可以完成对./write 的整个调用,然后执行[2],但将无限期停止,因为./write 已经尝试唤醒任何休眠的@ 987654339@s 之前。

除了使用整个独立的成熟服务器进程之外,有没有简单的方法可以做我想做的事?此外,对于那些好奇的人,我想为 CGI 中的应用程序这样做。

【问题讨论】:

  • 你可以使用信号量而不是互斥体。
  • 在我看来你想要进程间通信。我建议阅读 linux 的命名管道或 ZeroMQ 而不是您的临时方案。
  • 你们的 cmets 都没有解决我指出的竞争条件。我知道信号量,我知道 IPC。
  • 如果读取释放信号量,然后在读取到达下一行之前写入信号量,信号量将为 1,读取将继续。有不同的问题吗?
  • 首先,如果我们简单地将上面的mutex0 替换为信号量,您所说的就没有意义。我认为您的意思是使用第二个信号量,所以我们称它为semaphore0semaphore1,其中semaphore0 替换上面的mutex0,而semaphore1 信号read。仍然不起作用,因为write 不知道增加多少次semaphore1,因为任何数量的reads 都可以休眠,如果您仍然认为自己是对的,请随时发布更具体的你正确的例子。

标签: c linux asynchronous synchronization glibc


【解决方案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 接口也足够了。读者安装一个 fanotifyFAN_MODIFY 标记,然后只需等待相应的事件。编写者不需要合作,除了使用咨询锁(它的存在只是为了在文件被修改时阻止读者阅读)。

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


advisory 文件锁定和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 标记,以便在文件被截断或写入时收到通知。在这种情况下,重要的是要记住,如果文件随后被替换(重命名,或删除并重新创建),读取器和写入器将看不到新文件,但将在旧文件上操作,现在不可见-文件系统文件内容。

给读者的程序流程:

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 接口在人类时间尺度上足够快速和可靠;在毫秒和亚毫秒的机器时间尺度上并非如此。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多