OS Lab Manual Swapna
OS Lab Manual Swapna
OS Lab Manual Swapna
Description:
First in, first out (FIFO), also known as first come, first served (FCFS), is the simplest scheduling
algorithm. FIFO simply queues processes in the order that they arrive in the ready queue. In this,
the process that comes first will be executed first and next process starts only after the previous
gets fully executed. We are given with the n number of processes i.e. P1, P2, P3,.......,Pn and their
corresponding burst times. The task is to find the average waiting time and average turnaround
time using FCFS CPU Scheduling algorithm. Here we are considering that arrival time for all
processes is 0.
● Turnaround Time is the time interval between the submission of a process and its
completion.
Turnaround Time = completion of a process – submission of a process
● Waiting Time is the difference between turnaround time and burst time
Waiting Time = turnaround time – burst time
Algorithm:
1. Start
2. Declare the following array to maximum size 15.
a. WT[ MAX ] to store waiting time of each process.
b. BT[ MAX ] to store Burst time of each process.
c. TT [ MAX ] to store turnaround time of each process.
3. Read the number of processes.
4. Read the Burst time for all the processes.
5. Calculate the waiting time of each process
a. WT [ i ] = WT [ i-1] + BT [i-1]
6. Calculate the Turnaround time of each process
a. TT [ i ] = WT [ i ] + BT [ i ]
7. Calculate average waiting time and average turnaround time.
8. Display all the values
9. Stop.
Program:
#include<stdio.h>
int main()
{
int processes[] = { 1, 2, 3};
int n =3;
int bt[] = {10, 5, 8};
1
wt[0] = 0;
return 0;
}
OUTPUT
RUN 1:
Processes Burst time Waiting time Turn around time
1 10 0
10
2 5 10
15
3 8 15
23
Average waiting time = 8
Average turn around time = 16
VIVA Questions:
1. What are the features of FCFS scheduling algorithm?
3
9. Difference Between Process and Thread
● The thread is also called as a lightweight process. It requires fewer resources as compare
to process to run.
● One single process can consist of multiple threads.
● Every process requires its own address space to run on a processor. Whereas threads
within a single process share the single address space. So threads are easier to create than
process.
● Threads read and write at the same place. It is good and easy for the communication
between multiple threads in the single process.
● The data communications between multiple processes are difficult and it is carried out by
IPC (inter-process communication).
● Context switching overhead is less in the thread as compare to process. So threads
decrease overall execution time and the cost of the communication.
● Threads are useful to execute lightweight task whereas processes are responsible for
running heavyweight tasks.
Description: Shortest Job First (SJF) is an algorithm in which the process having the smallest
execution time is chosen for the next execution. This scheduling method can be preemptive or non-
4
preemptive. It significantly reduces the average waiting time for other processes awaiting execution. The
full form of SJF is Shortest Job First.
Algorithm:
10. Start
11. Declare Process structure with following members.
a. PID : to Store Process ID
b. WT : to store waiting time of process.
c. BT : to store Burst time of process.
d. TT : to store turnaround time of process.
12. Read the number of processes.
13. Read the Burst time and Process Id for all the processes.
14. Sort the Array of process structure based on Burt time.
15. Calculate the waiting time of each process
a. After sorting first process in the list will have waiting time 0
i. Proc[0] .wt = 0
b. Find Waiting time for all other process
i. Proc[ i ] . wt = Proc[ i-1] .wt + Pro [ i-1] .bt
16. Calculate the Turnaround time of each process
i. Proc[ i ] . tt = Proc[ i] .wt + Pro [ i] .bt
17. Calculate average waiting time
a. Total_waitingTime / Number of Process
18. Calculate average turnaround time.
a. Total_waitingTime / Number of Process
19. Display all the values
20. Stop.
Program:
#include <stdio.h>
#define MAX 15
struct process
{
int pid;
int bt;
int wt;
int tt;
};
int main()
{
struct process proc[MAX], temp;
int n , i , j;
int total_wt=0 , total_tt=0;
float avg_wt, avg_tt;
5
printf(" Enter Process Id and Brust time of the process \n");
for( i=0; i< n; i++)
{
scanf("%d%d", &proc[i].pid , &proc[i].bt );
}
//Bubble sort, to sort the process
for (i = 0; i < n-1; i++)
for (j = 0; j < n-i-1; j++)
if (proc[j].bt > proc[j+1].bt)
{
temp=proc[j];
proc[j]= proc[j+1];
proc[j+1]=temp;
}
printf(" Process ID Burst time Waiting Time Turn Around Time \n");
return 0;
OUTPUT
RUN1 :
enter number of process (max of 15) 4
6
2 45
3 56
1 78
47
Viva Questions:
3. Shortest job first is also known as shortest job next (SJN) and shortest process next
(SPN).
7
2. The need to know the execution time for each process beforehand. Often, this is almost
impossible in many environments.
7. What is starvation ?
Starvation or indefinite blocking is phenomenon associated with the Priority scheduling
algorithms, in which a process ready to run for CPU can wait indefinitely because of low
priority. In heavily loaded computer system, a steady stream of higher-priority processes
can prevent a low-priority process from ever getting the CPU.
Description: The round-robin (RR) scheduling is designed especially for time-sharing systems.
It is similar to FCFS scheduling, but preemption is added to switch between process. A small unit
of time, called a time quantum, or time slice, is defined. A time quantum is generally from 10 to
100ms. The ready queue is treated as a circular queue. RR goes around the ready queue, allocating
the CPU to each process for a time interval of up to 1 time quantum. RR is preemptive. The
performance of RR depends heavily on the size of the time quantum selected. At one extreme, if
the quantum is very large (infinite), RR is the same as the FCFS policy. If the time quantum is
8
very small, it would appear each process among n processes has its own processor running at 1/n
the speed of the real processor.
ALGORITHM:
21. Start
22. Declare Process structure with following members.
a. PID : to Store Process ID
b. WT : to store waiting time of process.
c. BT : to store Burst time of process.
d. TT : to store turnaround time of process.
e. rem_bt : to store remaining burst time
23. Read the number of processes.
24. Read the Burst time and Process Id for all the processes.
25. Read the time quantum value.
26. The process are selected from the queue in first come first serve order and following
steps are repeated until remaining burst time for all process is zero.
a. If Burst time > time quantum
i. The process is preempted after time quantum and added to the end of
the queue. Remaing burst time is recorded. CPU execution time is also
recorded
1. t = t + quantum
2. Proc[i] . rem_bt = Proc[i].rem_bt - quantum
b. If Burst Time <= time quantum
i. Process remaining burst time is set to zero and waiting time is
calculated
1. t = t + Proc[i] . rem_bt
2. Proc[i] . rem_bt = 0
3. Proc[ i ] .wt = t - Proc [ i ] .bt
27. Calculate the Turnaround time of each process
i. Proc[ i ] . tt = Proc[ i] .wt + Pro [ i] .bt
28. Calculate average waiting time
a. Total_waitingTime / Number of Process
29. Calculate average turnaround time.
a. Total_waitingTime / Number of Process
30. Display all the values
31. Stop.
Program:
9
#include <stdio.h>
#define MAX 15
struct process
{
int pid;
int bt;
int wt;
int tt;
int rem_bt;
};
int main()
{
struct process proc[MAX] ;
int n , i , j ,quantum , t;
int total_wt=0 , total_tt=0;
float avg_wt, avg_tt;
int done=0;
t = 0;
// Keep traversing processes in round robin manner until all of them are not done.
printf("Order of Process Excecution is \n");
do
{
done = 1;
for (int i = 0 ; i < n; i++) // Traverse all processes one by one repeatedly
{
// If burst time of a process is greater than 0 then only need to process further
if (proc[i].rem_bt > 0)
{
done = 0; // There is a pending process
printf(" %d : ",proc[i].pid);
if (proc[i].rem_bt > quantum)
{
// Increase the value of t which shows how much time a process has been processed
t += quantum;
10
t = t + proc[i].rem_bt;
// As the process gets fully executed make its remaining burst time = 0
proc[i].rem_bt = 0;
}
}
}
}while(done != 1);
printf("\n");
for (i = 0; i < n; i++)
{
total_wt = total_wt + proc[i].wt; // total waiting time calculation
proc[i].tt = proc[i].wt + proc[i].bt ; // turn around time calculation
total_tt = total_tt + proc[i].tt; // total turn around time calculation
}
enter number of process (max of 15)
4
Enter Process Id and Brust time of the process
1 4
2 3
3 5
4 6
Enter time Quantum
2
Order of Process Excecution is
1: 2: 3: 4: 1: 2: 3: 4: 3: 4:
Processes are executed in following order
PID | BT | WT | TaT
1 : 4 : 6 : 10
2 : 3 : 8 : 11
3 : 5 : 11 : 16
4 : 6 : 12 : 18
Average waiting time 9.250000
Average Turnaround time 13.750000
VIVA QUESTIONS
1. What are the characteristic of RR?
a. Round Robin is a CPU scheduling algorithm where each process is assigned a fixed time
slot in a cyclic way.
b. It is simple, easy to implement, and starvation-free as all processes get fair share of CPU.
c. One of the most commonly used technique in CPU scheduling as a core.
11
d. It is preemptive as processes are assigned CPU only for a fixed slice of time at most.
e. The disadvantage of it is more overhead of context switching.
4. What does Round Robin Scheduling (RRS) mean?Round robin scheduling (RRS) is a job-
scheduling algorithm that is considered to be very fair, as it uses time slices that are assigned
to each process in the queue or line. Each process is then allowed to use the CPU for a given
amount of time, and if it does not finish within the allotted time, it is preempted and then
moved at the back of the line so that the next process in line is able to use the CPU for the
same amount of time.
Description: In priority scheduling, a priority is associated with each process, and the CPU is
allocated to the process with the highest priority. Equal-priority processes are scheduled in FCFS
order. SJF is simply a priority algorithm where the priority (p) is the inverse of the (predicted)
next CPU burst. The large the CPU burst, the lower the priority. Priorities are generally some
fixed range of numbers, such as 0 to 7. In this text, we assume that low numbers represent high
priority.
Algorithm:
a. Start
b. Declare Process structure with following members.
i. PID : to Store Process ID
ii. Priority : to store priority of the process
iii. WT : to store waiting time of the process.
iv. BT : to store Burst time of the process.
v. TT : to store turnaround time of the process.
c. Read the number of processes.
d. Read the Process Id, Burst time and Priority for all the processes.
e. Sort the Array of process structure based on Priority.
f. Calculate the waiting time of each process
a. After sorting first process in the list will have waiting time 0
i. Proc[0] .wt = 0
b. Find Waiting time for all other process
12
i. Proc[ i ] . wt = Proc[ i-1] .wt + Pro [ i-1] .bt
32. Calculate the Turnaround time of each process
i. Proc[ i ] . tt = Proc[ i] .wt + Pro [ i] .bt
33. Calculate average waiting time
a. Total_waitingTime / Number of Process
34. Calculate average turnaround time.
a. Total_waitingTime / Number of Process
35. Display all the values
36. Stop.
#include <stdio.h>
#define MAX 15
struct process
{
int pid;
int priority;
int bt;
int wt;
int tt;
};
int main()
{
struct process proc[MAX], temp;
int n , i , j;
int total_wt=0 , total_tt=0;
float avg_wt, avg_tt;
printf("enter number of process (max of 15) \n");
scanf("%d",&n);
printf(" Enter Process Id and Brust time and priority of the process \n");
for( i=0; i< n; i++)
{
scanf("%d%d%d", &proc[i].pid , &proc[i].bt , &proc[i].priority);
}
//Bubble sort, to sort the process
for (i = 0; i < n-1; i++)
for (j = 0; j < n-i-1; j++)
if (proc[j].priority > proc[j+1].priority)
{
temp=proc[j];
proc[j]= proc[j+1];
proc[j+1]=temp;
}
13
}
else
{
proc[i].wt = proc[i-1].wt + proc[i-1].tt; // waiting time calculation
total_wt = total_wt + proc[i].wt; // total waiting time calculation
printf(" | Process ID | Priority | Burst time | Waiting Time | Turn Around Time | \n");
OUTPUT:
VIVA Questions
4. What is aging?
A remedy to starvation is aging, which is a technique used to gradually increase the
priority of those processes that wait for long periods in the system.
Description:
System Calls:
The way that programs talk to the operating system is via ``system calls.''
A system call looks like a procedure call , but it's different -- it is a request to the operating
system to perform some activity.
● System calls are expensive. While a procedure call can usually be performed in a few
machine instructions, a system call requires the computer to save its state, let the operating
system take control of the CPU, have the operating system perform some function, have
the operating system save its state, and then have the operating system give control of the
CPU back to you.
● System calls are system dependent. Knowing this, it would not be a good idea to directly
use system calls when portability cannot be neglected. System calls are also quite
complex. Most often, it involves the duo of TRAP and RET (or some variations of those
two). To implement system call, one would need specialized knowledge of I/O registers,
the sequence of operations needed to use them; and most important of all, implement
enough protection because I/O resources are generally shared among multiple users and/or
processes.
15
1. int open(char *path, int flags [ , int mode ] );
2. int close(int fd);
3. int read(int fd, char *buf, int size);
4. int write(int fd, char *buf, int size);
5. off_t lseek(int fd, off_t offset, int whence);
AIM: To write a C program to copy content from one file to another file.
Program:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
int main(void)
{
int n,fd1,fd2;
char buff[BUFF_SIZE];
return 0;
}
INPUT &OUTPUT:
lseek (C System Call): lseek is a system call that is used to change the location of the read/write
pointer of a file descriptor. The location can be set either in absolute or relative terms.
off_t lseek(int fildes, off_t offset, int whence);
The lseek() function shall set the file offset for the open file description associated with the file
descriptor fildes, as follows:
● If whence is SEEK_END, the file offset shall be set to the size of the file plus offset.
The symbolic constants SEEK_SET, SEEK_CUR, and SEEK_END are defined in <unistd.h>.
Upon successful completion, the resulting offset, as measured in bytes from the beginning of the
file, shall be returned. Otherwise, (off_t)-1 shall be returned, errno shall be set to indicate the
error, and the file offset shall remain unchanged.
Program:
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h
#include<sys/stat.h>
17
#int main()
{
int n,f;
char buff[10];
f=open("seeking",O_RDWR);
read(f,buff,10);
write(1,buff,10);
read(f,buff,10);
write(1,buff,10);
}
Pre-requisite: Create a file “seeking” and write “1234567890abcdefghijxxxxxxxxxx” into it.
Output:
The output will be the first 10 characters “1234567890” followed by “fghijxxxxx”. The inbetween
5 characters are skipped because we used lseek to reposition the pointer 5 characters ahead from
the current (SEEK_CUR) position.
Description:
Stat system call is a system call in Linux to check the status of a file such as to check
when the file was accessed. The stat() system call actually returns file attributes. The file
attributes of an inode are basically returned by Stat() function. An inode contains the metadata of
the file. An inode contains: the type of the file, the size of the file, when the file was accessed
(modified, deleted) that is time stamps, and the path of the file, the user ID and the group ID, links
of the file, and physical address of file content.
To use the stat system call in C programming language we should include the following header
file:
#include <sys/stat.h>
int stat(const char *path, struct stat *buf)
● The return type of the function in int, if the function is executed successfully, 0 is returned if
there are any errors, -1 will be returned.
● const char *path specifies the name of the file.
● stat structure The data or information about the file is stored which uses a pointer named buf,
which is passed in as a paramteter and filled in during the execution of the call and readable
by the user after the call.
Program :
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
18
#include <dirent.h>
#include<unistd.h>
struct stat statbuf;
char dirpath[256];
{
stat(dp->d_name, &statbuf);
printf("the file name is %s \n", dp->d_name);
printf("dir = %d\n", S_ISDIR(statbuf.st_mode));
printf("file size is %ld in bytes \n", statbuf.st_size);
printf("last modified time is %ld in seconds \n", statbuf.st_mtime);
printf("last access time is %ld in seconds \n", statbuf.st_atime);
printf("The device containing the file is %ld\n", statbuf.st_dev);
printf("File serial number is %ld\n\n", statbuf.st_ino);
}
}
OUTPUT:
19
Program 2.4 : C Program for opendir(), readdir() and closedir
system calls
Description:
The opendir() function shall open a directory stream corresponding to the directory named by the
dirname argument. Directory entries represent files or subdirectory.
#include <dirent.h>
DIR *opendir(const char *dirname);
● Upon successful completion, opendir() shall return a pointer to an object of type DIR.
Otherwise, a null pointer shall be returned and errno set to indicate the error.
The readdir() function shall return a pointer to a structure representing the directory entry at the
current position in the directory stream specified by the argument dirp, and position the directory
stream at the next entry. It shall return a null pointer upon reaching the end of the directory
stream.
* readdir(DIR *dirp);
* Upon successful completion, readdir() shall return a pointer to an object of type struct
dirent. When an error is encountered, a null pointer shall be returned and errno shall be set to
indicate the error.
The closedir() function shall close the directory stream referred to by the argument dirp. Upon
successful completion, closedir() shall return 0; otherwise, -1 shall be returned and errno set to
indicate the error.
PROGRAM:
#include<stdio.h>
#include<dirent.h>
struct dirent *dptr;
20
int main(int argc, char *argv[])
{
char buff[100];
DIR *dirp;
printf("\n\n ENTER DIRECTORY NAME");
scanf("%s", buff);
if( ( dirp=opendir( buff ) ) == NULL)
{
printf("The given directory does not exist");
exit(1);
}
while(dptr = readdir ( dirp ) )
{
printf("%s\n",dptr->d_name);
} closedir(dirp); }
Program 2.5: C Program for fork() and getpid() system call
Description: fork() creates a new process by duplicating the calling process.The new process
is referred to as the child process. The calling process is referred to as the parent process. The
child has its own unique process ID.The child's parent process ID is the same as the parent's
process ID.
#include <unistd.h>
pid_t fork(void);
● On success, the PID of the child process is returned in the parent, and 0 is returned in the
child. On failure, -1 is returned in the parent, no child process is created, and errno is set to
indicate the error.
Getpid() is the function used to get the process ID of the process that calls that function.
ALGORITHM:
PROGRAM:
#include<stdio.h>
#include<unistd.h>
main()
{
21
int pid,pid1,pid2;
pid=fork();
if(pid== -1)
{
printf("ERROR IN PROCESS CREATION \n");
exit(1);
}
if(pid!=0)
{
pid1=getpid();
printf("\n the parent process ID is %d\n", pid1);
}
else
{
pid2=getpid();
printf("\n the child process ID is %d\n", pid2);
}}
2.6 C program to execute another C program using exec system
call.
Description:
The exec family of functions replaces the current running process with a new process. It can be
used to run a C program by using another C program. It comes under the header file
unistd.h. There are many members in the exec family which are shown below with examples.
● execvp : Using this command, the created child process does not have to run the same
program as the parent process does. The exec type system calls allow a process to run any
program files, which include a binary executable or a shell script . Syntax:
file: points to the file name associated with the file being executed.
argv: is a null terminated array of character pointers.
We will have two .C files , EXEC.c and execDemo.c and we will replace the execDemo.c with
EXEC.c by calling execvp() function in execDemo.c .
//EXEC.c
#include<stdio.h>
#include<unistd.h>
int main()
{
int i;
22
printf("I am EXEC.c called by execvp()
");
printf("\n");
return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
//A null terminated array of character
//pointers
char *args[]={"./EXEC",NULL};
execvp(args[0],args);
return 0;
}
After running the executable file of execDemo.cby using command ./excDemo, we get the
following output:
23
Program 3. Write a C Program to implement Banker’s
Algorithm for Deadlock Avoidance and Detection
Description:
The Banker algorithm, sometimes referred to as the detection algorithm, is a resource allocation
and deadlock avoidance algorithm developed by Edsger Dijkstra.
The algorithm tests for safety by simulating the allocation for predetermined maximum possible amounts
of all resources, then makes an “s-state” check to test for possible activities, before deciding whether
allocation should be allowed to continue .
Algorithm:
1. Available[ m ] indicates how many resources are currently available of each type.
2. Max[ n ][ m ] indicates the maximum demand of each process of each resource.
3. Allocation[ n ][ m ] indicates the number of each resource category allocated to each
process.
4. Need[ n ][ m ] indicates the remaining resources needed of each type for each process.
( Note that Need[ i ][ j ] = Max[ i ][ j ] - Allocation[ i ][ j ] for all i, j. )
In order to apply the Banker's algorithm, we first need an algorithm for determining whether or
not a particular state is safe.This algorithm determines if the current state of a system is safe,
according to the following steps:
24
o Work is a working copy of the available resources, which will be modified during
the analysis.
o Finish is a vector of booleans indicating whether a particular process can finish.
( or has finished so far in the analysis. )
o Initialize Work to Available, and Finish to false for all elements.
2. Find an i such that both
o Finish[ i ] == false, and
o Need[ i ] < Work. This process has not finished, but could with the given available
working set. If no such i exists, go to step 4.
3. Set Work = Work + Allocation[ i ], and
o set Finish[ i ] to true. This corresponds to process i finishing up and releasing its
resources back into the work pool. Then loop back to step 2.
4. If finish[ i ] == true for all i, then the state is a safe state, because a safe sequence has been
found.
Now that we have a tool for determining if a particular state is safe or not, we are now ready to look at the
Banker's algorithm itself. This algorithm determines if a new request is safe, and grants it only if it is safe
to do so.
● When a request is made ( that does not exceed currently available resources ), pretend it has been
granted, and then see if the resulting state is a safe one. If so, grant the request, and if not, deny the
request, as follows:
1. Let Request[ n ][ m ] indicate the number of resources of each type currently requested by
processes.
▪ If Request[ i ] > Need[ i ] for any process i, raise an error condition.
2. If Request[ i ] > Available for any process i, then that process must wait for resources to
become available. Otherwise the process can continue to step 3.
3. Check to see if the request can be granted safely, by pretending it has been granted and
then seeing if the resulting state is safe. If so, grant the request, and if not, then the process
must wait until its request can be granted safely. The procedure for granting a request ( or
pretending to for testing purposes ) is:
▪ Available = Available – Request ,
▪ Allocation = Allocation + Request
▪ Need = Need - Request
Program:
#include <stdio.h>
int main()
{
// P0, P1, P2, P3, P4 are the Process names here
int n, m, i, j, k;
n = 5; // Number of processes
m = 3; // Number of resources
int alloc[5][3] = { { 0, 1, 0 }, // P0 // Allocation Matrix
{ 2, 0, 0 }, // P1
{ 3, 0, 2 }, // P2
{ 2, 1, 1 }, // P3
{ 0, 0, 2 } }; // P4
25
int max[5][3] = { { 7, 5, 3 }, // P0 // MAX Matrix
{ 3, 2, 2 }, // P1
{ 9, 0, 2 }, // P2
{ 2, 2, 2 }, // P3
{ 4, 3, 3 } }; // P4
printf("Enter the new request of the process%d in the order resources[A, B,C]\n", ProNo);
scanf("%d",&newRequest[ProNo][0]);
scanf("%d",&newRequest[ProNo][1]);
scanf("%d",&newRequest[ProNo][2]);
avail[0]= avail[0]-newRequest[ProNo][0];
avail[1]= avail[1]-newRequest[ProNo][1];
avail[2]= avail[2]-newRequest[ProNo][2];
26
for (k = 0; k < 5; k++)//numer of cycles to complete exceution
{
for (i = 0; i < n; i++)
{
if (f[i] == 0)
{
flag = 0;
for (j = 0; j < m; j++)
{
if (need[i][j] > avail[j])
{
flag = 1;
break;
}
}
if (flag == 0)
{
ans[ind++] = i; //creating the Safe State
for (y = 0; y < m; y++)
avail[y] += alloc[i][y]; //update the Av
f[i] = 1;
}
}
}
}
if(flag == 1)
{
printf("Request cannot be granted now , as it may lead to Deadlock\n as their is no safe
sequence\n");
printf("your request is not granted to Avoid DeadLock");
printf(" Process has to wait\n");
return 0;
}
printf("Request can be granted, as a safe sequence present\n");
printf("Their will be no deadlock\n");
printf("Following is the SAFE Sequence\n");
for (i = 0; i < n - 1; i++)
printf(" P%d ->", ans[i]);
printf(" P%d", ans[n - 1]);
return (0);
}OUTPUT:
RUN 1:
Banker's Algorithm
For the new request: enter process no
1
Enter the new request of the process1 in the order resources[A, B,C]
1 0 2
27
Request can be granted, as a safe sequence present
RUN 2:
Banker's Algorithm
For the new request: enter process no
0
Enter the new request of the process0 in the order resources[A, B,C]
020
Request can be granted, as a safe sequence present
RUN 3:
Banker's Algorithm
For the new request: enter process no
4
Enter the new request of the process4 in the order resources[A, B,C]
330
Request cannot be granted now , as it may lead to Deadlock as their is no safe sequence
What are the different types of Deadlock Types and Handling Strategies?
There are multiple deadlocks handling strategies to avoid deadlock.
We can prevent the deadlock not to occur and it is the optimal solution. But preventing the system
not to occur deadlock is not easy as we require prior information about processes as well as
resources.
On other hands, if it occurs we have a recovery mechanism.
There are four strategies to handle deadlock.
● Prevention of Deadlock
● Avoidance of Deadlock
● Deadlock Detection and Recover
● Deadlock Ignorance
28
What is Deadlock in the Operating System?
Deadlock is the condition that occurs when two processes wait for each other to complete and halt
without proceeding further. Here two processes wait for the resources acquired by each other.
29
Program 4: Producer Consumer Problem using Semaphore –
using PThread
Description:
In the producer-consumer problem, there is one Producer that is producing something and
there is one Consumer that is consuming the products produced by the Producer. The
producers and consumers share the same memory buffer that is of fixed-size.
The job of the Producer is to generate the data, put it into the buffer, and again start
generating data. While the job of the Consumer is to consume the data from the buffer.
The following are the problems that might occur in the Producer-Consumer:
● The producer should produce data only when the buffer is not full. If the buffer is
full, then the producer shouldn't be allowed to put any data into the buffer.
● The consumer should consume data only when the buffer is not empty. If the
buffer is empty, then the consumer shouldn't be allowed to take any data from the
buffer.
● The producer and consumer should not access the buffer at the same time.
The above three problems can be solved with the help of semaphores. In the producer-
consumer problem, we use three semaphore variables:
30
2. Semaphore E: This semaphore variable is used to define the empty space in the
buffer. Initially, it is set to the whole space of the buffer i.e. "n" because the buffer
is initially empty.
3. Semaphore F: This semaphore variable is used to define the space that is filled by
the producer. Initially, it is set to "0" because there is no space filled by the
producer initially.
By using the above three semaphore variables and by using the wait() and signal()
function, we can solve our problem(the wait() function decreases the semaphore variable
by 1 and the signal() function increases the semaphore variable by 1).
The POSIX system in Linux presents its own built-in semaphore library. To use it, we have to :
1. Include semaphore.h
2. Compile the code by linking with -lpthread –lrt
Syntax:
● thread: pointer to an unsigned integer value that returns the thread id of the thread created.
● attr: pointer to a structure that is used to define thread attributes like detached state,
scheduling policy, stack address, etc. Set to NULL for default thread attributes.
● start_routine: pointer to a subroutine that is executed by the thread. The return type and
parameter type of the subroutine must be of type void *. The function has a single attribute
but if multiple values need to be passed to the function, a struct must be used.
● arg: pointer to void that contains the arguments to the function defined in the earlier
argument
● th: thread id of the thread for which the current thread waits.
● thread_return: pointer to the location where the exit status of the thread mentioned in th is
stored.
Algorithm:
wait(Empty)
wait(Mutex)
produce()
append()
signal(Mutex)
signal(Full)
}
}
void consumer()
{
for(n<=10)
{
wait(Full)
wait(Mutex)
take()
signal(Mutex)
signal(Empty)
use()
}
32
}
Program :
#include<unistd.h>
#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>
}
}
void *consume(void *arg)
{
int item,i;
for(i=0;i<10;i++)
{
sem_wait(&full); //Wait for item and decrement the full
sem_wait(&mutex); // Wait if producer s producing a item
item=buf[(++f)%5];
printf("consumed item is %d\n",item);
sleep(1);
33
pthread_join(tid2,NULL);
return 0;
}
VIVA Questions
What is a Thread?
A thread is a path of execution within a process. A process can contain multiple threads.
Why Multithreading?
A thread is also known as 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.
Process vs Thread?
The primary difference is that threads within the same process run in a shared memory space,
while processes run in separate memory spaces.
Threads are not independent of one another like processes are, and as a result threads share with
other threads their code section, data section, and OS resources (like open files and signals). But,
like process, a thread has its own program counter (PC), register set, and stack space.
What is a Monitor?
A Monitor is characterized by a set of programmer-defined operators. The representation of a
Monitor
type consists of declaration of variables whose value define the state of an instance of the type, as
well as
the bodies of procedures or functions that implement operations on the type.
34
This system call would create a pipe for one-way communication i.e., it creates two descriptors,
first one is connected to read from the pipe and other one is connected to write into the pipe.
Descriptor pipedes[0] is for reading and pipedes[1] is for writing. Whatever is written into
pipedes[1] can be read from pipedes[0].
This call would return zero on success and -1 in case of failure. To know the cause of failure,
check with errno variable or perror() function.
● The file descriptor id is to identify the respective file, which is returned after calling
open() or pipe() system call. The file needs to be opened before reading from the file.
It automatically opens in case of calling pipe() system call.
● This call would return the number of bytes read (or zero in case of encountering the
end of the file) on success and -1 in case of failure. The return bytes can be smaller
than the number of bytes requested, just in case no data is available or file is closed.
4. ssize_t write(int fd, void *buf, size_t count)
● The above system call is to write to the specified file with arguments of the file
descriptor fd, a proper buffer with allocated memory (either static or dynamic) and the
size of buffer.
● The file descriptor id is to identify the respective file, which is returned after calling
open() or pipe() system call.
● The file needs to be opened before writing to the file. It automatically opens in case of
calling pipe() system call.
● Program to write and read two messages through the pipe using the parent and the
child processes.
35
Algorithm
Step 1 − Create a pipe.
Step 2 − Create a child process.
Step 3 − Parent process writes to the pipe.
Step 4 − Child process retrieves the message from the pipe and writes it to the standard
output.
Step 5 − Repeat step 3 and step 4 once again.
#include<stdio.h>
#include<unistd.h>
int main()
{
int pipefds[2];
int returnstatus;
int pid;
char writemessages[2][20]={"Hi", "Hello"};
char readmessage[20];
returnstatus = pipe(pipefds);
if (returnstatus == -1)
{
printf("Unable to create pipe\n");
return 1;
}
pid = fork();
// Child process
if (pid == 0)
{
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Child Process - Reading from pipe – Message 1 is %s\n", readmessage);
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Child Process - Reading from pipe – Message 2 is %s\n", readmessage);
} else
{ //Parent process
printf("Parent Process - Writing to pipe - Message 1 is %s\n", writemessages[0]);
write(pipefds[1], writemessages[0], sizeof(writemessages[0]));
36
printf("Parent Process - Writing to pipe - Message 2 is %s\n", writemessages[1]);
write(pipefds[1], writemessages[1], sizeof(writemessages[1]));
}
return 0;
}
#include<stdio.h>
#include<unistd.h>
int main()
{
37
int pipefds1[2], pipefds2[2];
int returnstatus1, returnstatus2;
int pid;
char pipe1writemessage[20] = "Hi";
char pipe2writemessage[20] = "Hello";
char readmessage[20];
returnstatus1 = pipe(pipefds1);
if (returnstatus1 == -1)
{
printf("Unable to create pipe 1 \n");
return 1;
}
returnstatus2 = pipe(pipefds2);
if (returnstatus2 == -1)
{
printf("Unable to create pipe 2 \n");
return 1;
}
pid = fork();
Inter Process Communication. IPC is used to pass information between two or more
processes.Schemes are pipes, shared memory & semaphore.Below are the different IPC
methods:
1. Semaphores
2. FIFO's (Also called Named Pipes)
3. Message Queues
4. Shared Memory
39
Program 5.2 : C program to implement interprocess
communication using FIFO(Named Pipes)
FIFOs are pipes with a name and are also commonly referred to as named pipes. A FIFO is
different because it has a name like a file.
We can create a FIFO from the shell using the mknod command. We can remove a FIFO just like
a file using the rm command.
We can create a FIFO from a program using the mkfifo system call.
#include <sys/stat.h>
2. A FIFO can be opened using the open system call. After open, we can use the read and
write system calls for reading from and writing to the FIFO, using the file descriptor
returned by open.
Step 1 − Create two processes, one is fifoserver and another one is fifoclient.
● Creates a named pipe (using system call mknod()) with name “MYFIFO”, if not created.
● Opens the named pipe for read only purposes.
● Here, created FIFO with permissions of read and write for Owner. Read for Group and no
permissions for Others.
● Waits infinitely for message from the Client.
● If the message received from the client is not “end”, prints the message. If the message is
“end”, closes the fifo and ends the process.
int main()
{
int fd;
OUTPUT
Hello
42
VIVA Question:
What is Safe State?
● A state is safe if the system can allocate all resources requested by all processes ( up to their
stated maximums ) without entering a deadlock state.
● More formally, a state is safe if there exists a safe sequence of processes { P0, P1, P2, ...,
PN } such that all of the resource requests for Pi can be granted using the resources currently
allocated to Pi and all processes Pj where j < i. ( I.e. if all the processes prior to Pi finish and
free up their resources, then Pi will be able to finish also, using the resources that they have
freed up. )
● If a safe sequence does not exist, then the system is in an unsafe state, which MAY lead to
deadlock.
43
Program 5.3: To implement IPC using Shared Memory
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
using namespace std;
int main()
{
// ftok to generate unique key
key_t key = ftok("shmfile",65);
return 0;
}
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
using namespace std;
int main()
{
// ftok to generate unique key
key_t key = ftok("shmfile",65);
return 0;
}
Output:
Unrelated processes (say one process running in one terminal and another process in another
terminal) communication can be performed using Named Pipes or through popular IPC
techniques of Shared Memory and Message Queues.
45
2. What are two types of process?
A process can be of two types: Independent process and Co-operating process.
An independent process is not affected by the execution of other processes while a co-operating process
can be affected by other executing processes
int main()
{
key_t key;
int msgid;
46
// ftok to generate unique key
key = ftok("progfile", 65);
message.mesg_type = 1;
gets(message.mesg_text);
return 0;
}
int main()
{
key_t key;
int msgid;
47
0);
return 0;
}
OUTPUT: Run the two programs simultaneously on two terminals.
Terminal 1: Terminal 2:
$cc writer.c -o writer $cc reader.c -o reader
$./writer $./reader
Hello Received message is : Hello
48
Program 6.1: To simulate paging technique of memory
management.
Description:
Paging is a memory management scheme that eliminates the need for contiguous allocation of
physical memory. This scheme permits the physical address space of a process to be non –
contiguous.
● The main idea behind the paging is to divide each process in the form of pages. The main
memory will also be divided in the form of frames.
● One page of the process is to be stored in one of the frames of the memory. The pages can
be stored at the different locations of the memory but the priority is always to find the
contiguous frames or holes.
● Pages of the process are brought into the main memory only when they are required
otherwise they reside in the secondary storage.
● Different operating system defines different frame sizes. The sizes of each frame must be
equal. Considering the fact that the pages are mapped to the frames in Paging, page size
needs to be as same as frame size
Algorithm:
Program:
#include<stdio.h>
int main()
{
int ms, Fsize, NoFrames, NoProcess, RemFrames, i, j, x, y, pa, offset;
int NoPages, PageTable[10];
49
NoFrames = ms/Fsize;
printf("\n The no. of Frames available in memory are -- %d ", NoFrames);
RemFrames = NoFrames;
if(NoPages >RemFrames)
{
printf("\n Memory is Full");
return (0);
}
RemFrames = RemFrames - NoPages;
printf("\n ---Enter page table --- ");
for(j=0;j<NoPages;j++)
scanf("%d", &PageTable[j]);
int yes=1;
do
{
printf("\nEnter Logical Address to find Physical Address ");
printf("\nEnter page number and offset -- ");
scanf(" %d %d",&y, &offset);
if( y>=NoPages || offset>=Fsize)
{
printf("\n trap: Page Number or offset illegal");
return(0);
}
else
{
pa = (PageTable[y]*Fsize) + offset;
printf("Fsize=%d,offset=%d\n,frame no=%d",Fsize,offset,PageTable[y]);
printf("\n The Physical Address is -- %d", pa);
}
printf("\nContinue : yes=1,no=0\n");
scanf("%d",&yes);
}while(yes==1);
return 0;
}
Enter the memory size -- 32
Enter the Frame size -- 4
The no. of Frames available in memory are -- 8
Enter no. of pages required : 4
---Enter page table ---
50
5
6
1
2
---page table ---
| PNo || FNo |
| 0 || 5 |
| 1 || 6 |
| 2 || 1 |
| 3 || 2 |
Enter Logical Address to find Physical Address
Enter page number and offset -- 0 0
Fsize=4,offset=0
,frame no=5
The Physical Address is -- 20
Continue : yes=1,no=0
1
VIVA Questions
1. What is paging?
Paging is a memory management scheme that eliminates the need for contiguous
allocation of physical memory. This scheme permits the physical address space of a process to be
non – contiguous.
51
● A Translation look aside buffer can be defined as a memory cache which can be used to
reduce the time taken to access the page table again and again.
● It is a memory cache which is closer to the CPU and the time taken by CPU to access TLB
is lesser then that taken to access main memory.
● TLB is faster and smaller than the main memory but cheaper and bigger than the register.
● TLB follows the concept of locality of reference which means that it contains only the
entries of those many pages that are frequently accessed by the CPU.
5. How memory Protection is provided for Page table?
● Memory protection implemented by associating protection bit with each frame to
indicate if read-only or read-write access is allowed. Can also add more bits to
indicate page execute-only, and so on
● Valid-invalid bit attached to each entry in the page table:“valid” indicates that the
associated page is in the process’ logical address space, and is thus a legal
page.“invalid” indicates that the page is not in the process’ logical address space,
Or use page-table length register (PTLR).
6. What is PTLR?
Page-table length register (PTLR) indicates size of the page table. These registers are
reloaded on context switch by dispatcher.
9. What is Protection ?
If paging or segmentation is provided, different sections of a user program can be declared execute-
only, read-only, or read -write. This restriction is necessary with shared code or data and is generally useful in
any case to provide simple run-time checks for common programming errors.
11. Difference between Symbolic address, Relative address and Physical address?
Symbolic addresses: The addresses used in a source code. The variable names, constants,
and instruction labels are the basic elements of the symbolic address space.
52
Relative addresses: At the time of compilation, a compiler converts symbolic addresses
into relative addresses
Physical addresses: The loader generates these addresses at the time when a program is loaded
into main memory.
Description: Segmentation is a memory-management scheme that supports this user view of memory. A
logical address space is a collection of segments. Each segment has a name and a length. The addresses specify
both the segment name
and the offset within the segment. The user therefore specifies each address by two quantities: a segment name
and an offset.
Segment table contains mainly two information about segment:
The Segment number is mapped to the segment table. The limit of the respective segment is
compared with the offset. If the offset is less than the limit then the address is valid otherwise it
throws an error as the address is invalid. In the case of valid address, the base address of the
segment is added to the offset to get the physical address of actual word in the main memory.
Algorithm:
Program:
#include<stdio.h>
int main()
{
53
int i, y, PhyAddre, offset;
int NoSeg, SegmentTable[10][10];
int yes=1;
do
{
printf("\nEnter Logical Address to find Physical Address ");
printf("\nEnter segment number and offset\n ");
scanf(" %d %d",&y, &offset);
if(offset>SegmentTable[y][1])
{
printf("Trap : Addressing Error\n");
}
else
{
PhyAddre=SegmentTable[y][0]+offset;
printf("\n The Physical Address is -- %d", PhyAddre);
printf("\nContinue : yes=1,no=0\n");
scanf("%d",&yes);
}
}while(yes==1);
return 0;
}
OUTPUT:
Enter the Segmentation Table data: Base value & Limit Value
--
219 600
2300 14
90 100
1327 580
1952 96
54
|| 2 || 90 || 100 ||
|| 3 || 1327 || 580 ||
|| 4 || 1952 || 96 ||
Continue : yes=1,no=0
Viva Questions
1. What is the functionality of Memory Management Component?
1. Memory management is the functionality of an operating system which handles or manages
primary memory and moves processes back and forth between main memory and disk during
execution.
2. Memory management keeps track of each and every memory location, regardless of either it is
allocated to some process or it is free.
3. It checks how much memory is to be allocated to processes.
4. It decides which process will get memory at what time.
5. It tracks whenever some memory gets freed or unallocated and correspondingly it updates the
status.
2. Difference between Physical and logical address?
● Virtual and physical addresses are the same in compile-time and load-time address-binding
● The set of all logical addresses generated by a program is referred to as a logical address
space. The set of all physical addresses corresponding to these logical addresses is referred to
as a physical address space.
3. What is MMU?
55
● The runtime mapping from virtual to physical address is done by the memory management
unit (MMU) which is a hardware device. MMU uses following mechanism to convert
virtual address to physical address.
4. What is swapping?
memory (or move) to secondary storage (disk) and make that memory available to other
processes.
multiple and big processes in parallel and that's the reason Swapping is also known as a
technique for memory
PROGRAM:
#include<stdio.h>
56
void main()
{
int ms,i,ps[20],n,size,p[20],s,intr=0;
printf("Enter size of memory:");
scanf("%d",&ms);
printf("Enter memory for OS:");
scanf("%d",&s);
ms-=s;
printf("Enter no.of partitions to be divided:");
scanf("%d",&n);
size=ms/n;
printf("Size of each partition is %d \n",size);
for(i=0;i<n;i++)
{
printf("Enter process and process size");
scanf("%d %d",&p[i],&ps[i]);
if(ps[i]<=size)
{
intr=intr+size-ps[i];
printf("process[%d] is allocated\n",p[i]);
}
else
printf("process[%d] size is greater that size of the partition : blocked",p[i]);
}
printf("total Internal fragmentation is %d",intr);
}
OUTPUT:
Enter size of memory:50
Enter memory for OS:10
Enter no.of partitions to be divided:4
Enter process and process size1 10
process[1] is allocated
Enter process and process size2 9
process[2] is allocated
Enter process and process size3 9
process[3] is allocated
Enter process and process size4 8
process[4] is allocated
total Internal fragmentation is 4
MVT ALGORITHM:
Step1: start
Step2: Declare variables.
Step3: Enter total memory size.
Step4: Read the no of processes
Step5: Allocate memory for os.
Step6: read the size of each process
Step7:calculate available memory by subtracting the memory of os from total memory
Step8: If available memory >= size of process then allocate memory to that process.
Step9: Display the wastage of memory.
Step10: Stop .
PROGRAM:
57
#include<stdio.h>
void main()
{
int i,m,n,tot,s[20];
printf("Enter total memory size:");
scanf("%d",&tot);
printf("Enter no. of processes:");
scanf("%d",&n);
printf("Enter memory for OS:");
scanf("%d",&m);
for(i=0;i<n;i++)
{
printf("Enter size of process %d:",i+1);
scanf("%d",&s[i]);
}
tot=tot-m;
for(i=0;i<n;i++)
{
if(tot>=s[i])
{
printf("Allocate memory to process %d\n",i+1);
tot=tot-s[i];
}
else
printf("process p%d is blocked\n",i+1);
}
printf("External Fragmentation is=%d",tot);
}
OUTPUT:
Enter total memory size : 50
Enter no.of pages : 4
Enter memory for OS :10
Enter size of page : 10
Enter size of page : 9
Enter size of page : 9
Enter size of page : 10
External Fragmentation is = 2
VIVA QUESTIONS:
1. Differentiate MFT and MVT?
1. MFT or fixed partitioning Scheme:
● In fixed scheme, the OS will be divided into fixed sized blocks. It takes
place at the time of installation.
● At compile time, we can bind only addresses
5. What is Compaction?
Compaction is a solution for the problem of External Fragmentation .The main goal is to
shuffle the memory contents so as to place all the free memory together in one large block
59
A C program to implement FIFO page replacement algorithm
Description:
In an operating system that uses paging for memory management, a page replacement algorithm is
needed to decide which page needs to be replaced when new page comes in.
Page Fault – A page fault happens when a running program accesses a memory page that is
mapped into the virtual address space, but not loaded in physical memory.
First In First Out (FIFO) –
This is the simplest page replacement algorithm. In this algorithm, the operating system keeps
track of all pages in the memory in a queue, the oldest page is in the front of the queue. When a
page needs to be replaced page in the front of the queue is selected for removal.
Program:
#include<stdio.h>
int main()
{
int i,j,n,a[50],frame[10],no,k,avail,count=0;
printf("\n ENTER THE NUMBER OF PAGES:\n");
scanf("%d",&n);
printf("\n ENTER THE PAGE NUMBER :\n");
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
for(i=0;i<no;i++)
frame[i]= -1;
j=0;
printf("\tref string\t page frames\n");
60
for(i=1;i<=n;i++)
{
printf("%d\t\t",a[i]);
avail=0;
for(k=0;k<no;k++)
if(frame[k]==a[i])
avail=1;
if (avail==0)
{
frame[j]=a[i];
j=(j+1)%no;
count++;
for(k=0;k<no;k++)
printf("%d\t",frame[k]);
}
printf("\n");
}
printf("Page Fault Is %d",count);
return 0;
}
OUTPUT
ENTER THE NUMBER OF PAGES: 20
ENTER THE PAGE NUMBER : 70120304230321201701
ENTER THE NUMBER OF FRAMES :3
ref string page frames
7 7 -1 -1
0 7 0 -1
1 7 0 1
2 2 0 1
0
3 2 3 1
0 2 3 0
4 4 3 0
2 4 2 0
3 4 2 3
0 0 2 3
3
2
1 0 1 3
2 0 1 2
0
1
7 7 1 2
0 7 0 2
1 7 0 1
Page Fault Is 15
Viva questions
What is Belay’s anomaly?
Bélády's anomaly is the phenomenon in which increasing the number of page frames results in
an increase in the number of page faults for certain memory access patterns. This phenomenon is
commonly experienced when using the first-in first-out (FIFO) page replacement algorithm.
61
What is page replacement?
Page replacement is a process of swapping out an existing page from the frame of a main memory
and replacing it with the required page.
Program:
#include <stdio.h>
int main()
{
int arrival_time[10], burst_time[10], temp[10];
int i, smallest, count = 0, time, limit;
double wait_time = 0, turnaround_time = 0, end;
float average_waiting_time, average_turnaround_time;
printf("nEnter the Total Number of Processes:t");
scanf("%d", &limit);
printf("nEnter Details of %d Processesn", limit);
for(i = 0; i < limit; i++)
{
printf("nEnter Arrival Time:t");
scanf("%d", &arrival_time[i]);
printf("Enter Burst Time:t");
scanf("%d", &burst_time[i]);
temp[i] = burst_time[i];
}
burst_time[9] = 9999;
for(time = 0; count != limit; time++)
{
smallest = 9;
for(i = 0; i < limit; i++)
{
if(arrival_time[i] <= time && burst_time[i] < burst_time[smallest] && burst_time[i] >
0)
{
smallest = i;
}
}
burst_time[smallest]--;
if(burst_time[smallest] == 0)
{
count++;
end = time + 1;
62
wait_time = wait_time + end - arrival_time[smallest] – temp[smallest];
turnaround_time = turnaround_time + end - arrival_time[smallest];
}
}
OUTPUT:
AT WT
07
23
72
Id WT TAT
1 5 12
2 0 3
3 0 2
VIVA Qeuestions:
What is a process?
A program in execution is called a process.
63
What is meant by Dispatch latency?
The time taken by the dispatcher to stop one process and start another running is known as
Dispatch
Latency.
64