[go: up one dir, main page]

0% found this document useful (0 votes)
29 views82 pages

OS Lab Manual (For Students)

The document outlines a syllabus for experiments related to operating systems, including hardware and software requirements for UNIX, Linux, and Windows versions. It details various UNIX system calls for process and file management, CPU scheduling policies, and includes programming tasks to implement these concepts. Additionally, it provides information on inter-process communication techniques and specific programming examples in C for practical understanding.

Uploaded by

mehulbhatia44
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
29 views82 pages

OS Lab Manual (For Students)

The document outlines a syllabus for experiments related to operating systems, including hardware and software requirements for UNIX, Linux, and Windows versions. It details various UNIX system calls for process and file management, CPU scheduling policies, and includes programming tasks to implement these concepts. Additionally, it provides information on inter-process communication techniques and specific programming examples in C for practical understanding.

Uploaded by

mehulbhatia44
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 82

List of Experiments

(CO-Wise Syllabus)

1.​ Study of hardware and software requirements of different operating systems


(UNIX,LINUX,WINDOWS XP,WINDOWS7/8
2.​ Execute various UNIX system calls for
i.​ Process management
ii.​ File management
iii.​ Input/output Systems calls
3.​ Implement CPU Scheduling Policies:
i.​ SJF
ii.​ Priority
iii.​ FCFS
iv.​ Multi-level Queue
4.​ Implement file storage allocation technique:
i.​ Contiguous(using array)
ii.​ Linked –list(using linked-list)
iii.​ Indirect allocation (indexing)
5.​ Implementation of contiguous allocation techniques:
i.​ Worst-Fit
ii.​ Best- Fit
iii.​ First- Fit
6.​ Calculation of external and internal fragmentation
i.​ Free space list of blocks from system
ii.​ List process file from the system
7.​ Implementation of compaction for the continually changing memory layout and
calculate total movement of data
8.​ Implementation of resource allocation graph RAG)
9.​ Implementation of Banker’s algorithm
10.​Conversion of resource allocation graph (RAG) to wait for graph (WFG) for each
type of method used for storing graph.

Experiments Beyond Syllabus:


1.​ Implement the solution for Bounded Buffer (producer-consumer)problem using
inter process communication techniques-Semaphores
2.​ Implement the solutions for Readers-Writers problem using inter process
communication technique –Semaphore
1.​Study of hardware and software requirements of different operating systems (UNIX, LINUX,
WINDOWS XP,and WINDOWS7/8 listed below :-

The hardware and software requirements for different operating systems vary based on the specific
version and edition. Please note that the requirements may have changed with newer releases, and
it's always a good idea to check the official documentation for the most up-to-date information.

A.​UNIX:

UNIX is a family of multitasking, multiuser computer operating systems. It includes various


versions, such as AIX, HP-UX, Solaris, and more. Hardware and software requirements can vary
between these versions. In general:

Hardware Requirements:

•​ UNIX systems are versatile and can run on a wide range of hardware architectures.

•​ Common hardware includes processors from Intel, AMD, SPARC, PowerPC, etc.

•​ Memory requirements depend on the specific UNIX variant and its purpose but typically range
from a few hundred megabytes to several gigabytes.

Software Requirements:

•​ UNIX operating systems come with their own set of software tools and utilities.

•​ Applications and software packages can be installed based on the specific needs of the user or
organization.

B.​ Linux:

Linux is a Unix-like open-source operating system kernel. Like UNIX, Linux distributions (distros)
have varying hardware and software requirements.

Hardware Requirements:

•​ Linux can run on a wide range of hardware, from low-powered devices to high-end servers.

•​ Common hardware includes processors from Intel, AMD, ARM, etc.

•​ Memory requirements vary but are typically in the range of a few hundred megabytes to several
gigabytes.

Software Requirements:

•​ Linux distributions come with a variety of software packages, and additional software can be
installed using package managers.
•​ Different desktop environments and window managers are available, providing flexibility in user
experience.

C.​ Windows XP:

Windows XP is an older operating system, and Microsoft no longer provides official support for
it.

Hardware Requirements:

•​ Pentium 233-megahertz (MHz) processor or faster.

•​ 64 megabytes (MB) of RAM or higher.

•​ 1.5 gigabytes (GB) of available hard disk space.

•​ Super VGA (800 x 600) or higher resolution video adapter and monitor.

Software Requirements:

•​ Windows XP comes with its set of system utilities and applications.

•​ Additional software can be installed based on user requirements.

D.​ Windows 7/8:

Windows 7 and Windows 8 are also older versions of the Windows operating system, and
Microsoft has moved on to newer versions.

Hardware Requirements (for Windows 7):

•​ 1 gigahertz (GHz) or faster 32-bit (x86) or 64-bit (x64) processor.

•​ 1 gigabyte (GB) RAM (32-bit) or 2 GB RAM (64-bit).

•​ 16 GB available hard disk space (32-bit) or 20 GB (64-bit).

•​ DirectX 9 graphics device with WDDM 1.0 or higher driver.

Software Requirements (for Windows 7):

•​ Comes with a set of system utilities, and additional software can be installed.

•​ Supports a wide range of applications compatible with the Windows platform.

Hardware Requirements (for Windows 8):

•​ 1 gigahertz (GHz) or faster processor.


•​ 1 gigabyte (GB) RAM (32-bit) or 2 GB RAM (64-bit).

•​ 16 GB available hard disk space (32-bit) or 20 GB (64-bit).

•​ DirectX 9 graphics device with WDDM 1.0 or higher driver.

Software Requirements (for Windows 8):

•​ Similar to Windows 7, comes with system utilities and supports various applications compatible
with the Windows platform.
2.​ Execute various UNIX system calls for

Write Basic Commands for VI-Editor used in Linux.

Commands Description
1. i ​ ​ ​ ​ -​ ​ Instruction mode
2. a ​ ​​ -​ ​ Append to right mode
3. w​ ​ ​ ​ -​ ​ Advance to next mode
4. b​ ​ ​ ​ -​ ​ Move to the previous word
5. 3b​​ ​ ​ -​ ​ Move backwards 3 words
6. YY ​ ​ ​ -​ ​ Delete line
7. 3dd​ ​ ​ -​ ​ Delete 3 lines
8.o(small)​ ​ ​ -​ ​ Open space for new line below the cursor line
9. O (big) ​ ​ ​ -​ ​ Opens a line above the cursor
10. Ctrl-w ​ ​ ​ -​ ​ Move back a word in append mode
11. u (small)​​ ​ -​ ​ Undo last
12. U​​ ​ ​ -​ ​ Undo all changes to current line
13. Esc ​ ​ ​ -​ ​ Command mode
14. :wq ​ ​ ​ -​ ​ Save and quit
15. :q!​ ​ ​ -​ ​ Quit without saving
16. J ​​ ​ ​ -​ ​ Joins 2 lines
17. K​​ ​ ​ -​ ​ ↑
18. h​​ ​ ​ -​ ​ ←
19. j​​ ​ ​ -​ ​ ↓
20. l​​ ​ ​ -​ ​ →
21. H ​ ​ ​ -​ ​ Move the cursor to the highest line on the space
22. L ​​ ​ ​ -​ ​ Move the cursor to the lowest line on the screen
23. M ​ ​ ​ -​ ​ Position the cursor at the midpoint of the screen
24. 0 (ZERO) ​ ​ -​ ​ Move the cursor to the beginning of the line it is on
25. :wq filename.c​ ​ -​ ​ Saving a C file
26. gcc space –o ​ ​ -​ ​ space filename space filename.c compilation
command
27. ./filename ​ ​ -​ ​ run c code
28. Vi ​ ​ ​ -​ ​ Open new vi editor
29. Vi filename.c ​ ​ -​ ​ Return to your file
Write a basic program on VI Editor in Linux.
#include<stdio.h>
#include<sys/types.h>
main ()
{
int n;
printf("Enter an integer\n");
scanf("%d",&n);
if ( n%2 == 0 )
printf("Even\n");
else printf("Odd\n");
return 0;
}

Write a program to show the process ID and parent process ID of a process.


#include<stdio.h>
#include<sys/types.h>
main ()
{
int pid;
pid=getpid();
ppid=getppid();
printf("id of the process is=%d\n", getpid());
printf("id of the process is=%d\n", getppid());
}
3.​ Execute various UNIX system calls for
i.​ Process management
Fork()
The Fork system call is used for creating a new process in Linux, and Unix
systems, which is called the child process, which runs concurrently with the
process that makes the fork() call (parent process). After a new child process is
created, both processes will execute the next instruction following the fork()
system call.

Write a program in C to implement the process management system call.

#include &lt;stdio.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;unistd.h&gt;
int main()
{

// make two process which run same


// program after this instruction
pid_t p = fork();
if(p&lt;0){
perror(&quot;fork fail&quot;);
exit(1);
}
printf(&quot;Hello world!, process_id(pid) = %d \n&quot;,getpid());
return 0;
}

Output
Hello world!, process_id(pid) = 31
Hello world!, process_id(pid) = 32

Viva Question

1.​ What is the function of processor management?


2.​ List out features of Process Management.

3.​ Define interrupt.

4.​ What is process management in Unix system?


ii.​ File management

File management system calls handle file manipulation jobs like creating a file, reading, and writing,
etc. The Linux System calls under this are open(), read(), write(), close().

●​ open():
●​ It is the system call to open a file.
●​ This system call just opens the file, to perform operations such as read and write, we
need to execute different system call to perform the operations.
●​ read():
●​ This system call opens the file in reading mode.
●​ We can not edit the files with this system call.
●​ Multiple processes can execute the read() system call on the same file
simultaneously.
●​ write():
●​ This system call opens the file in writing mode.
●​ We can edit the files with this system call.
●​ Multiple processes can not execute the write() system call on the same file
simultaneously.
●​ close():
●​ This system call closes the opened file.

Write a program in C to implement the file management system call using UNIX.

#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>

int main()
{
int n, fd;
char buff[50]; // declaring buffer

// Message printing on the display


printf("Enter text to write in the file:\n");
// Read from keyboard, specifying 0 as fd for std input device
// Here, n stores the number of characters
n = read(0, buff, 50);

// creating a new file using open.


fd = open("file", O_CREAT | O_RDWR, 0777);

// Writting input data to file (fd)


write(fd, buff, n);
// Write to display (1 is standard fd for output device)
write(1, buff, n);
// closing the file
int close(int fd);

return 0;
}

Output


Enter text to write in the file:
Hello world, welcome @ IncludeHelp
Hello world, welcome @ IncludeHelp

Viva Question

1.​ What are the different types of files that you can create in a Linux system?
In a Linux system, you can create three different types of files: regular files, directories, and
special files. Regular files are the most common type of file and are used to store data.
Directories are used to store information about the structure of the file system. Special files are
used to store information about devices or to provide access to specific kernel features.

2.​ How can you change the permissions for a file or folder in Linux?
You can use the chmod command to change the permissions for a file or folder in Linux. For
example, to give read, write, and execute permissions to all users for a file named “myfile”, you
would use the command “chmod a+rwx my file”.
iii.​ Input/output Systems calls
a.​ Open()

Open () system call is used to know the file descriptor of user created files.

Syntax of open()

int open(const char* path,int)

Write a program in c to implement I/O open() system call.

#include <errno.h>

#include <fcntl.h>

#include <stdio.h>

#include <unistd.h>

extern int errno;

int main()

// if file does not have in directory

// then file foo.txt is created.

int fd = open("foo.txt", O_RDONLY | O_CREAT);

printf("fd = %d\n", fd);

if (fd == -1) {

// print which type of error have in a code

printf("Error Number % d\n", errno);

// print program detail "Success or failure"

perror("Program");

return 0;

Output

fd = 3

b.​ close()

The close() function in the C tells the operating system that you are done with a file descriptor
and closes the file pointed by the file descriptor .It is defined inside <unistd.h> header file.
Syntax of close() in C
int close(int fd);

Write a program in C to implement I/O


close() system call.

#include <fcntl.h>

#include <stdio.h>

#include <unistd.h>

int main()

int fd1 = open("foo.txt", O_RDONLY);

if (fd1 < 0) {

perror("c1");

exit(1);

printf("opened the fd = % d\n", fd1);

// Using close system Call

if (close(fd1) < 0) {

perror("c1");

exit(1);

printf("closed the fd.\n");

Output
opened the fd = 3

closed the fd.

c.​ read( )

Syntax of read( )
size_t read (int fd, void* buf, size_t cnt);
Write a program in C to implement the I/O read() system call.
#include <fcntl.h>

#include <stdio.h>

#include <unistd.h>

int main()

int fd, sz;

char* c = (char*)calloc(100, sizeof(char));

fd = open("foo.txt", O_RDONLY);

if (fd < 0) {

perror("r1");

exit(1);

sz = read(fd, c, 10);

printf("called read(% d, c, 10). returned that"

" %d bytes were read.\n",

fd, sz);

c[sz] = '\0';

printf("Those bytes are as follows: % s\n", c);

return 0;

Output
called read(3, c, 10). returned that 10 bytes were read.

Those bytes are as follows: 0 0 0 foo.


3. Implement CPU Scheduling Policies:

i.​SJF
SJF (Shortest Job First) is a scheduling strategy that gives the process with the quickest CPU burst
time to the CPU first. As this technique is non-preemptive, once a process has begun to run, it
cannot be stopped until it has finished. The SJF scheduling method is ideal since it reduces
the average waiting time for a set of processes.

Write a program to implement SJF scheduling Algorithm.


#include<stdio.h>
int main()
{
int bt[20],p[20],wt[20],tat[20],i,j,n,total=0,totalT=0,pos,temp;
float avg_wt,avg_tat;
printf("Enter number of process:");
scanf("%d",&n);

printf("\nEnter Burst Time:\n");


for(i=0;i<n;i++)
{
printf("p%d:",i+1);
scanf("%d",&bt[i]);
p[i]=i+1;
}

//sorting of burst times


for(i=0;i<n;i++)
{
pos=i;
for(j=i+1;j<n;j++)
{
if(bt[j]<bt[pos])
pos=j;
}

temp=bt[i];
bt[i]=bt[pos];
bt[pos]=temp;

temp=p[i];
p[i]=p[pos];
p[pos]=temp;
}

wt[0]=0;

//finding the waiting time of all the processes


for(i=1;i<n;i++)
{
wt[i]=0;
for(j=0;j<i;j++)
//individual WT by adding BT of all previous completed processes
wt[i]+=bt[j];

//total waiting time


total+=wt[i];
}
//average waiting time
avg_wt=(float)total/n;

printf("\nProcess\t Burst Time \tWaiting Time\tTurnaround Time");


for(i=0;i<n;i++)
{
//turnaround time of individual processes
tat[i]=bt[i]+wt[i];

//total turnaround time


totalT+=tat[i];
printf("\np%d\t\t %d\t\t %d\t\t\t%d",p[i],bt[i],wt[i],tat[i]);
}

//average turnaround time


avg_tat=(float)totalT/n;
printf("\n\nAverage Waiting Time=%f",avg_wt);
printf("\nAverage Turnaround Time=%f",avg_tat);
}

OUTPUT
Enter number of process:4

Enter Burst Time:


p1:5
p2:4
p3:12
p4:7

Process Burst Time Waiting Time Turnaround Time


p2 4 0 4
p1 5 4 9
p4 7 9 16
p3 12 16 28

Average Waiting Time=7.250000


Average Turnaround Time=14.250000

Viva question

1.​ The optimum CPU scheduling algorithm is


a.​ FIFO
b.​ SJF with preemption
c.​ SJF without preemption
d.​ Round Robin
2.​ What are the dis-advantages of SJF Scheduling Algorithm?
Shortest Job First(SJF) Starting with the Advantages: ofShortest Job First scheduling
algorithm.
According to the definition, short processes are executed first and then followed by longer
processes. The throughput is increased because more processes can be executed in less
amount of time.
3.​ Which of the following scheduling reduces process flow time?
a.​ FCFS
b.​ LIFO
c.​ SJF
d.​ All of the these
4.​ Under multiprogramming, turnaround time for short jobs is usually ________ and that for
long jobs is slightly ___________
a.​ Lengthened; Shortened
b.​ Shortened; Lengthened
c.​ Shortened; Shortened
d.​ Shortened; Unchanged
5.​ When should you not use SJF?
If the run-times of the jobs are not known in advance, then SJF will not work well, since it is
basing its decisions on estimates. In addition, SJF can be unfair to longer-running jobs, since
they will always be scheduled after shorter jobs, even if they are ready to run sooner. Finally,
SJF can be inefficient if the run-times of the jobs vary greatly, since it will often end up
rescheduling jobs as new information about their run-times becomes available.
ii.​Priority
Priority scheduling is a CPU scheduling algorithm where processes are assigned priorities, and the
process with the highest priority is selected for execution. In priority scheduling, the scheduler
chooses the process with the highest priority number from the ready queue and allocates the
CPU to that process. The priority of a process is usually determined by its importance, the
amount of CPU time it needs, or its deadline.

Write a Program to implement priority scheduling.


#include <stdio.h>

//Function to swap two variables


void swap(int *a,int *b)
{
int temp=*a;
*a=*b;
*b=temp;
}
int main()
{
int n;
printf("Enter Number of Processes: ");
scanf("%d",&n);

// b is array for burst time, p for priority and index for process id
int b[n],p[n],index[n];
for(int i=0;i<n;i++)
{
printf("Enter Burst Time and Priority Value for Process %d: ",i+1);
scanf("%d %d",&b[i],&p[i]);
index[i]=i+1;
}
for(int i=0;i<n;i++)
{
int a=p[i],m=i;

//Finding out highest priority element and placing it at its desired


position
for(int j=i;j<n;j++)
{
if(p[j] > a)
{
a=p[j];
m=j;
}
}

//Swapping processes
swap(&p[i], &p[m]);
swap(&b[i], &b[m]);
swap(&index[i],&index[m]);
}

// T stores the starting time of process


int t=0;

//Printing scheduled process


printf("Order of process Execution is\n");
for(int i=0;i<n;i++)
{
printf("P%d is executed from %d to %d\n",index[i],t,t+b[i]);
t+=b[i];
}
printf("\n");
printf("Process Id Burst Time Wait Time TurnAround Time\n");
int wait_time=0;
for(int i=0;i<n;i++)
{
printf("P%d %d %d
%d\n",index[i],b[i],wait_time,wait_time + b[i]);
wait_time += b[i];
}
return 0;
}

OUTPUT
Enter Number of Processes: 3
Enter Burst Time and Priority Value for Process 1: 10 2
Enter Burst Time and Priority Value for Process 2: 5 0
Enter Burst Time and Priority Value for Process 3: 8 1
Order of process Execution is
P1 is executed from 0 to 10
P3 is executed from 10 to 18
P2 is executed from 18 to 23

Process Id Burst Time Wait Time TurnAround Time


P1 10 0 10
P3 8 10 18
P2 5 18 23

Viva question

1.​ What is priority scheduling ?


Priority Scheduling is a type of CPU Scheduling algorithm which is used for process scheduling.
In
Priority Scheduling, we assign some priorities to each process. The process which has higher
priority
among all the processes is assigned with the CPU first.
2.​ What are the advantages of priority scheduling?
a.​ Priority scheduling is simple to understand.
b.​ Priority scheduling is a user-friendly algorithm.
c.​ Based on priority, processes are executed. So, the process which has the highest priority
does not need to wait for more time
d.​ Priorities in the Priority scheduling are chosen on the basis of time requirements, memory
requirements, and user preferences.
3.​ How many types of priority scheduling algorithm ?
There are two types of priority scheduling algorithm. One is Preemptive priority scheduling
while the other is Non Preemptive Priority scheduling.
iii.​ FCFS

First Come, First Served (FCFS) also known as First In, First Out (FIFO) is the CPU scheduling
algorithm in which the CPU is allocated to the processes in the order they are queued in the ready
queue.

FCFS follows non-preemptive scheduling which mean once the CPU is allocated to a process it does
not leave the CPU until the process will not get terminated or may get halted due to some I/O
interrupt.

Write a program to implement First-Come-First-Serve Algorithm.


#include <stdio.h>
int main()
{
int pid[15];
int bt[15];
int n;
printf("Enter the number of processes: ");
scanf("%d",&n);

printf("Enter process id of all the processes: ");


for(int i=0;i<n;i++)
{
scanf("%d",&pid[i]);
}

printf("Enter burst time of all the processes: ");


for(int i=0;i<n;i++)
{
scanf("%d",&bt[i]);
}

int i, wt[n];
wt[0]=0;

//for calculating waiting time of each process


for(i=1; i<n; i++)
{
wt[i]= bt[i-1]+ wt[i-1];
}

printf("Process ID Burst Time Waiting Time TurnAround Time\n");


float twt=0.0;
float tat= 0.0;
for(i=0; i<n; i++)
{
printf("%d\t\t", pid[i]);
printf("%d\t\t", bt[i]);
printf("%d\t\t", wt[i]);

//calculating and printing turnaround time of each process


printf("%d\t\t", bt[i]+wt[i]);
printf("\n");

//for calculating total waiting time


twt += wt[i];
//for calculating total turnaround time
tat += (wt[i]+bt[i]);
}
float att,awt;

//for calculating average waiting time


awt = twt/n;

//for calculating average turnaround time


att = tat/n;
printf("Avg. waiting time= %f\n",awt);
printf("Avg. turnaround time= %f",att);
}

OUTPUT
Enter the number of processes: 3
Enter process id of all the processes: 1 2 3
Enter burst time of all the processes: 5 11 11
Process ID Burst Time Waiting Time TurnAround Time
1 5 0 5
2 11 5 16
3 11 16 27
Avg. waiting time= 7.000000
Avg. turnaround time= 16.000000

Viva question

1.​ What is First Come First Serve Method?


First Come First Serve (FCFS) is an operating system scheduling algorithm that automatically
executes queued requests and processes in order of their arrival. It is the easiest and simplest
CPU scheduling algorithm. In this type of algorithm, processes which requests the CPU first get
the CPU allocation first.
2.​ Give example of FCFS scheduling.
A real-life example of the FCFS method is buying a movie ticket on the ticket counter. In this
scheduling algorithm, a person is served according to the queue manner. The person who
arrives first in the queue first buys the ticket and then the next one. This will continue until the
last person in the queue purchases the ticket. Using this algorithm, the CPU process works in a
similar manner.
3.​ What are the Advantages of FCFS?
●​ The First come first serve process scheduling algorithm is one of the simple and easy processe
scheduling algorithms.
●​ The process that arrives first will be executed first.
4.​ What are some limitations of using an FCFS algorithm?
FCFS can cause starvation if there are processes with very long run times. Additionally, FCFS can
lead to convoy effects, where a long process can cause a number of shorter processes to have
to wait a long time.
iv.​ Multi-Level Queue
Multilevel Queue Scheduling is a CPU scheduling algorithm that is used in operating systems to
manage the allocation of CPU resources to different processes.
In this algorithm, the ready queue is divided into multiple queues based on the process
characteristics such as priority, CPU burst time, memory requirement, etc. Each queue is assigned a
different priority level, and each queue has its own scheduling algorithm. The highest priority queue
gets the highest priority for CPU allocation, and the lowest priority queue gets the lowest priority.

Write a program to implement Multilevel Queue Scheduling


#include <stdio.h>

#include <stdlib.h>

// Structure to represent a process

typedef struct {

int process_id;

int priority;

int burst_time;

int remaining_time;

} Process;

// Structure to represent a queue

typedef struct {

Process** processes;

int front, rear, capacity;

} Queue;

// Function to initialize a queue

Queue* initializeQueue(int capacity) {

Queue* queue = (Queue*)malloc(sizeof(Queue));

queue->processes = (Process**)malloc(capacity * sizeof(Process*));

queue->front = -1;

queue->rear = -1;

queue->capacity = capacity;

return queue;
}

// Function to check if a queue is empty

int isQueueEmpty(Queue* queue) {

return queue->front == -1;

// Function to check if a queue is full

int isQueueFull(Queue* queue) {

return (queue->rear + 1) % queue->capacity == queue->front;

// Function to enqueue a process into a queue

void enqueue(Queue* queue, Process* process) {

if (isQueueFull(queue)) {

printf("Queue is full. Cannot enqueue.\n");

return;

if (isQueueEmpty(queue)) {

queue->front = 0;

queue->rear = 0;

} else {

queue->rear = (queue->rear + 1) % queue->capacity;

queue->processes[queue->rear] = process;

// Function to dequeue a process from a queue

Process* dequeue(Queue* queue) {

if (isQueueEmpty(queue)) {

printf("Queue is empty. Cannot dequeue.\n");


return NULL;

Process* process = queue->processes[queue->front];

if (queue->front == queue->rear) {

queue->front = -1;

queue->rear = -1;

} else {

queue->front = (queue->front + 1) % queue->capacity;

return process;

// Function to display the contents of a queue

void displayQueue(Queue* queue) {

printf("Queue: ");

if (isQueueEmpty(queue)) {

printf("Empty");

} else {

int i = queue->front;

while (i != queue->rear) {

printf("%d(%d) -> ", queue->processes[i]->process_id,


queue->processes[i]->remaining_time);

i = (i + 1) % queue->capacity;

printf("%d(%d)", queue->processes[i]->process_id,
queue->processes[i]->remaining_time);

printf("\n");

}
// Function to simulate multilevel queue scheduling

void multilevelQueueScheduling(Process** processes, int num_processes) {

// Create three queues for three priority levels

Queue* queues[3];

for (int i = 0; i < 3; ++i) {

queues[i] = initializeQueue(num_processes);

// Enqueue processes based on their priority

for (int i = 0; i < num_processes; ++i) {

enqueue(queues[processes[i]->priority], processes[i]);

// Simulation

int time = 0;

while (1) {

printf("Time %d\n", time);

// Process each queue in round-robin fashion

for (int i = 0; i < 3; ++i) {

Queue* currentQueue = queues[i];

if (!isQueueEmpty(currentQueue)) {

Process* currentProcess = dequeue(currentQueue);

printf("Executing process %d from Queue %d\n",


currentProcess->process_id, i);

// Simulate a time unit of processing

currentProcess->remaining_time--;

if (currentProcess->remaining_time > 0) {

// Reenqueue the process if it still has remaining time

enqueue(currentQueue, currentProcess);

} else {
// Free memory allocated for the completed process

free(currentProcess);

// Display the state of queues after each time unit

for (int i = 0; i < 3; ++i) {

displayQueue(queues[i]);

// Check if all queues are empty

int allQueuesEmpty = 1;

for (int i = 0; i < 3; ++i) {

if (!isQueueEmpty(queues[i])) {

allQueuesEmpty = 0;

break;

if (allQueuesEmpty) {

printf("Simulation complete.\n");

break;

time++;

int main() {

// Create an array of processes (for demonstration purposes)

int num_processes = 5;

Process* processes[num_processes];
for (int i = 0; i < num_processes; ++i) {

processes[i] = (Process*)malloc(sizeof(Process));

processes[i]->process_id = i + 1;

processes[i]->priority = i % 3;

processes[i]->burst_time = (i % 2 == 0) ? 2 : 3;

processes[i]->remaining_time = processes[i]->burst_time;

// Display information about the processes

printf("Processes:\n");

for (int i = 0; i < num_processes; ++i) {

printf("Process %d: Priority %d, Burst Time %d\n",


processes[i]->process_id, processes[i]->priority, processes[i]->burst_time);

// Run the multilevel queue scheduling simulation

multilevelQueueScheduling(processes, num_processes);

// Free memory allocated for processes

for (int i = 0; i < num_processes; ++i) {

free(processes[i]);

return 0;

Output
Processes:

Process 1: Priority 0, Burst Time 2

Process 2: Priority 1, Burst Time 3

Process 3: Priority 2, Burst Time 2

Process 4: Priority 0, Burst Time 3

Process 5: Priority 1, Burst Time 2

Time 0
Executing process 1 from Queue 0

Executing process 2 from Queue 1

Executing process 3 from Queue 2

Queue: 4(3) -> 1(1)

Queue: 5(2) -> 2(2)

Queue: 3(1)

Time 1

Executing process 4 from Queue 0

Executing process 5 from Queue 1

Executing process 3 from Queue 2

Queue: 1(1) -> 4(2)

Queue: 2(2) -> 5(1)

Queue: Empty

Time 2

Executing process 1 from Queue 0

Executing process 2 from Queue 1

Queue: 4(2)

Queue: 5(1) -> 2(1)

Queue: Empty

Time 3

Executing process 4 from Queue 0

Executing process 5 from Queue 1

Queue: 4(1)

Queue: 2(1)

Queue: Empty

Time 4

Executing process 4 from Queue 0

Executing process 2 from Queue 1

Queue: Empty

Queue: Empty

Queue: Empty

Simulation complete.
free(): double free detected in tcache 2

Aborted

Viva Question

1.​ What is Multilevel Queue Scheduling?

A method of organizing the tasks or processes that a computer must perform is multilevel queue
scheduling. The computer system divides tasks or processes into different queues based on their
priority in this method. A task’s priority can be determined by factors such as memory capacity,
process priority, or type.

2.​ Advantages and Disadvantages of Multilevel Queue Scheduling


There are various advantages and disadvantages of multilevel queue scheduling. Some of the
advantages and disadvantages of the multilevel queue scheduling are as follows:

Advantages

a.​ You can use multilevel queue scheduling to apply different scheduling methods to distinct
processes.
b.​ It will have low overhead in terms of scheduling.

Disadvantages

a.​ There is a risk of starvation for lower priority processes.


b.​ It is rigid in nature.

3.​ Give example of multilevel queue scheduling.


Let's take an example of a multilevel queue-scheduling algorithm with five queues to
understand how this scheduling works:

a.​ System process


b.​ Interactive processes
c.​ Interactive editing processes
d.​ Batch processes
e.​ Student processes

Every queue would have an absolute priority over the low-priority queues. No process may
execute until the high-priority queues are empty. In the above instance, no other process may
execute until and unless the queues for system, interactive, and editing processes are empty. If
an interactive editing process enters the ready queue while a batch process is underway, the
batch process will be preempted.

There are the descriptions of the processes that are used in the above example:

System Process

The OS has its process to execute, which is referred to as the System Process.

Interactive Process

It is a process in which the same type of interaction should occur.

Batch Process

Batch processing is an operating system feature that collects programs and data into a batch
before processing starts.

Student Process

The system process is always given the highest priority, whereas the student processes are
always given the lowest.
4. Implement file storage allocation technique:

i.​ Contiguous(using array)


Contiguous memory allocation in os is a memory allocation process in which each process is
allocated a contiguous block of memory, meaning that the memory locations for a process are
consecutive.
Contiguous memory allocation is one of these memory allocation strategies. We use this technique
to allocate contiguous blocks of memory to each process, as the name suggests. Therefore, we allot
a continuous segment from the entirely empty space to the process based on its size whenever a
process requests to enter the main memory.

Write a program to implement contiguous file allocation technique


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(){
int start[10],num=0,count=0,length[10],j,f=1,i=0;
char name[20][10];
char del[2];
int ch=0;
while(1){
printf("Contiguous file allocation\n");
printf("1.File Creation\n");
printf("2.File Deletion\n");
printf("3.Display File Allocation Table\n");
printf("4.Exit\n");
printf("Enter your choice\n");
scanf("%d",&ch);
switch(ch){
case 1:
printf("Enter the name of the file\n");
scanf("%s",&name[i][0]);
printf("Enter the start block of the file\n");
scanf("%d",&start[i]);
printf("Enter the length of the file\n");
scanf("%d",&length[i]);
num++;
i++;
if(f==1){
f++;
}
for(j=0;j<num;j++){
if(start[j+1]<=start[j] || start[j+1]>=length[j]){

}
else{
count++;
}
}
if(count==1){
printf("%s cannot be allocated disk space\n",name[i-1]);
}
else{
printf("file %s allocated disk space\n",name[i-1]);
}
break;
case 2:
printf("Enter the name of the file to be deleted\n");
scanf("%s",&del[0]);
printf("file %s deleted\n",&del[0]);
f--;
f--;
break;
case 3:
printf("File Allocation Table\n");
printf("File Name Start Block Length\n");
if(f==2){
printf("%s %d %d\n",name[0],start[0],length[0]);
}
for(int k=0,n=1;k<num && n<num ;k++,n++){
if(start[k+1]<=start[k] || start[k+1]>=length[k]){
printf("%s %d %d\n",name[n],start[n],length[n]);
}
}
break;
case 4:
exit(1);
default:
printf("invalid");
}
getchar();
}
return 0;
}

OUTPUT
Contiguous file allocation
1.File Creation
2.File Deletion
3.Display File Allocation Table
4.Exit
Enter your choice
2
Enter the name of the file to be deleted
java
file java deleted
Contiguous file allocation
1.File Creation
2.File Deletion
3.Display File Allocation Table
4.Exit
Enter your choice

Viva question

1.​ What is Contiguous Memory Allocation in Operating Systems?

Contiguous memory allocation refers to a memory management technique in which whenever


there occurs a request by a user process for the memory, one of the sections of the contiguous
memory block would be given to that process, in accordance with its requirement.

2.​ Types of Partitions

Contiguous memory allocation can be achieved when we divide the memory into the following
types of partitions:

1. Fixed-Sized Partitions
Another name for this is static partitioning. In this case, the system gets divided into multiple
fixed-sized partitions. In this type of scheme, every partition may consist of exactly one
process. This very process limits the extent at which multiprogramming would occur, since the
total number of partitions decides the total number of processes. Read more on fixed-sized
partitions here.

2. Variable-Sized Partitions
Dynamic partitioning is another name for this. The scheme allocation in this type of partition
is done dynamically. Here, the size of every partition isn’t declared initially. Only once we
know the process size, will we know the size of the partitions. But in this case, the size of the
process and the partition is equal; thus, it helps in preventing internal fragmentation.

3.​ Advantages of Contiguous Memory Allocation


●​ Simplicity − Contiguous memory allocation is a relatively simple and straightforward
technique for memory management. It requires less overhead and is easy to implement.
●​ Efficiency − Contiguous memory allocation is an efficient technique for memory
management. Once a process is allocated contiguous memory, it can access the entire
memory block without any interruption.
●​ Low fragmentation − Since the memory is allocated in contiguous blocks, there is a lower
risk of memory fragmentation. This can result in better memory utilization, as there is less
memory wastage.
ii. Linked –list(using linked-list)
Linked List allocation solves all problems of contiguous allocation. In linked list allocation, each file is
considered as the linked list of disk blocks. However, the disks blocks allocated to a particular file
need not to be contiguous on the disk. Each disk block allocated to a file contains a pointer which
points to the next disk block allocated to the same file.

Write a Program to implement Linked File Allocation

#include <stdio.h>
#include <stdlib.h>

struct node {
int block_number;
struct node *next;
};

struct file {
int file_size;
struct node *head;
};

void create_file(struct file *f) {


f->file_size = 0;
f->head = NULL;
}

void add_block(struct file *f, int block_number) {


struct node *new_node = (struct node *)malloc(sizeof(struct node));
new_node->block_number = block_number;
new_node->next = f->head;
f->head = new_node;
f->file_size++;
}

void delete_block(struct file *f, int block_number) {


struct node *current_node = f->head;
struct node *previous_node = NULL;

while (current_node != NULL && current_node->block_number != block_number) {


previous_node = current_node;
current_node = current_node->next;
}

if (current_node == NULL) {
printf("Block not found.\n");
return;
}

if (previous_node == NULL) {
f->head = current_node->next;
} else {
previous_node->next = current_node->next;
}

free(current_node);
f->file_size--;
}

void print_file(struct file *f) {


struct node *current_node = f->head;

while (current_node != NULL) {


printf("%d ", current_node->block_number);
current_node = current_node->next;
}

printf("\n");
}

int main() {
struct file f;

create_file(&f);

add_block(&f, 10);
add_block(&f, 20);
add_block(&f, 30);

print_file(&f);

delete_block(&f, 20);

print_file(&f);

return 0;
}

OUTPUT

30 20 10
30 10

...Program finished with exit code 0

Press ENTER to exit console.

Viva Question

1.​ What is Linked List Allocation?


Linked List allocation solves all problems of contiguous allocation. In linked list allocation,
each file is considered as the linked list of disk blocks. However, the disks blocks allocated to
a particular file need not to be contiguous on the disk. Each disk block allocated to a file
contains a pointer which points to the next disk block allocated to the same file.
2.​ What are the Advantages of linked list allocation?
1.​ There is no external fragmentation with linked allocation.
2.​ Any free block can be utilized in order to satisfy the file block requests.
3.​ File can continue to grow as long as the free blocks are available.
4.​ Directory entry will only contain the starting block address.

3.​ What are the disadvantage of linked list allocation?


1.​ Random Access is not provided.
2.​ Pointers require some space in the disk blocks.
3.​ Any of the pointers in the linked list must not be broken otherwise the file will get corrupted.
4.​ Need to traverse each block.
ii. Indirect allocation (indexing)
Instead of maintaining a file allocation table of all the disk pointers, Indexed allocation scheme
stores all the disk pointers in one of the blocks called as indexed block. Indexed block doesn't hold
the file data, but it holds the pointers to all the disk blocks allocated to that particular file. Directory
entry will only contain the index block address.

Write a Program to implement Indexed File Allocation


#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

int files[50], indexBlock[50], indBlock, n;


void recurse1();
void recurse2();

void recurse1(){
printf("Enter the index block: ");
scanf("%d", &indBlock);
if (files[indBlock] != 1){
printf("Enter the number of blocks and the number of files needed for the
index %d on the disk: ", indBlock);
scanf("%d", &n);
}
else{
printf("%d is already allocated\n", indBlock);
recurse1();
}
recurse2();
}

void recurse2(){
int ch;
int flag = 0;
for (int i=0; i<n; i++){
scanf("%d", &indexBlock[i]);
if (files[indexBlock[i]] == 0)
flag++;
}
if (flag == n){
for (int j=0; j<n; j++){
files[indexBlock[j]] = 1;
}
printf("Allocated\n");
printf("File Indexed\n");
for (int k=0; k<n; k++){
printf("%d ------> %d : %d\n", indBlock, indexBlock[k],
files[indexBlock[k]]);
}
}
else{
printf("File in the index is already allocated\n");
printf("Enter another indexed file\n");
recurse2();
}
printf("Do you want to enter more files?\n");
printf("Enter 1 for Yes, Enter 0 for No: ");
scanf("%d", &ch);
if (ch == 1)
recurse1();
else
exit(0);
return;
}
int main()
{
for(int i=0;i<50;i++)
files[i]=0;

recurse1();
return 0;
}

OUTPUT
Enter the index block: 5
Enter no of blocks needed and no of files for the index 5 on the disk :
4
1 2 3 4
Allocated
File Indexed
5-------->1 : 1
5-------->2 : 1
5-------->3 : 1
5-------->4 : 1
Do you want to enter more file(Yes - 1/No - 0)1
Enter the index block: 4
4 index is already allocated
Enter the index block: 6
Enter no of blocks needed and no of files for the index 6 on the disk :
2
7 8
A5llocated
File Indexed
6-------->7 : 1
6-------->8 : 1
Do you want to enter more file(Yes - 1/No - 0)
Enter 1 for Yes, Enter 0 for No:

Viva Question

1.​ What is indexed allocation?


Indexed allocation is a disk allocation method that ensures efficient disk space utilization and
faster access to disk blocks.
2.​ How does indexed allocation work?
In indexed allocation, all the pointers to scattered blocks are placed together in one
location. This location is called the index block. Each file in memory has its index block. The
index block contains a list of disk block addresses where the file's data blocks are stored.
3.​ What are some advantages of indexed allocation?
Some advantages of indexed allocation include:
●​ No external fragmentation
●​ No file size limitation
●​ Faster access to disk blocks
●​ Supports direct access
●​ A bad data block causes the lost of only that block

4.​ What are some disadvantages of indexed allocation?


Some disadvantages of indexed allocation include:
●​ Trade-off for selecting the index block size
●​ Pointer overhead
5. Implementation of contiguous allocation techniques:

i.​ Worst-fit
Worst-fit memory allocation is an algorithm that searches for the largest free memory block that
can accommodate the request size. It tries to maximize the size of the remaining free memory after
the allocation, hoping that it can be used for future requests. However, this algorithm can also cause
fragmentation, as it may split a large block into a small allocated block and a large free block,
leading to internal fragmentation. Also, it can degrade the performance of the system, as it may
have to search for the largest block repeatedly or maintain a sorted list of free blocks.

Write a program implement contiguous Allocation for Worst-fit

#include <stdio.h>

void implimentWorstFit(int blockSize[], int blocks, int processSize[], int


processes)
{
// This will store the block id of the allocated block to a process
int allocation[processes];

// initially assigning -1 to all allocation indexes


// means nothing is allocated currently
for(int i = 0; i < processes; i++){
allocation[i] = -1;
}

// pick each process and find suitable blocks


// according to its size ad assign to it
for (int i=0; i<processes; i++)
{

int indexPlaced = -1;


for (int j=0; j<blocks; j++)
{
if (blockSize[j] >= processSize[i])
{
// place it at the first block fit to accomodate process
if (indexPlaced == -1)
indexPlaced = j;

// if any future block is larger than the current block where


// process is placed, change the block and thus indexPlaced
else if (blockSize[indexPlaced] < blockSize[j])
indexPlaced = j;
}
}

// If we were successfully able to find block for the process


if (indexPlaced != -1)
{
// allocate this block j to process p[i]
allocation[i] = indexPlaced;

// Reduce available memory for the block


blockSize[indexPlaced] -= processSize[i];
}
}
printf("\nProcess No.\tProcess Size\tBlock no.\n");
for (int i = 0; i < processes; i++)
{
printf("%d \t\t\t %d \t\t\t", i+1, processSize[i]);
if (allocation[i] != -1)
printf("%d\n",allocation[i] + 1);
else
printf("Not Allocated\n");
}
}

// Driver code
int main()
{
int blockSize[] = {5, 4, 3, 6, 7};
int processSize[] = {1, 3, 5, 3};
int blocks = sizeof(blockSize)/sizeof(blockSize[0]);
int processes = sizeof(processSize)/sizeof(processSize[0]);

implimentWorstFit(blockSize, blocks, processSize, processes);

return 0 ;
}

OUTPUT

Process No. Process Size Block no.

1 1 5

2 3 4

3 5 5

4 3 1

Viva Question

1.​ Write the Advantages of Worst-Fit Allocation : ​


Since this process chooses the largest hole/partition, therefore there will be large internal
fragmentation. Now, this internal fragmentation will be quite big so that other small processes
can also be placed in that leftover partition.
2.​ Write the Disadvantages of Worst-Fit Allocation : ​
It is a slow process because it traverses all the partitions in the memory and then selects the
largest partition among all the partitions, which is a time-consuming process.
ii.​ Best-Fit
Best-Fit Allocation is a memory allocation technique used in operating systems to allocate
memory to a process. In Best-Fit, the operating system searches through the list of free blocks of
memory to find the block that is closest in size to the memory request from the process.

Write a Program to implement contiguous Allocation for Best-Fit

#include<stdio.h>

void main()
{
int fragment[20],b[20],p[20],i,j,nb,np,temp,lowest=9999;
static int barray[20],parray[20];
printf("\n\t\t\tMemory Management Scheme - Best Fit");
printf("\nEnter the number of blocks:");
scanf("%d",&nb);
printf("Enter the number of processes:");
scanf("%d",&np);
printf("\nEnter the size of the blocks:-\n");
for(i=1;i<=nb;i++)
{
printf("Block no.%d:",i);
scanf("%d",&b[i]);
}
printf("\nEnter the size of the processes :-\n");
for(i=1;i<=np;i++)
{
printf("Process no.%d:",i);
scanf("%d",&p[i]);
}
for(i=1;i<=np;i++)
{
for(j=1;j<=nb;j++)
{
if(barray[j]!=1)
{
temp=b[j]-p[i];
if(temp>=0)
if(lowest>temp)
{
parray[i]=j;
lowest=temp;
}
}
}
fragment[i]=lowest;
barray[parray[i]]=1;
lowest=10000;
}
printf("\nProcess_no\tProcess_size\tBlock_no\tBlock_size\tFragment");
for(i=1;i<=np && parray[i]!=0;i++)
printf("\n%d\t\t%d\t\t%d\t\t%d\t\t%d",i,p[i],parray[i],b[parray[i]],fragment[i]);
}
OUTPUT

Enter the number of blocks:5

Enter the number of processes:4

Enter the size of the blocks:-

Block no.1:10

Block no.2:15

Block no.3:5

Block no.4:9

Block no.5:3

Enter the size of the processes :-

Process no.1:1

Process no.2:4

Process no.3:7

Process no.4:12

Process_no Process_size Block_no Block_size Fragment

1 ​ 1​ 5 ​ 3 2

2 4 3 5 1

3 7 4 9 2

4 12 2 15 3

Viva Question

1.​ How to use the best fit algorithm:


●​ Initialize all memory blocks as free
●​ Input memory blocks and processes with sizes
●​ Search the entire list of free partitions
●​ Find the smallest hole that is adequate
●​ Split the block into two parts
●​ Allocate the portion to the process
●​ Reserve the remaining free block
2.​ What are the best fit disadvantages?
●​ It's slower because it scans the entire list each time
●​ The holes produced may be too small to load a process
iii.​ First-Fit

First-Fit Allocation is a memory allocation technique used in operating systems to allocate memory
to a process. In First-Fit, the operating system searches through the list of free blocks of memory,
starting from the beginning of the list, until it finds a block that is large enough to accommodate the
memory request from the process. Once a suitable block is found, the operating system splits the
block into two parts: the portion that will be allocated to the process, and the remaining free block.

Write a Program to implement contiguous Allocation for First-Fit

#include<stdio.h>
void main()
{
​ int bsize[10], psize[10], bno, pno, flags[10], allocation[10], i, j;
​ for(i = 0; i < 10; i++)
​ {
​ ​ flags[i] = 0;
​ ​ allocation[i] = -1;
​ }
​ printf("Enter no. of blocks: ");
​ scanf("%d", &bno);
​ printf("\nEnter size of each block: ");
​ for(i = 0; i < bno; i++)
​ ​ scanf("%d", &bsize[i]);
​ printf("\nEnter no. of processes: ");
​ scanf("%d", &pno);
​ printf("\nEnter size of each process: ");
​ for(i = 0; i < pno; i++)
​ ​ scanf("%d", &psize[i]);
​ for(i = 0; i < pno; i++) //allocation as per first fit
​ ​ for(j = 0; j < bno; j++)
​ ​ ​ if(flags[j] == 0 && bsize[j] >= psize[i])
​ ​ ​ {
​ ​ ​ ​ allocation[j] = i;
​ ​ ​ ​ flags[j] = 1;
​ ​ ​ ​ break;
​ ​ ​ }
​ //display allocation details
​ printf("\nBlock no.\tsize\t\tprocess no.\t\tsize");
​ for(i = 0; i < bno; i++)
​ {
​ ​ printf("\n%d\t\t%d\t\t", i+1, bsize[i]);
​ ​ if(flags[i] == 1)
​ ​ ​ printf("%d\t\t\t%d",allocation[i]+1,psize[allocation[i]]);
​ ​ else
​ ​ ​ printf("Not allocated");
​ }
}
Output
Enter size of each block: 8

10

12

Enter no. of processes: 3

Enter size of each process: 56

14

12

Block no. size process no. size

1 8 Not allocated

2 10 Not allocated

3 12 3 12

Viva Question

1.​ What is First Fit Algorithm in OS?


The First Fit in OS is an algorithm is a memory allocation technique used in operating systems to
allocate available memory partitions to processes. It is part of the broader category of
partitioning algorithms, where the system's memory is divided into fixed-sized or variable-sized
partitions. When a process requests memory, the First Fit Algorithm searches for the first
available partition that can accommodate the process's memory requirements. Once found, the
process is allocated memory in that partition.

2.​ Advantages of First Fit Algorithm


The First Fit in OS has several advantages:

●​ Simplicity: It is easy to implement and understand, making it a suitable choice for systems with
limited computational resources.
●​ Quick Allocation: Since it starts searching from the beginning of the memory, it can allocate
memory relatively quickly when there is a free partition at the beginning.
●​ Minimal Fragmentation: In cases where processes have similar memory requirements, First Fit
in OS can lead to minimal fragmentation as it uses the first available partition that fits the
process.

3.​ Disadvantages of First Fit Algorithm


While the First Fit in OS has its merits, it also has some drawbacks:

●​ Fragmentation: Over time, the First Fit Algorithm can lead to both external and internal
fragmentation. External fragmentation occurs when there are many small free memory
partitions scattered across the memory, making it challenging to allocate larger processes.
Internal fragmentation arises when a process is assigned an excessive amount of memory,
resulting in space wastage.
●​ Inefficient Memory Usage: It may not always allocate memory in the most optimal way, leading
to inefficient memory utilization.
●​ Suboptimal for Large Processes: For large processes, the First Fit Algorithm may not be the best
choice, as it may have to search through many smaller partitions before finding a suitable one,
resulting in slower allocation times.
6. Calculation of external and internal fragmentation

Internal Fragmentation: ​
Internal fragmentation happens when the memory is split into mounted-sized blocks. Whenever
a method is requested for the memory, the mounted-sized block is allotted to the method. In the
case where the memory allotted to the method is somewhat larger than the memory requested,
then the difference between allotted and requested memory is called internal
fragmentation. We fixed the sizes of the memory blocks, which has caused this issue. If we use
dynamic partitioning to allot space to the process, this issue can be solved. ​

External Fragmentation: ​
External fragmentation happens when there’s a sufficient quantity of area within the memory to
satisfy the memory request of a method. However, the process’s memory request cannot be
fulfilled because the memory offered is in a non-contiguous manner. Whether you apply a first-fit
or best-fit memory allocation strategy it’ll cause external fragmentation.

i.​ Free space list of blocks from system

#include <stdio.h>
#include <stdlib.h>

main()
{
int ms, mp[10], i,
temp, n = 0;
char ch = 'y';
printf("\nEnter the total memory available (in Bytes)-- ");
scanf("%d", &ms);
temp = ms;
for (i = 0; ch == 'y'; i++, n++)
{
printf("\nEnter memory required for process %d (in Bytes) -- ",
i + 1);
scanf("%d", &mp[i]);
if (mp[i] <= temp)
{
printf("\nMemory is allocated for Process %d ", i + 1);
temp = temp - mp[i];
}
else
{
printf("\nMemory is Full");
break;
}
printf("\nDo you want to continue(y/n) -- ");
scanf(" %c", &ch);
}
printf("\n\nTotal Memory Available -- %d", ms);
printf("\n\n\tPROCESS\t\t MEMORY ALLOCATED ");
for (i = 0; i < n; i++)
printf("\n \t%d\t\t%d", i + 1, mp[i]);
printf("\n\nTotal Memory Allocated is %d", ms - temp);
printf("\nTotal External Fragmentation is %d", temp);
}

Output

Enter the total memory available (in Bytes)—50


Enter memory required for process 1 (in Bytes) -- 40

Memory is allocated for Process 1


Do you want to continue(y/n) -- y

Enter memory required for process 2 (in Bytes) -- 20

Memory is Full

Total Memory Available -- 50

PROCESS MEMORY ALLOCATED


1 40

Total Memory Allocated is 40


Total External Fragmentation is 10

...Program finished with exit code 0


Press ENTER to exit console.
ii.​ List process file from the system
#include <stdio.h>
#include <stdlib.h>

main()
{
int ms, mp[10], i,
temp, n = 0;
char ch = 'y';
printf("\nEnter the total memory available (in Bytes)-- ");
scanf("%d", &ms);
temp = ms;
for (i = 0; ch == 'y'; i++, n++)
{
printf("\nEnter memory required for process %d (in Bytes) -- ", i + 1);
scanf("%d", &mp[i]);
if (mp[i] <= temp)
{
printf("\nMemory is allocated for Process %d ", i + 1);
temp = temp - mp[i];
}
else
{
printf("\nMemory is Full");
break;
}
printf("\nDo you want to continue(y/n) -- ");
scanf(" %c", &ch);
}
printf("\n\nTotal Memory Available -- %d", ms);
printf("\n\n\tPROCESS\t\t MEMORY ALLOCATED ");
for (i = 0; i < n; i++)
printf("\n \t%d\t\t%d", i + 1, mp[i]);
printf("\n\nTotal Memory Allocated is %d", ms - temp);
printf("\nTotal External Fragmentation is %d", temp);
}

Output
Enter the total memory available (in Bytes)-- 40

Enter memory required for process 1 (in Bytes) -- 20

Memory is allocated for Process 1


Do you want to continue(y/n) -- y

Enter memory required for process 2 (in Bytes) -- 10

Memory is allocated for Process 2

Do you want to continue(y/n) -- n

Total Memory Available -- 40

PROCESS MEMORY ALLOCATED

1 20

2 10

Total Memory Allocated is 30

Total External Fragmentation is 10

...Program finished with exit code 0

Press ENTER to exit console.

Viva Question

1.​ What is internal fragmentation?


Occurs when memory is split into fixed-sized blocks. If the allocated memory is larger than the
requested memory, the difference is called internal fragmentation.
2.​ What is external fragmentation?
Occurs when variable-sized memory blocks are allocated to a method. This can happen when a
process or process swapping breaks free memory into small pieces
3.​ What are the solutions to internal and external fragmentation?:
Here are some solutions to fragmentation:
Internal fragmentation: The solution to internal fragmentation is the best-fit block.
External fragmentation: The solution to external fragmentation is compaction and
paging. Compaction is a technique where all the memory contents of memory are shuffled and
all free memory is put together in one large block.
4.​ What is paging?
Paging is a memory management technique that allows programs to use virtual memory
addresses for efficient memory allocation. It can also enhance reliability by reducing the chances
of memory errors and failures.
Implementation of compaction for the continually changing memory layout and calculate total
movement of data

#include <stdio.h>

#include <stdlib.h>

void create(int, int);

void del(int);

void compaction();

void display();

int fname[10], fsize[10], fstart[10], freest[10], freesize[10], m = 0, n = 0,


start;

int main()

int name, size, ch, i;

int *ptr;

// clrscr();

ptr = (int *)malloc(sizeof(int) * 100);

start = freest[0] = (int)ptr;

freesize[0] = 500;
printf("\n\n");

printf(" Free start address Free Size \n\n");

for (i = 0; i <= m; i++)

printf(" %d %d\n", freest[i], freesize[i]);

printf("\n\n");

while (1)

printf("1.Create.\n");

printf("2.Delete.\n");

printf("3.Compaction.\n");

printf("4.Exit.\n");

printf("Enter your choice: ");

scanf("%d", &ch);

switch (ch)

case 1:

printf("\nEnter the name of file: ");

scanf("%d", &name);
printf("\nEnter the size of the file: ");

scanf("%d", &size);

create(name, size);

break;

case 2:

printf("\nEnter the file name which u want to delete: ");

scanf("%d", &name);

del(name);

break;

case 3:

compaction();

printf("\nAfter compaction the tables will be:\n");

display();

break;

case 4:

exit(1);

default:

printf("\nYou have entered a wrong choice.\n");


}

void create(int name, int size)

int i, flag = 1, j, a;

for (i = 0; i <= m; i++)

if (freesize[i] >= size)

a = i, flag = 0;

if (!flag)

for (j = 0; j < n; j++)

n++;

fname[j] = name;

fsize[j] = size;

fstart[j] = freest[a];
freest[a] = freest[a] + size;

freesize[a] = freesize[a] - size;

printf("\n The memory map will now be: \n\n");

display();

else

printf("\nNo enough space is available.System compaction......");

flag = 1;

compaction();

display();

for (i = 0; i <= m; i++)

if (freesize[i] >= size)

a = i, flag = 0;

if (!flag)

for (j = 0; j < n; j++)

;
n++;

fname[j] = name;

fsize[j] = size;

fstart[j] = freest[a];

freest[a] += size;

freesize[a] -= size;

printf("\n The memory map will now be: \n\n");

display();

else

printf("\nNo enough space.\n");

void del(int name)

int i, j, k, flag = 1;

for (i = 0; i < n; i++)

if (fname[i] == name)
break;

if (i == n)

flag = 0;

printf("\nNo such process exists......\n");

else

m++;

freest[m] = fstart[i];

freesize[m] = fsize[i];

for (k = i; k < n; k++)

fname[k] = fname[k + 1];

fsize[k] = fsize[k + 1];

fstart[k] = fstart[k + 1];

n--;
}

if (flag)

printf("\n\n After deletion of this process the memory map will be : \n\n");

display();

void compaction()

int i, j, size1 = 0, f_size = 0;

if (fstart[0] != start)

fstart[0] = start;

for (i = 1; i < n; i++)

fstart[i] = fstart[i - 1] + fsize[i - 1];

else

{
for (i = 1; i < n; i++)

fstart[i] = fstart[i - 1] + fsize[i - 1];

f_size = freesize[0];

for (j = 0; j <= m; j++)

size1 += freesize[j];

freest[0] = freest[0] - (size1 - f_size);

freesize[0] = size1;

m = 0;

void display()

int i;

printf("\n *** MEMORY MAP TABLE *** \n");

printf("\n\nNAME SIZE STARTING ADDRESS \n\n");

for (i = 0; i < n; i++)

printf(" %d%10d%10d\n", fname[i], fsize[i], fstart[i]);

printf("\n\n");
printf("\n\n*** FREE SPACE TABLE ***\n\n");

printf("FREE START ADDRESS FREE SIZE \n\n");

for (i = 0; i <= m; i++)

printf(" %d %d\n", freest[i], freesize[i]);

Output
The memory map will now be:

*** MEMORY MAP TABLE ***

NAME SIZE STARTING ADDRESS

12 200 2688

*** FREE SPACE TABLE ***

FREE START ADDRESS FREE SIZE

2888 300

FREE START ADDRESS FREE SIZE

2688 500

1.Create.

2.Delete.

3.Compaction.

4.Exit.

Enter your choice: 1

Enter the name of file: 12

Enter the size of the file: 200


Viva Question

1.​ What is compaction technique in memory management?

Compaction is a technique to collect all the free memory present in the form of fragments into
one large chunk of free memory, which can be used to run other processes.

2.​ What is the Purpose of Compaction in Operating System

While allocating memory to process, the operating system often faces a problem when there’s a
sufficient amount of free space within the memory to satisfy the memory demand of a process.
however the process’s memory request can’t be fulfilled because the free memory available is in
a non-contiguous manner, this problem is referred to as external fragmentation. To solve such
kinds of problems compaction technique is used.

3.​ What are the Issues with Compaction?

Although the compaction technique is very useful in making memory utilization efficient and
reduces external fragmentation of memory, the problem with it is that a large amount of time is
wasted in the process and during that time the CPU sits idle hence reducing the efficiency of the
system.

4.​ What are the Advantages of Compaction?


●​ Reduces external fragmentation.
●​ Make memory usage efficient.
●​ Memory becomes contiguous.
●​ Since memory becomes contiguous more processes can be loaded to memory, thereby
increasing scalability of OS.
●​ Fragmentation of file system can be temporarily removed by compaction.
●​ Improves memory utilization as their is less gap between memory blocks.
8. Implementation of resource allocation graph (RAG)

A resource allocation graphs shows which resource is held by which process and which process is
waiting for a resource of a specific kind. It is amazing and straight – forward tool to outline how
interacting processes can deadlock. Therefore, resource allocation graph describe what the
condition of the system as far as process and resources are concern like what number of resources
are allocated and what is the request of each process. Everything can be represented in terms of
graph. One of the benefit of having a graph is, sometimes it is conceivable to see a deadlock straight
forward by utilizing RAG and however you probably won’t realize that by taking a glance at the
table. Yet tables are better if the system contains bunches of process and resource and graph is
better if the system contains less number of process and resource.

Program
#include<stdio.h>

int main()

int np, nr, temp, temp1;

printf("enter number of resources: ");

scanf("%d", &nr);

printf("enter number of processs: ");

scanf("%d", &np);

int rag[nr+np][nr+np];

int i, j;

for(i=0;i<np+nr;i++)

for(j=0; j<np+nr;j++)

rag[i][j]=0;

for(i=0;i<np;i++)

printf("enter the number of resources process %d, holding", i);

scanf("%d", &temp);

for(j=0; j<temp;j++)

{
printf("enter the ressorce number process %d holding: ", j);

scanf("%d", &temp1);

rag[np+temp1][i]=1;

printf("enter the number of resources process %d, requesting", i);

scanf("%d", &temp);

for(j=0; j<temp;j++)

printf("enter the ressorce number process %d requesting: ", i);

scanf("%d", &temp1);

rag[i][np+temp1]=1;

for(i=0;i<np+nr;i++)

for(j=0; j<np+nr;j++)

printf("%d ", rag[i][j]);

printf("\n ");

return 0;

Output
Process Resource Resource
ID Holding Rquesting
Process R0 R2
0
Process R1 R0
1
Process R2 R1
2

Output:

P0 P1 P2 R0 R1 R2

P0 0 0 0 0 0 1
P1 0 0 0 1 0 0

P2 0 0 0 0 1 0

R0 1 0 0 0 0 0

R1 0 1 0 0 0 0

R2 0 0 1 0 0 0

Viva Question

1.​ What is a Resource Allocation Graph (RAG)?


A RAG is a directed graph that is used to illustrate the state of a system graphically and describe
deadlocks. It contains all the information about all of the activities that are holding or waiting for
resources. It also provides information on all instances of all resources, whether available or in use
by processes.

2.​ What are the two types of edges in a RAG?


A Resource Allocation Graph (RAG) has two types of edges:
●​ Assign edges
These edges point from the resource to the process, and indicate that a resource has been
assigned to a process.
●​ Request edges
These edges point from the process to the resource, and indicate that a process may want a
resource in the future.

3.​ How does a cycle in a RAG indicate deadlock?


In a Resource Allocation Graph (RAG), a cycle indicates deadlock if all resources in the loop have the
same instance. This is a required condition, but not sufficient, for deadlock in RAGs with
multi-instanced resource types.

4.​ What is the disadvantage of a RAG?


RAG is useful when we have less number of processes and resources.

With large number of resources or processes, it is better to store data in a table rather than RAG.
If there are large number of resources or processes, then the graph will be difficult to understand and will
become complex.
9.​ Implementation of Banker’s algorithm

Banker’s Algorithm
The Banker’s algorithm is a resource allocation and deadlock avoidance algorithm that 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.
Following Data structures are used to implement the Banker’s Algorithm:
Let ‘n’ be the number of processes in the system and ‘m’ be the number of resources types.
Available:
●​ It is a 1-d array of size ‘m’ indicating the number of available resources of each type.
●​ Available[ j ] = k means there are ‘k’ instances of resource type Rj
Max:
●​ It is a 2-d array of size ‘n*m’ that defines the maximum demand of each process in a
system.
●​ Max[ i, j ] = k means process Pi may request at most ‘k’ instances of resource type Rj.
Allocation:
●​ It is a 2-d array of size ‘n*m’ that defines the number of resources of each type
currently allocated to each process.
●​ Allocation[ i, j ] = k means process Pi is currently allocated ‘k’ instances of resource
type Rj
Need :
●​ It is a 2-d array of size ‘n*m’ that indicates the remaining resource need of each
process.
●​ Need [ i, j ] = k means process Pi currently need ‘k’ instances of resource type Rj
for its execution.
●​ Need [ i, j ] = Max [ i, j ] – Allocation [ i, j ]
Allocationi specifies the resources currently allocated to process Pi and Needi specifies the
additional resources that process Pi may still request to complete its task.
Banker’s algorithm consists of Safety algorithm and Resource request algorithm.
Safety Algorithm
1) Let Work and Finish be vectors of length ‘m’ and ‘n’ respectively.
Initialize: Work = Available
Finish[i] = false; for i=1, 2, 3, 4....n
2) Find an i such that both
a) Finish[i] = false
b) Needi <= Work
if no such i exists goto step (4)
3) Work = Work + Allocation[i]
Finish[i] = true
goto step (2)
4) if Finish [i] = true for all i
then the system is in a safe state
Resource-Request Algorithm
Let Requesti be the request array for process Pi. Requesti [j] = k means process Pi wants k
instances of resource type Rj. When a request for resources is made by process Pi, the
following actions are taken:
1) If Requesti <= Needi
Goto step (2) ; otherwise, raise an error condition, since the process has exceeded its
maximum claim.
2) If Requesti <= Available
Goto step (3); otherwise, Pi must wait, since the resources are not available.
3) Have the system pretend to have allocated the requested resources to process Pi by
modifying the state as
follows:
Available = Available – Requesti
Allocationi = Allocationi + Requesti
Needi = Needi– Requesti

Example program for Implementation of Banker’s algorithm

#include <stdio.h>
int main()
{
int n, m, i, j, k;
n = 5;
m = 3;
int alloc[5][3] = { { 0, 1, 0 },
{ 2, 0, 0 },
{ 3, 0, 2 },
{ 2, 1, 1 },
{ 0, 0, 2 } };
int max[5][3] = { { 7, 5, 3 },
{ 3, 2, 2 },
{ 9, 0, 2 },
{ 2, 2, 2 },
{ 4, 3, 3 } };
int avail[3] = { 3, 3, 2 };
int f[n], ans[n], ind = 0;
for (k = 0; k < n; k++)
{
f[k] = 0;
}
int need[n][m];

for (i = 0; i < n; i++)


{
for (j = 0; j < m; j++)
need[i][j] = max[i][j] - alloc[i][j];
}
int y = 0;
for (k = 0; k < 5; k++)
{
for (i = 0; i < n; i++)

if (f[i] == 0)
{

int flag = 0;
for (j = 0; j < m; j++)

if (need[i][j] > avail[j])

flag = 1;
break;
}
}
if (flag == 0)

ans[ind++] = i;
for (y = 0; y < m; y++)
avail[y] += alloc[i][y];
f[i] = 1;
}
}
}
}
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;
}
Input:

Output:
Following is the SAFE Sequence
P1 -> P3 -> P4 -> P0 -> P2

Viva Question

1.​ What is the Banker's Algorithm


The Banker's Algorithm is a deadlock avoidance algorithm that is used for deadlock detection. It is a
combination of two algorithms:
●​ Safety algorithm: Checks if the system is in a safe state.
●​ Resource request algorithm: Checks if requests can be granted safely.

2.​ What does the Banker's Algorithm use


The Banker's Algorithm uses the following data structures:
●​ Available array: Represents the number of resources.
●​ Max array: Represents the maximum number of resources for each process.
●​ Allocation array: Represents the resources currently allocated to each process.
●​ Need array: Represents the remaining need of resources for each process.

3.​ How does the Banker's Algorithm work


The Banker's Algorithm tests for safety by simulating the allocation for predetermined maximum
possible amounts of all resources. It then makes an ``s-state'' check to test for possible activities,
before deciding whether allocation should be allowed to continue.
10.​Conversion of resource allocation graph (RAG) to wait for graph (WFG) for each type of
method used for storing graph.
#include<stdio.h>

int main()

int np, nr, temp, temp1;

printf("enter number of resources: ");

scanf("%d", &nr);

printf("enter number of processs: ");

scanf("%d", &np);

int rag[nr+np][nr+np];

int i, j;

for(i=0;i<np+nr;i++)

for(j=0; j<np+nr;j++)

rag[i][j]=0;

for(i=0;i<np;i++)

printf("enter the number of resources process %d, holding", i);

scanf("%d", &temp);

for(j=0; j<temp;j++)

printf("enter the ressorce number process %d holding: ", j);

scanf("%d", &temp1);

rag[np+temp1][i]=1;

printf("enter the number of resources process %d, requesting", i);


scanf("%d", &temp);

for(j=0; j<temp;j++)

printf("enter the ressorce number process %d requesting: ", i);

scanf("%d", &temp1);

rag[i][np+temp1]=1;

for(i=0;i<np+nr;i++)

for(j=0; j<np+nr;j++)

printf("%d ", rag[i][j]);

printf("\n ");

int wfg[np][np];

for(i=0;i<np;i++)

for(j=0; j<np;j++)

wfg[i][j]=0;

int k;

for(i=0;i<np;i++)

for(j=np;j<np + nr; j++)

{
if(rag[i][j] == 1)

for(k=0;k<np;k++)

if(rag[j][k] == 1)

wfg[i][k] = 1;

for(i=0;i<np;i++)

for(j=0; j<np;j++)

printf("%d ", wfg[i][j]);

printf("\n ");

return 0;

Input:

Process ID Resource Resource


Holding Requesting
Process 0 R0 R2
Process 1 R1 R0
Process 2 R2 R1
Output:
P0 P1 P2 R0 R1 R2

P0 0 0 0 0 0 1

P1 0 0 0 1 0 0

P2 0 0 0 0 1 0

R0 1 0 0 0 0 0

R1 0 1 0 0 0 0

R2 0 0 1 0 0 0

WFG
P0 P1 P2

P0 0 0 1

P1 1 0 0

P2 0 1 0

Viva Question

1.​ What is the difference between rag and WFG?

There are two kinds of traditional models for detecting deadlocks: Resource-Allocation Graph (RAG)
and Wait-for Graph (WFG). In general, RAG is used for detecting deadlocks of standalone devices ,
and WFG is used for detecting deadlocks of distributed systems

2.​ How do you obtain a wait-for graph from Resource Allocation Graph?

A wait-for graph can be constructed from a resource-allocation graph by eliminating the resources
and collapsing the associated edges, as shown in the figure below. An arc from Pi to Pj in a wait-for
graph indicates that process Pi is waiting for a resource that process Pj is currently holding.

3.​ What is the purpose of a wait-for graph?

A wait-for graph in computer science is a directed graph used for deadlock detection in operating
systems and relational database systems.
11.​ Implement the solution for Bounded Buffer (producer-consumer)problem using inter process
communication techniques-Semaphores
#include <stdio.h>

#include <stdlib.h>

// Initialize a mutex to 1

Int mutex = 1;

// Number of full slots as 0

int full = 0;

// Number of empty slots as size

// of buffer

int empty = 10, x = 0;

// Function to produce an item and

// add it to the buffer

void producer()

​ // Decrease mutex value by 1

​ --mutex;

​ // Increase the number of full

​ // slots by 1

​ ++full;

​ // Decrease the number of empty

​ // slots by 1

​ --empty;

​ // Item produced

​ x++;

​ printf("\nProducer produces"
​ ​ "item %d",

​ ​ x);

​ // Increase mutex value by 1

​ ++mutex;

// Function to consume an item and

// remove it from buffer

void consumer()

​ // Decrease mutex value by 1

​ --mutex;

​ // Decrease the number of full

​ // slots by 1

​ --full;

​ // Increase the number of empty

​ // slots by 1

​ ++empty;

​ printf("\nConsumer consumes "

​ ​ "item %d",

​ ​ x);

​ x--;

​ // Increase mutex value by 1

​ ++mutex;

// Driver Code

int main()

{
​ int n, i;

​ printf("\n1. Press 1 for Producer"

​ ​ "\n2. Press 2 for Consumer"

​ ​ "\n3. Press 3 for Exit");

// Using '#pragma omp parallel for'

// can give wrong value due to

// synchronization issues.

// 'critical' specifies that code is

// executed by only one thread at a

// time i.e., only one thread enters

// the critical section at a given time

#pragma omp critical

​ for (i = 1; i> 0; i++) {

​ ​ printf("\nEnter your choice:");

​ ​ scanf("%d", &n);

​ ​ // Switch Cases

​ ​ switch (n) {

​ ​ case 1:

​ ​ ​ // If mutex is 1 and empty

​ ​ ​ // is non-zero, then it is

​ ​ ​ // possible to produce

​ ​ ​ if ((mutex == 1)

​ ​ ​ ​ && (empty != 0)) {

​ ​ ​ ​ producer();

​ ​ ​ }

​ ​ ​ // Otherwise, print buffer


​ ​ ​ // is full

​ ​ ​ else {

​ ​ ​ ​ printf("Buffer is full!");

​ ​ ​ }

​ ​ ​ break;

​ ​ case 2:

​ ​ ​ // If mutex is 1 and full

​ ​ ​ // is non-zero, then it is

​ ​ ​ // possible to consume

​ ​ ​ if ((mutex == 1)

​ ​ ​ ​ && (full != 0)) {

​ ​ ​ ​ consumer();

​ ​ ​ }

​ ​ ​ // Otherwise, print Buffer

​ ​ ​ // is empty

​ ​ ​ else {

​ ​ ​ ​ printf("Buffer is empty!");

​ ​ ​ }

​ ​ ​ break;

​ ​ // Exit Condition

​ ​ case 3:

​ ​ ​ exit(0);

​ ​ ​ break;

​ ​ }

​ }

}
Output

Viva Question

1.​ What producer and consumer do?

The producer process will do the following:

1.​ Wait on the empty semaphore.


2.​ Produce an item and put it into the buffer.
3.​ Signal the full semaphore.
The consumer process will do the following:

1.​ Wait on the full semaphore.


2.​ Consume an item from the buffer.
3.​ Signal the empty semaphore.
2.​ How is semaphore used in the producer-consumer problem and the bounded-buffer problem?

In the bounded-buffer problem, you can use one semaphore named data to count the number of
data items in the buffer. Initialize this semaphore to 0. A consumer must wait on this semaphore
before reading from the buffer. A producer will signal this semaphore after writing to the buffer.

3.​ How can the bounded buffer problem be used in real-world applications?

The bounded buffer problem, also known as the producer-consumer problem, is a classic
example of concurrent access to a shared resource. It can be used in real-world applications such
as burger pipelines at McDonald's
12.​Implement the solution for reader-writer problem using inter process communication
techniques-semaphores
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>

#define NUM_READERS 3
#define NUM_WRITERS 2

sem_t mutex, write_mutex;


int data = 0; // Shared resource

void *reader(void *arg) {


int reader_id = *(int *)arg;

while (1) {
// Reader entry section
sem_wait(&mutex);

// Critical section for readers


printf("Reader %d is reading data: %d\n", reader_id, data);

// Reader exit section


sem_post(&mutex);

// Sleep for some time to simulate reading


sleep(rand() % 3);
}
}

void *writer(void *arg) {


int writer_id = *(int *)arg;

while (1) {
// Writer entry section
sem_wait(&write_mutex);

// Critical section for writers


data++; // Increment data to simulate writing
printf("Writer %d is writing data: %d\n", writer_id, data);

// Writer exit section


sem_post(&write_mutex);

// Sleep for some time to simulate writing


sleep(rand() % 5);
}
}

int main() {
pthread_t readers[NUM_READERS], writers[NUM_WRITERS];

int reader_ids[NUM_READERS], writer_ids[NUM_WRITERS];

// Initialize semaphores
sem_init(&mutex, 0, 1);
sem_init(&write_mutex, 0, 1);
// Create reader threads
for (int i = 0; i < NUM_READERS; i++) {
reader_ids[i] = i + 1;
pthread_create(&readers[i], NULL, reader, &reader_ids[i]);
}

// Create writer threads


for (int i = 0; i < NUM_WRITERS; i++) {
writer_ids[i] = i + 1;
pthread_create(&writers[i], NULL, writer, &writer_ids[i]);
}

// Join threads
for (int i = 0; i < NUM_READERS; i++) {
pthread_join(readers[i], NULL);
}

for (int i = 0; i < NUM_WRITERS; i++) {


pthread_join(writers[i], NULL);
}

// Destroy semaphores
sem_destroy(&mutex);
sem_destroy(&write_mutex);

return 0;
}
Output
Reader 1 is reading data: 0
Reader 2 is reading data: 0
Reader 3 is reading data: 0
Writer 1 is writing data: 1
Writer 2 is writing data: 2
Reader 1 is reading data: 2
Reader 2 is reading data: 2
Writer 1 is writing data: 3
Reader 3 is reading data: 3
...

Viva Question

i.​ What is the reader-writer problem of a process using semaphore?


The readers-writer problem is a synchronization issue that occurs when multiple threads try to
access the same shared resource at the same time. The problem arises when balancing the need
for simultaneous access by multiple readers against exclusive access for a single writer.

ii.​ What is a reader and writer class in OS?


Readers and writers are character-based streams. A reader is used to read character-based data
from a data source, and a writer is used to write character-based data.

iii.​ What conditions are generally associated with the readers-writers problem?
The readers-writers problem can be assumed to follow these conditions:
●​ Multiple processes can be either readers and writers.
●​ The shared resource could be a file or a database.
●​ Two processes cannot be allowed to write into shared data parallelly.
●​ Even if one process is writing on data and the other is reading, they cannot be allowed to have
access to shared resources.
●​ Any number of readers may simultaneously read the file.
●​ Only one writer at a time may write to the file.
●​ If a writer is writing to the file, no reader may read it.

You might also like