简体   繁体   中英

flock() does not seem to work with fork()

This is embarrasing, apparently I don't (again?) understand something basic about forking...

I expected the code (under Linux, Centos 6.3) below to print

lock returned 0
unlock
lock returned 0
unlock

but it does not, both locks succeed at once:

lock returned 0
lock returned 0
unlock
unlock

Why?

#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);
}

If I delete fork() and just start two processes by hand, then everything works as expected, one lock succeeds, the other blocks and succeeds later.

According to man flock :

Locks created by flock() are associated with a file, or, more precisely, an open file table entry. This means that duplicate file descriptors (created by, for example, fork(2) or dup(2)) refer to the same lock, and this lock may be modified or released using any of these descriptors. Furthermore, the lock is released either by an explicit LOCK_UN operation on any of these duplicate descriptors, or when all such descriptors have been closed.

So, Because you fork the process after opening the file, they share a same lock. It means that another process cannot flock on the same file, until both child and parent close the file.

If you want exclusive locks not preserved across forks, you can use fcntl .

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

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

There's some weasel wording in the manual for flock() :

Locks created by flock() are associated with an open file table entry. This means that duplicate file descriptors (created by, for example, fork(2) or dup(2) ) refer to the same lock, and this lock may be modified or released using any of these descriptors.

You can demonstrate this by more thoroughly instrumenting the program, and by adding another fork judiciously. It's also a good idea to ensure that the file is created properly — when you use O_CREAT , open() needs a third argument to specify the file mode.

#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;
}

Sample run:

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

Note how the processes that do not share the same open file description are blocked; those that do share the same open file description are not blocked.

Also note that time stamping and (especially) 'PID stamping' the output helps make the output unambiguous.

From the Linux flock man page:

Locks created by flock() are associated with an open file description (see open(2)). This means that duplicate file descriptors (created by, for example, fork(2) or dup(2)) refer to the same lock , and this lock may be modified or released using any of these descriptors.

The behavior you're seeing is expected.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM