0) Unit 2 Master
0) Unit 2 Master
PROCESS MANAGEMENT:
Process Concept
A process is basically a program in execution. The execution of a process must progress in a sequential fashion. To put it in simple terms, we write our
computer programs in a text file and when we execute this program, it becomes a process which performs all the tasks mentioned in the program.
When a program is loaded into the memory and it becomes a process, it can be divided into four sections ─ stack, heap, text and data. The following image
shows a simplified layout of a process inside main memory
PROCES CONTROL BLOCK
Stack
The process Stack contains the temporary data such as method/function parameters, return address and local variables.
Heap
This is dynamically allocated memory to a process during its run time.
Text
This includes the current activity represented by the value of Program Counter and the contents of the processor's registers
Data
This section contains the global and static variables.
Program
A program is a piece of code which may be a single line or millions of lines. A computer program is usually written by a computer programmer in a programming
language. For example, here is a simple program written in C programming language
A computer program is a collection of instructions that performs a specific task when executed by a computer. When we compare a program with a process,
we can conclude that a process is a dynamic instance of a computer program.
A part of a computer program that performs a well-defined task is known as an algorithm. A collection of computer programs, libraries and related data are
referred to as a software.
1
Start
This is the initial state when a process is first started/created.
2
Ready
The process is waiting to be assigned to a processor. Ready
processes are waiting to have the processor allocated to them by
the operating system so that they can run. Process may come into
this state after Start state or while running it by but interrupted by
the scheduler to assign CPU to some other process.
3
Running
Once the process has been assigned to a processor by the OS
scheduler, the process state is set to running and the processor
executes its instructions.
4
Waiting
Process moves into the waiting state if it needs to wait for a
resource, such as waiting for user input, or waiting for a file to
become available.
5
Terminated or Exit
Once the process finishes its execution, or it is terminated by the
operating system, it is moved to the terminated state where it waits
to be removed from main memory.
Process Control Block (PCB)
A Process Control Block is a data structure maintained by the Operating System for every process. The PCB is identified by an integer process ID (PID). A PCB
keeps all the information needed to keep track of a process as listed below in the table −
1
Process State
The current state of the process i.e., whether it is ready, running, waiting, or
whatever.
2
Process privileges
This is required to allow/disallow access to system resources.
3
Process ID
Unique identification for each of the process in the operating system.
4
Pointer
A pointer to parent process.
5
Program Counter
Program Counter is a pointer to the address of the next instruction to be
executed for this process.
6
CPU registers
Various CPU registers where process need to be stored for execution for
running state.
7
CPU Scheduling Information
Process priority and other scheduling information which is required to
schedule the process.
8
Memory management information
This includes the information of page table, memory limits, Segment table
depending on memory used by the operating system.
9
Accounting information
This includes the amount of CPU used for process execution, time limits,
execution ID etc.
10
IO status information
This includes a list of I/O devices allocated to the process.
The architecture of a PCB is completely dependent on Operating System and may contain different information in different operating systems. Here is a simplified
diagram of a PCB −
The PCB is maintained for a process throughout its lifetime, and is deleted once the process terminates.
Process Scheduling
The process scheduling is the activity of the process manager that handles the removal of the running process from the CPU and the selection of another
process on the basis of a particular strategy.
Process scheduling is an essential part of a Multiprogramming operating systems. Such operating systems allow more than one process to be loaded into the
executable memory at a time and the loaded process shares the CPU using time multiplexing.
Categories of Scheduling
1. Non-preemptive: Here the resource can’t be taken from a process until the process completes execution. The switching of resources occurs when
the running process terminates and moves to a waiting state.
2. Preemptive: Here the OS allocates the resources to a process for a fixed amount of time. During resource allocation, the process switches from
running state to ready state or from waiting state to ready state. This switching occurs as the CPU may give priority to other processes and replace
the process with higher priority with the running process.
The OS maintains all Process Control Blocks (PCBs) in Process Scheduling Queues. The OS maintains a separate queue for each of the process states and
PCBs of all processes in the same execution state are placed in the same queue. When the state of a process is changed, its PCB is unlinked from its current
queue and moved to its new state queue.
The Operating System maintains the following important process scheduling queues −
• Job queue − This queue keeps all the processes in the system.
• Ready queue − This queue keeps a set of all processes residing in main memory, ready and waiting to execute. A new process is always put in this
queue.
• Device queues − The processes which are blocked due to unavailability of an I/O device constitute this queue.
The OS can use different policies to manage each queue (FIFO, Round Robin, Priority, etc.). The OS scheduler determines how to move processes between
the ready and run queues which can only have one entry per processor core on the system; in the above diagram, it has been merged with the CPU.
Schedulers
Schedulers are special system software which handle process scheduling in various ways. Their main task is to select the jobs to be submitted into the system
and to decide which process to run. Schedulers are of three types −
• Long-Term Scheduler
• Short-Term Scheduler
• Medium-Term Scheduler
It is also called a job scheduler. A long-term scheduler determines which programs are admitted to the system for processing. It selects processes from the
queue and loads them into memory for execution. Process loads into the memory for CPU scheduling.
The primary objective of the job scheduler is to provide a balanced mix of jobs, such as I/O bound and processor bound. It also controls the degree of
multiprogramming. If the degree of multiprogramming is stable, then the average rate of process creation must be equal to the average departure rate of
processes leaving the system.
On some systems, the long-term scheduler may not be available or minimal. Time-sharing operating systems have no long term scheduler. When a process
changes the state from new to ready, then there is use of long-term scheduler.
It is also called as CPU scheduler. Its main objective is to increase system performance in accordance with the chosen set of criteria. It is the change of ready
state to running state of the process. CPU scheduler selects a process among the processes that are ready to execute and allocates CPU to one of them.
Short-term schedulers, also known as dispatchers, make the decision of which process to execute next. Short-term schedulers are faster than long-term
schedulers.
Medium-term scheduling is a part of swapping. It removes the processes from the memory. It reduces the degree of multiprogramming. The medium-term
scheduler is in-charge of handling the swapped out-processes.
A running process may become suspended if it makes an I/O request. A suspended processes cannot make any progress towards completion. In this condition,
to remove the process from memory and make space for other processes, the suspended process is moved to the secondary storage. This process is
called swapping, and the process is said to be swapped out or rolled out. Swapping may be necessary to improve the process mix.
2 Speed is lesser than short term scheduler Speed is fastest among other two Speed is in between both short and long term
scheduler.
3 It controls the degree of multiprogramming It provides lesser control over degree It reduces the degree of multiprogramming.
of multiprogramming
4 It is almost absent or minimal in time sharing It is also minimal in time sharing It is a part of Time sharing systems.
system system
5 It selects processes from pool and loads them It selects those processes which are It can re-introduce the process into memory and
into memory for execution ready to execute execution can be continued.
Context Switching
A context switching is the mechanism to store and restore the state or context of a CPU in Process Control block so that a process execution can be resumed
from the same point at a later time. Using this technique, a context switcher enables multiple processes to share a single CPU. Context switching is an essential
part of a multitasking operating system features.
When the scheduler switches the CPU from executing one process to execute another, the state from the current running process is stored into the process
control block. After this, the state for the process to run next is loaded from its own PCB and used to set the PC, registers, etc. At that point, the second process
can start executing.
Context switches are computationally intensive since register and memory state must be saved and restored. To avoid the amount of context switching time,
some hardware systems employ two or more sets of processor registers. When the process is switched, the following information is stored for later use.
• Program Counter
• Scheduling information
• Base and limit register value
• Currently used register
• Changed State
• I/O State information
• Accounting information
Operations on Processes
Operation on a Process: The execution of a process is a complex activity. It involves various operations. Following are the operations that are perfo rmed
1. Shared Memory
2. Message passing
Figure 1 below shows a basic structure of communication between processes via the shared memory method and via the message passing method.
An operating system can implement both methods of communication. First, we will discuss the shared memory methods of co mmunication and then
message passing. Communication between processes using shared memory requires processes to share some variable, and it comple tely depends on
how the programmer will implement it. One way of communication using shared memory can be imagined like this: Suppose process1 and process2 are
executing simultaneously, and they share some resources or use some information from another process. Process1 generates info rmation about certain
computations or resources being used and keeps it as a record in shared memory. When process2 needs to use the shared information, it will check in the
record stored in shared memory and take note of the information generated by process1 and act accordingly. Processes can use shared memory for
extracting information as a record from another process as well as for delivering any specific information to other processes.
Let’s discuss an example of communication between processes using the shared memory method.
• Establish a communication link (if a link already exists, no need to establish it again.)
• Start exchanging messages using basic primitives.
We need at least two primitives:
– send(message, destination) or send(message)
– receive(message, host) or receive(message)
The message size can be of fixed size or of variable size. If it is of fixed size, it is easy for an OS designer but complicated for a programmer and if it is of
variable size then it is easy for a programmer but complicated for the OS designer. A standard message can have two parts: header and body.
The header part is used for storing message type, destination id, source id, message length, and control information. The control information contains
information like what to do if runs out of buffer space, sequence number, priority. Generally, message is sent using FIFO sty le.
Message Passing through Communication Link.
Direct and Indirect Communication link
Now, We will start our discussion about the methods of implementing communication links. While implementing the link, there are some questions that
need to be kept in mind like :
Sockets
Sockets facilitate communication between two processes on the same machine or different machines. They are used in a client/server framework and consist
of the IP address and port number. Many application protocols use sockets for data connection and data transfer between a client and a server.
Socket communication is quite low-level as sockets only transfer an unstructured byte stream across processes. The structure on the byte stream is imposed
by the client and server applications.
A diagram that illustrates sockets is as follows −
These are interprocess communication techniques that are used for client-server based applications. A remote procedure call is also known as a subroutine
call or a function call.
A client has a request that the RPC translates and sends to the server. This request may be a procedure or a function call to a remote server. When the server
receives the request, it sends the required response back to the client.
A diagram that illustrates remote procedure calls is given as follows −
Pipes
These are interprocess communication methods that contain two end points. Data is entered from one end of the pipe by a process and consumed from the
other end by the other process.
The two different types of pipes are ordinary pipes and named pipes. Ordinary pipes only allow one way communication. For two way communication, two
pipes are required. Ordinary pipes have a parent child relationship between the processes as the pipes can only be accessed by processes that created or
inherited them.
Named pipes are more powerful than ordinary pipes and allow two way communication. These pipes exist even after the processes using them have terminated.
They need to be explicitly deleted when not required anymore.
A diagram that demonstrates pipes are given as follows −
Threads:
Within a program, a Thread is a separate execution path. It is a lightweight process that the operating system can schedule and run concurrently with other
threads. The operating system creates and manages threads, and they share the same memory and resources as the program that c reated them. This
enables multiple threads to collaborate and work efficiently within a single program.
A thread is a single sequence stream within a process. Threads are also called lightweight processes as they possess some of the properties of processes.
Each thread belongs to exactly one process. In an operating system that supports multithreading, the process can consist of many threads.
Why Do We Need Thread?
• Threads run in parallel improving the application performance. Each such thread has its own CPU state and stack, but they sha re the address space of
the process and the environment.
• Threads can share common data so they do not need to use interprocess communication. Like the processes, threads also have st ates like ready,
executing, blocked, etc.
• Priority can be assigned to the threads just like the process, and the highest priority thread is scheduled first.
• Each thread has its own Thread Control Block (TCB). Like the process, a context switch occurs for the thread, and register contents are saved in (TCB).
As threads share the same address space and resources, synchronization is also required for the various activities of the thr ead.
Why Multi-Threading?
A thread is also known as a lightweight process. The idea is to achieve parallelism by dividing a process into multiple threads. For example, in a browser,
multiple tabs can be different threads. MS Word uses multiple threads: one thread to format the text, another thread to process inputs, etc. More advantages
of multithreading are discussed below.
Multithreading is a technique used in operating systems to improve the performance and responsiveness of computer systems. Multithreading allows multiple
threads (i.e., lightweight processes) to share the same resources of a single process, such as the CPU, memory, and I/O devices.
Multicore Programming
Multicore programming helps to create concurrent systems for deployment on multicore processor and multiprocessor systems. A multicore processor system
is basically a single processor with multiple execution cores in one chip. It has multiple processors on the motherboard or chip. A Field-Programmable Gate
Array (FPGA) is might be included in a multiprocessor system. A FPGA is an integrated circuit containing an array of programmable logic blocks and a hierarchy
of reconfigurable interconnects. Input data is processed by to produce outputs. It can be a processor in a multicore or multiprocessor system, or a FPGA.
The multicore programming approach has following advantages −
• Multicore and FPGA processing helps to increase the performance of an embedded system.
• Also helps to achieve scalability, so the system can take advantage of increasing numbers of cores and FPGA processing power over time.
Concurrent systems that we create using multicore programming have multiple tasks executing in parallel. This is known as concurrent execution. When
multiple parallel tasks are executed by a processor, it is known as multitasking. A CPU scheduler, handles the tasks that execute in parallel. The CPU
implements tasks using operating system threads. So that tasks can execute independently but have some data transfer between them, such as data transfer
between a data acquisition module and controller for the system. Data transfer occurs when there is a data dependency.
, Multithreading Models
Multithreading Model:
Multithreading allows the application to divide its task into individual threads. In multi-threads, the same process or task can be done by the number of threads,
or we can say that there is more than one thread to perform the task in multithreading. With the use of multithreading, multitasking can be achieved.
The main drawback of single threading systems is that only one task can be performed at a time, so to overcome the drawback of this single threading, there
is multithreading that allows multiple tasks to be performed.
For example:
In the above example, client1, client2, and client3 are accessing the web server without any waiting. In multithreading, several tasks can run at the same time.
In an operating system, threads are divided into the user-level thread and the Kernel-level thread. User-level threads handled independent form above the
kernel and thereby managed without any kernel support. On the opposite hand, the operating system directly manages the kernel-level threads. Nevertheless,
there must be a form of relationship between user-level and kernel-level threads.
There exists three established multithreading models classifying these relationships are:
The many to one model maps many user levels threads to one kernel thread. This type of relationship facilitates an effective context-switching environment,
easily implemented even on the simple kernel with no thread support.
The disadvantage of this model is that since there is only one kernel-level thread schedule at any given time, this model cannot take advantage of the hardware
acceleration offered by multithreaded processes or multi-processor systems. In this, all the thread management is done in the userspace. If blocking comes,
this model blocks the whole system.
In the above figure, the many to one model associates all user-level threads to single kernel-level threads.
The one-to-one model maps a single user-level thread to a single kernel-level thread. This type of relationship facilitates the running of multiple threads in
parallel. However, this benefit comes with its drawback. The generation of every new user thread must include creating a corresponding kernel thread causing
an overhead, which can hinder the performance of the parent process. Windows series and Linux operating systems try to tackle this problem by limiting the
growth of the thread count.
In the above figure, one model associates that one user-level thread to a single kernel-level thread.
Many to Many Model multithreading model
In this type of model, there are several user-level threads and several kernel-level threads. The number of kernel threads created depends upon a particular
application. The developer can create as many threads at both levels but may not be the same. The many to many model is a compromise between the other
two models. In this model, if any thread makes a blocking system call, the kernel can schedule another thread for execution. Also, with the introduction of
multiple threads, complexity is not present as in the previous models. Though this model allows the creation of multiple kernel threads, true concurrency cannot
be achieved by this model. This is because the kernel can schedule only one process at a time.
Many to many versions of the multithreading model associate several user-level threads to the same or much less variety of kernel-level threads in the above
figure.
Thread Libraries
A thread is a lightweight of process and is a basic unit of CPU utilization which consists of a program counter, a stack, and a set of registers.
Given below is the structure of thread in a process −
A process has a single thread of control where one program can counter and one sequence of instructions is carried out at any given time. Dividing an
application or a program into multiple sequential threads that run in quasi-parallel, the programming model becomes simpler.
Thread has the ability to share an address space and all of its data among themselves. This ability is essential for some specific applications.
Threads are lighter weight than processes, but they are faster to create and destroy than processes.
Thread Library
A thread library provides the programmer with an Application program interface for creating and managing thread.
Ways of implementing thread library
There are two primary ways of implementing thread library, which are as follows −
• The first approach is to provide a library entirely in user space with kernel support. All code and data structures for the library exist in a local function call
in user space and not in a system call.
• The second approach is to implement a kernel level library supported directly by the operating system. In this case the code and data structures for the
library exist in kernel space.
Invoking a function in the application program interface for the library typically results in a system call to the kernel.
The main thread libraries which are used are given below −
• POSIX threads − Pthreads, the threads extension of the POSIX standard, may be provided as either a user level or a kernel level library.
• WIN 32 thread − The windows thread library is a kernel level library available on windows systems.
• JAVA thread − The JAVA thread API allows threads to be created and managed directly as JAVA programs.
Implicit Threading
One way to address the difficulties and better support the design of multithreaded applications is to transfer the creation and management of threading from
application developers to compilers and run-time libraries. This, termed implicit threading, is a popular trend today.
Implicit threading is mainly the use of libraries or other language support to hide the management of threads. The most common implicit threading library is
OpenMP, in context of C.
OpenMP is a set of compiler directives as well as an API for programs written in C, C++, or FORTRAN that provides support for parallel programming in shared-
memory environments. OpenMP identifies parallel regions as blocks of code that may run in parallel. Application developers insert compiler directives into their
code at parallel regions, and these directives instruct the OpenMP run-time library to execute the region in parallel. The following C program illustrates a
compiler directive above the parallel region containing the printf() statement:
Threading Issues
We can discuss some of the issues to consider in designing multithreaded programs. These issued are as follows −
The fork() and exec() system calls
The fork() is used to create a duplicate process. The meaning of the fork() and exec() system calls change in a multithreaded program.
If one thread in a program which calls fork(), does the new process duplicate all threads, or is the new process single-threaded? If we take, some UNIX systems
have chosen to have two versions of fork(), one that duplicates all threads and another that duplicates only the thread that invoked the fork() system call.
If a thread calls the exec() system call, the program specified in the parameter to exec() will replace the entire process which includes all threads.
Signal Handling
Generally, signal is used in UNIX systems to notify a process that a particular event has occurred. A signal received either synchronously or asynchronously,
based on the source of and the reason for the event being signalled.
All signals, whether synchronous or asynchronous, follow the same pattern as given below −
• A signal is generated by the occurrence of a particular event.
• The signal is delivered to a process.
• Once delivered, the signal must be handled.
Cancellation
Thread cancellation is the task of terminating a thread before it has completed.
For example − If multiple database threads are concurrently searching through a database and one thread returns the result the remaining threads might be
cancelled.
Process Synchronization:
Process Synchronization is the coordination of execution of multiple processes in a multi-process system to ensure that they access shared resources in a
controlled and predictable manner. It aims to resolve the problem of race conditions and other synchronization issues in a concurrent system.
The main objective of process synchronization is to ensure that multiple processes access shared resources without interfering with each other, and to
prevent the possibility of inconsistent data due to concurrent access. To achieve this, various synchronization techniques su ch as semaphores, monitors,
and critical sections are used.
In a multi-process system, synchronization is necessary to ensure data consistency and integrity, and to avoid the risk of deadlocks and other
synchronization problems. Process synchronization is an important aspect of modern operating systems, and it plays a crucial role in ensuring the correct
and efficient functioning of multi-process systems.
On the basis of synchronization, processes are categorized as one of the following two types:
• Independent Process: The execution of one process does not affect the execution of other processes.
• Cooperative Process: A process that can affect or be affected by other processes executing in the system.
Process synchronization problem arises in the case of Cooperative process also because resources are shared in Cooperative pr ocesses.
In the entry section, the process requests for entry in the Critical Section.
Any solution to the critical section problem must satisfy three requirements:
• Mutual Exclusion: If a process is executing in its critical section, then no other process is allowed to execute in the critical section.
• Progress: If no process is executing in the critical section and other processes are waiting outside the critical section, then only those processes that
are not executing in their remainder section can participate in deciding which will enter in the critical section next, and t he selection can not be
postponed indefinitely.
• Bounded Waiting: A bound must exist on the number of times that other processes are allowed to enter their critical sections after a process has
made a request to enter its critical section and before that request is granted.
Peterson’s Solution:
Peterson’s Solution is a classical software-based solution to the critical section problem. In Peterson’s solution, we have two shared variables:
• boolean flag[i]: Initialized to FALSE, initially no one is interested in entering the critical section
• int turn: The process whose turn is to enter the critical section.
Semaphores:
A semaphore is a signaling mechanism and a thread that is waiting on a semaphore can be signaled by another thread. This is different than a mutex as
the mutex can be signaled only by the thread that is called the wait function.
A semaphore uses two atomic operations, wait and signal for process synchronization.
A Semaphore is an integer variable, which can be accessed only through two operations wait() and signal().
There are two types of semaphores: Binary Semaphores and Counting Semaphores.
• Binary Semaphores: They can only be either 0 or 1. They are also known as mutex locks, as the locks can provide mutual exclusion. All the
processes can share the same mutex semaphore that is initialized to 1. Then, a process has to wait until the lock becomes 0. Then, the process can
make the mutex semaphore 1 and start its critical section. When it completes its critical section, it can reset the value of the mutex semaphore to 0 and
some other process can enter its critical section.
• Counting Semaphores: They can have any value and are not restricted over a certain domain. They can be used to control access to a resource that
has a limitation on the number of simultaneous accesses. The semaphore can be initialized to the number of instances of the resource. Whenever a
process wants to use that resource, it checks if the number of remaining instances is more than zero, i.e., the process has a n instance available. Then,
the process can enter its critical section thereby decreasing the value of the counting semaphore by 1. After the process is over with the use of the
instance of the resource, it can leave the critical section thereby adding 1 to the number of available instances of the reso urce.
Synchronization Hardware
In Synchronization hardware, we explore several more solutions to the critical-section problem using techniques ranging from hardware to software based APIs
available to application programmers. These solutions are based on the premise of locking; however, the design of such locks can be quite sophisticated.
These Hardware features can make any programming task easier and improve system efficiency. Here, we present some simple hardware instructions that
are available on many systems and show how they can be used effectively in solving the critical-section problem. If we could prevent interrupts from occurring
while a shared variable was being modified. The critical-section problem could be solved simply in a uniprocessor environment. In this manner, we would be
assuring that the current sequence of instructions would be allowed to execute in order without preemption. No other instructions would be run, so no
unexpected modifications could be made to the shared variable. This is the approach taken by non-preemptive kernels. But unfortunately, this solution is not
as feasible in a multiprocessor environment. Since the message is passed to all the processors, disabling interrupts on a multiprocessor can be time consuming.
System efficiency decreases when this massage passing delays entry into each critical section. Also the effect on a system’s clock is considered if the clock is
kept updated by interrupts. So many modern computer systems therefore provide special hardware instructions that allow us that we can test and modify the
content of a word or to swap the contents of two words atomically—that is, as one uninterruptible unit. We may use these special instructions to solve the
critical-section problem in a relatively simple manner. Now we abstract the main concepts behind these types of instructions. The TestAndSet() instruction can
be defined as shown in below code.
boolean test and set(boolean *target){
boolean rv = *target;
*target = true;
return rv;
}
Definition of the test and set() instruction.
The essential characteristic is that this instruction is executed atomically. So, if two TestAndSet C) instructions are executed simultaneously (each on a different
CPU), they will be executed sequentially in some arbitrary order. we can implement mutual exclusion by declaring a Boolean variable lock, initialized to false,
if the machine supports the TestAndSet () instruction. The structure of process P, is shown in below.
Example
do {
while (test and set(&lock)) ;
/* do nothing */
/* critical section */
lock = false;
/* remainder section */
}
while (true);
Mutual-exclusion implementation with test and set().
The SwapO instruction, in contrast to the TestAndSet0 instruction, operates on the contents of two words; it is executed atomically. mutual exclusion can be
provided as follows if the machine supports the SwapO instruction. Here, a global Boolean variable lock is declared and is initialized to false. Additionally, each
process has a local Boolean variable key. The structure of process P, is shown in figure below.
int compare_and_swap(int *val, int expected, int new val){
int temp = *val;
if (*val == expected)
*val = new val;
return temp;
}
Definition of the compare and swap() instruction.
do{
while (compare_and_swap(&lock, 0, 1) != 0) ;
/* do nothing */
/* critical section */
lock = 0;
/* remainder section */
}
while (true);
Mutual-exclusion implementation with the compare and swap() instruction.
Since these algorithms satisfy the mutual-exclusion requirement, they do not satisfy the bounded-waiting requirement. In below code, we present another
algorithm using the TestAndSet() instruction that satisfies all the critical-section requirements.
do{
waiting[i] = true;
key = true;
while (waiting[i] && key)
key = test and set(&lock);
waiting[i] = false;
/* critical section */
j = (i + 1) % n;
while ((j != i) && !waiting[j])
j = (j + 1) % n;
if (j == i)
lock = false;
else
waiting[j] = false;
/* remainder section */
}
while (true);
Bounded-waiting mutual exclusion with test and set().
The same data structures are boolean waiting[n]; boolean lock; These data structures are initialized to false. To prove the point that the mutual exclusion
requirement is met, we note that process P; can enter its critical section only if either waiting[i] == false or key -- false. The value of key can become false only
if the TestAndSet() is executed. The first process to execute the TestAndSet() will find key == false; all others must wait. The variable waiting[i] may become
false only if another process leaves its critical section; only one waiting [i] is set to false, maintaining the mutual-exclusion requirement.
To prove the point that the progress requirement is met, we note that the arguments presented for mutual exclusion also apply here, since a process exiting
the critical section either set lock to false or sets waiting[j] to false. Both of them allow a process that is waiting to enter its critical section to proceed. To prove
the point that the bounded-waiting requirement is met, we note that, when a process leaves its critical section, it scans the array waiting in the cyclic ordering
(z' + 1, i + 2,..., n — 1, 0, ..., i — 1). It designates the first process in this ordering that is in the entry section (waiting [j] =- true) as the next one to enter the
critical section.
Any process that waiting to enter its critical section will thus do so within n — 1 turns. Unfortunately for hardware designers, implementing atomic TestAndSet()
instructions on multiprocessors is not a trivial task.
Mutex Locks
Mutex
Mutex is a mutual exclusion object that synchronizes access to a resource. It is created with a unique name at the start of a program. The Mutex is a locking
mechanism that makes sure only one thread can acquire the Mutex at a time and enter the critical section. This thread only releases the Mutex when it exits
the critical section.
This is shown with the help of the following example −
wait (mutex);
…..
Critical Section
…..
signal (mutex);
A Mutex is different than a semaphore as it is a locking mechanism while a semaphore is a signalling mechanism. A binary semaphore can be used as a Mutex
but a Mutex can never be used as a semaphore.
semaphore is a signalling mechanism and a thread that is waiting on a semaphore can be signaled by another thread. This is different than a mutex as the
mutex can be signaled only by the thread that called the wait function.
A semaphore uses two atomic operations, wait and signal for process synchronization.
The wait operation decrements the value of its argument S, if it is positive. If S is negative or zero, then no operation is performed.
wait(S)
{
while (S<=0);
S--;
}
The signal operation increments the value of its argument S.
signal(S)
{
S++;
}
SEMAPHORE
There are mainly two types of semaphores i.e. counting semaphores and binary semaphores.
Counting Semaphores are integer value semaphores and have an unrestricted value domain. These semaphores are used to coordinate the resource access,
where the semaphore count is the number of available resources.
The binary semaphores are like counting semaphores but their value is restricted to 0 and 1. The wait operation only works when the semaphore is 1 and the
signal operation succeeds when semaphore is 0
Bound-Buffer problem
Also known as the Producer-Consumer problem. In this problem, there is a buffer of n slots, and each buffer is capable of storing one unit of data.
There are two processes that are operating on the buffer – Producer and Consumer. The producer tries to insert data and the consumer tries to
remove data.
If the processes are run simultaneously they will not yield the expected output.
The solution to this problem is creating two semaphores, one full and the other empty to keep a track of the concurrent processes.
Monitors
Monitors
Monitors are a synchronization construct that were created to overcome the problems caused by semaphores such as timing errors.
Monitors are abstract data types and contain shared data variables and procedures. The shared data variables cannot be directly accessed by a process and
procedures are required to allow a single process to access the shared data variables at a time.
This is demonstrated as follows:
monitor monitorName
{
data variables;
Procedure P1(....)
{
Procedure P2(....)
{
Procedure Pn(....)
{
Initialization Code(....)
{
}
}
Only one process can be active in a monitor at a time. Other processes that need to access the shared variables in a monitor have to line up in a queue and
are only provided access when the previous process release the shared variables.