Ertos 2025
Ertos 2025
The Operating System acts as a bridge between the user applications/tasks and the
underlying system resources through a set of system functionalities and services
User Applications
Application Programming
Interface (API)
Memory Management
Kernel Services
Process Management
Time Management
File System Management
I/O System Management
Device Driver
Interface
Underlying Hardware
Figure 1: The Architecture of Operating System
The Kernel:
It is responsible for managing the system resources and the communication among
the hardware and other system services
Kernel acts as the abstraction layer between system resources and user
applications
For a general purpose OS, the kernel contains different services like
Process Management
Primary Memory Management
File System management
I/O System (Device) Management
Secondary Storage Management
Protection
Time management
Interrupt Handling
Kernel Space and User Space:
The memory space at which the kernel code is located is known as ‘Kernel Space’
All user applications are loaded to a specific area of primary memory and this
memory area is referred as ‘User Space’
The partitioning of memory into kernel and user space is purely Operating System
dependent
An operating system with virtual memory support, loads the user applications into its
corresponding virtual memory space with demand paging technique. Most of the
operating systems keep the kernel application code in main memory and it is not
swapped out into the secondary memory
Monolithic Kernel:
All kernel modules run within the same memory space under a single kernel thread
The tight internal integration of kernel modules in monolithic kernel
architecture allows the effective utilization of the low-level features of the
underlying system
The major drawback of monolithic kernel is that any error or failure in any one
of the kernel modules leads to the crashing of the entire kernel application
Microkernel:
The microkernel design incorporates only the essential set of Operating System services into
the kernel.
The rest of the Operating System services are implemented in programs known as
'Servers' which runs in user space.
The kernel design is highly modular provides OS-neutral abstraction.
Memory management, process management, timer systems and interrupt handlers are
examples of essential services, which forms the part of the microkernel.
Examples for microkernel: QNX, Minix 3 kernels.
Benefits of Microkernel:
Robustness: If a problem is encountered in any services in server can reconfigured and
re-started without the need for re-starting the entire OS.
Configurability: Any services , which run as ‘server’ application can be changed
without need to restart the whole system.
ii. The kernel is more generalized and contains all the required servicesto execute
generic applications
iv. May inject random delays into application software and thus cause slow
responsiveness of an application at unexpected times
vi. Personal Computer/Desktop system is a typical example for a system where GPOSs
are deployed.
vii. Windows XP/MS-DOS etc are examples of General Purpose Operating System
2. Real Time Purpose Operating System (RTOS):
i. Operating Systems, which are deployed in embedded systems demanding real-
time response
ii. Deterministic in execution behavior. Consumes only known amount of time for
kernel applications
v. Windows CE, QNX, VxWorks , MicroC/OS-II etc are examples of Real Time
Operating Systems (RTOS)
The Real Time Kernel: The kernel of a Real Time Operating System is referred as Real
Time kernel. In complement to the conventional OS kernel, the Real Time kernel is highly
specialized and it contains only the minimal set of services required for running the user
applications/tasks. The basic functions of a Real Time kernel are
a) Task/Process management
b) Task/Process scheduling
c) Task/Process synchronization
d) Error/Exception handling
e) Memory Management
f) Interrupt handling
g) Time management
Real Time Kernel Task/Process Management: Deals with setting up the memory space
for the tasks, loading the task’s code into the memory space, allocating system resources,
setting up a Task Control Block (TCB) for the task and task/process termination/deletion.
A Task Control Block (TCB) is used for holding the information corresponding to a task.
TCB usually contains the following set of information
Task Type: Task type. Indicates what is the type for this task. The task can be a
hard real time or soft real time or background task.
Task Priority: Task priority (E.g. Task priority =1 for task with priority = 1)
Task Context Pointer: Context pointer. Pointer for context saving
Task Memory Pointers: Pointers to the code memory, data memoryand stack
memory for the task
Task Pointers: Pointers to other TCBs (TCBs for preceding, next and waiting
tasks)
Task/Process Scheduling: Deals with sharing the CPU among various tasks/processes. A
kernel application called ‘Scheduler’ handles the task scheduling. Scheduler is nothing
but an algorithm implementation, which performs the efficient and optimal scheduling of
tasks to provide a deterministic behavior.
Since predictable timing and deterministic behavior are the primary focus for an
RTOS, RTOS achieves this by compromising the effectiveness of memory
allocation
RTOS generally uses ‘block’ based memory allocation technique, instead of the
usual dynamic memory allocation techniques used by the GPOS.
RTOS kernel uses blocks of fixed size of dynamic memory and the block is
allocated for a task on a need basis. The blocks are stored in a ‘Free buffer
Queue’.
Most of the RTOS kernels allow tasks to access any of the memory blocks without
any memory protection to achieve predictable timing and avoid the timing
overheads
RTOS kernels assume that the whole design is proven correct and protection is
unnecessary. Some commercial RTOS kernels allow memory protection as
optional and the kernel enters a fail-safe mode when an illegal memory access
occurs
A few RTOS kernels implement Virtual Memory concept for memory allocation if
the system supports secondary memory storage (like HDD and FLASH memory).
In the ‘block’ based memory allocation, a block of fixed memory is always
allocated for tasks on need basis and it is taken as a unit. Hence, there will not be
any memory fragmentation issues.
Interrupt Handling:
For synchronous interrupts, the interrupt handler runs in the same context of the
interrupting task.
The interrupts generated by external devices (by asserting the Interrupt line of the
processor/controller to which the interrupt line of the device is connected)
connected to the processor/controller, timer overflow interrupts, and serial data
reception / transmission interrupts etc are examples for asynchronous interrupts.
For asynchronous interrupts, the interrupt handler is usually written as
separate task (Depends on OS Kernel implementation) and it runs in a
different context. Hence, a context switch happens while handling the
asynchronous interrupts.
Priority levels can be assigned to the interrupts and each interrupts can be enabled
or disabled individually.
Time Management:
Accurate time management is essential for providing precise time reference for all
applications
The ‘Timer tick’ is taken as the timing reference by the kernel. The ‘Timer tick’
interval may vary depending on the hardware timer. Usually the ‘Timer tick’ varies
in the microseconds range
The time parameters for tasks are expressed as the multiples of the ‘Timer tick’
The System time is updated based on the ‘Timer tick’
If the System time register is 32 bits wide and the ‘Timer tick’ interval
232 * 10-6/ (24 * 60 * 60) = 49700 Days =~ 0.0497 Days = 1.19 Hours
If the ‘Timer tick’ interval is 1 millisecond, the System time register will reset in
The ‘Timer tick’ interrupt is handled by the ‘Timer Interrupt’ handler of kernel. The
‘Timer tick’ interrupt can be utilized for implementing the following actions.
Increment the System time register by one. Generate timing error and reset the
System time register if the timer tick count is greater than the maximum range
available for System time register
Update the timers implemented in kernel (Increment or decrement the timer registers
for each timer depending on the count direction setting for each register. Increment
registers with count direction setting = ‘count up’ and decrement registers with count
direction setting = ‘count down’)
Invoke the scheduler and schedule the tasks again based on the scheduling algorithm
Delete all the terminated tasks and their associated data structures (TCBs)
Load the context for the first task in the ready queue. Due to the re- scheduling, the
ready task might be changed to a new one from the task, which was pre-empted by the
‘Timer Interrupt’ task
Hard Real-time System:
A Real Time Operating Systems which strictly adheres to the timing constraints
for a task.
A Hard Real Time system must meet the deadlines for a task without any slippage
Missing any deadline may produce catastrophic results for Hard Real Time
Systems, including permanent data lose and irrecoverable damages to the
system/users
As a rule of thumb, Hard Real Time Systems does not implement the virtual
memory model for handling the memory. This eliminates the delay in swapping in
and out the code corresponding to the task to and from the primary memory
The presence of Human in the loop (HITL) for tasks introduces un- expected
delays in the task execution. Most of the Hard Real Time Systems are automatic
and does not contain a ‘human in the loop’
Missing deadlines for tasks are acceptable if the frequency of deadline missing
is within the compliance limit of the Quality of Service(QoS)
Soft Real Time systems most often have a ‘human in the loop (HITL)’
Automatic Teller Machine (ATM) is a typical example of Soft Real Time System. If
the ATM takes a few seconds more than the ideal operation time, nothing fatal
happens.
An audio video play back system is another example of Soft Real Time system. No
potential damage arises if a sample comes late by fraction of a second, for play
back.
In the Operating System context, a task is defined as the program in execution and
the related information maintained by the Operating system for the program
Concurrent execution is achieved through the sharing of CPU among the processes.
A process mimics a processor in properties and holds a set of registers, process status,
a Program Counter (PC) to point to the next executable instruction of the process, a
stack for holding the local variables associated with the process and the code
corresponding to the process
Process
A process, which inherits all
Stack
the properties of the CPU, (Stack Pointer)
can be considered as a virtual
Working Registers
processor, awaiting its turn to
have its properties switched Status Registers
into the physical processor
Program Counter (PC)
When the process gets its turn, its registers and Program counter register
becomes mapped to the physical registers of the CPU
Memory organization of Processes:
The memory occupied by the process is segregated into three regions namely; Stack
memory, Data memory and Code memory.
The Stack memory holds all temporary data such as variables local to the process
The Code memory contains the program code (instructions) corresponding to the
process
The cycle through which a process changes its state from ‘newly created’ to
‘execution completed’ is known as ‘Process Life Cycle’. The various states through
which a process traverses through during a Process Life Cycle indicates the current
status of the process with respect to time and also provides information on what it is
allowed to do next
Created State: The state at which a process is being created is referred as ‘Created
State’. The Operating System recognizes a process in the ‘Created State’ but no
resources are allocated to the process
Ready State: The state, where a process is incepted into the memory and awaiting
the processor time for execution, is known as ‘Ready State’. At this stage, the process
is placed in the ‘Ready list’ queue maintained by the OS
Running State: The state where in the source code instructions corresponding to the
process is being executed is called ‘Running State’. Running state is the state at
which the process execution happens
When a process changes its state from Ready to running or from running to
blocked or terminated or from blocked to running, the CPU allocation for the
process may also change
Threads
Thread Process
Thread is a single unit of execution and is part of Process is a program in execution and
process. contains one or more threads.
A thread does not have its own data memory and Process has its own code memory, data
heap memory. It shares the data memory and heap memory and stack memory.
memory with other threads of the same process.
A thread cannot live independently; it lives within A process contains at least one thread.
the process.
There can be multiple threads in a process. The first Threads within a process share the code, data
thread (main thread) calls the main function and and heap memory. Each thread holds
occupies the start of the stack memory of the separate memory area for stack (shares the
process. total stack memory of the process).
Threads are very inexpensive to create Processes are very expensive to create.
Involves many OS overhead.
Context switching is inexpensive and fast Context switching is complex and involves
lot of OS overhead and is comparatively
slower.
If a thread expires, its stack is reclaimed by the If a process dies, the resources allocated to it
process. are reclaimed by the OS and all the
associated threads of the process also dies.
Advantages of Threads:
1. Better memory utilization: Multiple threads of the same process share the address
space for data memory. This also reduces the complexity of inter thread
communication since variables can be shared across the threads.
3. Speeds up the execution of the process: The process is split into different threads,
when one thread enters a wait state, the CPU can be utilized by other threads of the
process that do not require the event, which the other thread is waiting, for processing.
Multiprocessor systems possess multiple CPUs and can execute multiple processes
simultaneously
The ability of the Operating System to have multiple programs in memory, which are
ready for execution, is referred as multiprogramming
Multitasking refers to the ability of an operating system to hold multiple processes in
memory and switch the processor (CPU) from executing one process to another
process
Context switching refers to the switching of execution context from task to other
During context switching, the context of the task to be executed is retrieved from the
saved context list. This is known as Context retrieval.
‘Context Switching’
‘Context Switching’
Time
• Non-preemptive Multitasking: The process/task, which is currently given the CPU time,
is allowed to execute until it terminates (enters the ‘Completed’ state) or enters the
‘Blocked/Wait’ state, waiting for an I/O. The co- operative and non-preemptive
multitasking differs in their behavior when they are in the ‘Blocked/Wait’ state. In co-
operative multitasking, the currently executing process/task need not relinquish the
CPU when it enters the ‘Blocked/Wait’ sate, waiting for an I/O, or a shared resource
access or an event to occur whereas in non-preemptive multitasking the currently
executing task relinquishes the CPU when it waits for an I/O.
Task Scheduling:
In a multitasking system, there should be some mechanism in place to share the CPU
among the different tasks and to decide which process/task is to be executed at a
given point of time
Scheduling policies forms the guidelines for determining which task is to be executed
when
The scheduling policies are implemented in an algorithm and it is run by the kernel as
a service
Depending on the scheduling policy the process scheduling decision may take place
when a process switches its state to
Allocates CPU time to the processes based on the order in which they enters the
‘Ready’ queue
Drawbacks:
Favors monopoly of process. A process, which does not contain any I/O
operation, continues its execution until it finishes its task
In general, FCFS favors CPU bound processes and I/O bound processes may have to
wait until the completion of CPU bound process, if the currently executing process is
a CPU bound process. This leads to poor device utilization.
The average waiting time is not minimal for FCFS scheduling algorithm
EXAMPLE: Three processes with process IDs P1, P2, P3 with estimated completion time
10, 5, 7 milliseconds respectively enters the ready queue together in the order P1, P2, P3.
Calculate the waiting time and Turn around Time (TAT) for each process and the Average
waiting time and Turn Around Time (Assuming there is no I/O waiting for the processes).
P1 P2 P3
0 10 15 22
10 5 7
Assuming the CPU is readily available at the time of arrival of P1, P1 starts executing
without any waiting in the ‘Ready’ queue. Hence the waiting time for P1 is zero.
Waiting Time for P3 = 15 ms (P3 starts executing after completing P1 and P2)
Average waiting time = (Waiting time for all processes) / No. of Processes
Do-)
Average Turn around Time= (Turn around Time for all processes) / No. of Processes
= (Turn Around Time for (P1+P2+P3)) / 3
= (10+15+22)/3 = 47/3
= 15.66 milliseconds
Allocates CPU time to the processes based on the order in which theyare entered
in the ‘Ready’ queue
LCFS scheduling is also known as Last In First Out (LIFO) where the process, which
is put last into the ‘Ready’ queue, is serviced first
Drawbacks:
Favors monopoly of process. A process, which does not contain any I/O operation,
continues its execution until it finishes its task
In general, LCFS favors CPU bound processes and I/O bound processes may have to
wait until the completion of CPU bound process, if the currently executing process is
a CPU bound process. This leads to poor device utilization.
The average waiting time is not minimal for LCFS scheduling algorithm
EXAMPLE: Three processes with process IDs P1, P2, P3 with estimated completion time
10, 5, 7 milliseconds respectively enters the ready queue together in the order P1, P2, P3
(Assume only P1 is present in the ‘Ready’ queue when the scheduler picks up it and P2, P3
entered ‘Ready’ queue after that). Now a new process P4 with estimated completion time 6ms
enters the ‘Ready’ queue after 5ms of scheduling P1. Calculate the waiting time and Turn
around Time (TAT) for each process and the Average waiting time and Turn around Time
(Assuming there is no I/O waiting for the processes).Assume all the processes contain only
CPU operation and no I/O operations are involved.
Solution: Initially there is only P1 available in the Ready queue and the scheduling sequence
will be P1, P3, P2. P4 enters the queue during the execution of P1 and becomes the last
process entered the ‘Ready’ queue. Now the order of execution changes to P1, P4, P3, and P2
as given below.
P1 P4 P3 P2
0 10 16 23 28
10 6 7 5
The waiting time for all the processes are given as Waiting
Waiting Time for P4 = 5 ms (P4 starts executing after completing P1. But P4 arrived after
5ms of execution of P1. Hence its waiting time = Execution start time
– Arrival Time = 10-5 = 5)
Waiting Time for P3 = 16 ms (P3 starts executing after completing P1 and P4)
Waiting Time for P2 = 23 ms (P2 starts executing after completing P1, P4 and P3)
Average waiting time = (Waiting time for all processes) / No. of Processes
= (0 + 5 + 16 + 23)/4 = 44/4
= 11 milliseconds
Turn around Time (TAT) for P1 = 10 ms (Time spent in Ready Queue + Execution Time)
Turn around Time (TAT) for P3 = 23 ms (Time spent in Ready Queue + Execution Time)
Turn Around Time (TAT) for P2 = 28 ms (Time spent in Ready Queue + Execution Time)
Average Turn Around Time = (Turn Around Time for all processes) / No. of Processes
= (Turn Around Time for (P1+P4+P3+P2)) / 4
= (10+11+23+28)/4 = 72/4
= 18 milliseconds
Non-preemptive scheduling – Shortest Job First (SJF) Scheduling.
Allocates CPU time to the processes based on the execution completion time for tasks
The average waiting time for a given set of processes is minimal in SJF scheduling
A process whose estimated execution completion time is high may not get a chance to
execute if more and more processes with least estimated execution time enters the
‘Ready’ queue before the process with longest estimated execution time starts its
execution
May lead to the ‘Starvation’ of processes with high estimated completion time
Difficult to know in advance the next shortest process in the ‘Ready’ queue for
scheduling since new processes with different estimated execution time keep entering
the ‘Ready’ queue at any point of time.
The priority of a task is expressed in different ways, like a priority number, the time
required to complete the execution etc.
In number based priority assignment the priority is a number ranging from 0 to the
maximum priority supported by the OS. The maximum level of priority is OS
dependent.
Windows CE supports 256 levels of priority (0 to 255 priority numbers, with 0 being
the highest priority)
The priority is assigned to the task on creating it. It can also be changed dynamically
(If the Operating System supports this feature)
The non-preemptive priority based scheduler sorts the ‘Ready’ queue based on the
priority and picks the process with the highest level of priority for execution.
EXAMPLE: Three processes with process IDs P1, P2, P3 with estimated completion time
10, 5, 7 milliseconds and priorities 0, 3, 2 (0- highest priority, 3 lowest priority) respectively
enters the ready queue together. Calculate the waiting time and Turn Around Time (TAT) for
each process and the Average waiting time and Turn Around Time (Assuming there is no I/O
waiting for the processes) in priority based scheduling algorithm.
Solution: The scheduler sorts the ‘Ready’ queue based on the priority and schedules the
process with the highest priority (P1 with priority number 0) first and the next high priority
process (P3 with priority number 2) as second and so on. The order in which the processes
are scheduled for execution is represented as
P1 P3 P2
0 10 17 22
10 7 5
The waiting time for all the processes are given as Waiting
Time for P1 = 0 ms (P1 starts executing first)
Waiting Time for P3 = 10 ms (P3 starts executing after completing P1)
Waiting Time for P2 = 17 ms (P2 starts executing after completing P1 and P3)
Average waiting time = (Waiting time for all processes) / No. of Processes
= (Waiting time for (P1+P3+P2)) / 3
= (0+10+17)/3 = 27/3
= 9 milliseconds
Turn Around Time (TAT) for P1 = 10 ms (Time spent in Ready Queue + Execution Time)
= (10+17+22)/3 = 49/3
= 16.33 milliseconds
Drawbacks:
The technique of gradually raising the priority of processes which are waiting in the
‘Ready’ queue as time progresses, for preventing ‘Starvation’, is known as ‘Aging’.
Preemptive scheduling:
Every task in the ‘Ready’ queue gets a chance to execute. When and how often each
process gets a chance to execute (gets the CPU time) is dependent on the type of
preemptive scheduling algorithm used for scheduling the processes
The scheduler can preempt (stop temporarily) the currently executing task/process
and select another task from the ‘Ready’ queue for execution
When to pre-empt a task and which task is to be picked up from the ‘Ready’ queue for
execution after preempting the current task is purely dependent on the scheduling
algorithm
A task which is preempted by the scheduler is moved to the ‘Ready’ queue. The act of
moving a ‘Running’ process/task into the ‘Ready’ queue by the scheduler, without the
processes requesting for it is known as‘Preemption’
The non preemptive SJF scheduling algorithm sorts the ‘Ready’ queue only after the
current process completes execution or enters wait state, whereas the preemptive SJF
scheduling algorithm sorts the ‘Ready’ queue when a new process enters the ‘Ready’
queue and checks whether the execution time of the new process is shorter than the
remaining of the total estimated execution time of the currently executing process
If the execution time of the new process is less, the currently executing process is
preempted and the new process is scheduled for execution
Always compares the execution completion time (ie the remaining execution time for
the new process) of a new process entered the ‘Ready’ queue with the remaining time
for completion of the currently executing process and schedules the process with
shortest remaining time for execution.
EXAMPLE: Three processes with process IDs P1, P2, P3 with estimated completion time
10, 5, 7 milliseconds respectively enters the ready queue together. A new process P4 with
estimated completion time 2ms enters the ‘Ready’ queue after 2ms. Assume all the processes
contain only CPU operation and no I/O operations are involved.
Solution: At the beginning, there are only three processes (P1, P2 and P3) available in the
‘Ready’ queue and the SRT scheduler picks up the process with the Shortest remaining time
for execution completion (In this example P2 with remaining time 5ms) for scheduling. Now
process P4 with estimated execution completion time 2ms enters the ‘Ready’ queue after 2ms
of start of execution of P2. The processes are re-scheduled for execution in the following
order
P2 P4 P2 P3 P1
0 2 4 7 14 24
2 2 3 7 10
Waiting Time for P2 = 0 ms + (4 -2) ms = 2ms (P2 starts executing first and is
interrupted by P4 and has to wait till the completion of P4 to
get the next CPU slot)
Waiting Time for P4 = 0 ms (P4 starts executing by preempting P2 since the
execution time for completion of P4 (2ms) is less than that
of the Remaining time for execution completion of P2
(Here it is 3ms))
Waiting Time for P3 = 7 ms (P3 starts executing after completing P4 and P2)
Waiting Time for P1 = 14 ms (P1 starts executing after completing P4, P2 and P3)
Average waiting time = (Waiting time for all the processes) / No. of Processes
= (0 + 2 + 7 + 14)/4 = 23/4
= 5.75 milliseconds
Turn around Time (TAT) for P2 = 7 ms (Time spent in Ready Queue + Execution Time)
Turn Around Time (TAT) for P4 = 2 ms (Time spent in Ready Queue + Execution Time
= (Execution Start Time – Arrival Time) + Estimated Execution Time = (2-2) + 2)
Turn around Time (TAT) for P3 = 14 ms (Time spent in Ready Queue + Execution Time)
Turn around Time (TAT) for P1 = 24 ms (Time spent in Ready Queue +Execution Time)
Average Turn around Time = (Turn around Time for all the processes) / No. of Processes
The term Round Robin is very popular among the sports and games activities. You might
have heard about 'Round Robin' league or 'Knock out' league associated with any football or
cricket tournament. In the 'Round Robin' league each team in a group gets an equal chance
to play against the rest of the teams in the same group whereas in the 'Knock out' league the
losing team in a match moves out of the tournament .
In Round Robin scheduling, each process in the 'Ready' queue is executed for a pre-defined
time slot.
The execution starts with picking up the first process in the 'Ready' queue. It is executed for
a pre-defined time and when the pre-defined time elapses or the process completes (before
the pre-defined time slice), the next process in the 'Ready' queue is selected for execution.
This is repeated for all the processes in the 'Ready' queue. Once each process in the 'Ready'
queue is executed for the pre-defined time period, the scheduler comes back and picks the
first process in the 'Ready' queue again for execution.
The sequence is repeated. This reveals that the Round Robin scheduling is similar to the
FCFS scheduling and the only difference is that a time slice based preemption is added to
switch the execution between the processes in the `Ready' queue.
Figure: Round Robin Scheduling
Once each process in the ‘Ready’ queue is executed for the pre-defined time period,
the scheduler comes back and picks the first process in the ‘Ready’ queue again for
execution.
Round Robin scheduling is similar to the FCFS scheduling and the only difference is
that a time slice based preemption is added to switch the execution between the
processes in the ‘Ready’ queue
EXAMPLE: Three processes with process IDs P1, P2, P3 with estimated completion time 6,
4, 2 milliseconds respectively, enters the ready queue together in the order P1, P2, P3.
Calculate the waiting time and Turn Around Time (TAT) for each process and the Average
waiting time and Turn Around Time (Assuming there is no I/O waiting for the processes) in
RR algorithm with Time slice= 2ms.
Solution: The scheduler sorts the ‘Ready’ queue based on the FCFS policy and picks up the
first process P1 from the ‘Ready’ queue and executes it for the time slice 2ms. When the time
slice is expired, P1 is preempted and P2 is scheduled for execution. The Time slice expires
after 2ms of execution of P2. Now P2 is preempted and P3 is picked up for execution. P3
completes its execution within the time slice and the scheduler picks P1 again for execution
for the next time slice. This procedure is repeated till all the processes are serviced. The order
in which the processes are scheduled for execution is represented as
P1 P2 P3 P1 P2 P1
0 2 4 6 8 10 12
2 2 2 2 2 2
The waiting time for all the processes are given as
Waiting Time for P1 = 0 + (6-2) + (10-8) = 0+4+2= 6ms (P1 starts executing first
and waits for two time slices to get execution back and
again 1 time slice for getting CPU time)
Waiting Time for P2 = (2-0) + (8-4) = 2+4 = 6ms (P2 starts executing after P1
executes for 1 time slice and waits for two time
slices to get the CPU time)
Waiting Time for P3 = (4 -0) = 4ms (P3 starts executing after completing the first time
slices for P1 and P2 and completes its execution in a single time slice.)
Average waiting time = (Waiting time for all the processes) / No. of Processes
= (6+6+4)/3 = 16/3
= 5.33 milliseconds
Turn around Time (TAT) for P1 = 12 ms (Time spent in Ready Queue + Execution Time)
Average Turn around Time = (Turn around Time for all the processes) / No. of Processes
= (12+10+6)/3 = 28/3
= 9.33 milliseconds.
Preemptive scheduling – Priority based Scheduling
Same as that of the non-preemptive priority based scheduling except for the switching
of execution between tasks
In preemptive priority based scheduling, any high priority process entering the
‘Ready’ queue is immediately scheduled for execution whereas in the non-preemptive
scheduling any high priority process entering the ‘Ready’ queue is scheduled only
after the currently executing process completes its execution or only when it
voluntarily releases the CPU
EXAMPLE: Three processes with process IDs P1, P2, P3 with estimated completion time
10, 5, 7 milliseconds and priorities 1, 3, 2 (0- highest priority, 3 lowest priority) respectively
enters the ready queue together. A new process P4 with estimated completion time 6ms and
priority 0 enters the ‘Ready’ queue after 5ms of start of execution of P1. Assume all the
processes contain only CPU operation and no I/O operations are involved.
Solution: At the beginning, there are only three processes (P1, P2 and P3) available in the
‘Ready’ queue and the scheduler picks up the process with the highest priority (In this
example P1 with priority 1) for scheduling. Now process P4 with estimated execution
completion time 6ms and priority 0 enters the ‘Ready’ queue after 5ms of start of execution of
P1. The processes are re-scheduled for execution in the following order
P1 P4 P1 P3 P2
0 5 11 16 23 28
5 6 5 7 5
The waiting time for all the processes are given as
Waiting Time for P1 = 0 + (11-5) = 0+6 =6 ms (P1 starts executing first and gets
Preempted by P4 after 5ms and again gets the CPU time after
completion of P4)
Waiting Time for P3 = 16 ms (P3 starts executing after completing P1 and P4)
Waiting Time for P2 = 23 ms (P2 starts executing after completing P1, P4 and P3)
Average waiting time = (Waiting time for all the processes) / No. of Processes
= (6 + 0 + 16 + 23)/4 = 45/4
= 11.25 milliseconds
Turn Around Time (TAT) for P1 = 16 ms (Time spent in Ready Queue + Execution Time)
Turn Around Time (TAT) for P4 = 6ms (Time spent in Ready Queue + Execution Time
= (Execution Start Time – Arrival Time) + Estimated Execution Time = (5-5) + 6 = 0 + 6)
Turn Around Time (TAT) for P3 = 23 ms (Time spent in Ready Queue + Execution
Time) Turn Around Time (TAT) for P2 = 28 ms (Time spent in Ready Queue +
Execution Time) Average Turn Around Time= (Turn Around Time for all the processes) /
No. of Processes
= (16+6+23+28)/4 = 73/4
= 18.25 milliseconds
1. Functional
2. Non-functional requirements.
1. Functional Requirements:
1. Processor support:
2. Memory Requirements:
The RTOS requires ROM memory for holding the OS files and it is
normally stored in a non-volatile memory like FLASH.
3. Real-Time Capabilities:
It is not mandatory that the OS for all embedded systems need to be Real-
Time and all embedded OS’s are ‘Real-Time’ in behavior.
The kernel of the OS may disable interrupts while executing certain services and it
may lead to interrupt latency.
For an embedded system whose response requirements are high, this latency should
be minimal.
It is very useful if the OS supports modularization where in which the developer can
choose the essential modules and re-compile the OS image for functioning.
7. Support for Networking and Communication:
The OS kernel may provide stack implementation and driver support for a bunch of
communication interfaces and networking.
Ensure that the OS under consideration provides support for all the interfaces required
by the embedded product.
Certain OS’s include the run time libraries required for running applications written in
languages like JAVA and C++.
The OS may include these components as built-in component, if not; check the
availability of the same from a third party.
2. Non-Functional Requirements:
It may be possible to build the required features by customizing an open source OS.
The decision on which to select is purely dependent on the development cost, licensing
fees for the OS, development time and availability of skilled resources.
2. Cost:
The total cost for developing or buying the OS and maintaining it in terms of
commercial product and custom build needs to be evaluated before taking a decision on
the selection of OS.
Certain OS’s may be superior in performance, but the availability of tools for
supporting the development may be limited.
4. Ease of Use:
How easy it is to use a commercial RTOS is another important feature that needs to be
considered in the RTOS selection.
5. After Sales:
For a commercial embedded RTOS, after sales in the form of e-mail, on-call services
etc. for bug fixes, critical patch updates and support for production issues etc. should be
analyzed thoroughly.
3.2 TASK COMMUNICATION:
In a multitasking system, multiple tasks/processes run concurrently (in pseudo parallelism)
and each process may or may not interact between. Based on the degree of interaction, the
processes running on an OS are classified as,
1. Co-operating Processes: In the co-operating interaction model one process requires the
inputs from other processes to complete its execution.
2. Competing Processes: The competing processes do not share anything among themselves
but they share the system resources. The competing processes compete for the system
resources such as file, display device, etc.
Co-operation through Sharing: The co-operating process exchange data through some
shared resources.
Co-operation through Communication: No data is shared between the processes. But they
communicate for synchronization.
The mechanism through which processes/tasks communicate each other is known as “Inter
Process/Task Communication (IPC)”. Inter Process Communication is essential for process
co-ordination. The various types of Inter Process Communication (IPC) mechanisms adopted
by process are kernel (Operating System) dependent. Some of the important IPC mechanisms
adopted by various kernels are explained below.
3.2.1.1 Pipes:
'Pipe' is a section of the shared memory used by processes for communicating. Pipes follow
the client-server architecture. A process which creates a pipe is known as a pipe server and a
process which connects to a pipe is known as pipe client. A pipe can be considered as a
conduit for information flow and has two conceptual ends. It can be unidirectional, allowing
information flow in one direction or bidirectional allowing bi-directional information flow. A
unidirectional pipe allows the process connecting at one end of the pipe to write to the pipe
and the process connected at the other end of the pipe to read the data, whereas a bi-
directional pipe allows both reading and writing at one end. The unidirectional pipe can be
visualized as
Anonymous Pipes: The anonymous pipes-are unnamed, unidirectional pipes used for data
transfer between two processes.
Named Pipes: Named pipe is a named, unidirectional or bi-directional pipe for data
exchange between processes. Like anonymous pipes, the process which creates the named
pipe is known as pipe server. A process which connects to the named pipe is known as pipe
client.
With named pipes, any process can act as both client and server allowing point-to-point
communication. Named pipes can be used for communicating between processes running on
the same machine or between processes running on different machines connected to a
network.
Please refer to the Online Learning Centre for details on the Pipe implementation
under Windows Operating Systems.
Message Queue.
Mailbox.
Signaling.
3.2.2.1 Message Queue: Usually the process which wants to talk to another process posts the
message to a First-In-First-Out (FIFO) queue called 'Message queue', which stores the
messages temporarily in a system defined memory object, to pass it to the desired process
(Fig. 10.20). Messages are sent and received through send (Name of the process to which the
message is to be sent,-message) and receive (Name of the process from which the message is
to be received, message) methods. The messages are exchanged through a message queue.
The implementation of the message queue, send and receive methods are OS kernel
dependent. The Windows XP OS kernel maintains a single system message queue and one
process/thread (Process and threads are used interchangeably here, since thread is the basic
unit of process in windows) specific message queue. A thread which wants to communicate
with another thread posts the message to the system message queue. The kernel picks up the
message from the system message queue one at a time and examines the message for finding
the destination thread and then posts the message to the message queue of the corresponding
thread. For posting a message to a thread's message queue, the kernel fills a message structure
MSG and copies it to the message queue of the thread. The message structure MSG contains
the handle of the process/thread for which the message is intended, the message parameters,
the time at which the message is posted, etc. A thread can simply post a message to another
thread and can continue its operation or it may wait for a response from the thread to which
the message is posted. The messaging mechanism is classified into synchronous and
asynchronous based on the behaviour of the message posting thread. In asynchronous
messaging, the message posting thread just posts the message to the queue and it will not wait
for an acceptance (return) from the thread to which the message is posted, whereas in
synchronous messaging, the thread which posts a message enters waiting state and waits for
the message result from the thread to which the message is posted. The thread which invoked
the send message becomes blocked and the scheduler will not pick it up for scheduling. The
PostMessage (HWND hWnd, UINT Msg, WPARAM wParam, LPARAM /Param) or
PostThreadMessage (DWORD idThread, UNT Msg, WPARAM wParam, LPARAM IParam)
API is used by a thread in Windows for posting a message to its own message queue or to the
message queue of another thread.
The PostMessage API does not always guarantee the posting of messages to message queue.
The PostMessage API will not post a message to the message queue when the message queue
is full. Hence it is recommended to check the return value of PostMessage API to confirm the
posting of message. The SendMessage (HWND hWnd, U1NT Msg, WPARAM wParam,
LPARAM 1Param) API call sends a message to the thread specified by the handle hWnd and
waits for the callee thread to process the message. The thread which calls the SendMessage
API enters waiting state and waits for the message result from the thread to which the
message is posted. The thread which invoked the SendMessage API call becomes blocked
and the scheduler will not pick it up for scheduling.
3.2.2.2 Mailbox:
The thread which creates the mailbox is known. as 'mailbox server' and the threads which
subscribe to the mailbox are known as 'mailbox clients'. The mailbox server posts messages
to the mailbox and notifies it to the clients which are subscribed to the mailbox. The clients
read the message from the mailbox on receiving the notification.
Figure: Concept of mailbox based indirect messaging for IPC.
The mailbox creation, subscription, message reading and writing are achieved through OS
kernel provided API calls. Mailbox and message queues are same in functionality. The only
difference is in the number of messages supported by them. Both of them are used for passing
data in the form of message(s) from a task to another task(s).
Mailbox is used for exchanging a single, message between two tasks or between an Interrupt
Service Routine (ISR) and a task. Mailbox associates a pointer pointing to the mailbox and a
wait list to hold the tasks waiting for a message to appear in the mailbox. The implementation
of mailbox is OS kernel dependent. The MicroC/OS-II implements mailbox as a mechanism
for inter-task communication.
3.2.2.3 Signaling:
On security front, RPC employs authentication mechanisms to protect the systems against
vulnerabilities. The client applications (processes)-should authenticate themselves with the
server for getting access. Authentication mechanisms like IDs, public-key cryptography, etc.
are used by the client for authentication. Without authentication, any client can access the
remote procedure. This may lead to potential security risks.
Sockets are used for RPC communication. The socket is a logical endpoint in a two-way
communication link between two applications running on a network. A port number is
associated with a socket so that the network layer of the communication channel can deliver
the data to the designated application. Sockets are of different types, namely, Internet sockets
(INET), UNIX sockets, etc. The INET socket works on internet communication protocol
TCP/IP, UDP (User Datagram Protocol), etc. are the communication protocols used by INET
sockets. INET sockets are classified into:
1. Stream sockets
2. Datagram sockets
Stream sockets are connection-oriented and they use TCP to establish liable connection. On
the other hand, Datagram sockets rely on UDP for establishing a connection. The UDP
connection is unreliable when compared to TCP. The client-server communication model
uses a socket at the client-side and a socket at the server-side. A port number is assigned to
both of these sockets. The client and server should be aware of the port number associated
with the socket. In order to start the communication, the client needs to send a connection
request to the server at the specified port number.
The client should be aware of the name of the server along with its port number. The server
always listens to the specified port number on the network. Upon receiving a connection
request from the client, based on the success of authentication, the server grants the
connection request and a communication channel is established between the client and server.
The client uses the hostname and port number of the server for sending requests and the
server uses the client's name and port number for sending responses.
3.3 TASK SYNCHRONISATION:
In a multitasking environment, multiple processes run concurrently (in pseudo parallelism)
and share the system resources. Apart from this, each process has its own boundary wall and
they communicate with each other with different IPC mechanisms including shared memory
and variables. Imagine a situation where two processes try to access display hardware
connected to the system or two processes try to access a shared memory area where one
process tries to write to a memory location when the other process is trying to read from this.
What could be the result in these scenarios? Obviously unexpected results. How these issues
can be addressed? The solution is, make each process aware of the access of a shared
resource either directly or indirectly. The act of making processes aware of the access of
shared resources by each process to avoid conflicts is known as `Task/Process
Synchronization'. Various synchronization issues may arise in a multitasking environment if
processes are not synchronized properly.
The following sections describe the major task communication/ synchronization issues
observed in multitasking and the commonly adopted synchronization techniques to overcome
these issues.
At the processor instruction level, the value of the variable counter is loaded to the
Accumulator register (EAX register). The memory variable counter is represented using a
pointer. The base pointer register (EBP register) is used for pointing to the memory variable
counter. After loading the contents of the variable-counter to the Accumulator, the
Accumulator content is incremented by one using the add instruction. Finally the content of
Accumulator is loaded to the memory location which represents the variable counter. Both
the processes Process A and Process B contain the program statement counter++; Translating
this into the machine instruction.
Imagine a situation where a process switching (context switching) happens from Process A to
Process B when Process A is executing the counter++; statement. Process A accomplishes the
counter++; statement through three different low-level instructions. Now imagine that the
process switching happened at the point where Process A executed the low-level instruction,
`mov eax,dword ptr [ebp-4]' and is about to execute the next instruction 'add eax,1'.
3.3.1.2 Deadlock:
A race condition produces incorrect results whereas a deadlock condition creates a situation
where none of the processes are able to make any progress in their execution, resulting in a
get of deadlocked processes. A situation very similar to our traffic jam issues in a junction.
In its simplest form 'deadlock' is the condition in which a process is waiting for a resource
held by another process which is waiting for a resource held by the first process.
Hold and Walt: The condition in which a process holds a shared resource by acquiring the
lock controlling the shared access and waiting for additional resources held by other
processes.
No Resource Preemption: The criteria that operating system cannot take back a resource
from a process which is currently holding it and the resource can only be released voluntarily
by the process holding it.
Circular Wait: A process is waiting for a resource which is currently held by another
process which in turn is waiting for a resource held by the first process. In general, there
exists a set of waiting process P0, P1, Pn with P0 is waiting for a resource held by P1 and P1
is waiting for a resource held P0, Pn is waiting for a resource held by P0 and P0 is waiting for
a resource held by Pn and so on... This forms a circular wait queue.
Deadlock Handling: A smart OS may foresee the deadlock condition and will act
proactively to avoid such a situation. Now if a deadlock occurred, how the OS responds to it?
The reaction to deadlock condition by OS is nonuniform. The OS may adopt any of the
following techniques to detect and prevent deadlock conditions.
(i).Ignore Deadlocks: Always assume that the system design is deadlock free. This is
acceptable for the reason the cost of removing a deadlock is large compared to the chance of
happening a deadlock. UNIX is an example for an OS following this principle. A life critical
system cannot pretend that it is deadlock free for any reason.
(ii). Detect and Recover: This approach suggests the detection of a deadlock situation and
recovery from it. This is similar to the deadlock condition that may arise at a traffic junction.
When the vehicles from different directions compete to cross the junction, deadlock (traffic
jam) condition is resulted. Once a deadlock (traffic jam) is happened at the junction, the only
solution is to back up the vehicles from one direction and allow the vehicles from opposite
direction to cross the junction. If the traffic is too high, lots of vehicles may have to be
backed up to resolve the traffic jam. This technique is also known as `back up cars' technique.
Operating systems keep a resource graph in their memory. The resource graph is updated on
each resource request and release.
Avoid Deadlocks: Deadlock is avoided by the careful resource allocation techniques by the
Operating System. It is similar to the traffic light mechanism at junctions to avoid the traffic
jams.
Prevent Deadlocks: Prevent the deadlock condition by negating one of the four conditions
favoring the deadlock situation.
• Ensure that a process does not hold any other resources when it requests a resource. This
can be achieved by implementing the following set of rules/guidelines in allocating resources
to processes.
1. A process must request all its required resource and the resources should be allocated
before the process begins its execution.
2. Grant resource allocation requests from processes only if the process does not hold a
resource currently.
• Ensure that resource preemption (resource releasing) is possible at operating system level.
This can be achieved by implementing the following set of rules/guidelines in resources
allocation and releasing.
1. Release all the resources currently held by a process if a request made by the
process for a new resource is not able to fulfil immediately.
2. Add the resources which are preempted (released) to a resource list describing the
resources which the process requires to complete its execution.
3. Reschedule the process for execution only when the process gets its old resources
and the new resource which is requested by the process.
Imposing these criterions may introduce negative impacts like low resource utilization and
starvation of processes.
Livelock: The Livelock condition is similar to the deadlock condition except that a process in
livelock condition changes its state with time. While in deadlock a process enters in wait state
for a resource and continues in that state forever without making any progress in the
execution, in a livelock condition a process always does something but is unable to make any
progress in the execution completion. The livelock condition is better explained with the real
world example, two people attempting to cross each other in a narrow corridor. Both the
persons move towards each side of the corridor to allow the opposite person to cross. Since
the corridor is narrow, none of them are able to cross each other. Here both of the persons
perform some action but still they are unable to achieve their target, cross each other. We will
make the livelock, the scenario more clear in a later section—The Dining Philosophers '
Problem, of this chapter.
Starvation: In the multitasking cont on is the condition in which a process does not get the
resources required to continue its execution for a long time. As time progresses the process
starves on resource. Starvation may arise due to various conditions like byproduct of
preventive measures of deadlock, scheduling policies favoring high priority tasks and tasks
with shortest execution time, etc.
Let's analyze the various scenarios that may occur in this situation.
Scenario 1: All the philosophers involve in brainstorming together and try to eat together.
Each philosopher picks up the left fork and is unable to proceed since two forks are required
for eating the spaghetti present in the plate. Philosopher 1 thinks that Philosopher 2 sitting to
the right of him/her will put the fork down and waits for it. Philosopher 2 thinks that
Philosopher 3' sitting to the right of him/her will
put the fork down and waits for it, and so on. This forms a circular chain of un-granted
requests. If the philosophers continue in this state waiting for the fork from the philosopher
sitting to the right of each, they will not make any progress in eating and this will result in
starvation of the philosophers and deadlock.
Scenario 2: All the philosophers start brainstorming together. One of the philosophers is
hungry and he/ she picks up the left fork. When the philosopher is about to pick up the right
fork, the philosopher sitting. to his right also become hungry and tries to grab the left fork
which is the right fork of his neighboring philosopher who is trying to lift it, resulting in a
'Race condition'..
Scenario 3: All the philosophers involve in brainstorming together and by to eat together.
Each philosopher picks up the left fork and is unable to proceed, since two forks are required
for eating the spaghetti present in the plate. Each of them anticipates that the adjacently
sitting philosopher will put his/her fork down and waits for a fixed duration grid after this
puts the fork down. Each of them again tries to lift the fork after a fixed duration of time.
Since all philosophers are trying to lift the fork at the same time, none of them will be able to
grab two forks. This condition leads to livelock and starvation of philosophers, where each
philosopher tries to do something, but they are unable to make any progress in achieving the
target.
Solution: We need to find out alternative solutions to avoid the.deadlock, livelock, racing
and starvation condition that may arise due to the concurrent access of forks by philosophers.
This situation can be handled in many ways by allocating the forks in different allocation
techniques including round Robin allocation, FIFO allocation: etc.
But the requirement is that the solution should be optimal, avoiding deadlock and starvation
of the philosophers and allowing maximum number of philosophers to eat at a time. One
solution that we could think of is:
• Imposing rules in accessing the forks by philosophers, like: The philosophers should put
down the fork he/she already have in hand (left fork) after waiting for a fixed duration for the
second fork (right fork) and should wait for a fixed time before making the next attempt.
This solution works fine to some extent, but, if all the philosophers try to lift the forks
at the same time, a livelock situation is resulted.
Another solution which gives maximum concurrency that can be thought of is each
philosopher ac-quires a semaphore (mutex) before picking up any fork. When a philosopher
feels hungry he/she checks whether the philosopher sitting to the left and right of him is
already using the fork, by checking the state of the associated semaphore. If the forks are in
use by the neighboring philosophers, the philosopher waits till the forks are available. A
philosopher when finished eating puts the forks down and informs the philosophers sitting to
his/her left and right, who are hungry (waiting for the forks), by signaling the semaphores
associated with the forks.
Figure: The 'Real Problems' in the 'Dining Philosophers problem' (a) Starvation
and Deadlock (b) Racing (c) Livelock and Starvation
We will discuss about semaphores and mutexes at a latter section of this chapter. In the
operating system context, the dining philosophers represent the processes and forks represent
the resources. The dining philosophers' problem is an analogy of processes competing for
shared resources and the different problems like racing, deadlock, starvation and livelock
arising from the competition.
1. 'Producer thread' is scheduled more frequently than the 'consumer thread': There are
chances for overwriting the data in the buffer by the 'producer thread'. This leads to
inaccurate data.
2. Consumer thread' is scheduled more frequently than the 'producer thread': There are
chances for reading the old data in the buffer again by the 'consumer thread'. This will also
lead to inaccurate data.
The output of the above program when executed on a Windows XP machine is shown in Fig.
10.29. The output shows that the consumer thread runs faster than the producer thread and
most often leads to buffer under-run and thereby inaccurate data.
Note
It should be noted that the scheduling of the threads 'producer_thread' ,and ‘consumer_thread’
is OS kernel scheduling policy dependent and you may not get the same output all the time
when you run this piece of code in Windows XP.
The producer-consumer problem can be rectified in various methods. One simple solution is
the `sleep and wake-up'. The 'sleep and wake-up' can be implemented in various process
synchronization techniques like semaphores, mutex, monitors, etc. We will discuss it in a
latter section of this chapter.
Figure: Output of win32 program illustrating producer-consumer problem
Imagine a situation where Process C is ready and is picked up for execution by the scheduler
and 'Process C' tries to access the shared variable 'X'. 'Process C' acquires the 'Semaphore S'
to indicate the other processes that it is accessing the shared variable 'X'. Immediately after
'Process C' acquires the 'Semaphore S', 'Process B' enters the 'Ready' state. Since 'Process B'
is of higher priority compared to 'Process C', 'Process C' is preempted, and 'Process B' starts
executing. Now imagine 'Process A' enters the 'Ready' state at this stage. Since 'Process A' is
of higher priority than 'Process B', 'Process B' is preempted, and 'Process A' is scheduled for
execution. 'Process A' involves accessing of shared variable 'X' which is currently being
accessed by 'Process C'. Since 'Process C' acquired the semaphore for signaling the access of
the shared variable 'X', 'Process A' will not be able to access it. Thus 'Process A' is put into
blocked state (This condition is called Pending on resource). Now 'Process B' gets the CPU
and it continues its execution until it relinquishes the CPU voluntarily or enters a wait state or
preempted by another high priority task. The highest priority process 'Process A' has to wait
till 'Process C' gets a chance to execute and release the semaphore. This produces unwanted
delay in the execution of the high priority task which is supposed to be executed immediately
when it was 'Ready'. Priority inversion may be sporadic in nature but can lead to potential
damages as a result f missing critical deadlines. Literally speaking, priority inversion 'inverts'
the priority of a high priority task with that of a low priority task. Proper workaround
mechanism should be adopted for handling the priority inversion problem. The commonly
adopted priority inversion workarounds are:
through a mutual exclusion mechanism like Binary Semaphore S. Imagine a situation where
Process C is ready and is picked up for execution by the scheduler and 'Process C' tries to
access the shared variable 'X'. 'Process C' acquires the 'Semaphore S' to indicate the other
processes that it is accessing the shared variable 'X'. Immediately after 'Process C' acquires
the 'Semaphore S', 'Process B' enters the 'Ready' state. Since 'Process B' is of higher priority
compared to 'Process C', 'Process C' is preempted, and 'Process B' starts executing. Now
imagine 'Process A' enters the 'Ready' state at this stage. Since 'Process A' is of higher priority
than 'Process B', 'Process B' is preempted, and 'Process A' is scheduled for execution. 'Process
A' involves accessing of shared variable 'X' which is currently being accessed by 'Process C'.
Since 'Process C' acquired the semaphore for signaling the access of the shared variable 'X',
'Process A' will not be able to access it. Thus 'Process A' is put into blocked state (This
condition is called Pending on resource). Now 'Process B' gets the CPU and it continues its
execution until it relinquishes the CPU voluntarily or enters a wait state or preempted by
another high priority task. The highest priority process 'Process A' has to wait till 'Process C'
gets a chance to execute and release the semaphore. This produces unwanted delay in the
execution of the high priority task which is supposed to be executed immediately when it was
'Ready'. Priority inversion may be sporadic in nature but can lead to potential damages as a
result f missing critical deadlines. Literally speaking, priority inversion 'inverts' the priority of
a high priority task with that of a low priority task. Proper workaround mechanism should be
adopted for handling the priority inversion problem. The commonly adopted priority
inversion workarounds are:
Priority Inheritance: A low-priority task that is currently accessing (by holding the lock) a
shared resource requested by a high-priority task temporarily 'inherits' the priority of that
high-priority task, from the moment the high-priority task raises the request. Boosting the
priority of the low priority task to that of the priority of the task which requested the shared
resource holding by the low priority task eliminates the preemption of the low priority task by
other tasks whose priority are below that of the task requested the shared resource 'and
thereby reduces the delay in waiting to get the resource requested by the high priority task.
The priority of the low priority task which is temporarily boosted to high is brought to the
original value when it releases the shared resource. Implementation of Priority inheritance
workaround in the priority inversion problem discussed for Process A, Process B and Process
C example will change the execution sequence as shown in Figure.
Figure: Handling Priority Inversion problem with priority Inheritance.
Priority inheritance is only a work around and it will not eliminate the delay in
waiting the high priority task to get the resource from the low priority task. The only thing is
that it helps the low priority task to continue its execution and release the shared resource as
soon as possible. The moment, at which the low priority task releases the shared resource, the
high priority task kicks the low priority task out and grabs the CPU - A true form of
selfishness. Priority inheritance handles priority inversion at the cost of run-time overhead at
scheduler. It imposes the overhead of checking the priorities of all tasks which tries to access
shared resources and adjust the priorities dynamically.
Priority Ceiling: In 'Priority Ceiling', a priority is associated with each shared resource. The
priority associated to each resource is the priority of the highest priority task which uses this
shared resource. This priority level is called 'ceiling priority'. Whenever a task accesses a
shared resource, the scheduler elevates the priority of the task to that of the ceiling priority of
the resource. If the task which accesses the shared resource is a low priority task, its priority
is temporarily boosted to the priority of the highest priority task to which the resource is also
shared. This eliminates the pre-emption of the task by other medium priority tasks leading to
priority inversion. The priority of the task is brought back to the original level once the task
completes the accessing of the shared resource. 'Priority Ceiling' brings the added advantage
of sharing resources without the need for synchronization techniques like locks. Since the
priority of the task accessing a shared resource is boosted to the highest priority of the task
among which the resource is shared, the concurrent access of shared resource is automatically
handled. Another advantage of 'Priority Ceiling' technique is that all the overheads are at
compile time instead of run-time. Implementation of 'priority ceiling' workaround in the
priority inversion problem discussed for Process A, Process B and Process C example will
change the execution sequence as shown in Figure.
Figure: Handling Priority Inversion problem with priority Ceiling.
The biggest drawback of 'Priority Ceiling' is that it may produce hidden priority inversion.
With 'Priority Ceiling' technique, the priority of a task is always elevated no matter another
task wants the shared resources. This unnecessary priority elevation always boosts the
priority of a low priority task to that of the highest priority tasks among which the resource is
shared and other tasks with priorities higher than that of the low priority task is not allowed to
preempt the low priority task when it is accessing a shared resource. This always gives the
low priority task the luxury of running at high priority when accessing shared resources.
3.3.2 Task Synchronization Techniques
So far we discussed about the various task/process synchronization issues encountered in
multitasking systems due to concurrent resource access. Now let's have a discussion on the
various techniques used for synchronization in concurrent access in multitasking.
Process/Task synchronization is essential for
The code memory area which holds the program instructions (piece of code) for accessing a
shared resource (like shared memory, shared variables, etc.) is known as 'critical section'. In
order to synchronize the access to shared resources, the access to the critical section should
be exclusive. The exclusive access to critical section of code is provided through mutual
exclusion mechanism. Let us have a look at how mutual exclusion is important in concurrent
access. Consider two processes Process A and Process B running on a multitasking system.
Process A is currently running and it enters its critical section. Before Process A completes its
operation in the critical section, the scheduler preempts Process A and schedules Process B
for execution (Process B is of higher priority compared to Process A). Process B also
contains the access to the critical section which is already in use by Process A. If Process B
continues its execution and enters the critical section which is already in use by Process A, a
racing condition will be resulted. A mutual exclusion policy enforces mutually exclusive
access of critical sections. Mutual exclusions can be enforced in different ways. Mutual
exclusion blocks a process. Based on the behaviour of the blocked process, mutual exclusion
methods can be classified into two categories. In the following section we will discuss them
in detail.
3.3.2.1 Mutual Exclusion through Busy Waiting/Spin Lock: 'Busy waiting' is the simplest
method for enforcing mutual exclusion. The following code snippet illustrates how 'Busy
waiting' enforces mutual exclusion.
The 'Busy waiting' technique uses a lock variable for implementing mutual exclusion. Each
process/ thread checks this lock variable before entering the critical section. The lock is set to
'1' by a process/ thread if the process/thread is already in its critical section; otherwise the
lock is set to '0'. The major challenge in implementing the lock variable based
synchronization is the non-availability of a single atomic instruction which combines the
reading, comparing and setting of the lock variable. Most often the three different operations
related to the locks, viz. the operation of Reading the lock variable, checking its present
value, and setting it are achieved with multiple low-level instructions. The low-level
implementation of these operations are dependent on the underlying processor instruction set
and the (cross) compiler in use. The low-level implementation of the 'Busy waiting' code
snippet, which we discussed earlier, under Windows XP operating system running on an Intel
Centrino Duo processor is given below. The code snippet is compiled with Microsoft Visual
Studio 6.0 compiler.
The assembly language instructions reveals that the two high level instructions
(while(bFlag==false); and bFlag=true;), corresponding to the operation of reading the lock
variable, checking its present value and setting it is implemented in the processor level using
six low level instructions. Imagine a situation where ‘Process 1' read the lock variable and
tested it and found that the lock is available and it is about to set the lock for acquiring the
critical section. But just before 'Process 1' sets the lock variable, 'Process 2' preempts 'Process
1' and starts executing. 'Process 2' contains a critical section code and it tests the lock variable
for its availability. Since 'Process 1' was unable to set the lock variable, its state is still '0' and
'Process 2' sets it and acquires the critical section. Now the scheduler preempts 'Process 2' and
schedules 'Process 1' before 'Process 2' leaves the critical section. Remember, `Process l' was
preempted at a point just before setting the lock variable (‘Process 1' has already tested the
lock variable just before it is preempted and found that the lock is available). Now 'Process 1'
sets the lock variable and enters the critical section. It violates the mutual exclusion policy
and may pro-duce unpredicted results.
Device Driver
Device driver is a piece of software that acts as a bridge between the operating system and
the hardware. In an operating system based product architecture, the user applications talk to
the Operating System kernel for all necessary information exchange including
communication with the hardware peripherals. The architecture of the OS kernel will not
allow direct device access from the user application. All the device related access should flow
through the OS kernel and the OS kernel mutes it to the concerned hardware peripheral. OS
provides interfaces in the form of Application Programming Interfaces (APIs) for accessing
the hardware. The device driver abstracts the hardware from user applications. The topology
of user applications and hardware interaction in an RTOS based system is depicted in Fig.
Device drivers are responsible for initiating and managing the communication with
the hardware peripherals. They are responsible for establishing the connectivity, initializing
the hardware (setting up various registers of the hardware device) and transferring data. An
embedded product may contain different types of hardware components like Wi-Fi module,
File systems, Storage device interface, etc. The initialization of these devices and the
protocols required for communicating with these devices may be different. All these
requirements are implemented in drivers and a single driver will not be able to satisfy all
these. Hence each hardware (more specifically each class of hardware) requires a unique
driver component.
Device Drivers
Hardware
Certain drivers come as part of the OS kernel and certain drivers need to be installed
on the fly. For example, the program storage memory for an embedded product, say NAND
Flash memory requires a NAND Flash driver to read and write data from/to it. This driver
should come as part of the OS kernel image. Certainly the OS will not contain the drivers for
all devices and peripherals under the Sun. It contains only the necessary drivers to
communicate with the onboard devices (Hardware devices which are part of the platform)
and for certain set of devices supporting standard protocols and device class (Say USB Mass
storage device or HID devices like Mouse/keyboard). If an external device, whose driver
software is not available with OS kernel image, is connected to the embedded device (Say a
medical device with custom USB class implementation is connected to the USB port of the
embedded product), the OS prompts the user to install its driver manually. Device drivers
which are part of the OS image are known as 'Built-in drivers' or 'On-board drivers'. These
drivers are loaded by the OS at the time of booting the device and are always kept in the
RAM. Drivers which need to be installed for accessing a device are known. as 'Installable
drivers'. These drivers are loaded by the OS on a need basis. Whenever the device is
connected, the OS loads the corresponding driver to memory. When the device is removed,
the driver is unloaded from memory. The Operating system maintains a record of the drivers
corresponding to each hardware.
It is very essential to know the hardware interfacing details like the memory address assigned
to the device, the Interrupt used, etc. of on-board peripherals for writing a driver for that
peripheral. It varies on the hardware design of the product. Some Real-Time operating
systems like 'Windows CE' support a layered architecture for the driver which separates out
the low level implementation from the OS specific interface. The low level implementation
part is generally known as Platform Dependent Device (PDD) layer. The OS specific
interface part is known as Model Device Driver (MDD) or Logical Device Driver (LDD). For
a standard driver, for a specific operating system, the MDD/LDD always remains the same
and only the PDD part needs to be modified according to the target hardware for a particular
class of devices.
Most of the time, the hardware developer provides the implementation for all on board
devices for a specific OS along with the platform. The drivers are normally shipped in the
form of Board Support Package. The Board Support Package contains low level driver
implementations for the onboard peripherals and OEM Adaptation Layer (OAL) for
accessing the various chip level functionalities and a bootloader for loading the operating
system. The OAL facilitates communication between the Operating System (OS) and the
target device and includes code to handle interrupts, timers, power management, bus
abstraction; generic I/O control codes (IOCTLs), etc. The driver files are usually in the form
of a dll file. Drivers can run on either user space or kernel space. Drivers which run in user
space are known as user mode drivers and the drivers which run in kernel space are known as
kernel mode drivers. User mode drivers are safer than kernel mode drivers. If an error or
exception occurs in a user mode driver, it won't affect the services of the kernel. On the other
hand, if an exception occurs in the kernel mode driver, it may lead to the kernel crash. The
way how a device driver is written and how the interrupts are handled in it are operating
system and target hardware specific. However regardless of the OS types, a device driver
implements the following: