flock():在没有争用条件的情况下删除锁定的文件?

flock():在没有争用条件的情况下删除锁定的文件?,c,flock,C,Flock,我将flock()用于名为mutexe的进程间(即,某些进程可以决定对“some_name”持有锁,这是通过锁定临时目录中名为“some_name”的文件来实现的: lockfile = "/tmp/some_name.lock"; fd = open(lockfile, O_CREAT); flock(fd, LOCK_EX); do_something(); unlink(lockfile); flock(fd, LOCK_UN); 锁定文件应该在某个时候删除,以避免临时目录中充满数百

我将flock()用于名为mutexe的进程间(即,某些进程可以决定对“some_name”持有锁,这是通过锁定临时目录中名为“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 !)

有没有一种方法可以在不引入竞争条件的情况下在某个时刻删除锁文件?

如果您仅将这些文件用于锁定,而不实际写入它们,那么我建议您将目录项本身的存在视为持有锁的指示,并避免完全使用

为此,您需要构造一个操作来创建目录条目并报告错误(如果已经存在)。在Linux和大多数文件系统上,将
O_exc
传递到将适用于此。但某些平台和某些文件系统(尤其是较旧的NFS)不支持此操作。因此,建议使用另一种方法:

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

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

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


上述方法的一个缺点是它们不会阻塞:锁定尝试失败将导致错误,但不能等到锁可用。您必须轮询锁,这可能会因锁争用而出现问题。在这种情况下,您可能希望进一步脱离基于文件系统的方法,使用第三方实现。但是关于如何使用ipc互斥锁的一般问题已经被问过了,所以我建议您特别看看结果。顺便说一句,这些标签可能对您的帖子也很有用。

如果我回答了一个死问题,很抱歉:

锁定文件后,打开文件的另一个副本,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编号

我实际上在状态机模型中证明了这一点,使用了以下属性:

如果p_i在文件系统上锁定了一个描述符,那么关键部分中就没有其他进程了

如果p_i位于具有正确inode的stat之后,或者位于critical部分,那么它会将描述符锁定在文件系统上

  • 在Unix中,可以在文件打开时删除该文件-inode将一直保留,直到所有进程结束并将其包含在文件描述符列表中为止
  • 在Unix中,通过检查变为零时的链接计数,可以检查文件是否已从所有目录中删除
  • 因此,不必比较新旧文件路径的ino值,只需检查已打开文件的nlink计数即可。它假定该文件只是临时锁文件,而不是真正的互斥资源或设备

    lockfile = "/tmp/some_name.lock";
    
    for(int attempt; attempt < timeout; ++attempt) {
        int fd = open(lockfile, O_CREAT, 0444);
        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;
    
    lockfile=“/tmp/some_name.lock”;
    for(int尝试;尝试<超时;++尝试){
    int fd=打开(锁文件,O_CREAT,0444);
    int done=flock(fd,LOCK|EX | LOCK|NB);
    如果(完成!=0){
    关闭(fd);
    sleep(1);//由另一个进程持有的锁
    继续;
    }
    结构统计st0;
    fstat(fd和st0);
    如果(st0.st_nlink==0){
    关闭(fd);//锁文件已删除,创建一个新文件
    继续;
    }
    做某事;
    解除链接(lockfile);//解除锁定前,nlink:=0
    鸥群(fd,锁定);
    关闭(fd);//如果没有其他程序,则释放ino
    返回true;
    }
    返回false;
    
    问题是您试图实现细粒度锁定策略(文件表示资源),但您在共享粗粒度资源(文件系统)上存在争用。在更新细粒度锁之前,您需要对锁文件目录进行全局锁定,或者重新设计锁定策略。您可以详细说明您的需要吗?例如,如果所有程序都锁定相同的概念资源,为什么不让程序使用一个已知的文件名?您可以在解除链接并关闭文件后解锁?手册s“在fd指定的打开文件上应用或删除建议锁”的声明。您不应该在最后关闭fd以防止泄漏吗?不可移植!例如,在Windows上
    st_ino
    始终为0。状态机模型是什么?此解决方案不起作用,问题是关于3个进程a、B、C。此方法遇到的问题是,如果进程在持有锁时死亡,则没有可靠的方法自动关闭获取锁。相反,在
    flock
    方法中,如果一个进程在持有锁时死亡,该文件将保留在文件系统中,但它将不再
    flock
    ;其他进程现在可能获得锁。@davidg:That