Next: , Previous: File Status Flags, Up: Low-Level I/O


13.15 File 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.
off_t l_start
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 l_whence member.
off_t l_len
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.
pid_t l_pid
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.

— 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:

          fcntl (filedes, F_GETLK, lockp)
     

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:

EBADF
The filedes argument is invalid.
EINVAL
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:

          fcntl (filedes, F_SETLK, lockp)
     

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 waiting for other processes to release locks. If fcntl succeeds, it return a value other than -1.

The following errno error conditions are defined for this function:

EAGAIN
EACCES
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. (The GNU system always uses EAGAIN.)
EBADF
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.
EINVAL
Either the lockp argument doesn't specify valid lock information, or the file associated with filedes doesn't support locks.
ENOLCK
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:

EINTR
The function was interrupted by a signal while it was waiting. See Interrupted Primitives.
EDEADLK
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.

F_RDLCK
This macro is used to specify a read (or shared) lock.


F_WRLCK
This macro is used to specify a write (or exclusive) lock.


F_UNLCK
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 a voluntary 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.