OS Lab Manual (For Students)
OS Lab Manual (For Students)
(CO-Wise Syllabus)
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:
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.
• 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.
Windows XP is an older operating system, and Microsoft no longer provides official support for
it.
Hardware Requirements:
• Super VGA (800 x 600) or higher resolution video adapter and monitor.
Software Requirements:
Windows 7 and Windows 8 are also older versions of the Windows operating system, and
Microsoft has moved on to newer versions.
• Comes with a set of system utilities, and additional software can be installed.
• Similar to Windows 7, comes with system utilities and supports various applications compatible
with the Windows platform.
2. Execute various UNIX system calls for
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;
}
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
Output
Hello world!, process_id(pid) = 31
Hello world!, process_id(pid) = 32
Viva Question
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
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()
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main()
if (fd == -1) {
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);
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main()
if (fd1 < 0) {
perror("c1");
exit(1);
if (close(fd1) < 0) {
perror("c1");
exit(1);
Output
opened the fd = 3
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()
fd = open("foo.txt", O_RDONLY);
if (fd < 0) {
perror("r1");
exit(1);
sz = read(fd, c, 10);
fd, sz);
c[sz] = '\0';
return 0;
Output
called read(3, c, 10). returned that 10 bytes were read.
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.
temp=bt[i];
bt[i]=bt[pos];
bt[pos]=temp;
temp=p[i];
p[i]=p[pos];
p[pos]=temp;
}
wt[0]=0;
OUTPUT
Enter number of process:4
Viva question
// 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;
//Swapping processes
swap(&p[i], &p[m]);
swap(&b[i], &b[m]);
swap(&index[i],&index[m]);
}
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
Viva question
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.
int i, wt[n];
wt[0]=0;
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
#include <stdlib.h>
typedef struct {
int process_id;
int priority;
int burst_time;
int remaining_time;
} Process;
typedef struct {
Process** processes;
} Queue;
queue->front = -1;
queue->rear = -1;
queue->capacity = capacity;
return queue;
}
if (isQueueFull(queue)) {
return;
if (isQueueEmpty(queue)) {
queue->front = 0;
queue->rear = 0;
} else {
queue->processes[queue->rear] = process;
if (isQueueEmpty(queue)) {
if (queue->front == queue->rear) {
queue->front = -1;
queue->rear = -1;
} else {
return process;
printf("Queue: ");
if (isQueueEmpty(queue)) {
printf("Empty");
} else {
int i = queue->front;
while (i != queue->rear) {
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
Queue* queues[3];
queues[i] = initializeQueue(num_processes);
enqueue(queues[processes[i]->priority], processes[i]);
// Simulation
int time = 0;
while (1) {
if (!isQueueEmpty(currentQueue)) {
currentProcess->remaining_time--;
if (currentProcess->remaining_time > 0) {
enqueue(currentQueue, currentProcess);
} else {
// Free memory allocated for the completed process
free(currentProcess);
displayQueue(queues[i]);
int allQueuesEmpty = 1;
if (!isQueueEmpty(queues[i])) {
allQueuesEmpty = 0;
break;
if (allQueuesEmpty) {
printf("Simulation complete.\n");
break;
time++;
int main() {
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;
printf("Processes:\n");
multilevelQueueScheduling(processes, num_processes);
free(processes[i]);
return 0;
Output
Processes:
Time 0
Executing process 1 from Queue 0
Queue: 3(1)
Time 1
Queue: Empty
Time 2
Queue: 4(2)
Queue: Empty
Time 3
Queue: 4(1)
Queue: 2(1)
Queue: Empty
Time 4
Queue: Empty
Queue: Empty
Queue: Empty
Simulation complete.
free(): double free detected in tcache 2
Aborted
Viva Question
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.
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
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
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:
}
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
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.
#include <stdio.h>
#include <stdlib.h>
struct node {
int block_number;
struct node *next;
};
struct file {
int file_size;
struct node *head;
};
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--;
}
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
Viva Question
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
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.
#include <stdio.h>
// 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]);
return 0 ;
}
OUTPUT
1 1 5
2 3 4
3 5 5
4 3 1
Viva Question
#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
Block no.1:10
Block no.2:15
Block no.3:5
Block no.4:9
Block no.5:3
Process no.1:1
Process no.2:4
Process no.3:7
Process no.4:12
1 1 5 3 2
2 4 3 5 1
3 7 4 9 2
4 12 2 15 3
Viva Question
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.
#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
14
12
1 8 Not allocated
2 10 Not allocated
3 12 3 12
Viva Question
● 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.
● 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.
#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
Memory is Full
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
1 20
2 10
Viva Question
#include <stdio.h>
#include <stdlib.h>
void del(int);
void compaction();
void display();
int main()
int *ptr;
// clrscr();
freesize[0] = 500;
printf("\n\n");
printf("\n\n");
while (1)
printf("1.Create.\n");
printf("2.Delete.\n");
printf("3.Compaction.\n");
printf("4.Exit.\n");
scanf("%d", &ch);
switch (ch)
case 1:
scanf("%d", &name);
printf("\nEnter the size of the file: ");
scanf("%d", &size);
create(name, size);
break;
case 2:
scanf("%d", &name);
del(name);
break;
case 3:
compaction();
display();
break;
case 4:
exit(1);
default:
int i, flag = 1, j, a;
a = i, flag = 0;
if (!flag)
n++;
fname[j] = name;
fsize[j] = size;
fstart[j] = freest[a];
freest[a] = freest[a] + size;
display();
else
flag = 1;
compaction();
display();
a = i, flag = 0;
if (!flag)
;
n++;
fname[j] = name;
fsize[j] = size;
fstart[j] = freest[a];
freest[a] += size;
freesize[a] -= size;
display();
else
int i, j, k, flag = 1;
if (fname[i] == name)
break;
if (i == n)
flag = 0;
else
m++;
freest[m] = fstart[i];
freesize[m] = fsize[i];
n--;
}
if (flag)
printf("\n\n After deletion of this process the memory map will be : \n\n");
display();
void compaction()
if (fstart[0] != start)
fstart[0] = start;
else
{
for (i = 1; i < n; i++)
f_size = freesize[0];
size1 += freesize[j];
freesize[0] = size1;
m = 0;
void display()
int i;
printf("\n\n");
printf("\n\n*** FREE SPACE TABLE ***\n\n");
Output
The memory map will now be:
12 200 2688
2888 300
2688 500
1.Create.
2.Delete.
3.Compaction.
4.Exit.
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.
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.
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.
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()
scanf("%d", &nr);
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++)
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;
scanf("%d", &temp);
for(j=0; j<temp;j++)
scanf("%d", &temp1);
rag[i][np+temp1]=1;
for(i=0;i<np+nr;i++)
for(j=0; j<np+nr;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
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
#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];
if (f[i] == 0)
{
int flag = 0;
for (j = 0; j < m; 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
int main()
scanf("%d", &nr);
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++)
scanf("%d", &temp);
for(j=0; j<temp;j++)
scanf("%d", &temp1);
rag[np+temp1][i]=1;
for(j=0; j<temp;j++)
scanf("%d", &temp1);
rag[i][np+temp1]=1;
for(i=0;i<np+nr;i++)
for(j=0; j<np+nr;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++)
{
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("\n ");
return 0;
Input:
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
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.
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;
int full = 0;
// of buffer
void producer()
--mutex;
// slots by 1
++full;
// slots by 1
--empty;
// Item produced
x++;
printf("\nProducer produces"
"item %d",
x);
++mutex;
void consumer()
--mutex;
// slots by 1
--full;
// slots by 1
++empty;
"item %d",
x);
x--;
++mutex;
// Driver Code
int main()
{
int n, i;
// synchronization issues.
scanf("%d", &n);
// Switch Cases
switch (n) {
case 1:
// is non-zero, then it is
// possible to produce
if ((mutex == 1)
producer();
}
else {
printf("Buffer is full!");
}
break;
case 2:
// is non-zero, then it is
// possible to consume
if ((mutex == 1)
consumer();
}
// is empty
else {
printf("Buffer is empty!");
}
break;
// Exit Condition
case 3:
exit(0);
break;
}
}
}
Output
Viva Question
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
while (1) {
// Reader entry section
sem_wait(&mutex);
while (1) {
// Writer entry section
sem_wait(&write_mutex);
int main() {
pthread_t readers[NUM_READERS], writers[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]);
}
// Join threads
for (int i = 0; i < NUM_READERS; i++) {
pthread_join(readers[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
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.