How do I `lock' a file?

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 ;
}






Home FAQ