sh(1), and
have an understanding of the UNIX™ process model, exit codes,
and have coded several scripts. Some understanding of
advisory file locking
(see flock(2))
would be helpful, but not required.
Since flock may use file descriptors by
number it would be helpful to have some understanding of their
mapping (viz. stdin is 0,
stdout is 1, and
stderr is 2), and the
exec shell internal, which we use to
manipulate descriptors.
flock?
When more than 1 process needs to update a common resource we
lock either the resource itself, or a file
built just to represent the resource with flock.
Then, if everyone plays by the rules, we know it is safe to
make our update. Every other process that wants to update the
resource is blocked by our lock, when we release our lock one of
the others gets a turn.
flock [-cfn] [-EX|SH|UN|NB]file|fd[cmd]
flock-h
flock-V
The first usage is the most common: request a lock or unlock operation on
an already open descriptor (via fd) or on
a file or directory via file.
See the manual page
flock(1)
for details.
ksh function which needs a lock:
function autoseq { [ -f $SEQ ] || echo 0 >$SEQ read Cur <$SEQ echo $((Cur+1)) >$SEQ print $Cur } SEQ=$(mktemp ${LOGNAME}XXXXXX) autoseq & autoseq & autoseq & autoseq & autoseq & autoseq & autoseq & autoseq & rm -f $SEQ exit
Sometimes that code outputs 8 numbers, sometimes they are all
different, mostly they are 1 or 2. It races for
access to the sequence file, and near the end the last few
processes race with the rm command,
which means the sequence file might still exist after the
script exits.
To fix that code we need 3 enhancements:
function autoseq { exec 9<&0 <$SEQ flock -EX 0 read Cur echo $((Cur+1)) >$SEQ flock -UN 0 exec 0<&9 9<- print $Cur } SEQ=$(mktemp ${LOGNAME}XXXXXX) touch $SEQ autoseq & autoseq & autoseq & autoseq & autoseq & autoseq & autoseq & autoseq & wait rm -f $SEQ exit
wait for the workers before we
cleanup the sequence file.
flock the sequence file while we update it.
stdin
then ask for an exclusive lock on the file. Each process blocks on
that call until it is the only one that holds the lock (they take
turns like you learned to do in kindergarten). We read and update the
sequence file, then release the lock for the next peer.
We really could just close the locked
stdin to release the lock, but I thought
it was more clear this way.
Alternatively we could bundle the update into a shell command-string and hold the lock only for the life of the
sub-shell:
function autoseq { flock -EX 0 <$SEQ ksh -c \ "read Cur; echo \$((Cur+1))>$SEQ; print \$Cur" }
This takes advantage of flock's
cmd parameter to run a process while
holding a lock.
stdout of the script, rather than
the sequence file itself. Any resource that the program uses
as a local convention is fine.
The most common reason this might fail is when some other process connected to the same output uses the same locking protocol. That may be avoided trivially by wrapping thefunction autoseq { flock -EX 1 ksh -c \ "read Cur <$SEQ; echo \$((Cur+1))>$SEQ; print \$Cur" }
autoseq
processes in a sub-shell with a pipe to cat.
(This allows our process to lock the pipe, rather than
the common output descriptor.)
So a process might create a unique file (or file descriptor) to
represent the resource (resources) that it needs to manage.
There is little reason for the code to lock the actual file,
since it controls the locking protocol. Personally I like to
lock the whole data directory when my process uses multiple files, which
only works with advisory locking.
I find fcntl locking harder to use, less
flexible, and much slower.
these allow me to assue that I don't have more than a single shell open in the directory, so I don't botch critial updates.alias lockdir='exec 7<. && flock -NB -EX 0 0<&7 2>/dev/null || echo "ksh: $PWD: already locked" && false' alias unlockdir='exec 7<&-'
Very few operations require such care, but it is nice to know you can do it. This also stops other engineers or admins from stepping on eachother's critial updates. Use this spell sparingly, as it quickly get to be more of a joke when every operation you need to do it blocked by an active shell.
xclate output filter allows many
processes to output in parallel as it buffers the
output from each until it can get a lock on the common output --
but it doesn't use advisory file locking, it actually uses
access rights. See the HTML
document for details.
The local program lock_n_load grabs an
exclusive lock for accouting updates. This allows a single administrator
access to the revision control system for accounting changes, with
a shell $TIMEOUT set to
prevent idle shells from denying access forever. The implemention of
this program might be best done with op and maybe
a jacket. (Today it is a short
C program.)
$Id: flock.html,v 1.7 2012/07/02 17:00:34 ksb Exp $