1. Check if a specified condition exist
2. If the condition does not exist, create the condition and continue running; else exit
The mechanism appears straightforward but there is one problem - there are 2 distinct steps in the mechanism. In modern day multiprocessing operating systems, these 2 steps within a program cannot be guaranteed to be run in succession. To show why this is a concern, we look at 2 scenarios of two instances of a program started somewhat concurrently. We will assume the condition to be checked does not exist in both scenarios.
Scenario 1
In scenario 1, Instance-A checks
for the condition and detects the condition does not exist yet, all within execution cycle-1. Before
it could create the condition, the OS's process scheduler swaps it out
(makes a context switch) and executes Instance-B in cycle-2. Instance-B also now thinks the
condition is missing. At this point, each instance thinks it is the only one about to run - but both continue to run after creating the conditions (assuming repetitive creation of the condition is not flagged as a problem).
Scenario 2
In scenario 2, we have condition checking and the ensuing action executed as 2 continuous sub-steps within one complete (bigger) step. We term this as an atomic operation - an operation which is indivisible. The 2 sub-steps within the atomic step must be completed one after another or none at all. This way, there is a guarantee only either of the two instances will end up running.
Compiled languages in general usually have facilities built-in the language or as external API calls for the programmer to guarantee a single running instance of her program. The shell script developer will have to resort to indirect methods to ensure a script will indeed be the sole running instance because of no innate facilities within the typical shell scripting language.
We have at least 3 (indirect) methods for the shell script developer to create a singleton process (i.e single instance process).
Method A - Lock by creating a directory - mkdir is atomic
/tmp/myprog$ ls
/tmp/myprog$ mkdir lock_dir
/tmp/myprog$ ls
lock_dir
/tmp/myprog$ mkdir lock_dir
mkdir: cannot create directory `lock_dir': File exists
/tmp/myprog$
/tmp/myprog$ mkdir lock_dir
/tmp/myprog$ ls
lock_dir
/tmp/myprog$ mkdir lock_dir
mkdir: cannot create directory `lock_dir': File exists
/tmp/myprog$
- After creating the lock directory, you could create a file under this directory containing the PID of the locking process.
Method B - Lock by setting Bash/Korn shell's noclobber option
/tmp/myprog$ ls
/tmp/myprog$ set -o noclobber
/tmp/myprog$ echo "1" > lock_file
/tmp/myprog$ ls
lock_file
/tmp/myprog$ echo "2" > lock_file
bash: lock_file: cannot overwrite existing file
/tmp/myprog$
/tmp/myprog$ set -o noclobber
/tmp/myprog$ echo "1" > lock_file
/tmp/myprog$ ls
lock_file
/tmp/myprog$ echo "2" > lock_file
bash: lock_file: cannot overwrite existing file
/tmp/myprog$
- It's good to have the PID of the locking process stored in the lock file.
Method C - Lock by creating a fifo - mkfifo is atomic
/tmp/myprog$ ls -l
total 0
/tmp/myprog$ mkfifo ./lock_fifo
/tmp/myprog$ ls -l
total 0
prw-r--r-- 1 openet openet 0 2012-05-04 16:41 lock_fifo
/tmp/myprog$ mkfifo ./lock_fifo
mkfifo: cannot create fifo `./lock_fifo': File exists
/tmp/myprog$
If allowed by your environment or requirements, /tmp will be a suitable location to create the lock directory, file or FIFO.
Reference:
- UNIX Network Programming, Volume 2: Interprocess Communications (2nd Edition) - Richard Stevens

