原则上我想要的很简单。
两个可执行文件./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中的应用程序执行此操作。
答案 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界面在人类时间尺度上足够快速和可靠;不是毫秒和亚毫秒的机器时间尺度。