Friday, May 4, 2012

Unix: How to ensure only one instance of a Unix script is running

There are times when you need to ensure only one instance of your program or script is running. The reason for this is usually related to maintaining data integrity.The common general mechanism employed by any program/script to ensure itself to be the single running instance is simply this:

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$
  • 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$

  • 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

No comments:

Post a Comment