Operating System - Hardware-Based Solution for Process Synchronization



To ensure proper synchronization between processes in a multitasking operating system, we can implement software-based solutions or hardware-based solutions. In this chapter, we will focus on the hardware-based solution for process synchronization.

The hardware-based solutions uses special machine instructions like interrupts and atomic operations to achieve process synchronization. These instructions are designed to be indivisible, meaning that once they start executing, they cannot be stopped until they complete. This ensures that no other process can interfere while the instruction is being executed.

Following are some common hardware-based solutions for process synchronization −

Test and Set Instruction

The Test and Set instruction is a simple atomic operation that can be used to implement mutual exclusion. It works by testing the value of a memory location and setting it to a new value in a single atomic operation.

This is the assembly code for Test and Set instruction −

TEST_AND_SET:
    LOAD R1, M;   // Load the value of memory location M into register R1
    MOV R2, #1;   // Set R2 to 1 (indicating lock is acquired)
    STORE R2, M;  // Store the value of R2 back to memory location M
    RETURN R1;    // Return the original value of M

Here, M is the memory location that is being tested and set. The instruction first loads the value of M into register R1. It then sets register R2 to 1, indicating that the lock is being acquired. Next, it stores the value of R2 back to memory location M. Finally, it returns the original value of M.

When you execute the Test and Set instruction −

  • CPU puts a write lock on the memory cache line (using MESI protocol).
  • The other cores or CPU that try to read/write the same memory location will be blocked
  • Once the instruction is completed, the write lock is released.

Here is C++ function that implements the Test and Set instruction −

bool test_and_set(bool *lock) {
    bool old = *lock;
    *lock = true;
    return old;
}

In this code snippet, the test_and_set function takes a pointer to the lock variable as an argument. It first stores the original value of the lock variable in the old variable. It then sets the lock variable to true (indicating that the lock is acquired) and returns the original value of the lock variable.

Compare and Swap Instruction

The Compare and Swap instruction is another atomic operation that can be used for process synchronization. It works by comparing the value of a memory location with a given value and, if they are equal, swapping it with a new value. The comparison and swap operation can be represented as follows −

COMPARE_AND_SWAP:
    LOAD R1, M;    // Load the value of memory location M into R1
    CMP R1, R2;    // Compare R1 with the expected value in R2
    BEQ SWAP;      // If equal, branch to SWAP
    RETURN R1;     // Return the original value of M
SWAP:
    STORE R3, M;   // Store the new value in R3 to memory location M
    RETURN R1;     // Return the original value of M 

This is the assembly code for Compare and Swap instruction. Here, M is the memory location being compared and swapped. The instruction first loads the value of M into register R1. It then compares R1 with the expected value in register R2. If they are equal, it branches to the SWAP section. Finally, it returns the original value of M.

When you execute the Compare and Swap instruction, the CPU put a write lock on the memory cache line similar to Test and Set instruction. So the other cores of CPU can't read/write the same memory location.

bool compare_and_swap(int *ptr, int expected, int new_value) {
    int old = *ptr;
    if (old == expected) {
        *ptr = new_value;
    }
    return old;
}

This is the C++ implementation of the Compare and Swap instruction. The function takes a pointer to the memory location, the expected value, and the new value as arguments.

Spinlocks

Spinlocks are implemented using atomic operations like Test and Set or Compare and Swap. A spinlock is a lock that make a thread to wait in a loop ("spin") while repeatedly checking if the lock is available. For example, here is a simple implementation of a spinlock using the Test and Set instruction −

class Spinlock {
    volatile bool lock = false;
public:
    void acquire() {
        while (test_and_set(&lock)) {
            // Busy wait
        }
    }
    void release() {
        lock = false;
    }
};

Here, the Spinlock class has two methods: acquire() and release(). The acquire method uses the Test and Set instruction to acquire the lock. If the lock is already held by another thread, it will stuck in a busy-wait loop until the lock becomes available. The release method will set the lock variable to false. So that other threads can acquire the lock.

Conclusion

Hardware-based process synchronization are achieved using special atomic instructions like Test and Set and Compare and Swap. These make the critical sections of code to be executed without any interruption from other processes. This way mutual exclusion and synchronization between processes can be achieved successfully.

Advertisements