
Introduction
Process synchronization is a method of coordinating processes that use shared data. It happens between cooperative processes in an Operating System.
Process synchronization helps to preserve shared data consistency and cooperative process execution when running several concurrent processes.
Processes must be scheduled to avoid inconsistencies caused by concurrent access to common data. A race situation might occur as a result of a data discrepancy.
A race condition is when two or more operations are run at the same time, they are not scheduled in the right order, and they have not exited appropriately in the critical section.
The Lock Variable synchronization mechanism is the simplest synchronization mechanism for processes.
Also see: Multiprogramming vs Multitasking and Open Source Operating System
Lock Variable Synchronization Mechanism
- This is the most basic synchronization method. It is a User-mode implementation of a Software Mechanism, and it is also a multi-process busy waiting solution that may be used for many processes.
- A Lock variable lock is employed in this mechanism. There are two potential lock values: 0 and 1. The lock value 0 indicates that the critical section is empty, whereas the lock value 1 indicates that it is full.
- A process that wants to enter the critical section first validates the lock variable's value. If it is 0, the lock value is set to 1, and the critical section is entered; otherwise, it waits.
Pseudocode
Entry section : while(lock != 0); Lock = 0; |
The following code sample shows a more formal approach to the Lock Variable technique for process synchronisation:
char buffer[SIZE];
int counter = 0,
begin = 0,
finish = 0;
struct lock lck;
// initialising lock variable
lock_init(&lck);
void put(char ch)
{
// Entry section
lock_acquire(&lck);
// Critical section starts
while (counter == SIZE) {
lock_release(&lck);
lock_acquire(&lck);
}
counter++;
buffer[begin] = ch;
begin++;
if (begin == SIZE) {
begin = 0;
}
// Critical Section ends
// Exit section
lock_release(&lck);
}
char get()
{
char ch;
// Entry section
lock_acquire(&lck);
// Critical Section starts
while (counter == 0) {
lock_release(&lck);
lock_acquire(&lck);
}
counter--;
ch = buffer[finish];
finish++;
if (finish == SIZE) {
finish = 0;
}
// Critical Section ends
// Exit section
lock_release(&lck);
return ch;
}
A famous implementation of the reader-writer problem may be seen here.
Many processes are attempting to read or write a character to the buffer, which is shared memory. We use a lock variable to limit concurrent access to prevent data ambiguity. We've also set a limit on the number of readers/writers who may use the system.
Every synchronization technique is evaluated using three major criteria:
- Mutual Exclusion.
- Progress.
- Bounded Waiting.
The most crucial of all parameters is mutual exclusion. In some circumstances, the Lock Variable does not offer mutual exclusion. This fact can be best proven by creating its pseudo-code in assembly language, as shown below.
1. Load Lock, R0 ; (Storing Lock's value in Register R0.) 2. CMP R0, #0 ; (Comparing the value of register R0 with 0.) 3. JNZ Step 1 ; (Jumping to step 1 if the value of R0 is not 0.) 4. Store #1, Lock; (Setting the new value of Lock as 1.) Entering critical section 5. Store #0, Lock; (Setting Lock's value as 0 again.) |
Assume that processes P0 and P1 are contending for Critical Section and that their execution sequence is as follows: (lock initial value = 0) -
- Statement 1 is executed by P0, and it is pre-empted.
- P1 enters the Critical Section and is pre-empted after executing statements 1, 2, 3, and 4.
- P0 performs statements 2, 3, and 4 and enters the Critical Section.
The R0 of process P0 initially keeps the lock value as 0 but fails to upgrade it to 1. As a result, when P1 runs, it finds the LOCK value to be 0 and enters the Critical section by changing the LOCK value to 1.
However, the underlying issue is that when P0 runs again, it does not verify the changed value of Lock. It just validates the previous value in R0, which was 0, before proceeding to the critical section.
This is only one example of many potential execution sequences. Some may even allow mutual exclusion. So, like all easy things, the Lock Variable Synchronization approach has its drawbacks, but it's an excellent starting place for us to design better Synchronization Algorithms to address the issues we're dealing with.