如何在 Linux 和 C 中使用文件作为互斥锁?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7324864/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to use a file as a mutex in Linux and C?
提问by Simone
I have different processes concurrently accessing a named pipe in Linux and I want to make this access mutually exclusive.
我有不同的进程同时访问 Linux 中的命名管道,我想让这种访问互斥。
I know is possible to achieve that using a mutex placed in a shared memory area, but being this a sort of homework assignment I have some restrictions.
我知道可以使用放置在共享内存区域中的互斥锁来实现这一点,但作为一种家庭作业,我有一些限制。
Thus, what I thought about is to use locking primitives on files to achieve mutual exclusion; I made some try but I can't make it work.
于是,我想到的是在文件上使用锁定原语来实现互斥;我做了一些尝试,但我不能让它工作。
This is what i tried:
这是我试过的:
flock(lock_file, LOCK_EX)
// critic section
flock(lock_file, LOCK_UN)
Different projects will use different file descriptors but referring to the same file. Is it possible to achieve something like that? Can you provide some example.
不同的项目将使用不同的文件描述符但引用同一个文件。有可能实现这样的目标吗?你能提供一些例子吗。
采纳答案by dmckee --- ex-moderator kitten
Your example is as good as you're going to get using flock (2)
(which is after all, merely an "advisory" lock (which is to say not a lockat all, really)). The man page for it on my Mac OS X system has a couple of possibly important provisos:
您的示例与您将要使用的一样好flock (2)
(毕竟,这只是一个“建议”锁(也就是说根本不是锁,真的))。我的 Mac OS X 系统上的手册页有几个可能重要的条件:
Locks are on files, not file descriptors. That is, file descriptors duplicated through dup(2) or fork(2) do not result in multiple instances of a lock, but rather multiple references to a single lock. If a process holding a lock on a file forks and the child explicitly unlocks the file, the parent will lose its lock
锁定在文件上,而不是文件描述符上。也就是说,通过 dup(2) 或 fork(2) 复制的文件描述符不会导致锁的多个实例,而是对单个锁的多次引用。如果持有文件锁的进程分叉并且子进程明确解锁文件,则父进程将失去其锁
and
和
Processes blocked awaiting a lock may be awakened by signals.
等待锁定的进程可能会被信号唤醒。
both of which suggest ways it could fail.
两者都表明了它可能失败的方式。
// would have been a comment, but I wanted to quote the man page at some length
// 本来是一个评论,但我想引用一定长度的手册页
回答by Jonathan Leffler
The standard lock-file technique uses options such as O_EXCL
on the open()
call to try and create the file. You store the PID of the process using the lock, so you can determine whether the process still exists (using kill()
to test). You have to worry about concurrency - a lot.
标准的锁定文件技术使用诸如O_EXCL
onopen()
调用之类的选项来尝试创建文件。您使用锁存储进程的PID,因此您可以确定该进程是否仍然存在(kill()
用于测试)。您必须担心并发性 - 很多。
Steps:
脚步:
- Determine name of lock file based on name of FIFO
- Open lock file if it exists
- Check whether process using it exists
- If other process exists, it has control (exit with error, or wait for it to exit)
- If other process is absent, remove lock file
- At this point, lock file did not exist when last checked.
- Try to create it with
open()
andO_EXCL
amongst the other options. - If that works, your process created the file - you have permission to go ahead.
- Write your PID to the file; close it.
- Open the FIFO - use it.
- When done (
atexit()
?) remove the lock file.
- 根据FIFO的名称确定锁定文件的名称
- 打开锁文件(如果存在)
- 检查使用它的进程是否存在
- 如果其他进程存在,它有控制权(错误退出,或者等待它退出)
- 如果其他进程不存在,则删除锁定文件
- 此时,上次检查时锁定文件不存在。
- 尝试使用创建
open()
和O_EXCL
当中的其他选项。 - 如果可行,则您的进程创建了该文件 - 您有权继续。
- 将您的 PID 写入文件;关闭它。
- 打开 FIFO - 使用它。
- 完成后 (
atexit()
?) 删除锁定文件。
Worry about what happens if you open the lock file and read no PID...is it that another process just created it and hasn't yet written its PID into it, or did it die before doing so? Probably best to back off - close the file and try again (possibly after a randomized nanosleep()
). If you get the empty file multiple times (say 3 in a row) assume that the process is dead and remove the lock file.
担心如果您打开锁定文件但没有读取 PID 会发生什么……是另一个进程刚刚创建了它并且尚未将其 PID 写入其中,还是在这样做之前它就死了?可能最好退出 - 关闭文件并重试(可能在随机之后nanosleep()
)。如果您多次获得空文件(比如连续 3 个),则假定该进程已死并删除锁定文件。
You could consider having the process that owns the file maintain an advisory lock on the file while it has the FIFO open. If the lock is absent, the process has died. There is still a TOCTOU (time of check, time of use) window of vulnerability between opening the file and applying the lock.
您可以考虑让拥有该文件的进程在 FIFO 打开时保持对该文件的咨询锁定。如果没有锁,则进程已经死亡。在打开文件和应用锁定之间仍然存在一个 TOCTOU(检查时间,使用时间)漏洞窗口。
Take a good look at the open()
man page on your system to see whether there are any other options to help you. Sometimes, processes use directories (mkdir()
) instead of files because even root can't create a second instance of a given directory name, but then you have issues with how to know the PID of the process with the resource open, etc.
仔细查看系统上的open()
手册页,看看是否有任何其他选项可以帮助您。有时,进程使用目录 ( mkdir()
) 而不是文件,因为即使是 root 也无法创建给定目录名称的第二个实例,但是您会遇到如何在资源打开的情况下知道进程的 PID 等问题。
回答by David Z
I'd definitely recommend using an actual mutex (as has been suggested in the comments); for example, the pthread library provides an implementation. But if you want to do it yourself using a file for educational purposes, I'd suggest taking a look at this answerI posted a while ago which describes a method for doing so in Python. Translated to C, it should look something like this (Warning: untested code, use at your own risk; also my C is rusty):
我绝对建议使用实际的互斥锁(正如评论中所建议的那样);例如,pthread 库提供了一个实现。但是,如果您想出于教育目的使用文件自己完成此操作,我建议您查看我不久前发布的这个答案,其中描述了在 Python 中执行此操作的方法。转换为 C,它应该看起来像这样(警告:未经测试的代码,使用风险自负;我的 C 也生锈了):
// each instance of the process should have a different filename here
char* process_lockfile = "/path/to/hostname.pid.lock";
// all processes should have the same filename here
char* global_lockfile = "/path/to/lockfile";
// create the file if necessary (only once, at the beginning of each process)
FILE* f = fopen(process_lockfile, "w");
fprintf(f, "\n"); // or maybe write the hostname and pid
fclose(f);
// now, each time you have to lock the file:
int lock_acquired = 0;
while (!lock_acquired) {
int r = link(process_lockfile, global_lockfile);
if (r == 0) {
lock_acquired = 1;
}
else {
struct stat buf;
stat(process_lockfile, &buf);
lock_acquired = (buf.st_nlink == 2);
}
}
// do your writing
unlink(global_lockfile);
lock_acquired = 0;