Add Book to My BookshelfPurchase This Book Online

Chapter 6 - Special-Purpose File Operations

UNIX Systems Programming for SVR4
David A. Curry
 Copyright © 1996 O'Reilly & Associates, Inc.

File and Record Locking
When more than one process is writing the same file, or when one process is writing the file while another is reading it, it is usually necessary for the processes to coordinate their actions to prevent havoc. Consider, for example, what happens when two processes start at about the same time and both open the same log file for writing. Each process seeks to the end of the file in order to append new log messages to the existing file. When the first process writes a log message, its read/write offset is advanced. However, the read/write offset of the second process is not advanced, and when this process writes a log message, it overwrites the message written by the first process.
One way to avoid this particular case is to open the file with the O_APPEND option (see Chapter 3, Low-Level I/O Routines), which guarantees that all writes to the file are appended to the end of the file. The kernel takes care of advancing the read/write offset before writing the data if the file has grown since the last write. However, this option does not solve other problems that can occur. For example, if two processes try to update a database at the same time, they will probably destroy each others' work, and they would certainly leave the database in an unknown state. In order to prevent these situations, most modern UNIX systems provide some form of file locking.
There are two types of file locking: advisory and mandatory. Advisory file locks, which are provided by most versions of UNIX, allow cooperating processes to block each other out during critical periods (such as when one of the processes is writing the file). In advisory file locking, each process is required to check for the existence of a lock on the file before going ahead with its work. If a lock is present, the process waits until the lock is removed and sets a lock of its own before proceeding. However, advisory file locking is only useful between processes that agree to follow the locking convention. Processes that do not care about file locks can still read or write the file, even if another process has a lock set.
Mandatory file locks are provided by some versions of UNIX, including SVR4. When a mandatory lock is present on a file, the kernel causes any calls to creat, open, read, and write issued by processes other than the one with the lock to fail, returning the EAGAIN error. This is more secure, in the sense that even processes that are not aware that the file must be accessed with a lock cannot access the file out of turn. However, mandatory file locks are also dangerous. If a process that holds a lock on some critical system file goes into an infinite loop or otherwise fails to remove the lock, it can cause the entire system to hang or even crash. For this reason, it is usually advisable to use advisory locks whenever possible. Mandatory locks are enabled on a per-file basis by setting the set-group-id bit and clearing the group execute bit in the file's permission modes (see Chapter 5, Files and Directories). This implies that it is not possible to set a mandatory file lock on a directory or an executable program.
There are two functions used for setting and removing file locks in SVR4. The fcntl function, introduced earlier in this chapter, provides the POSIX interface, and the lockf function provides the System V interface. The two interfaces are very similar; the principal reason for continuing to supply the lockf interface is to provide backward compatibility with earlier operating system versions.
Locking Files with fcntl
As discussed earlier, the fcntl function is called as follows:
    #include <sys/types.h>
    #include <fcntl.h>
    int fcntl(int fd, int cmd, /* arg */ ...);
The fd argument is a file descriptor referring to the file to lock, the cmd argument indicates the operation to perform, and the arg parameter is a pointer to a structure of type flock_t that describes the type of lock to create.
Legal values for the cmd argument that apply to file locking are:
F_SETLK
Set or clear a lock, according to the contents of the flock_t structure pointed to by arg (see below). If the lock cannot be created, fcntl immediately returns -1 and stores the reason for failure in the external variable errno.
F_SETLKW
This command is identical to F_SETLK, except that if the lock cannot be created, the process is blocked until it can be created. This allows a process to request a lock and wait until it can be made, without having to test repeatedly to see if the file is unlocked.
F_GETLK
If the type of lock requested by the flock_t structure pointed to by arg can be created, then the structure is passed back unchanged, except that the lock type is set to F_UNLCK, and the l_whence field is set to SEEK_SET.
If the lock cannot be created, then the structure is overwritten with a description of the first lock that is preventing its creation. The structure also contains the process ID and system ID of the process holding the lock.
This command never creates a lock; it only tests whether or not a particular lock could be created.
Two different types of locks can be created with fcntl. A read lock prevents any process from write locking the protected area. More than one read lock can exist for a given segment of a file at any given time. The file descriptor on which the read lock is placed must have been opened with read access. A write lock prevents any process from read locking or write locking the protected area. Only one write lock and no read locks can exist for a given segment of a file at any given time. The file descriptor on which the write lock is being placed must have been opened with write access.
The lock itself is described by a structure of type flock_t (declared in the include file fcntl.h) that contains at least the following members:
    typedef struct flock {
        short    l_type;
        short    l_whence;
        off_t    l_start;
        off_t    l_len;
        long     l_sysid;
        pid_t    l_pid;
    } flock_t;
The l_type field of the structure specifies the type of lock, and can be equal to one of the following:
F_RDLCK
Establish a read lock.
F_WRLCK
Establish a write lock.
F_UNLCK
Remove a previously established lock.
The l_start field specifies the offset of the beginning of the region to be locked, and the l_len field specifies the length of the region to be locked. The l_whence field specifies the point in the file from which the starting offset is referenced, and can take on the same values as the third argument to the lseek function:
SEEK_SET
The starting offset is relative to the beginning of the file.
SEEK_CUR
The starting offset is relative to the current position in the file.
SEEK_END
The starting offset is relative to the end of the file.
Locks can start and extend beyond the end of a file, but they cannot be negative relative to the beginning of the file. A lock can be set to extend to the end of the file by setting l_len to zero; if such a lock also has l_whence and l_start set to zero, the whole file is locked.
Unlocking a segment in the middle of a larger locked segment leaves two locked segments, one at each end. Locking a segment that is already locked by the same process results in removing the old lock and installing the new one.
Locks are removed from a file when the process removes them using F_UNLCK, when the process closes the file descriptor, or when the process terminates. Locks are not inherited by child processes.
Locking Files with lockf
The lockf function provides similar functionality to the file locking portion of fcntl, but it is called differently:
    #include <unistd.h>
    int lockf(int fd, int function, long size);
The fd argument is a file descriptor referencing the file to be locked; it must have been opened with either O_WRONLY or O_RDWR access permissions.
The function argument indicates the function to perform:
F_ULOCK
Unlock a previously locked section.
F_LOCK
Establish a lock on a section. If the section is already locked, the process blocks until the lock can be established.
F_TLOCK
Test a section to see if it can be locked. If it can, establish the lock. If the section is already locked, this command causes lockf to return -1 and stores the reason for failure in errno.
F_TEST
Test a section to see if it can be locked. If it can, lockf returns 0; otherwise, it returns -1 and stores the reason for the error in errno.
The size argument indicates the number of contiguous bytes to lock or unlock. The region extends forward from the current read/write offset for a positive value of size, and backward from the current read/write offset for a negative value of size. If size is zero, the region from the current read/write offset through the current or any future end of the file is indicated. An area does need to exist in the file to be locked; locks can extend past the end of the file.
It is possible to establish a lock on a section that overlaps with a previously locked section, although this results in the sections being combined so that a single, larger section is now locked (locks are a finite resource; this practice conserves them). If a section to be unlocked is part of a larger locked section, this results in two locked sections, one on either end of the unlocked area.
All locks held on a file by a process are released when the process closes the file or when the process terminates. Locks created by lockf are not inherited by children of the process creating the lock.
Porting Notes
BSD UNIX and vendor versions based on it offer another interface, called flock:
    #include <sys/file.h>
    int flock(int fd, int operation);
This function allows advisory locks to be created on the file referenced by the file descriptor fd. Only entire files can be locked; there is no facility to lock only a portion of a file. The operation argument indicates the function to perform:
LOCK_SH
Establish a shared lock on the file. More than one process can have a shared lock on the same file at the same time. This is analogous to a read lock as used with fcntl and lockf.
LOCK_EX
Establish an exclusive lock on the file. Only one exclusive lock can be placed on the file at a time, and no shared locks on the file can exist while the exclusive lock is in place. This is analogous to a write lock as used with fcntl and lockf.
LOCK_UN
Remove a previously established lock from the file.
LOCK_NB
This can be or ed with LOCK_SH or LOCK_EX to make the operation non-blocking; otherwise, these operations block until the lock can be created.
The flock function returns 0 on success. On failure, it returns -1 and places the reason for failure in the external variable errno.

Previous SectionNext Section
Books24x7.com, Inc © 2000 –  Feedback