[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

2.5 How do I `lock' a file?

There are three main file locking mechanisms available. All of them are `advisory'[*], which means that they rely on programs co-operating in order to work. It is therefore vital that all programs in an application should be consistent in their locking regime, and great care is required when your programs may be sharing files with third-party software.

[*] Well, actually some Unices permit mandatory locking via the sgid bit – RTFM for this hack.

Some applications use lock files – something like ‘FILENAME.lock’. Simply testing for the existence of such files is inadequate though, since a process may have been killed while holding the lock. The method used by UUCP (probably the most notable example: it uses lock files for controlling access to modems, remote systems etc.) is to store the PID in the lockfile, and test if that pid is still running. Even this isn't enough to be sure (since PIDs are recycled); it has to have a backstop check to see if the lockfile is old, which means that the process holding the lock must update the file regularly. Messy.

The locking functions are:

 
    flock();
    lockf();
    fcntl();

flock() originates with BSD, and is now available in most (but not all) Unices. It is simple and effective on a single host, but doesn't work at all with NFS. It locks an entire file. Perhaps rather deceptively, the popular Perl programming language implements its own flock() where necessary, conveying the illusion of true portability.

fcntl() is the only POSIX-compliant locking mechanism, and is therefore the only truly portable lock. It is also the most powerful, and the hardest to use. For NFS-mounted file systems, fcntl() requests are passed to a daemon (rpc.lockd), which communicates with the lockd on the server host. Unlike flock() it is capable of record-level locking.

lockf() is merely a simplified programming interface to the locking functions of fcntl().

Whatever locking mechanism you use, it is important to sync all your file IO while the lock is active:

 
    lock(fd);
    write_to(some_function_of(fd));
    flush_output_to(fd); /* NEVER unlock while output may be buffered */
    unlock(fd);
    do_something_else;   /* another process might update it */
    lock(fd);
    seek(fd, somewhere); /* because our old file pointer is not safe */
    do_something_with(fd);
    ...

A few useful fcntl() locking recipes (error handling omitted for simplicity) are:

 
#include <fcntl.h>
#include <unistd.h>

read_lock(int fd)   /* a shared lock on an entire file */
{
    fcntl(fd, F_SETLKW, file_lock(F_RDLCK, SEEK_SET));
}

write_lock(int fd)  /* an exclusive lock on an entire file */
{
    fcntl(fd, F_SETLKW, file_lock(F_WRLCK, SEEK_SET));
}

append_lock(int fd) /* a lock on the _end_ of a file -- other
                       processes may access existing records */
{ 
    fcntl(fd, F_SETLKW, file_lock(F_WRLCK, SEEK_END));
}

The function file_lock used by the above is

 
struct flock* file_lock(short type, short whence) 
{
    static struct flock ret ;
    ret.l_type = type ;
    ret.l_start = 0 ;
    ret.l_whence = whence ;
    ret.l_len = 0 ;
    ret.l_pid = getpid() ;
    return &ret ;
}

[ < ] [ > ]   [ << ] [ Up ] [ >> ]

This document was generated on September, 10 2007 using texi2html 1.77.