EXPERIMENT 6
AIM: Calculation of external and internal fragmentation
i. Free space list of blocks from system
ii. List process file from the system
REQUIREMENT: A system with UNIX/Linux operating system (Ubuntu, Fedora, etc.)
GCC compiler or any C compiler
Terminal access
Basic knowledge of C programming
THEORY:
In memory management, fragmentation refers to the condition where memory is used
inefficiently, reducing performance and storage capacity.
Internal Fragmentation occurs when memory is allocated in fixed-size blocks, and a
process does not use the entire allocated block, leading to wasted space inside the
allocated memory.
External Fragmentation happens when free memory is split into small blocks scattered
throughout the system. Even if total free memory is enough for a process, it can't be used
if it's not contiguous.
1. Free space list of blocks from system:
The Free Space List represents all the unallocated (free) memory blocks available in the system
at a given time. These memory blocks can vary in size and are used by the operating system to
fulfill memory allocation requests by processes.
Code:
#include<stdio.h>
#include<stdlib.h>
int disk[20];
struct blocks
{
int bid;
int size;
int index;
int status;
}block;
struct processs
{
int pid;
int size;
int flag;
struct blocks b;
int fragment;
}process;
int main()
{
int nb, np, i , j;
int disk_index=0;
printf(" Total disk size is 50 \n");
printf(" enter numebr of blocks: ");
scanf("%d", &nb);
struct blocks bls[nb], temp;
for(i=0;i<nb;i++)
{
bls[i].bid=i;
printf("enter the size of block %d ", i);
scanf("%d", &bls[i].size);
bls[i].index=disk_index;
disk_index=disk_index+bls[i].size;
bls[i].status = -1;
}
printf(" enter numebr of process: ");
scanf("%d",&np);
struct processs ps[np];
for(i=0;i<np;i++)
{
ps[i].pid=i;
printf("enter the size of process %d ", i);
scanf("%d", &ps[i].size);
ps[i].flag=-1;
}
for(i=0;i<np;i++)
{
for(j=0;j<nb;j++)
{
if(bls[j].size > ps[i].size && bls[j].status ==-1)
{
ps[i].b=bls[j];
bls[j].status = 1;
break;
}
}
}
printf(" \n now Free space list of blocks is \n");
int free=0;
for(i=0;i<nb;i++)
{
if(bls[i].status == -1)
{
printf(" Block id: %d Block size %d block index %d \n", bls[i].bid, bls[i].size, bls[i].index);
free= free + bls[i].size;
}
}
printf(" \n Total external free space is: %d \n", free);
return 0;
}
Output:
Figure 6.1: Implementation of Free space list of blocks from system in C.
2. List process file from the system:
In an operating system, each running process is assigned a Process Control Block (PCB), which
contains information about the process, including memory usage and files being used.
The List of Process Files refers to the files or programs currently loaded in memory and
associated with active processes.
Code:
#include<stdio.h>
#include<stdlib.h>
int disk[20];
struct blocks
{
int bid;
int size;
int index;
int status;
}block;
struct processs
{
int pid;
int size;
int flag;
struct blocks b;
int fragment;
}process;
int main()
{
int nb, np, i , j;
int disk_index=0;
printf(" Total disk size is 50 \n");
printf(" enter numebr of blocks: ");
scanf("%d", &nb);
struct blocks bls[nb], temp;
for(i=0;i<nb;i++)
{
bls[i].bid=i;
printf("enter the size of block %d ", i);
scanf("%d", &bls[i].size);
bls[i].index=disk_index;
disk_index=disk_index+bls[i].size;
bls[i].status = -1;
}
printf(" enter numebr of process: ");
scanf("%d",&np);
struct processs ps[np];
for(i=0;i<np;i++)
{
ps[i].pid=i;
printf("enter the size of process %d ", i);
scanf("%d", &ps[i].size);
ps[i].flag=-1;
}
for(i=0;i<np;i++)
{
for(j=0;j<nb;j++)
{
if(bls[j].size > ps[i].size && bls[j].status ==-1)
{
ps[i].b=bls[j];
bls[j].status = 1;
break;
}
}
}
printf(" \n now process block allocation list is \n");
int t_free=0;
for(i=0;i<np;i++)
{
printf(" process id: %d process size %d block id %d free space %d\n", ps[i].pid, ps[i].size,
ps[i].b.bid, ps[i].b.size-ps[i].size);
t_free = t_free + (ps[i].b.size-ps[i].size);
}
printf(" \n Total internal fragmentation space is: %d \n", t_free);
return 0;
}
Output:
Figure 6.2: Implementation of List process file from the system in C.
.
EXPERIMENT 7
AIM: Implementation of compaction for the continually changing memory layout and calculate
total movement of data
REQUIREMENT: A system with UNIX/Linux operating system (Ubuntu, Fedora, etc.)
GCC compiler or any C compiler
Terminal access
Basic knowledge of C programming
THEORY:
Compaction is used to reduce external fragmentation by moving allocated memory blocks
together, creating one large free block. It rearranges memory so that free space is contiguous.
This helps in efficient memory utilization. The total data movement is the sum of all shifted
memory sizes.
Code:
#include<stdio.h>
#include<stdlib.h>
int disk[20];
struct blocks
{
int bid;
int size;
int index;
int status;
}block;
struct processs
{
int pid;
int size;
int flag;
struct blocks b;
int fragment;
}process;
int main()
{
int nb, np, i , j;
int disk_index=0;
printf(" Total disk size is 50 \n");
printf(" enter numebr of blocks: ");
scanf("%d", &nb);
struct blocks bls[nb], temp;
for(i=0;i<nb;i++)
{
bls[i].bid=i;
printf("enter the size of block %d ", i);
scanf("%d", &bls[i].size);
bls[i].index=disk_index;
disk_index=disk_index+bls[i].size;
bls[i].status = -1;
}
printf(" enter numebr of process: ");
scanf("%d",&np);
struct processs ps[np];
for(i=0;i<np;i++)
{
ps[i].pid=i;
printf("enter the size of process %d ", i);
scanf("%d", &ps[i].size);
ps[i].flag=-1;
}
for(i=0;i<np;i++)
{
for(j=0;j<nb;j++)
{
if(bls[j].size > ps[i].size && bls[j].status ==-1)
{
ps[i].b=bls[j];
bls[j].status = 1;
break;
}
}
}
printf(" \n now process block allocation list is \n");
for(i=0;i<np;i++)
{
printf(" process id: %d process size %d block id %d free space %d\n", ps[i].pid, ps[i].size,
ps[i].b.bid, ps[i].b.size-ps[i].size);
}
printf(" \n now Free space list of blocks is \n");
int free=0;
for(i=0;i<nb;i++)
{
printf(" Block id: %d Block size %d block index %d \n", bls[i].bid, bls[i].size,
bls[i].index);
}
int sid, count=0;
for(i=0;i<nb-1;i++)
{
count = 0;
for(j=1; j<nb;j++)
{
if(bls[i].status == -1 && bls[j].status !=-1)
{
sid = bls[j].index;
bls[j].index= bls[i].index;
bls[i].index = sid;
}
else
{
count = count +1;
i=i+count;
}
}
i=i-count;
}
printf(" \n now After compaction list of blocks is \n");
free=0;
for(i=0;i<nb;i++)
{
if(bls[i].status == -1)
{
printf(" Block id: %d Block size %d New block index %d \n", bls[i].bid, bls[i].size,
bls[i].index);
free= free + bls[i].size;
}
}
printf(" \n Total external free space is: %d \n", free);
return 0;
}
Output:
Figure 7.1: Implementation and Output of Compaction for the continually changing memory
layout in C.
Conclusion:
The experiment shows how compaction improves memory usage by merging scattered free
blocks. It reduces fragmentation and allows larger processes to fit. We calculated the total data
moved during compaction. This highlights its role in effective memory management.
EXPERIMENT 8
AIM: Implementation of resource allocation graph (RAG)
REQUIREMENT: A system with UNIX/Linux operating system (Ubuntu, Fedora, etc.)
GCC compiler or any C compiler
Terminal access
Basic knowledge of C programming
THEORY:
A Resource Allocation Graph (RAG) is a directed graph used to represent the state of resource
allocation in a system. It includes process nodes and resource nodes, with edges showing
resource requests and assignments. It helps detect potential deadlocks. If the graph contains a
cycle, a deadlock may exist.
Code:
#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:
Figure 8.1: Implementation of resource allocation graph (RAG) in C.
Conclusion:
This experiment demonstrates how RAG visually represents resource allocation and process
dependencies. By analyzing the graph, we can identify deadlocks. Implementing RAG helps in
deadlock detection and prevention. It is a useful tool for managing system resources safely.
EXPERIMENT 9
AIM: Implementation of Banker’s algorithm
REQUIREMENT: A system with UNIX/Linux operating system (Ubuntu, Fedora, etc.)
GCC compiler or any C compiler
Terminal access
Basic knowledge of C programming
THEORY:
The Banker's Algorithm is a deadlock avoidance algorithm that checks whether a system is in a
safe state before allocating resources. It considers the maximum need, allocated resources, and
available resources. The algorithm simulates allocation to ensure no deadlock will occur. It
grants resources only if the system remains in a safe state.
Code:
#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;
// Initialize finish array to 0 (false)
for (k = 0; k < n; k++)
{
f[k] = 0;
}
int need[n][m];
// Calculate need matrix
for (i = 0; i < n; i++)
{
for (j = 0; j < m; j++)
{
need[i][j] = max[i][j] - alloc[i][j];
}
}
int y = 0;
// Find safe sequence
for (k = 0; k < n; 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\n", ans[n - 1]);
return 0;
}
Output:
Figure 9.1: Implementation of Banker’s algorithm in C.
Conclusion:
The experiment shows how the Banker's Algorithm prevents deadlock by checking system safety
before resource allocation. It helps ensure processes can complete without waiting forever. We
verified safe and unsafe states using input matrices. This algorithm is vital for secure resource
management in OS.
EXPERIMENT 10
AIM: Conversion of resource allocation graph (RAG) to wait for graph (WFG) for each type of
method used for storing graph.
REQUIREMENT: A system with UNIX/Linux operating system (Ubuntu, Fedora, etc.)
GCC compiler or any C compiler
Terminal access
Basic knowledge of C programming
THEORY:
A Resource Allocation Graph (RAG) shows processes and resources with edges for requests
and allocations. To detect deadlocks more simply, RAG is converted into a Wait-For Graph
(WFG), which has only processes as nodes and edges showing which process is waiting for
another. Different storage methods like adjacency matrix or list affect how this conversion is
implemented.
Code:
#include <stdio.h>
int main() {
int np, nr, temp, temp1;
printf("Enter number of resources: ");
scanf("%d", &nr);
printf("Enter number of processes: ");
scanf("%d", &np);
int rag[nr + np][nr + np]; // Resource Allocation Graph matrix
int i, j;
// Initialize rag matrix with 0
for (i = 0; i < np + nr; i++) {
for (j = 0; j < np + nr; j++) {
rag[i][j] = 0;
}
}
// Input resources held and requested by each process
for (i = 0; i < np; i++) {
printf("Enter the number of resources process %d is holding: ", i);
scanf("%d", &temp);
for (j = 0; j < temp; j++) {
printf("Enter the resource number process %d is holding: ", i);
scanf("%d", &temp1);
rag[np + temp1][i] = 1; // Edge from resource node to process node
}
printf("Enter the number of resources process %d is requesting: ", i);
scanf("%d", &temp);
for (j = 0; j < temp; j++) {
printf("Enter the resource number process %d is requesting: ", i);
scanf("%d", &temp1);
rag[i][np + temp1] = 1; // Edge from process node to resource node
}
}
// Print the Resource Allocation Graph matrix
printf("\nResource Allocation Graph (RAG) matrix:\n");
for (i = 0; i < np + nr; i++) {
for (j = 0; j < np + nr; j++) {
printf("%d ", rag[i][j]);
}
printf("\n");
}
// Construct the Wait-For Graph (WFG) from the RAG
int wfg[np][np];
// Initialize WFG matrix with 0
for (i = 0; i < np; i++) {
for (j = 0; j < np; j++) {
wfg[i][j] = 0;
}
}
// Build WFG: if process i is waiting for resource held by process k, wfg[i][k] = 1
for (i = 0; i < np; i++) {
for (j = np; j < np + nr; j++) { // Resources nodes
if (rag[i][j] == 1) { // Process i requests resource j-np
for (int k = 0; k < np; k++) { // Check which processes hold this resource
if (rag[j][k] == 1) {
wfg[i][k] = 1;
}
}
}
}
}
// Print the Wait-For Graph matrix
printf("\nWait-For Graph (WFG) matrix:\n");
for (i = 0; i < np; i++) {
for (j = 0; j < np; j++) {
printf("%d ", wfg[i][j]);
}
printf("\n");
}
return 0;
}
Output:
Figure 10.1: Conversion of RAG to WFG in C.
Conclusion:
This experiment explains converting RAG to WFG to simplify deadlock detection by focusing
on process dependencies. Using adjacency matrix or list changes the storage and traversal
approach. The WFG helps identify cycles indicating deadlocks. It improves efficiency in
resource management analysis.
EXPERIMENT 11
AIM: Implement disk scheduling algorithms:
(i) FCFS
(ii) SCAN
(iii) C-SCAN
REQUIREMENT: A system with UNIX/Linux operating system (Ubuntu, Fedora, etc.)
GCC compiler or any C compiler
Terminal access
Basic knowledge of C programming
THEORY:
Disk scheduling algorithms manage the order of servicing disk I/O requests to reduce seek time.
1. FCFS:
FCFS (First-Come-First-Serve) services disk I/O requests in the exact order they arrive,
without reordering. It’s simple and fair but can cause long wait times if requests are far apart on
the disk. The disk head moves to each request sequentially, which may lead to high average seek
time.
Code:
#include<stdio.h>
int main()
{
int queue[20],n,head,i,j,k,seek=0,max,diff;
float avg;
printf("Enter the max range of disk\n");
scanf("%d",&max);
printf("Enter the size of queue request\n");
scanf("%d",&n);
printf("Enter the queue of disk positions to be read\n");
for(i=1;i<=n;i++)
scanf("%d",&queue[i]);
printf("Enter the initial head position\n");
scanf("%d",&head);
queue[0]=head;
for(j=0;j<=n-1;j++)
{
diff=abs(queue[j+1]-queue[j]);
seek+=diff;
printf("Disk head moves from %d to %d with seek
%d\n",queue[j],queue[j+1],diff);
}
printf("Total seek time is %d\n",seek);
avg=seek/(float)n;
printf("Average seek time is %f\n",avg);
return 0;
}
Output:
Figure 11.1: Implementation of disk scheduling algorithms using FCFS in C.
2. SCAN:
SCAN (Elevator Algorithm) moves the disk head in one direction, servicing all requests until it
reaches the end, then reverses direction. This approach reduces the overall seek time by
minimizing unnecessary movement. It treats the disk like an elevator, servicing requests on the
way up and down.
Code:
#include <stdio.h>
#include <stdlib.h> // For abs()
int main() {
int queue[40], n, head, i, j, seek = 0, max, diff, temp;
int queue1[20], queue2[20];
int temp1 = 0, temp2 = 0;
float avg;
printf("Enter the max range of disk: ");
scanf("%d", &max);
printf("Enter the initial head position: ");
scanf("%d", &head);
if (head < 0 || head > max) {
printf("Invalid head position!\n");
return 1;
}
printf("Enter the size of queue request: ");
scanf("%d", &n);
printf("Enter the queue of disk positions to be read:\n");
for (i = 0; i < n; i++) {
scanf("%d", &temp);
if (temp < 0 || temp > max) {
printf("Invalid disk request %d\n", temp);
return 1;
}
if (temp >= head) {
queue1[temp1++] = temp; // Requests greater than or equal to head
} else {
queue2[temp2++] = temp; // Requests less than head
}
}
// Sort queue1 in ascending order
for (i = 0; i < temp1 - 1; i++) {
for (j = i + 1; j < temp1; j++) {
if (queue1[i] > queue1[j]) {
temp = queue1[i];
queue1[i] = queue1[j];
queue1[j] = temp;
}
}
}
// Sort queue2 in descending order
for (i = 0; i < temp2 - 1; i++) {
for (j = i + 1; j < temp2; j++) {
if (queue2[i] < queue2[j]) {
temp = queue2[i];
queue2[i] = queue2[j];
queue2[j] = temp;
}
}
}
// Construct the SCAN queue: head -> queue1 (ascending) -> max -> queue2 (descending) ->
0
queue[0] = head;
// Add all requests greater than or equal to head
for (i = 0; i < temp1; i++) {
queue[i + 1] = queue1[i];
}
// Add max (end of disk)
queue[temp1 + 1] = max;
// Add all requests less than head in descending order
for (j = 0; j < temp2; j++) {
queue[temp1 + 2 + j] = queue2[j];
}
// Add 0 (start of disk)
queue[temp1 + temp2 + 2] = 0;
// Total elements in queue for traversal
int total = temp1 + temp2 + 3;
// Calculate total seek time
for (i = 0; i < total - 1; i++) {
diff = abs(queue[i + 1] - queue[i]);
seek += diff;
printf("Disk head moves from %d to %d with seek %d\n", queue[i], queue[i + 1], diff);
}
printf("Total seek time is %d\n", seek);
avg = seek / (float)n;
printf("Average seek time is %f\n", avg);
return 0;
}
Output:
Figure 11.2: Implementation of disk scheduling algorithms using SCAN in C.
3. C-SCAN:
C-SCAN (Circular SCAN) moves the disk head in one direction only, servicing requests until it
reaches the last track, then jumps back to the beginning without servicing requests on the return.
This provides a more uniform wait time than SCAN by treating the disk as a circular list.
Code:
#include <stdio.h>
#include <stdlib.h> // For abs()
int main() {
int queue[40], queue1[20], queue2[20];
int n, head, i, j, temp;
int temp1 = 0, temp2 = 0;
int seek = 0, diff, max;
float avg;
printf("Enter the max range of disk: ");
scanf("%d", &max);
printf("Enter the initial head position: ");
scanf("%d", &head);
printf("Enter the size of queue request: ");
scanf("%d", &n);
printf("Enter the queue of disk positions to be read:\n");
for (i = 0; i < n; i++) {
scanf("%d", &temp);
if (temp >= head) {
queue1[temp1++] = temp; // Right side of head
} else {
queue2[temp2++] = temp; // Left side of head
}
}
// Sort queue1 in ascending order
for (i = 0; i < temp1 - 1; i++) {
for (j = i + 1; j < temp1; j++) {
if (queue1[i] > queue1[j]) {
temp = queue1[i];
queue1[i] = queue1[j];
queue1[j] = temp;
}
}
}
// Sort queue2 in descending order
for (i = 0; i < temp2 - 1; i++) {
for (j = i + 1; j < temp2; j++) {
if (queue2[i] < queue2[j]) {
temp = queue2[i];
queue2[i] = queue2[j];
queue2[j] = temp;
}
}
}
// Build the SCAN queue
int idx = 0;
queue[idx++] = head;
for (i = 0; i < temp1; i++)
queue[idx++] = queue1[i];
queue[idx++] = max; // End of disk
for (i = 0; i < temp2; i++)
queue[idx++] = queue2[i];
queue[idx++] = 0; // Start of disk
// Calculate seek time
for (i = 0; i < idx - 1; i++) {
diff = abs(queue[i + 1] - queue[i]);
seek += diff;
printf("Disk head moves from %d to %d with seek %d\n", queue[i], queue[i + 1], diff);
}
printf("Total seek time is %d\n", seek);
avg = seek / (float)n;
printf("Average seek time is %.2f\n", avg);
return 0;
}
Output:
Figure 11.3: Implementation of disk scheduling algorithms using SCAN in C.
Conclusion:
The experiment demonstrates different disk scheduling methods to optimize head movement.
FCFS is simple but inefficient. SCAN and C-SCAN reduce seek time by systematic arm
movement. Choosing the right algorithm improves disk performance and responsiveness.