file write lock and child process
Asked Answered
B

3

6

If a process give a file a write lock and then it spawn a child process, is lock inherited by the child process? If yes, then there is 2 process have the write lock, I learned that there is only 1 process can have a write lock, some truth? here is a test python code

#!/usr/bin/python

import fcntl
import time
import os

fp = open('test.ini','w')
fcntl.flock(fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
pid = os.fork()

if pid > 0:
    time.sleep(10)
    exit(0)
if pid == 0:
    time.sleep(100)
    exit(0)

when the parent exist, i tried to get the lock of file test.ini, but failed , so I guess the child has the lock

Bewley answered 1/9, 2014 at 5:38 Comment(0)
E
3

So, as you've noted in the man page for flock(2), the relationship between the lock and the file is as follows:

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 file descriptors. Furthermore, the lock is released either by an explicit LOCK_UN operation on any of these duplicate file descriptors, or when all such file descriptors have been closed.

To be clear, it notes two cases when the lock is released:

  • by an explicit LOCK_UN operation on any of these duplicate file descriptors
  • when all such file descriptors have been closed

In the code provided in the question, there is no explicit unlock in either the parent or child execution, so the first condition won't be met. Similarly, as the second condition requires that all such file descriptors have been closed, this won't be met by the earlier termination of the parent process; only when the child process terminates later.

You can satisfy yourself that this holds by adding an explicit unlock:

fcntl.flock(fp, fcntl.LOCK_UN)

in the parent code path before the exit, and then test taking the lock from a separate process before the child exits. Such modified code can be found below:

#!/usr/bin/python

import fcntl
import time
import os

fp = open('test.ini','w')
fcntl.flock(fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
pid = os.fork()

if pid > 0:
    time.sleep(10)
    fcntl.flock(fp, fcntl.LOCK_UN)
    exit(0)
if pid == 0:
    time.sleep(100)
    exit(0)

You can also read /proc/locks or use lslocks (a parser of same) to show the currently held file locks in the system. The contents of the former look like:

1: FLOCK  ADVISORY  WRITE 358 00:15:628 0 EOF
2: FLOCK  ADVISORY  WRITE 296 00:15:608 0 EOF
3: FLOCK  ADVISORY  WRITE 291 00:15:599 0 EOF
4: FLOCK  ADVISORY  WRITE 25874 b3:02:256617 0 EOF

and the output of the latter:

COMMAND           PID  TYPE SIZE MODE  M START END PATH
(unknown)       25874 FLOCK      WRITE 0     0   0 /...
cron              291 FLOCK      WRITE 0     0   0 /run...
(unknown)         296 FLOCK      WRITE 0     0   0 /run...
(unknown)         358 FLOCK      WRITE 0     0   0 /run...

When filtering this output by PID the one to use is the parent PID, unless the mode has been changed in the child.

Erupt answered 29/9, 2022 at 22:30 Comment(0)
T
0

I think the phrasing in the man page is confusing:

$ cat lock_assoc.py 
#!/usr/bin/python3

import fcntl
f3 = open('test.ini','w')
fcntl.flock(f3, fcntl.LOCK_EX | fcntl.LOCK_NB)
fcntl.flock(f3, fcntl.LOCK_EX | fcntl.LOCK_NB)
f4 = open('test.ini','w')
fcntl.flock(f4, fcntl.LOCK_EX | fcntl.LOCK_NB)
$ strace -f -e flock ./lock_assoc.py 
flock(3, LOCK_EX|LOCK_NB)               = 0
flock(3, LOCK_EX|LOCK_NB)               = 0
flock(4, LOCK_EX|LOCK_NB)               = -1 EAGAIN (Resource temporarily unavailable)
Traceback (most recent call last):
  File "./lock_assoc.py", line 8, in <module>
    fcntl.flock(f4, fcntl.LOCK_EX | fcntl.LOCK_NB)
BlockingIOError: [Errno 11] Resource temporarily unavailable
+++ exited with 1 +++

When the man page says:

Locks created by flock() are associated with an open file description [...]

What it apparently means is that the lock owner is associated with the open file, whereas the lock itself is (presumably obviously) associated with the file inode.

That's why I can take a lock that's already exclusively locked, if I'm using the same open file (f3 above) - it's an idempotent operation on the data structure that represents the lock owner.

The other file descriptor (f4) has another lock owner, which has the value "I'm not the owner", and when it tries to take ownership, it fails, because the inode knows the owner is f3.

$ cat lock_fork.py 
#!/usr/bin/python3

import fcntl
import time
import os

f3 = open('test.ini','w')
fcntl.flock(f3, fcntl.LOCK_EX | fcntl.LOCK_NB)
f4 = open('test.ini','w')
pid = os.fork()

if pid > 0:
    time.sleep(1)
    exit(0)
elif pid == 0:
    time.sleep(3)
    fcntl.flock(f3, fcntl.LOCK_EX | fcntl.LOCK_NB)
    fcntl.flock(f4, fcntl.LOCK_EX | fcntl.LOCK_NB)
    exit(0)
$ strace -f -e flock ./lock_fork.py 
flock(3, LOCK_EX|LOCK_NB)               = 0
strace: Process 146372 attached
[pid 146371] +++ exited with 0 +++
flock(3, LOCK_EX|LOCK_NB)               = 0
flock(4, LOCK_EX|LOCK_NB)               = -1 EAGAIN (Resource temporarily unavailable)
Traceback (most recent call last):
  File "./lock_fork.py", line 18, in <module>
    fcntl.flock(f4, fcntl.LOCK_EX | fcntl.LOCK_NB)
BlockingIOError: [Errno 11] Resource temporarily unavailable
+++ exited with 1 +++

Here we see that fork() duplicates both f3 and f4, and even through the parent dies, the lock owner data structures associated with f3 and f4 remain in the child. The child's f3 can still idempotently take the lock again, whereas f4 cannot.

Turaco answered 24/9, 2022 at 1:53 Comment(0)
S
-1

File locks are associated with the open file descriptors. This means that when you will duplicate your descriptor with dup() like system call (or when the child process will inherit the file descriptor from its parent), the locks are also inherited. For example

flock(fd, LOCK_EX);//get a lock


newfd = dup(oldfd);//duplicating the file descriptors


flock(newfd, LOCK_UN);//THis call will release the lock using the duplicated file descriptor.

I hope this info helps.

Summation answered 1/9, 2014 at 17:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.