- Linux c programming file lock
- Linux c programming file lock
- 2 write lock
- 3 file record locks
- File locking in Linux
- Introduction
- Advisory locking
- Common features
- Differing features
- File descriptors and i-nodes
- BSD locks (flock)
- POSIX record locks (fcntl)
- lockf function
- Open file description locks (fcntl)
- Emulating Open file description locks
- Test program
- Command-line tools
- Mandatory locking
- Example usage
Linux c programming file lock
This section describes record locks that are associated with the process. There is also a different type of record lock that is associated with the open file description instead of the process. See Open File Description Locks.
The remaining fcntl commands are used to support record locking, which permits multiple cooperating programs to prevent each other from simultaneously accessing parts of a file in error-prone ways.
An exclusive or write lock gives a process exclusive access for writing to the specified part of the file. While a write lock is in place, no other process can lock that part of the file.
A shared or read lock prohibits any other process from requesting a write lock on the specified part of the file. However, other processes can request read locks.
The read and write functions do not actually check to see whether there are any locks in place. If you want to implement a locking protocol for a file shared by multiple processes, your application must do explicit fcntl calls to request and clear locks at the appropriate points.
Locks are associated with processes. A process can only have one kind of lock set for each byte of a given file. When any file descriptor for that file is closed by the process, all of the locks that process holds on that file are released, even if the locks were made using other descriptors that remain open. Likewise, locks are released when a process exits, and are not inherited by child processes created using fork (see Creating a Process).
When making a lock, use a struct flock to specify what kind of lock and where. This data type and the associated macros for the fcntl function are declared in the header file fcntl.h .
Data Type: struct flock
This structure is used with the fcntl function to describe a file lock. It has these members:
short int l_type
Specifies the type of the lock; one of F_RDLCK , F_WRLCK , or F_UNLCK .
short int l_whence
This corresponds to the whence argument to fseek or lseek , and specifies what the offset is relative to. Its value can be one of SEEK_SET , SEEK_CUR , or SEEK_END .
This specifies the offset of the start of the region to which the lock applies, and is given in bytes relative to the point specified by the l_whence member.
This specifies the length of the region to be locked. A value of 0 is treated specially; it means the region extends to the end of the file.
This field is the process ID (see Process Creation Concepts) of the process holding the lock. It is filled in by calling fcntl with the F_GETLK command, but is ignored when making a lock. If the conflicting lock is an open file description lock (see Open File Description Locks), then this field will be set to -1.
Macro: int F_GETLK
This macro is used as the command argument to fcntl , to specify that it should get information about a lock. This command requires a third argument of type struct flock * to be passed to fcntl , so that the form of the call is:
If there is a lock already in place that would block the lock described by the lockp argument, information about that lock overwrites * lockp . Existing locks are not reported if they are compatible with making a new lock as specified. Thus, you should specify a lock type of F_WRLCK if you want to find out about both read and write locks, or F_RDLCK if you want to find out about write locks only.
There might be more than one lock affecting the region specified by the lockp argument, but fcntl only returns information about one of them. The l_whence member of the lockp structure is set to SEEK_SET and the l_start and l_len fields set to identify the locked region.
If no lock applies, the only change to the lockp structure is to update the l_type to a value of F_UNLCK .
The normal return value from fcntl with this command is an unspecified value other than -1, which is reserved to indicate an error. The following errno error conditions are defined for this command:
The filedes argument is invalid.
Either the lockp argument doesn’t specify valid lock information, or the file associated with filedes doesn’t support locks.
Macro: int F_SETLK
This macro is used as the command argument to fcntl , to specify that it should set or clear a lock. This command requires a third argument of type struct flock * to be passed to fcntl , so that the form of the call is:
If the process already has a lock on any part of the region, the old lock on that part is replaced with the new lock. You can remove a lock by specifying a lock type of F_UNLCK .
If the lock cannot be set, fcntl returns immediately with a value of -1. This function does not block while waiting for other processes to release locks. If fcntl succeeds, it returns a value other than -1.
The following errno error conditions are defined for this function:
The lock cannot be set because it is blocked by an existing lock on the file. Some systems use EAGAIN in this case, and other systems use EACCES ; your program should treat them alike, after F_SETLK . (GNU/Linux and GNU/Hurd systems always use EAGAIN .)
Either: the filedes argument is invalid; you requested a read lock but the filedes is not open for read access; or, you requested a write lock but the filedes is not open for write access.
Either the lockp argument doesn’t specify valid lock information, or the file associated with filedes doesn’t support locks.
The system has run out of file lock resources; there are already too many file locks in place.
Well-designed file systems never report this error, because they have no limitation on the number of locks. However, you must still take account of the possibility of this error, as it could result from network access to a file system on another machine.
Macro: int F_SETLKW
This macro is used as the command argument to fcntl , to specify that it should set or clear a lock. It is just like the F_SETLK command, but causes the process to block (or wait) until the request can be specified.
This command requires a third argument of type struct flock * , as for the F_SETLK command.
The fcntl return values and errors are the same as for the F_SETLK command, but these additional errno error conditions are defined for this command:
The function was interrupted by a signal while it was waiting. See Interrupted Primitives.
The specified region is being locked by another process. But that process is waiting to lock a region which the current process has locked, so waiting for the lock would result in deadlock. The system does not guarantee that it will detect all such conditions, but it lets you know if it notices one.
The following macros are defined for use as values for the l_type member of the flock structure. The values are integer constants.
This macro is used to specify a read (or shared) lock.
This macro is used to specify a write (or exclusive) lock.
This macro is used to specify that the region is unlocked.
As an example of a situation where file locking is useful, consider a program that can be run simultaneously by several different users, that logs status information to a common file. One example of such a program might be a game that uses a file to keep track of high scores. Another example might be a program that records usage or accounting information for billing purposes.
Having multiple copies of the program simultaneously writing to the file could cause the contents of the file to become mixed up. But you can prevent this kind of problem by setting a write lock on the file before actually writing to the file.
If the program also needs to read the file and wants to make sure that the contents of the file are in a consistent state, then it can also use a read lock. While the read lock is set, no other process can lock that part of the file for writing.
Remember that file locks are only an advisory protocol for controlling access to a file. There is still potential for access to the file by programs that don’t use the lock protocol.
Источник
Linux c programming file lock
Shared lockIf A process for a zone file plus a read lock, B process can also be added to read lock in this area, but the area is not write-lock on it.
2 write lock
Exclusive lockIf an area A process for the document added a write lock, B can not process this write-lock area, the region can not be added to this read lock.
When multiple processes simultaneously read and write to a file, in order to ensure the integrity and consistency of the file, these processes to be locked in sync.
When the process begins reading a zone file, first add the lock and then unlock after reading.
3 file record locks
File record locks
1. Function Prototype: int fcntl (int fd, int cmd, struct flcklock);
Parameters: fd: file descriptor;
cmd: the function symbols; (F_SETLK, used to set or release a lock; lock information used to obtain the F_GETLK;)
lock: a lock structure pointer stored information;
Return Value: call successfully returns 0, failure to return -1
2. The lock information structure
struct flock
<
short l_type; / The type of lock/
short l_whence; / Offset initial position:/
off_t l_start; / Offset from the l_whence/
off_t l_len; / The number of bytes from the beginning of l_start/
pid_t l_pid; / Lock your process ID (usually not) * /
>
l_type have F_RDLCK read lock, F_WRLCK write locks and F_UNLCK air lock.
(!! Note: read lock, it will not affect the data, you can always add; however write lock, can only be added once)
l_whence have SEEK_SET, SEEK_CUR and SEEK_END.
l_len expressed from the start until the maximum possible position is zero.
Here’s an example write-lock:
Questions about multiple processes locked, essentially the same steps, and then later review the succession more .
Источник
File locking in Linux
Table of contents
Introduction
File locking is a mutual-exclusion mechanism for files. Linux supports two major kinds of file locks:
- advisory locks
- mandatory locks
Below we discuss all lock types available in POSIX and Linux and provide usage examples.
Advisory locking
Traditionally, locks are advisory in Unix. They work only when a process explicitly acquires and releases locks, and are ignored if a process is not aware of locks.
There are several types of advisory locks available in Linux:
- BSD locks (flock)
- POSIX record locks (fcntl, lockf)
- Open file description locks (fcntl)
All locks except the lockf function are reader-writer locks, i.e. support exclusive and shared modes.
Note that flockfile and friends have nothing to do with the file locks. They manage internal mutex of the FILE object from stdio.
- File Locks, GNU libc manual
- Open File Description Locks, GNU libc manual
- File-private POSIX locks, an LWN article about the predecessor of open file description locks
Common features
The following features are common for locks of all types:
- All locks support blocking and non-blocking operations.
- Locks are allowed only on files, but not directories.
- Locks are automatically removed when the process exits or terminates. It’s guaranteed that if a lock is acquired, the process acquiring the lock is still alive.
Differing features
This table summarizes the difference between the lock types. A more detailed description and usage examples are provided below.
BSD locks | lockf function | POSIX record locks | Open file description locks | |
---|---|---|---|---|
Portability | widely available | POSIX (XSI) | POSIX (base standard) | Linux 3.15+ |
Associated with | File object | [i-node, pid] pair | [i-node, pid] pair | File object |
Applying to byte range | no | yes | yes | yes |
Support exclusive and shared modes | yes | no | yes | yes |
Atomic mode switch | no | — | yes | yes |
Works on NFS (Linux) | Linux 2.6.12+ | yes | yes | yes |
File descriptors and i-nodes
A file descriptor is an index in the per-process file descriptor table (in the left of the picture). Each file descriptor table entry contains a reference to a file object, stored in the file table (in the middle of the picture). Each file object contains a reference to an i-node, stored in the i-node table (in the right of the picture).
A file descriptor is just a number that is used to refer a file object from the user space. A file object represents an opened file. It contains things likes current read/write offset, non-blocking flag and another non-persistent state. An i-node represents a filesystem object. It contains things like file meta-information (e.g. owner and permissions) and references to data blocks.
File descriptors created by several open() calls for the same file path point to different file objects, but these file objects point to the same i-node. Duplicated file descriptors created by dup2() or fork() point to the same file object.
A BSD lock and an Open file description lock is associated with a file object, while a POSIX record lock is associated with an [i-node, pid] pair. We’ll discuss it below.
BSD locks (flock)
The simplest and most common file locks are provided by flock(2) .
- not specified in POSIX, but widely available on various Unix systems
- always lock the entire file
- associated with a file object
- do not guarantee atomic switch between the locking modes (exclusive and shared)
- up to Linux 2.6.11, didn’t work on NFS; since Linux 2.6.12, flock() locks on NFS are emulated using fcntl() POSIX record byte-range locks on the entire file (unless the emulation is disabled in the NFS mount options)
The lock acquisition is associated with a file object, i.e.:
- duplicated file descriptors, e.g. created using dup2 or fork , share the lock acquisition;
- independent file descriptors, e.g. created using two open calls (even for the same file), don’t share the lock acquisition;
This means that with BSD locks, threads or processes can’t be synchronized on the same or duplicated file descriptor, but nevertheless, both can be synchronized on independent file descriptors.
flock() doesn’t guarantee atomic mode switch. From the man page:
Converting a lock (shared to exclusive, or vice versa) is not guaranteed to be atomic: the existing lock is first removed, and then a new lock is established. Between these two steps, a pending lock request by another process may be granted, with the result that the conversion either blocks, or fails if LOCK_NB was specified. (This is the original BSD behaviour, and occurs on many other implementations.)
This problem is solved by POSIX record locks and Open file description locks.
POSIX record locks (fcntl)
POSIX record locks, also known as process-associated locks, are provided by fcntl(2) , see “Advisory record locking” section in the man page.
- specified in POSIX (base standard)
- can be applied to a byte range
- associated with an [i-node, pid] pair instead of a file object
- guarantee atomic switch between the locking modes (exclusive and shared)
- work on NFS (on Linux)
The lock acquisition is associated with an [i-node, pid] pair, i.e.:
- file descriptors opened by the same process for the same file share the lock acquisition (even independent file descriptors, e.g. created using two open calls);
- file descriptors opened by different processes don’t share the lock acquisition;
This means that with POSIX record locks, it is possible to synchronize processes, but not threads. All threads belonging to the same process always share the lock acquisition of a file, which means that:
- the lock acquired through some file descriptor by some thread may be released through another file descriptor by another thread;
- when any thread calls close on any descriptor referring to given file, the lock is released for the whole process, even if there are other opened descriptors referring to this file.
This problem is solved by Open file description locks.
lockf function
lockf(3) function is a simplified version of POSIX record locks.
- specified in POSIX (XSI)
- can be applied to a byte range (optionally automatically expanding when data is appended in future)
- associated with an [i-node, pid] pair instead of a file object
- supports only exclusive locks
- works on NFS (on Linux)
Since lockf locks are associated with an [i-node, pid] pair, they have the same problems as POSIX record locks described above.
The interaction between lockf and other types of locks is not specified by POSIX. On Linux, lockf is just a wrapper for POSIX record locks.
Open file description locks (fcntl)
Open file description locks are Linux-specific and combine advantages of the BSD locks and POSIX record locks. They are provided by fcntl(2) , see “Open file description locks (non-POSIX)” section in the man page.
- Linux-specific, not specified in POSIX
- can be applied to a byte range
- associated with a file object
- guarantee atomic switch between the locking modes (exclusive and shared)
- work on NFS (on Linux)
Thus, Open file description locks combine advantages of BSD locks and POSIX record locks: they provide both atomic switch between the locking modes, and the ability to synchronize both threads and processes.
These locks are available since the 3.15 kernel.
The API is the same as for POSIX record locks (see above). It uses struct flock too. The only difference is in fcntl command names:
- F_OFD_SETLK instead of F_SETLK
- F_OFD_SETLKW instead of F_SETLKW
- F_OFD_GETLK instead of F_GETLK
Emulating Open file description locks
What do we have for multithreading and atomicity so far?
- BSD locks allow thread synchronization but don’t allow atomic mode switch.
- POSIX record locks don’t allow thread synchronization but allow atomic mode switch.
- Open file description locks allow both but are available only on recent Linux kernels.
If you need both features but can’t use Open file description locks (e.g. you’re using some embedded system with an outdated Linux kernel), you can emulate them on top of the POSIX record locks.
Here is one possible approach:
Implement your own API for file locks. Ensure that all threads always use this API instead of using fcntl() directly. Ensure that threads never open and close lock-files directly.
In the API, implement a process-wide singleton (shared by all threads) holding all currently acquired locks.
Associate two additional objects with every acquired lock:
Now, you can implement lock operations as follows:
- First, acquire the RW-mutex. If the user requested the shared mode, acquire a read lock. If the user requested the exclusive mode, acquire a write lock.
- Check the counter. If it’s zero, also acquire the file lock using fcntl() .
- Increment the counter.
- Decrement the counter.
- If the counter becomes zero, release the file lock using fcntl() .
- Release the RW-mutex.
This approach makes possible both thread and process synchronization.
Test program
I’ve prepared a small program that helps to learn the behavior of different lock types.
The program starts two threads or processes, both of which wait to acquire the lock, then sleep for one second, and then release the lock. It has three parameters:
lock mode: flock (BSD locks), lockf , fcntl_posix (POSIX record locks), fcntl_linux (Open file description locks)
access mode: same_fd (access lock via the same descriptor), dup_fd (access lock via duplicated descriptors), two_fds (access lock via two descriptors opened independently for the same path)
concurrency mode: threads (access lock from two threads), processes (access lock from two processes)
Below you can find some examples.
Threads are not serialized if they use BSD locks on duplicated descriptors:
But they are serialized if they are used on two independent descriptors:
Threads are not serialized if they use POSIX record locks on two independent descriptors:
But processes are serialized:
Command-line tools
The following tools may be used to acquire and release file locks from the command line:
Provided by util-linux package. Uses flock() function.
There are two ways to use this tool:
run a command while holding a lock:
flock will acquire the lock, run the command, and release the lock.
open a file descriptor in bash and use flock to acquire and release the lock manually:
You can try to run these two snippets in parallel in different terminals and see that while one is sleeping while holding the lock, another is blocked in flock.
Provided by procmail package.
Runs the given command while holding a lock. Can use either flock() , lockf() , or fcntl() function, depending on what’s available on the system.
There are also two ways to inspect the currently acquired locks:
Provided by util-linux package.
Lists all the currently held file locks in the entire system. Allows to perform filtering by PID and to configure the output format.
A file in procfs virtual file system that shows current file locks of all types. The lslocks tools relies on this file.
Mandatory locking
Linux has limited support for mandatory file locking. See the “Mandatory locking” section in the fcntl(2) man page.
A mandatory lock is activated for a file when all of these conditions are met:
- The partition was mounted with the mand option.
- The set-group-ID bit is on and group-execute bit is off for the file.
- A POSIX record lock is acquired.
Note that the set-group-ID bit has its regular meaning of elevating privileges when the group-execute bit is on and a special meaning of enabling mandatory locking when the group-execute bit is off.
When a mandatory lock is activated, it affects regular system calls on the file:
When an exclusive or shared lock is acquired, all system calls that modify the file (e.g. open() and truncate() ) are blocked until the lock is released.
When an exclusive lock is acquired, all system calls that read from the file (e.g. read() ) are blocked until the lock is released.
However, the documentation mentions that current implementation is not reliable, in particular:
- races are possible when locks are acquired concurrently with read() or write()
- races are possible when using mmap()
Since mandatory locks are not allowed for directories and are ignored by unlink() and rename() calls, you can’t prevent file deletion or renaming using these locks.
Example usage
Below you can find a usage example of mandatory locking.
Mount the partition and create a file with the mandatory locking enabled:
Acquire a lock in the first terminal:
Try to read the file in the second terminal:
Источник