我有不同的进程同时访问 Linux 中的命名管道,我想让这种访问互斥。
我知道可以使用放置在共享内存区域中的互斥锁来实现这一点,但作为一种家庭作业,我有一些限制。
于是,我想到的是在文件上使用锁定原语来实现互斥;我做了一些尝试,但我不能让它工作。
这是我试过的:
flock(lock_file, LOCK_EX)
// critic section
flock(lock_file, LOCK_UN)
Run Code Online (Sandbox Code Playgroud)
不同的项目将使用不同的文件描述符但引用同一个文件。有可能实现这样的目标吗?你能提供一些例子吗。
标准的锁定文件技术使用诸如O_EXCL
onopen()
调用之类的选项来尝试创建文件。您使用锁存储进程的PID,因此您可以确定该进程是否仍然存在(kill()
用于测试)。您必须担心并发性 - 很多。
脚步:
open()
和O_EXCL
当中的其他选项。atexit()
?) 删除锁定文件。担心如果您打开锁定文件但没有读取 PID 会发生什么……是另一个进程刚刚创建了它并且尚未将其 PID 写入其中,还是在这样做之前它就死了?可能最好退出 - 关闭文件并重试(可能在随机之后nanosleep()
)。如果您多次获得空文件(比如连续 3 个),则假定该进程已死并删除锁定文件。
您可以考虑让拥有该文件的进程在 FIFO 打开时保持对该文件的咨询锁定。如果没有锁,则进程已经死亡。在打开文件和应用锁定之间仍然存在一个 TOCTOU(检查时间,使用时间)漏洞窗口。
仔细查看系统上的open()
手册页,看看是否有其他选项可以帮助您。有时,进程使用目录 ( mkdir()
) 而不是文件,因为即使是 root 也无法创建给定目录名称的第二个实例,但是您会遇到如何在资源打开的情况下知道进程的 PID 等问题。
我绝对建议使用实际的互斥锁(正如评论中所建议的那样);例如,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;
Run Code Online (Sandbox Code Playgroud)
您的示例与您将要使用的一样好flock (2)
(毕竟,这只是一个“咨询”锁(实际上根本不是锁))。我的 Mac OS X 系统上的手册页有几个可能很重要的附带条件:
锁位于文件上,而不是文件描述符上。也就是说,通过 dup(2) 或 fork(2) 复制的文件描述符不会导致锁的多个实例,而是导致对单个锁的多个引用。如果持有文件锁的进程分叉并且子进程显式解锁该文件,则父进程将失去其锁
和
被阻塞等待锁的进程可以被信号唤醒。
两者都暗示了它可能失败的方式。
// 本来是一条评论,但我想引用一定长度的手册页