Reputation: 6395
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.
Upvotes: 4
Views: 4627
Reputation: 754530
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)
ordup(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.
Upvotes: 2
Reputation: 3917
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
Upvotes: 3
Reputation: 341
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.
Upvotes: 2
Reputation: 206841
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.
Upvotes: 2