flock():删除没有竞争条件的锁定文件?

我正在将flock()用于进程间命名的互斥锁(即某些进程可以决定对“some_name”进行锁定,这是通过在temp目录中锁定名为“some_name”的文件来实现的:

lockfile = "/tmp/some_name.lock"; fd = open(lockfile, O_CREAT); flock(fd, LOCK_EX); do_something(); unlink(lockfile); flock(fd, LOCK_UN); 

应该在某个时候删除锁定文件,以避免使用数百个文件填充临时目录。

但是,此代码中存在明显的竞争条件; 进程A,B和C的示例:

 A opens file A locks file B opens file A unlinks file A unlocks file B locks file (B holds a lock on the deleted file) C opens file (a new file one is created) C locks file (two processes hold the same named mutex !) 

有没有办法在某些时候删除锁文件而不引入这种竞争条件?

对不起,如果我回答一个死的问题:

锁定文件后,打开它的另一个副本,fstat两个副本并检查inode编号,如下所示:

 lockfile = "/tmp/some_name.lock"; while(1) { fd = open(lockfile, O_CREAT); flock(fd, LOCK_EX); fstat(fd, &st0); stat(lockfile, &st1); if(st0.st_ino == st1.st_ino) break; close(fd); } do_something(); unlink(lockfile); flock(fd, LOCK_UN); 

这可以防止竞争条件,因为如果程序持有对仍在文件系统上的文件的锁定,则具有剩余文件的每个其他程序将具有错误的inode编号。

我实际上在状态机模型中使用以下属性certificate了它:

如果P_i在文件系统上锁定了描述符,则临界区中没有其他进程。

如果P_i在具有正确inode或关键部分的stat之后,则它将描述符锁定在文件系统上。

如果您只使用这些文件进行锁定,并且实际上没有写入它们,那么我建议您将目录条目本身的存在视为保持锁定的指示,并避免完全使用flock

为此,您需要构造一个创建目录条目的操作,并报告错误(如果已存在)。 在Linux和大多数文件系统上,将O_EXCL传递给open将适用于此。 但是某些平台和某些文件系统(特别是较旧的NFS)不支持此function。 因此, open的手册页提示了一个替代方案:

想要使用锁文件执行primefaces文件锁定的可移植程序,并且需要避免依赖对O_EXCL NFS支持,可以在同一文件系统上创建一个唯一的文件(例如,合并主机名和PID),并使用link (2)来建立一个锁定文件的链接。 如果link (2)返回0,则锁定成功。 否则,在唯一文件上使用stat (2)来检查其链接计数是否已增加到2,在这种情况下锁定也是成功的。

因此,这看起来像是一个正式记录的锁定方案,因此表明了一定程度的支持和最佳实践建议。 但我也见过其他方法。 例如, bzr在大多数地方使用目录而不是符号链接。 引自其源代码 :

锁在磁盘上由特定名称的目录表示,该目录包含信息文件。 通过将临时目录重命名到位来完成锁定。 我们使用临时目录,因为对于所有已知的传输和文件系统,我们认为只有一次声明锁定的尝试将成功,而其他尝试将失败。 (文件不会这样做,因为某些文件系统或传输只有重命名和覆盖,这使得很难分辨谁赢了。)

上述方法的一个缺点是它们不会阻塞:失败的锁定尝试将导致错误,但不要等到锁定变为可用。 您将不得不轮询锁定,这可能会因锁定争用而出现问题。 在这种情况下,您可能希望进一步偏离基于文件系统的方法,而是使用第三方实现。 但是有关如何进行ipc互斥的常见问题已经被提出,所以我建议你搜索[ipc] [mutex]并查看结果,特别是这个 。 顺便说一句,这些标签也可能对你的post很有用。

  1. 在unix中,可以在文件打开时删除它 – ino将被保留,直到所有进程都已结束,并将其放在文件描述符列表中
  2. 在unix中,可以通过检查链接计数来检查文件是否已从所有目录中删除,因为它变为零

因此,您只需检查已打开文件的nlink计数,而不是比较旧/新文件路径的ino值。 它假设它只是一个短暂的锁定文件而不是真正的互斥资源/设备。

 lockfile = "/tmp/some_name.lock"; for(int attempt; attempt < timeout; ++attempt) { int fd = open(lockfile, O_CREAT); int done = flock(fd, LOCK_EX | LOCK_NB); if (done != 0) { close(fd); sleep(1); // lock held by another proc continue; } struct stat st0; fstat(fd, &st0); if(st0.st_nlink == 0) { close(fd); // lockfile deleted, create a new one continue; } do_something(); unlink(lockfile); // nlink :=0 before releasing the lock flock(fd, LOCK_UN); close(fd); // release the ino if no other proc return true; } return false;