Monitor in OS (operating system) is a synchronization construct that enables multiple processes or threads to coordinate actions and ensures that they are not interfering with each other or producing unexpected results. Also, it ensures that only one thread is executed at a critical code section.
The monitors’ concept was introduced in the programming language Concurrent Pascal by Per Brinch Hansen in 1972. Since then, they have been implemented in various programming languages. Monitors are dynamic tools that help to manage concurrent access to shared resources in the operating system. Concurrent access means allowing more than one user to access a computer simultaneously.
Why are Monitors Used?
Monitors in operating systems are used to manage access to shared resources, like files or data, among multiple processes. They ensure that only one process can use a resource simultaneously, preventing conflicts and data corruption. Monitors simplify synchronization and protect data integrity, making it easier for programmers to create reliable software.
They serve as "guards" for critical code sections, ensuring that no two processes can enter them simultaneously. Monitors are like traffic lights that control access to resources, preventing crashes and ensuring a smooth flow of data and tasks in an operating system.
In the above pseudo code, monitor_name is the name of the monitor, and p1, p2, pn are the procedures that provide access to shared data and variables enclosed in monitors. They help ensure that only one thread is accessed at a time for using the shared resource. This help prevents race condition and synchronization problems.
A race condition occurs when multiple threads compete to access the same shared resource. As a result, the order of their execution is adversely affected.
Below is an example of how you can implement a monitor in C++ using the standard library's mutex and condition_variable. In C++, monitors are implemented using a class with synchronized methods, which provides mutual exclusion and condition variables for synchronization.
C++
C++
#include <mutex> #include <condition_variable>
class NinjaMonitor { private: std::mutex m_lock; std::condition_variable m_cv;
// Shared resources and variables int shared_variable_1; int shared_variable_2;
public: // Procedures for accessing the shared resources void NinjaProcedure1() { // Entry code for acquiring the monitor lock std::unique_lock<std::mutex> lock(m_lock);
// Code to access and manipulate the shared resources // and signal waiting threads using condition variables
// Wait for condition variable to be signaled m_cv.wait(lock, []{ return /* condition to check */; });
// Exit code to release the monitor lock }
void NinjaProcedure2() { // Same structure as NinjaProcedure1() } };
In the above syntax:
‘std::mutex’ class is used for defining a lock for mutual exclusion.’
The ‘std::condition_variable’ class defines the condition variable for synchronization. Procedures are defined within a class for accessing and manipulating shared resources.
‘std::unique_lock<std::mutex>’ class acquires the monitor lock in the entry code. The lock is released automatically when the ‘unique_lock’ object goes out of scope at the function end.
The ‘m_cv.wait()’ function waits for the condition variable to be signaled. Note that the syntax can vary depending on the programming style and certain use cases.
class NinjaMonitor { public: // Constructor to initialize the shared resource and synchronization variables NinjaMonitor() : SharedResource(0) {}
// Method to access the shared resource int AccessSharedResource() { std::unique_lock<std::mutex> lock(mtx_); while (SharedResource == 0) { CondVar.wait(lock); } int value = SharedResource; SharedResource = 0; return value; }
// Method to update the shared resource void UpdateSharedResource(int new_value) {
In the above code, the ‘NinjaMonitor’ class encapsulates shared resources ‘SharedResource’ and ‘mtx_,’ and ‘CondVar’ are the synchronization variables for thread synchronization. AccessSharedResource() is the method used to provide a way for safely accessing the shared resource by waiting on the condition variable until another thread signals it. On the other hand, the UpdateSharedResource() is the method that provides a way to safely update the shared resource by notifying waiting threads when it’s been updated.
While the NinjaThread() is the function that uses a monitor for accessing the shared resource. A worker thread is created in the main() function, and the shared resource is updated after some time. Therefore we get the resulting output mentioned above, which indicates that the updated value of the shared resource, after updation by the main thread using the monitor, is successfully read by the NinjaThread.
Characteristics of Monitors in OS
Here are the characteristics of monitors in an operating system are given below:
Monitors help multiple programs or processes efficiently share resources like CPU time and memory.
They ensure that programs coordinate and don't interfere with each other while accessing shared resources.
Monitors help protect shared data from being accessed or modified by multiple programs simultaneously.
They manage the sequence in which programs can access shared resources, preventing conflicts.
Monitors provide an easy-to-use abstraction for developers to manage resource sharing and synchronization.
Components of Monitor in the operating system
The components of the monitor in an operating system are as follows:
Initialization: Initialization code is executed when a monitor is initialized. It helps to initialize any shared data structures that the monitor will use to perform specific tasks. In a package, code for initialization is included. We need it once when we want to create monitors. It is executed when a monitor is initialized. It helps initialize any shared data structures that the monitor will use to perform specific tasks. Initialization code is used to initialize a variable when we want to use it.
Private Data: It is an essential feature of monitors that helps make the data private. It is involved in holding monitors’ confidential data, which includes private functions. Private fields and functions are not visible outside of the monitor.
Monitor Procedure: Procedures operate on shared variables defined inside the monitor. Monitor procedures or functions can be invoked outside the monitor. These are functions or methods which are executed within the monitors’ context. They are usually used for shared resource manipulation.
Queue: Queue data structure maintains a list of threads waiting for a shared resource. Whenever we request a thread to use shared resources but another thread is currently using the shared resource, then we cannot use the shared resource at that time. But whenever the resource is in its available state, the thread at the front is granted access to the resource.
Condition Variables: They are used when we have to check for a particular condition before moving on, in conjunction with a mutex, and in providing reliable synchronization. Condition variables are the container of threads. These threads wait for a particular condition. Thus, they determine if a specific condition is true or false. It can also be considered as a queue or a thread that stops its execution and enters the queue if the specified condition turns out to be false. But, if another thread makes the condition accurate, it sends a signal to the thread in front of a queue for continuing execution.
Condition Variables
Condition variables enable threads to wait for a particular condition and check if it is true before preceding. The components of condition variables of monitors are mentioned below:
Wait() Function: This function is used for releasing a mutex associated with the monitor. It waits for a particular condition to be valid. A mutex is a program object that enables sharing of the same resources by multiple programs by taking turns. When a thread calls the wait() function, it adds a thread to the list and puts it to sleep. It releases the mutex and enters a blocked state until another thread signals the condition. Therefore, variables are suspended and placed in the condition variables' block queue whenever we perform a wait operation.
Signal() Function: This function is used to wake up a waiting thread. When a thread calls the function, it allies the thread to proceed by waking it up. This function gives a chance to one of the blocked variables.
Advantages of monitors in OS
There are various advantages of a monitor in OS, some of which are mentioned below:
Preventing Deadlock: We know that a deadlock is a situation when a set of processes is blocked. Monitors can be used for prevention, to ensure that threads acquire locks in a particular order. They can be used to prevent a situation where the threads are waiting for each other to release a lock.
Mutual Exclusion: It is automatic in monitors. It ensures that at a time, only one thread can access the shared resource.
Modularity: Monitors are helpful for encapsulating individual modules of an extensive system, which helps in maintenance.
Parallel programming: Monitors help in making parallel programming easy.
Less error-prone: Monitors are less prone to errors as compared to semaphores. A semaphore can be defined as an integer variable. It is shared among various processes.
While monitors can be helpful in some situations, it also has some disadvantages. Some are listed below:
Difficult debugging: Debugging programs that use monitors can be tedious.
Single process limitation: Monitors are implemented within a thread or single process. This also means that synchronization access to shared resources is not possible across multiple threads.
Frequently Asked Questions
What is mutex and monitor in operating system?
A mutex (mutual exclusion object) is a synchronization primitive used to prevent multiple threads from accessing a shared resource simultaneously. A monitor, on the other hand, is a higher-level synchronization mechanism that combines mutual exclusion with the ability to wait for certain conditions to be met.
What do you mean by condition variables in monitors?
Condition variables are helpful in thread synchronization. They wait for certain conditions to be true before proceeding.
Do monitors help in preventing race conditions?
Yes, monitors help in preventing race conditions through mutual exclusion. This means that, at a time, only one thread can access a shared resource.
Can monitors avoid deadlocks?
Monitors help prevent deadlocks by ensuring all threads acquire locks in the same order and releasing them in the opposite order.
Conclusion
We hope this article helped you understand monitor in OS. We have discussed its Syntax, components, and its advantages and disadvantages. Refer to our other articles to better understand.
You will find straightforward explanations of almost every topic on this platform. You can read more such articles on our platform, Coding Ninjas Studio. So take your learning to the next level using Coding Ninjas.