flock()似乎不适用于fork()

时间:2015-12-31 06:10:14

标签: c linux

这是令人尴尬的,显然我不(再次?)了解有关分叉的基本内容......

我期望下面的代码(在Linux,Centos 6.3下)打印

lock returned 0
unlock
lock returned 0
unlock

但事实并非如此,两个锁都会立即成功:

lock returned 0
lock returned 0
unlock
unlock

为什么?

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/file.h>

void main() {
    int fd = open("lock.txt", O_WRONLY | O_CREAT);
    int lock_ret;

    fork();

    lock_ret = flock(fd, LOCK_EX);
    printf("lock returned %d\n", lock_ret);
    fflush(stdout);

    sleep(4);

    printf("unlock\n");
    fflush(stdout);
}

如果我删除fork()并且只是手动启动两个进程,那么一切都按预期工作,一个锁成功,另一个阻塞并稍后成功。

4 个答案:

答案 0 :(得分:2)

据男人们说:

  

flock()创建的锁与文件相关联,或者更多   确切地说,是一个打开文件表条目。这意味着重复文件   描述符(例如,由fork(2)或dup(2)创建)指的是   相同的锁,可以使用任何一个修改或释放此锁   这些描述符。此外,锁定由一个释放   对任何这些重复描述符进行显式LOCK_UN操作,或   当所有这些描述符都被关闭时。

因此,因为您在打开文件后分叉进程,所以它们共享同一个锁。这意味着另一个进程不能聚集在同一个文件上,直到child和parent关闭文件。

答案 1 :(得分:2)

如果您希望不在叉子中保留独占锁定,可以使用fcntl

struct flock fd_lock = { F_RDLCK, SEEK_SET, 0, 0, 0 };

fcntl(fd, F_SETLK, &fd_lock);  // not across fork/exec

答案 2 :(得分:2)

flock()手册中有一些狡猾的措辞:

  

flock()创建的锁与打开的文件表条目相关联。这意味着重复的文件描述符(例如,由fork(2)dup(2)创建)引用相同的锁,并且可以使用任何这些描述符修改或释放此锁。

您可以通过更全面地检测程序并明智地添加另一个fork来证明这一点。确保正确创建文件也是一个好主意 - 当您使用O_CREAT时,open()需要第三个参数来指定文件模式。

#include <fcntl.h>
#include <stdio.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>

int main(void)
{
    printf("%ld:%d: Before fork 1\n", (long)time(0), (int)getpid());
    fork();
    printf("%ld:%d: After  fork 1\n", (long)time(0), (int)getpid());

    int fd = open("lock.txt", O_WRONLY | O_CREAT, 0644);
    int lock_ret;

    fork();
    printf("%ld:%d: After  fork 2\n", (long)time(0), (int)getpid());

    lock_ret = flock(fd, LOCK_EX);
    printf("%ld:%d: lock returned %d\n", (long)time(0), (int)getpid(), lock_ret);
    fflush(stdout);

    sleep(4);

    lock_ret = flock(fd, LOCK_UN);
    printf("%ld:%d: unlock returned %d\n", (long)time(0), (int)getpid(), lock_ret);
    fflush(stdout);
    int corpse;
    int status;
    while ((corpse = wait(&status)) != -1)
        printf("%ld:%d: PID %d died with status 0x%.4X\n", (long)time(0), (int)getpid(), corpse, status);
    return 0;
}

示例运行:

1451543977:5731: Before fork 1
1451543977:5731: After  fork 1
1451543977:5731: After  fork 2
1451543977:5731: lock returned 0
1451543977:5732: After  fork 1
1451543977:5733: After  fork 2
1451543977:5733: lock returned 0
1451543977:5732: After  fork 2
1451543977:5734: After  fork 2
1451543981:5731: unlock returned 0
1451543981:5733: unlock returned 0
1451543981:5732: lock returned 0
1451543981:5731: PID 5733 died with status 0x0000
1451543985:5732: unlock returned 0
1451543985:5734: lock returned 0
1451543989:5734: unlock returned 0
1451543989:5732: PID 5734 died with status 0x0000
1451543989:5731: PID 5732 died with status 0x0000

请注意如何阻止不共享相同打开文件描述的进程;那些共享相同的打开文件描述的内容不会被阻止。

另请注意时间戳和(特别是)PID标记&#39;输出有助于使输出明确。

答案 3 :(得分:1)

来自Linux flock手册页:

  

flock()创建的锁与打开的文件描述相关联(参见open(2))。这意味着重复文件描述符(例如,由 fork(2)或dup(2)创建)引用相同的锁,并且可以使用任何这些描述符修改或释放此锁。

您期待看到的行为。