如何在 Linux 和 C 中使用文件作为互斥锁?

Sim*_*one 7 c linux mutex

我有不同的进程同时访问 Linux 中的命名管道,我想让这种访问互斥。

我知道可以使用放置在共享内存区域中的互斥锁来实现这一点,但作为一种家庭作业,我有一些限制。

于是,我想到的是在文件上使用锁定原语来实现互斥;我做了一些尝试,但我不能让它工作。

这是我试过的:

flock(lock_file, LOCK_EX)

// critic section

flock(lock_file, LOCK_UN)
Run Code Online (Sandbox Code Playgroud)

不同的项目将使用不同的文件描述符但引用同一个文件。有可能实现这样的目标吗?你能提供一些例子吗。

Jon*_*ler 5

标准的锁定文件技术使用诸如O_EXCLonopen()调用之类的选项来尝试创建文件。您使用锁存储进程的PID,因此您可以确定该进程是否仍然存在(kill()用于测试)。您必须担心并发性 - 很多。

脚步:

  • 根据FIFO的名称确定锁定文件的名称
  • 打开锁文件(如果存在)
  • 检查使用它的进程是否存在
    • 如果其他进程存在,它有控制权(错误退出,或者等待它退出)
    • 如果其他进程不存在,则删除锁定文件
  • 此时,上次检查时锁定文件不存在。
  • 尝试使用创建open()O_EXCL当中的其他选项。
  • 如果可行,则您的进程创建了该文件 - 您有权继续。
  • 将您的 PID 写入文件;关闭它。
  • 打开 FIFO - 使用它。
  • 完成后 ( atexit()?) 删除锁定文件。

担心如果您打开锁定文件但没有读取 PID 会发生什么……是另一个进程刚刚创建了它并且尚未将其 PID 写入其中,还是在这样做之前它就死了?可能最好退出 - 关闭文件并重试(可能在随机之后nanosleep())。如果您多次获得空文件(比如连续 3 个),则假定该进程已死并删除锁定文件。

您可以考虑让拥有该文件的进程在 FIFO 打开时保持对该文件的咨询锁定。如果没有锁,则进程已经死亡。在打开文件和应用锁定之间仍然存在一个 TOCTOU(检查时间,使用时间)漏洞窗口。

仔细查看系统上的open()手册页,看看是否有其他选项可以帮助您。有时,进程使用目录 ( mkdir()) 而不是文件,因为即使是 root 也无法创建给定目录名称的第二个实例,但是您会遇到如何在资源打开的情况下知道进程的 PID 等问题。


Dav*_*d Z 5

我绝对建议使用实际的互斥锁(正如评论中所建议的那样);例如,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)


dmc*_*kee 4

您的示例与您将要使用的一样好flock (2)(毕竟,这只是一个“咨询”锁(实际上根本不是锁))。我的 Mac OS X 系统上的手册页有几个可能很重要的附带条件:

锁位于文件上,而不是文件描述符上。也就是说,通过 dup(2) 或 fork(2) 复制的文件描述符不会导致锁的多个实例,而是导致对单个锁的多个引用。如果持有文件锁的进程分叉并且子进程显式解锁该文件,则父进程将失去其锁

被阻塞等待锁的进程可以被信号唤醒。

两者都暗示了它可能失败的方式。


// 本来是一条评论,但我想引用一定长度的手册页