LAB 1: Install VirtualBox and Ubuntu
Basic Concepts
What is VirtualBox?
VirtualBox is software that allows you to run another operating system (like Ubuntu)
inside your computer without removing your current OS.
What is a Virtual Machine (VM)?
A VM is a virtual computer created inside your real computer. It uses your computer's
resources (RAM, CPU, storage) to run another operating system separately.
What is Ubuntu?
Ubuntu is a free, open-source Linux operating system used for programming, learning,
and server work.
Part 1: Install VirtualBox
Step 1: Download VirtualBox
1. Go to:
[Link]
2. Click Downloads.
3. Select your operating system (e.g., Windows Hosts).
Step 2: Install VirtualBox
1. Open the downloaded setup file.
2. Follow the installation steps.
3. Keep default settings.
4. Click Install → Finish.
Part 2: Install Ubuntu on VirtualBox
Step 1: Download Ubuntu ISO
1. Go to:
[Link]
2. Download Ubuntu Desktop ISO.
3. Check your system type:
o Right-click This PC → Properties
o Check if your system is 32-bit or 64-bit.
Step 2: Create Ubuntu Virtual Machine
1. Open VirtualBox.
2. Click New.
3. Enter:
o Name: Ubuntu
o Type: Linux
o Version: Ubuntu (64-bit)
4. Click Next.
Step 3: Assign RAM
• Give half of your computer's RAM.
• Example:
o 8GB RAM → Give 4GB (4096 MB)
o 4GB RAM → Give 2GB (2048 MB)
Step 4: Create Virtual Hard Disk
Select:
• Create a virtual hard disk now
• Type: VDI
• Storage: Dynamically allocated
• Size: 25 GB or more
Step 5: Add Ubuntu ISO File
1. Select your Ubuntu VM.
2. Go to Settings → Storage.
3. Click the empty disk icon.
4. Choose your Ubuntu ISO file.
Step 6: Install Ubuntu
1. Click Start.
2. Ubuntu will open.
3. Select Install Ubuntu.
4. Complete setup:
o Language
o Time zone
o Username
o Password
Step 7: Finish Installation
1. Restart the Virtual Machine.
2. Ubuntu is now ready to use inside VirtualBox.
Result:
VirtualBox and Ubuntu have been successfully installed, and Ubuntu can run as a
virtual machine on your computer.
LAB 2: Basic Ubuntu & Linux Commands + C Program Compilation
1. Check GCC Version
GCC is a compiler used to run C programs.
Check GCC:
gcc --version
Install GCC (if not installed):
sudo apt install build-essential
2. Basic Terminal Commands
Clear Terminal
clear
Check Current Location
pwd
Shows your current directory.
Change Directory
Go to a folder:
cd foldername
Go back:
cd ..
Go to Home:
cd ~
3. Create Directory (Folder)
Create a folder:
mkdir cs604p
Enter folder:
cd cs604p
Create multiple folders:
mkdir -p CS604/Files/CFiles
Structure:
CS604
└── Files
└── CFiles
4. Create Files
Create empty file:
touch [Link]
Create file using >:
> [Link]
Difference:
Command Purpose
touch Creates file safely
> Creates file and removes old content
5. View Files
Show files in current folder:
ls
View file content:
cat filename
Example:
cat [Link]
6. Write, Compile & Run C Program
Step 1: Create C file
Example:
touch myprogram.c
(C programs always use .c extension)
Step 2: Write Code
Open the file and write your C program.
Example:
#include<stdio.h>
int main()
printf("Hello World");
return 0;
Save the file.
Step 3: Compile Program
Syntax:
gcc filename.c -o outputname
Example:
gcc myprogram.c -o myprogram
Step 4: Run Program
Syntax:
./filename
Example:
./myprogram
7. Remove Files (rm)
Delete a file:
rm filename
Example:
rm [Link]
Ask before deleting:
rm -i filename
Delete multiple files:
rm [Link] [Link] [Link]
Delete all txt files:
rm *.txt
rm permanently deletes files (does not move them to Trash).
8. Move & Rename Files (mv)
Rename file
Syntax:
mv oldname newname
Example:
mv [Link] [Link]
Move file
Syntax:
mv filename destination
Example:
mv [Link] ~/Documents/
Move multiple files
mv [Link] [Link] ~/Documents/
9. System Information (uname)
Check system information:
Command Shows
uname Kernel name
uname -a Complete information
uname -r Kernel version
uname -m Machine type
uname -o Operating system
Quick Command List (Must Remember)
Task Command
Clear screen clear
Current directory pwd
Change directory cd
Go back cd ..
Create folder mkdir
Create file touch
List files ls
View file cat
Delete file rm
Move/Rename mv
Compile C gcc file.c -o name
Run C program ./name
System info uname
LAB 3: Two-Way Inter-Process Communication Using Pipes in C
Objective
Learn how parent and child processes communicate with each other using pipes in
C.
In this lab:
1. Parent sends a message → Child receives it.
2. Child sends a reply → Parent receives it.
1. What is IPC?
IPC (Inter-Process Communication) is a method that allows two processes to
exchange data.
Example:
• Parent process communicates with child process.
2. What is a Pipe?
A pipe is a communication channel between processes.
A pipe has two ends:
Pipe End Purpose
pipe[0] Read data
pipe[1] Write/send data
Example:
Write End → Pipe → Read End
3. Parent and Child Process
When we use:
fork();
It creates two processes:
1. Parent Process
2. Child Process
Checking process:
pid > 0 → Parent
pid == 0 → Child
4. Important System Calls
Command Purpose
pipe() Creates communication pipe
fork() Creates child process
write() Sends data
read() Receives data
close() Closes unused pipe end
Program Logic
We use two pipes:
Pipe 1:
Parent → Child
Parent
| Hello Child
Child
Pipe 2:
Child → Parent
Child
| Hello Parent
↓
Parent
Important Code Explanation
Include Libraries
#include <stdio.h>
Used for printf().
#include <unistd.h>
Used for pipe(), fork(), read(), write().
#include <string.h>
Used for strlen().
Create Pipes
int pipe1[2], pipe2[2];
Creates two pipes.
Each pipe has:
pipe[0] = Read
pipe[1] = Write
Messages
char parent_msg[]="Hello Child";
char child_msg[]="Hello Parent";
Messages exchanged between processes.
Create Pipes
pipe(pipe1);
pipe(pipe2);
Creates communication channels.
Create Child Process
int pid=fork();
Creates child process.
After fork:
Parent
Child
Parent Process
Condition:
if(pid > 0)
Means parent process.
Close unused ends
close(pipe1[0]);
Parent does not read from pipe1.
close(pipe2[1]);
Parent does not write to pipe2.
Send Message
write(pipe1[1], parent_msg, strlen(parent_msg)+1);
Parent sends:
Hello Child
to child.
Receive Reply
read(pipe2[0], buffer, sizeof(buffer));
Parent receives:
Hello Parent
Display Message
printf("Parent received: %s\n", buffer);
Child Process
Condition:
else
Means child process.
Close unused ends
close(pipe1[1]);
Child does not write to pipe1.
close(pipe2[0]);
Child does not read from pipe2.
Receive Parent Message
read(pipe1[0], buffer, sizeof(buffer));
Child receives:
Hello Child
Display Message
printf("Child received: %s\n", buffer);
Send Reply
write(pipe2[1], child_msg, strlen(child_msg)+1);
Child sends:
Hello Parent
Expected Output
Child received: Hello Child
Parent received: Hello Parent
Steps to Run Program
1. Open Ubuntu Virtual Machine
Start Ubuntu from VirtualBox.
2. Open Terminal
Shortcut:
Ctrl + Alt + T
3. Go to Desktop
cd Desktop
4. Create C File
touch pipe.c
5. Open File and Write Code
Save the program.
6. Compile Program
Syntax:
gcc filename.c -o outputname
Example:
gcc pipe.c -o pipe
7. Run Program
./pipe
Output:
Child received: Hello Child
Parent received: Hello Parent
Quick Revision
Remember:
pipe() → Create communication channel
fork() → Create child process
write() → Send message
read() → Receive message
close() → Close unused side
Main Idea:
Parent sends data using pipe1, child replies using pipe2.
LAB 4: Thread Creation and Termination in C
Objective
Learn how to:
• Create threads in C
• Run multiple threads together
• Terminate threads properly using pthread functions
1. What is a Thread?
A thread is the smallest unit of execution inside a process.
• A process can have multiple threads.
• Threads share the same memory and resources.
• Threads make programs faster by running multiple tasks at the same time.
Example:
• In MS Word:
o One thread handles typing.
o Another thread handles spell checking.
2. Difference Between Process and Thread
Process Thread
Running program Small execution unit inside a process
Has its own memory Shares process memory
Slower to create Faster to create
Uses more resources Uses fewer resources
Communication needs IPC Uses shared memory
Easy Remember:
Program = Code stored on disk
Process = Running program
Thread = Task inside a process
3. Types of Threads
1. User-Level Threads
• Managed by user programs.
• Faster but limited.
2. Kernel-Level Threads
• Managed by Operating System.
• Supports real parallel execution.
4. Relationship Between Process and Threads
One process can contain multiple threads.
Example:
Process
-----------------
| | |
Thread1 Thread2 Thread3
Threads share:
• Memory
• Code
• Resources
5. Thread Creation in C
Linux uses pthread library.
Create thread:
pthread_create(&thread_id, NULL, function_name, NULL);
6. Important pthread Functions
Function Purpose
pthread_create() Creates a new thread
pthread_join() Waits for thread completion
pthread_exit() Terminates a thread
pthread_self() Gets thread ID
pthread_detach() Separates thread
7. Thread Termination
A thread ends by:
1. Returning from its function
Example:
return NULL;
2. Using:
pthread_exit();
8. Important Terms for Thread Program
Term Purpose
pthread_t Stores thread ID
pthread_create Creates thread
pthread_join Waits for thread
void *function(void *) Thread function format
usleep() Creates delay
printf() Displays output
gcc -pthread Compiles thread program
Program Aim
Create two threads:
• Thread 1
• Thread 2
Tasks:
1. Create both threads.
2. Display their IDs.
3. Run them simultaneously.
4. Terminate them after completion.
Program Explanation (Simple)
Libraries
#include <stdio.h>
Used for printf().
#include <stdlib.h>
Used for exit().
#include <pthread.h>
Used for thread functions.
#include <unistd.h>
Used for usleep().
Thread Function
void *thread_function(void *arg)
Function that each thread runs.
usleep(50000);
Pauses thread for a short time.
return NULL;
Ends thread.
Main Function
pthread_t t1,t2;
Creates two thread variables.
• t1 = Thread 1 ID
• t2 = Thread 2 ID
Create Thread 1
pthread_create(&t1,NULL,thread_function,NULL);
Creates Thread 1.
Create Thread 2
pthread_create(&t2,NULL,thread_function,NULL);
Creates Thread 2.
Display Thread IDs
printf("Thread ID: %lu", t1);
Shows thread ID.
Wait for Threads
pthread_join(t1,NULL);
pthread_join(t2,NULL);
Main program waits until both threads finish.
Compile and Run Program
Step 1: Create file
touch thread.c
Step 2: Compile
Important:
Use -pthread
gcc thread.c -o thread -pthread
Step 3: Run
./thread
Expected Output
Example:
Thread with ID: 140234 created
Thread with ID: 140235 created
Threads are going to be terminated one by one
(Thread IDs may be different every time.)
Quick Revision
Remember:
pthread_create() → Create thread
pthread_join() → Wait for thread
pthread_exit() → End thread
pthread_t → Store thread ID
-pthread → Compile thread program
Main Idea:
A thread is a small task inside a process. Multiple threads allow a program to perform
multiple tasks at the same time.
LAB 5: FCFS CPU Scheduling Algorithm in C
Objective
Implement the First Come First Serve (FCFS) CPU scheduling algorithm in C.
The program calculates:
• Waiting Time
• Turnaround Time
• Average Waiting Time
• Average Turnaround Time
1. What is FCFS?
FCFS (First Come First Served) is a CPU scheduling algorithm where processes
execute in the order they arrive.
Example:
P0 → P1 → P2 → P3
The first process runs first, then the next process.
Features:
• Non-preemptive algorithm
• Simple and easy to implement
• Process runs until completion
2. Assumptions
• All processes arrive at time 0.
• Processes execute in order:
P0, P1, P2, P3
• No process interruption.
• First process waiting time is always 0.
3. Important Terms
Burst Time
The total CPU time required by a process.
Example:
P0 = 10 seconds
Waiting Time
Time a process waits before execution starts.
Formula:
Waiting Time = Previous Burst Time + Previous Waiting Time
Turnaround Time
Total time from process arrival to completion.
Formula:
Turnaround Time = Waiting Time + Burst Time
4. Given Data
Process Burst Time
P0 10
P1 4
P2 6
Process Burst Time
P3 8
5. FCFS Algorithm Steps
Step 1:
Set first process waiting time:
P0 Waiting Time = 0
Step 2:
Calculate remaining waiting times:
Waiting[i] = Waiting[i-1] + Burst[i-1]
Step 3:
Calculate turnaround:
Turnaround = Waiting + Burst
Step 4:
Find average values:
Average = Total / Number of Processes
6. Code Explanation
Include Library
#include <stdio.h>
Used for:
• printf()
• scanf()
Number of Processes
const int n = 4;
There are 4 processes:
P0 P1 P2 P3
Arrays
int burst[n];
int waiting[n];
int turnaround[n];
Stores:
• Burst times
• Waiting times
• Turnaround times
Burst Time Values
burst[0]=10;
burst[1]=4;
burst[2]=6;
burst[3]=8;
Assigns CPU time to each process.
First Waiting Time
waiting[0]=0;
First process does not wait.
Calculate Waiting Time
for(int i=1;i<n;i++)
waiting[i]=waiting[i-1]+burst[i-1];
Calculates waiting time of each process.
Example:
P0 = 0
P1 = 0 + 10 = 10
P2 = 10 + 4 = 14
P3 = 14 + 6 = 20
Calculate Turnaround Time
turnaround[i]=waiting[i]+burst[i];
Example:
P1:
Waiting = 10
Burst = 4
Turnaround = 10 + 4 = 14
Calculate Average
avgWaiting = totalWaiting/n;
Calculates average waiting time.
avgTurnaround = totalTurnaround/n;
Calculates average turnaround time.
7. Compile and Run Program
Create C file
touch fcfs.c
Compile
gcc fcfs.c -o fcfs
Run
./fcfs
8. Expected Output
Process Burst Waiting Turnaround
P0 10 0 10
P1 4 10 14
P2 6 14 20
P3 8 20 28
Average Waiting Time = 11.00
Average Turnaround Time = 18.00
Quick Revision
Remember:
FCFS = First process comes, first executes
Waiting Time:
Previous Waiting + Previous Burst
Turnaround Time:
Waiting + Burst
Important Formula:
WT = Waiting Time
TAT = WT + Burst Time
Main Idea:
FCFS executes processes in order and calculates how long each process waits and
completes.
LAB 6: Round Robin CPU Scheduling Algorithm in C
Objective
Implement Round Robin (RR) CPU Scheduling in C and calculate:
• Waiting Time
• Turnaround Time
• Average Waiting Time
• Average Turnaround Time
1. What is Round Robin Scheduling?
Round Robin (RR) is a pre-emptive CPU scheduling algorithm.
Each process gets a fixed amount of CPU time called Time Quantum.
If a process is not completed within its time quantum:
• CPU stops it.
• It goes back to the end of the queue.
• The next process gets CPU time.
Example:
P0 → P1 → P2 → P3 → P0 → P1 ...
2. Process States
A process can have these states:
State Meaning
New Process is being created
Ready Waiting for CPU
Running Executing on CPU
Waiting Waiting for I/O or event
Terminated Execution completed
3. Important Concepts
Time Quantum
A fixed time given to each process.
Example:
Time Quantum = 4
A process can run for maximum 4 units at one turn.
Pre-emption
If a process is not completed:
Example:
Burst Time = 10
Quantum = 4
Execution:
Run 4 → Remaining 6
Run 4 → Remaining 2
Run 2 → Complete
Context Switching
When CPU changes from one process to another.
Example:
P0 stops → P1 starts
The system saves the old process state and loads the new one.
4. Working of Round Robin
Steps:
1. Put all processes in ready queue.
2. Give each process CPU time equal to quantum.
3. If process finishes:
o Remove it.
4. If not:
o Put it back in queue.
5. Repeat until all processes complete.
5. Advantages and Disadvantages
Advantages
Fair scheduling
Every process gets CPU time
Easy to implement
Disadvantages
Too small quantum causes many context switches.
Too large quantum behaves like FCFS.
6. Example Data
Time Quantum:
Processes:
Process Burst Time
P0 10
P1 4
P2 2
P3 8
7. Important Formulas
Turnaround Time
TAT = Finish Time - Arrival Time
Since arrival time is 0:
TAT = Finish Time
Waiting Time
WT = Turnaround Time - Burst Time
8. Program Logic
Store burst times:
P0 = 10
P1 = 4
P2 = 2
P3 = 8
Copy burst time into remaining array:
Example:
Remaining:
10 4 2 8
Every time a process executes:
Remaining Time = Remaining Time - Quantum
9. Code Explanation
Header File
#include <stdio.h>
Used for:
• printf()
Number of Processes
const int n = 4;
There are 4 processes.
Arrays
int burst[4];
Stores CPU burst time.
int remaining[4];
Stores remaining execution time.
int waiting[4];
Stores waiting time.
int turnaround[4];
Stores turnaround time.
int finish[4];
Stores completion time.
Time Quantum
int timeQuantum = 4;
Each process gets 4 CPU units.
Global Clock
int time = 0;
Tracks total CPU time.
Completed Counter
int completed = 0;
Counts finished processes.
10. Initialize Arrays
remaining[i] = burst[i];
Initially:
Remaining Time = Burst Time
Example:
P0 = 10
P1 = 4
P2 = 2
P3 = 8
11. Round Robin Execution
Loop continues until all processes finish:
while(completed < n)
Check every process:
for(int i=0;i<n;i++)
If process still has work:
if(remaining[i] > 0)
Case 1: Remaining Time > Quantum
Example:
Remaining = 10
Quantum = 4
Execute:
10 - 4 = 6
Code:
time += timeQuantum;
remaining[i] -= timeQuantum;
Case 2: Process Finishes
If:
Remaining <= Quantum
Process completes.
Code:
time += remaining[i];
Updates CPU time.
Set remaining time:
remaining[i]=0;
Process completed.
Calculate:
turnaround[i]=finish[i];
and:
waiting[i]=turnaround[i]-burst[i];
12. Compile and Run
Create file
touch round_robin.c
Compile
gcc round_robin.c -o lab
Run
./lab
13. Expected Output
Process Burst Waiting Turnaround
P0 10 14 24
P1 4 4 8
P2 2 8 10
P3 8 14 22
Average Waiting Time = 10.00
Average Turnaround Time = 16.00
Quick Revision
Remember:
Round Robin = Each process gets fixed time
Time Quantum = CPU time given to each process
Remaining Time = Burst - Executed Time
TAT = Finish Time
WT = TAT - Burst Time
Main Idea:
Round Robin gives every process a fair chance by executing them one by one for a fixed
time quantum until all processes finish.
LAB 7: Producer–Consumer Problem Using Circular Buffer in C
Objective
Implement the Producer–Consumer problem in C using a circular buffer.
The program will:
• Produce integer values.
• Consume integer values.
• Prevent buffer overflow and underflow.
• Display produced and consumed items.
1. What is Producer–Consumer Problem?
Producer–Consumer is a synchronization problem where:
Producer
Creates data and puts it into a shared buffer.
Consumer
Takes data from the buffer and uses it.
Example:
Producer → Buffer → Consumer
2. What is a Buffer?
A buffer is a temporary storage area where produced data is stored before
consumption.
Example:
Buffer:
[10] [20] [30]
3. Circular Buffer
A circular buffer works like a circle.
When the last position is reached, it starts again from the first position.
Formula:
(index + 1) % buffer_size
Example:
Buffer size = 3
0→1→2→0→1
4. Important Concepts
Buffer Full
When buffer has no empty space.
Example:
Buffer size = 3
[10][20][30]
Producer cannot add more items.
Buffer Empty
When there is no item to consume.
Example:
[]
Consumer cannot remove anything.
5. Important Variables
Variable Purpose
buffer Stores items
in Position where producer writes
out Position where consumer reads
counter Number of items currently in buffer
6. Algorithm
Producer:
1. Check if buffer is full.
2. If full → Display "Buffer Full".
3. Otherwise:
o Add item.
o Move index.
o Increase counter.
Consumer:
1. Check if buffer is empty.
2. If empty → Display "Buffer Empty".
3. Otherwise:
o Remove item.
o Move index.
o Decrease counter.
7. Required Functions
Function Purpose
printf() Display output
scanf() Take input
malloc() Allocate memory
free() Release memory
8. Program Explanation
Header Files
#include <stdio.h>
Used for input/output:
• printf()
• scanf()
#include <stdlib.h>
Used for:
• malloc()
• free()
9. Variables
int bufsize;
Stores buffer size.
Example:
Buffer size = 3
int in = 0;
Producer position.
int out = 0;
Consumer position.
int counter = 0;
Counts items inside buffer.
10. Taking Buffer Size
scanf("%d",&bufsize);
User enters buffer size.
Example:
Enter buffer size: 3
11. Creating Buffer
int *buffer = malloc(bufsize * sizeof(int));
Creates memory space for buffer.
Example:
[ ][ ][ ]
12. Producer Operation
Command:
Means produce item.
Check buffer:
if(counter == bufsize)
If true:
Buffer is Full
Store value:
buffer[in]=value;
Example:
buffer[0]=10
Move producer position:
in=(in+1)%bufsize;
Moves to next location.
Increase item count:
counter++;
13. Consumer Operation
Command:
Means consume item.
Check empty buffer:
if(counter==0)
Output:
Buffer is Empty
Read item:
consumed=buffer[out];
Move consumer position:
out=(out+1)%bufsize;
Decrease count:
counter--;
14. Quit Program
Command:
Ends program.
15. Compile and Run
Create File
touch producer_consumer.c
Compile
gcc producer_consumer.c -o producer_consumer
Run
./producer_consumer
16. Sample Output
Enter buffer size: 3
Commands:
p = Produce
c = Consume
q = Quit
Choice: p
Enter value: 10
Produced: 10
Choice: p
Enter value: 20
Produced: 20
Choice: p
Enter value: 30
Produced: 30
Choice: p
Buffer is Full
Choice: c
Consumed: 10
Choice: c
Consumed: 20
Choice: q
Exiting program.
Quick Revision
Remember:
Producer → Adds item
Consumer → Removes item
Buffer Full → Cannot Produce
Buffer Empty → Cannot Consume
in → Producer position
out → Consumer position
counter → Number of items
Main Idea:
The Producer puts data into the shared buffer, and the Consumer removes data from it
while preventing overflow and underflow problems.
Lab 8 — Dining Philosophers Problem in C
Lab Aim
To solve the Dining Philosophers Problem in C and understand how Operating
Systems avoid deadlock using synchronization.
1. What is Dining Philosophers Problem?
• There are 5 philosophers sitting around a circular table.
• Each philosopher can perform two tasks:
o Thinking
o Eating
• To eat, each philosopher needs two forks:
o Left fork
o Right fork
There are only 5 forks available for 5 philosophers.
2. Problem (Deadlock Situation)
If every philosopher follows the same order:
1. Take left fork
2. Take right fork
Then:
Philosopher 1 → Takes Fork 1
Philosopher 2 → Takes Fork 2
Philosopher 3 → Takes Fork 3
Philosopher 4 → Takes Fork 4
Philosopher 5 → Takes Fork 5
Now everyone waits for the second fork.
Example:
P1 waits for Fork 2
P2 waits for Fork 3
P3 waits for Fork 4
P4 waits for Fork 5
P5 waits for Fork 1
No one can continue.
This situation is called:
Deadlock
Deadlock: A condition where processes keep waiting for resources forever.
3. Deadlock Solution
To avoid deadlock, change the order for one philosopher.
Normal philosophers:
Take Left Fork
Then Take Right Fork
Last philosopher:
Take Right Fork
Then Take Left Fork
This breaks the circular waiting condition and prevents deadlock.
4. Short Theory
Dining Philosophers is a resource-sharing problem.
• Philosophers represent processes.
• Forks represent shared resources.
• Mutex locks are used to control access to forks.
• Only one philosopher can use a fork at one time.
The solution uses synchronization to avoid conflicts.
5. Assumptions
• Number of philosophers = 5
• Number of forks = 5
• Each philosopher is represented as a process/thread.
• Each fork is a shared resource.
• Last philosopher takes the right fork first.
• Each philosopher completes one eating cycle.
6. Algorithm (Easy Steps)
1. Create 5 forks.
2. Initialize all forks as free.
3. Create philosopher states.
4. Each philosopher tries to get forks.
5. If both forks are available:
o Eat food.
o Release forks.
6. Last philosopher changes fork order to avoid deadlock.
7. Continue until all philosophers finish eating.
7. Important Terms
Term Meaning
Process A running program
Resource Something required by a process
Fork Shared resource
Term Meaning
Mutex Lock used for synchronization
Deadlock Infinite waiting situation
Synchronization Managing multiple processes safely
8. Code Explanation (Important Parts)
Header Files
#include<stdio.h>
#include<stdlib.h>
Used for:
• printf()
• system()
Number of Philosophers
#define n 4
Defines the number of philosophers and forks.
(Note: In standard Dining Philosophers problem, it is usually 5.)
Fork Structure
struct fork {
int taken;
};
Stores fork status.
Values:
0 = Available
1 = Taken
Philosopher Structure
struct philosp {
int left;
int right;
};
Stores whether philosopher has:
• Left fork
• Right fork
9. Function: goForDinner()
void goForDinner(int philID)
This function controls philosopher activities.
It checks:
Case 1:
Philosopher already finished.
left = 10
right = 10
Case 2:
Philosopher has both forks.
left = 1
right = 1
Actions:
• Complete dinner
• Release forks
• Increase completed count
Case 3:
Philosopher has only left fork.
Now it tries to get the right fork.
If fork is available:
Take right fork
Otherwise:
Wait
Case 4:
Philosopher has no fork.
It tries to take the left fork.
10. Main Function
Initialize Forks
for(i=0;i<n;i++)
ForkAvil[i].taken=0;
All forks are initially free.
Execute Dinner Process
while(compltedPhilo<n)
Loop continues until all philosophers complete dinner.
Calling Philosopher Function
goForDinner(i);
Each philosopher tries to eat.
11. Program Flow
Start
↓
Create philosophers and forks
Initialize forks as free
Philosopher tries to get forks
If both forks available
Eat
Release forks
All philosophers finished?
↓
End
12. Compile and Run
Compile:
gcc dining.c -o dining
Run:
./dining
13. Expected Output Example
Fork 1 taken by Philosopher 1
Fork 2 taken by Philosopher 2
Philosopher 1 completed his dinner
Philosopher 1 released fork 1 and fork 5
Philosopher 2 completed his dinner
Till now num of philosophers completed dinner are 5
Quick Exam Revision
Dining Philosophers Problem:
• It is a synchronization problem.
• Philosophers need two forks to eat.
• Same fork order causes deadlock.
• Changing order for one philosopher prevents deadlock.
Memory Trick:
"Same order = Deadlock, Different order = Solution."
Lab 9 — Banker's Algorithm in C
Lab Aim
To implement Banker's Algorithm in C and check whether a system is in a safe state or
not by finding a safe sequence.
1. What is Banker's Algorithm?
Banker's Algorithm is a deadlock avoidance algorithm used in Operating Systems.
It checks whether allocating resources to processes will keep the system safe.
It works like a bank:
• Bank gives resources only if it can still complete all processes.
• Similarly, OS gives resources only if the system remains safe.
2. Important Concepts
Deadlock
A situation where processes wait forever because resources are not available.
Example:
P1 waiting for Resource A
P2 waiting for Resource B
Neither process can continue.
Safe State
A system is in a safe state when:
• All processes can complete their execution.
• Resources can be allocated without causing deadlock.
Safe Sequence
A sequence in which all processes can finish safely.
Example:
P1 → P0 → P2
Means:
1. Execute P1
2. Release its resources
3. Execute P0
4. Execute P2
3. Matrices Used in Banker's Algorithm
1. Allocation Matrix
Shows resources already given to processes.
Example:
Alloc
P0 0 1 0
P1 2 0 0
P2 3 0 2
2. Maximum Matrix
Shows maximum resources required by each process.
Example:
Max
P0 7 5 3
P1 3 2 2
P2 9 0 2
3. Need Matrix
Shows remaining required resources.
Formula:
Need = Max - Allocation
Example:
P0:
Max 753
Alloc 010
Need 743
4. Available Array
Shows currently available resources.
Example:
Available:
A=3
B=3
C=2
5. Example Data
Process Allocation Maximum Need
P0 010 753 743
Process Allocation Maximum Need
P1 200 322 122
P2 302 902 600
Available:
A=3
B=3
C=2
Safe Sequence:
P1 → P0 → P2
6. Algorithm (Easy Steps)
Step 1:
Calculate Need matrix.
Need = Max - Allocation
Step 2:
Set all processes as unfinished.
Finish[i] = 0
Step 3:
Find a process where:
Need <= Available
If found:
• Add process to safe sequence.
• Release its resources.
• Update Available.
Formula:
Available = Available + Allocation
Step 4:
Repeat until:
• All processes finish → Safe State
• No process found → Unsafe State
7. Program Statement
Write a C program that:
• Calculates Need matrix.
• Implements Banker's Algorithm.
• Finds safe sequence.
• Displays safe sequence.
8. Important Data Structures
Array Purpose
alloc[][] Resources already allocated
max[][] Maximum resource requirement
need[][] Remaining resources needed
avail[] Available resources
finish[] Checks completed processes
safeSeq[] Stores safe order
9. Code Explanation (Important Parts)
Header File
#include<stdio.h>
Used for:
• printf()
• scanf()
Number of Processes and Resources
int n = 3;
int m = 3;
n = number of processes
m = number of resources
Allocation Matrix
int alloc[3][3]
Stores currently assigned resources.
Maximum Matrix
int max[3][3]
Stores maximum requirement.
Available Resources
int avail[3]={3,3,2};
Stores free resources.
Calculate Need
need[i][j] = max[i][j] - alloc[i][j];
Calculates remaining requirement.
Finish Array
int finish[3]={0};
Initially:
0 = Process not completed
1 = Process completed
Finding Safe Sequence
The program checks:
Need <= Available
If true:
• Process can execute.
• Add it to safe sequence.
• Release resources.
10. Program Flow
Start
Input Allocation, Maximum and Available resources
Calculate Need Matrix
Find process whose Need <= Available
↓
Execute process
Release resources
Add process to Safe Sequence
All processes completed?
Print Safe Sequence
End
11. Compile and Run in Ubuntu
Create file:
nano bankers.c
Compile:
gcc bankers.c -o bankers
Run:
./bankers
12. Expected Output
Following is the SAFE Sequence:
P1 -> P0 -> P2
Q1: What is Banker's Algorithm?
Answer:
A deadlock avoidance algorithm that checks whether resource allocation keeps the
system safe.
Q2: What is a safe state?
Answer:
A state where all processes can complete execution without deadlock.
Q3: What is Need Matrix?
Answer:
Need = Maximum - Allocation
It shows remaining resources required by a process.
Q4: How is Available updated?
Answer:
After completing a process:
Available = Available + Allocation
Q5: What happens if no safe sequence exists?
Answer:
The system is in an unsafe state and resource allocation may cause deadlock.
Banker's Algorithm = Check before giving resources.
Need = Max - Allocation
Safe Sequence = Order in which all processes can finish safely.
Lab 10 — Logical vs Physical Address Translation
Lab Objective
To understand the difference between logical address and physical address and learn
how a logical address is converted into a physical address using the Segment:Offset
mechanism.
1. Logical Address
A logical address is the address generated by the CPU during program execution.
• It belongs to the process.
• It is also called a virtual address.
• It is converted into a physical address before accessing memory.
Example:
Segment : Offset
1000H : 0020H
2. Physical Address
A physical address is the actual location in the computer's RAM where data is stored.
• Generated after address translation.
• Used by memory hardware.
3. Segment and Offset
In 16-bit systems like 8086, memory is divided into segments.
A logical address contains:
Segment + Offset
Segment
• Represents the starting location of a memory block.
• Stored in segment registers:
CS → Code Segment
DS → Data Segment
SS → Stack Segment
ES → Extra Segment
The segment value alone is not the complete address.
Offset
• Shows the distance from the beginning of the segment.
• Identifies the exact location inside the segment.
4. Why Shift Segment by 4 Bits?
In 8086 architecture:
• Segment address is shifted left by 4 bits.
• This converts it into a physical base address.
Formula:
Segment × 16
OR
Segment << 4
5. Physical Address Formula
Physical Address = (Segment × 16) + Offset
or
Physical Address = (Segment << 4) + Offset
6. Example
Given:
Segment = 1000H
Offset = 0020H
Step 1: Shift Segment
1000H × 16 = 10000H
Step 2: Add Offset
10000H + 0020H = 10020H
Final:
Physical Address = 10020H
7. Algorithm (Easy Steps)
1. Take segment address from user.
2. Take offset address from user.
3. Shift segment address left by 4 bits.
4. Add offset value.
5. Display physical address.
8. C Program Explanation
Header File
#include<stdio.h>
Used for:
• printf()
• scanf()
Variables
unsigned int segment, offset, physicalAddress;
Stores:
• Segment address
• Offset address
• Final physical address
Taking Input
scanf("%x",&segment);
Reads hexadecimal value.
Example:
1000
Address Calculation
physicalAddress = (segment << 4) + offset;
Steps:
1. Shift segment by 4 bits.
2. Add offset.
3. Store result.
Display Result
printf("%X", physicalAddress);
Displays the physical address in hexadecimal.
9. Program Flow
Start
↓
Enter Segment Address
Enter Offset Address
Shift Segment Left by 4 bits
Add Offset
Display Physical Address
End
10. Compile and Run in Ubuntu
Create file:
nano lab10.c
Compile:
gcc lab10.c -o lab10
Run:
./lab10
11. Sample Input
Enter Segment Address (hex): 1000
Enter Offset Address (hex): 0020
12. Sample Output
Physical Address: 10020
Q1: What is a logical address?
Answer:
An address generated by CPU during program execution. It is also called a virtual
address.
Q2: What is a physical address?
Answer:
The actual address location in RAM where data is stored.
Q3: What is the formula for physical address?
Answer:
Physical Address = (Segment × 16) + Offset
Q4: Why do we shift segment by 4 bits?
Answer:
Because 8086 converts a 16-bit segment value into a 20-bit physical address by
multiplying it by 16.
Q5: What is an offset?
Answer:
Offset is the distance from the beginning of a segment that identifies the exact memory
location.
Logical Address = Segment + Offset
Physical Address = Segment × 16 + Offset
Segment Shift Left 4 Bits → Add Offset → Physical Address
Lab 11: Calculating Paging Parameters in C
Objective
To understand paging in Operating Systems and calculate:
• Number of pages
• Page table size
using a C program.
Short Theory
Paging is a memory management technique where:
• Logical memory is divided into small fixed-size blocks called pages.
• Physical memory is divided into blocks called frames.
• Pages are stored in frames for faster memory management.
Formula:
Number of Pages = Logical Memory Size ÷ Page Size
Page Table Size = Number of Pages × 4 bytes
(Assuming each page table entry takes 4 bytes)
Algorithm (Steps)
1. Start the program.
2. Enter logical memory size.
3. Enter page size.
4. Calculate number of pages:
5. Number of Pages = Logical Memory / Page Size
6. Calculate page table size:
7. Page Table Size = Number of Pages × 4
8. Display the results.
9. End the program.
C Program
#include <stdio.h>
int main() {
int logicalMemory, pageSize;
int numberOfPages, pageTableSize;
printf("Enter Logical Memory Size (in KB): ");
scanf("%d", &logicalMemory);
printf("Enter Page Size (in KB): ");
scanf("%d", &pageSize);
numberOfPages = logicalMemory / pageSize;
pageTableSize = numberOfPages * 4;
printf("Number of Pages: %d\n", numberOfPages);
printf("Page Table Size: %d bytes\n", pageTableSize);
return 0;
Simple Code Explanation
Code Meaning
#include <stdio.h> Allows use of input/output functions
int logicalMemory Stores memory size
int pageSize Stores page size
scanf() Takes input from user
numberOfPages = logicalMemory / pageSize Calculates total pages
pageTableSize = numberOfPages * 4 Calculates page table size
printf() Displays output
return 0 Ends program
Example
Input:
Enter Logical Memory Size (in KB): 64
Enter Page Size (in KB): 4
Calculation:
Number of Pages = 64 / 4
= 16
Page Table Size = 16 × 4
= 64 bytes
Output:
Number of Pages: 16
Page Table Size: 64 bytes
Steps to Run in Ubuntu
1. Open Terminal.
2. Create a file:
nano lab11.c
3. Write/paste the program.
4. Save the file.
5. Compile:
gcc lab11.c -o lab11
6. Run:
./lab11
Paging = Divide Memory into Pages
• Memory ÷ Page Size = Number of Pages
• Pages × 4 = Page Table Size
Lab 12: Demand Paging Effective Access Time (EAT)
Objective
To understand Demand Paging and calculate Effective Access Time (EAT) using a C
program.
Short Theory
What is Virtual Memory?
Virtual memory is a technique that allows a computer to use secondary storage (hard
disk/SSD) as an extension of RAM.
What is Paging?
Paging is a memory management technique where:
• Logical memory is divided into pages.
• Physical memory is divided into frames.
• A page table maps pages to frames.
What is Demand Paging?
Demand Paging loads a page into RAM only when it is needed.
If a required page is not available in RAM, a page fault occurs.
Page Fault
A page fault happens when the CPU tries to access a page that is not currently in main
memory.
When a page fault occurs:
1. Operating system finds the page on disk.
2. Loads the page into RAM.
3. Restarts the instruction.
Effective Access Time (EAT)
Effective Access Time is the average time required to access memory considering:
• Normal memory access time.
• Page fault handling time.
Formula used:
EAT = 100(1-p) + (pfo + 2ro + dirty*pst + dirty*2*pst) × p
Where:
Term Meaning
p Page fault rate
pfo Page fault overhead
pst Page swap time
dirty Probability of dirty page
ro Restart overhead
eat Effective Access Time
Algorithm (Steps)
1. Start the program.
2. Enter page fault rate.
3. Enter page fault overhead.
4. Enter page swap time.
5. Enter dirty page percentage.
6. Enter restart overhead time.
7. Apply EAT formula.
8. Display Effective Access Time.
9. End program.
C Program
#include <stdio.h>
#include <stdlib.h>
int main()
double p, pfo, pst, dirty, ro, eat;
printf("Calculating Effective Access Time for Demand Paging\n");
printf("Enter Page Fault Rate (%): ");
scanf("%lf", &p);
p = p / 100;
printf("Enter Page Fault Overhead: ");
scanf("%lf", &pfo);
printf("Enter Page Swap Time: ");
scanf("%lf", &pst);
printf("Enter Dirty Page Percentage (%): ");
scanf("%lf", &dirty);
dirty = dirty / 100;
printf("Enter Restart Overhead Time: ");
scanf("%lf", &ro);
eat = 100*(1-p) +
(pfo + 2*ro + (dirty*pst) + (dirty*2*pst)) * p;
printf("\nEffective Access Time = %lf\n", eat);
return 0;
Simple Code Explanation
Code Purpose
#include<stdio.h> Allows input/output functions
double variables Stores decimal values
scanf() Takes input from user
p = p/100 Converts percentage into decimal
dirty = dirty/100 Converts dirty percentage
eat = formula Calculates Effective Access Time
printf() Displays result
return 0 Ends program
Example
Input:
Page Fault Rate: 10
Page Fault Overhead: 50
Page Swap Time: 1000
Dirty Page Percentage: 20
Restart Overhead Time: 5
Output:
Effective Access Time = calculated value
Steps to Run in Ubuntu
1. Create file
nano lab12.c
2. Compile program
gcc lab12.c -o lab12
3. Run program
./lab12
Demand Paging:
• Page needed → Load into RAM.
• Page missing → Page Fault.
• More page faults = slower system.
EAT = Normal Access Time + Page Fault Cost
Lab 13: LRU (Least Recently Used) Page Replacement Algorithm
Objective
To implement the LRU Page Replacement Algorithm in C and calculate the number of
page faults.
Short Theory
What is Page Replacement?
When a page fault occurs and memory is full, the operating system removes an existing
page and loads the required page.
What is LRU Algorithm?
LRU (Least Recently Used) replaces the page that has not been used for the longest
time.
Simple idea:
The page that was not used recently is most likely not needed soon.
Example:
• Page used recently → Keep it.
• Page not used for a long time → Replace it.
Why LRU is Used?
• Reduces page faults.
• Improves memory performance.
• Uses past usage information to predict future needs.
Algorithm (Steps)
1. Start the program.
2. Enter number of memory frames.
3. Enter number of pages in reference string.
4. Enter the page reference string.
5. Check each page:
o If page already exists → update its recent usage time.
o If empty frame exists → insert page.
o If memory is full → replace the least recently used page.
6. Count page faults.
7. Display total page faults.
8. End program.
C Program
#include <stdio.h>
int findLRU(int time[], int n)
int i, minimum = time[0], pos = 0;
for(i = 1; i < n; i++)
if(time[i] < minimum)
minimum = time[i];
pos = i;
return pos;
int main()
int frames[10], pages[30];
int no_of_frames, no_of_pages;
int time[10], counter = 0;
int flag1, flag2;
int i, j, pos;
int faults = 0;
printf("Enter number of frames: ");
scanf("%d", &no_of_frames);
printf("Enter number of pages: ");
scanf("%d", &no_of_pages);
printf("Enter reference string: ");
for(i = 0; i < no_of_pages; i++)
scanf("%d", &pages[i]);
for(i = 0; i < no_of_frames; i++)
frames[i] = -1;
for(i = 0; i < no_of_pages; i++)
flag1 = flag2 = 0;
for(j = 0; j < no_of_frames; j++)
if(frames[j] == pages[i])
counter++;
time[j] = counter;
flag1 = flag2 = 1;
break;
if(flag1 == 0)
for(j = 0; j < no_of_frames; j++)
if(frames[j] == -1)
counter++;
faults++;
frames[j] = pages[i];
time[j] = counter;
flag2 = 1;
break;
if(flag2 == 0)
pos = findLRU(time, no_of_frames);
counter++;
faults++;
frames[pos] = pages[i];
time[pos] = counter;
printf("\n");
for(j = 0; j < no_of_frames; j++)
printf("%d\t", frames[j]);
printf("\n\nTotal Page Faults = %d", faults);
return 0;
Simple Code Explanation
Code Meaning
frames[] Stores pages currently in memory
Code Meaning
pages[] Stores page reference string
time[] Stores last used time of pages
findLRU() Finds the page used least recently
counter Keeps track of page usage time
faults Counts page faults
frames[i] = -1 Shows empty memory frame
faults++ Increases page fault count
Example
Input:
Enter number of frames: 3
Enter number of pages: 5
Enter reference string:
12314
Output:
1 -1 -1
1 2 -1
1 2 3
1 2 3
1 4 3
Total Page Faults = 4
Steps to Run in Ubuntu
1. Create file:
nano lab13.c
2. Compile:
gcc lab13.c -o lab13
3. Run:
./lab13
LRU = Remove the page that was used Least Recently
Steps:
1. Check page.
2. If present → update time.
3. If absent → replace oldest page.
4. Count page faults.
LAB 14: Demand Paging and Program Structure in C
Objective
To understand how program structure affects Demand Paging performance by
comparing row-major and column-major access of 2D arrays in C.
1. Theory (Short)
Demand Paging
• Demand Paging loads a page into RAM only when it is needed.
• If a required page is not in memory, a page fault occurs.
• The operating system loads that page from disk into RAM.
Page Fault
• A page fault happens when a program accesses a page that is not currently in
memory.
• More page faults mean slower performance.
2. Program Structure and Paging
The way a program accesses memory affects page faults.
Row-Major Order
• C stores 2D arrays in row-major order.
• Elements of the same row are stored together in memory.
• Accessing rows sequentially causes fewer page faults.
• Faster execution.
Example:
for(i=0;i<rows;i++)
for(j=0;j<columns;j++)
A[i][j]=0;
Column-Major Order
• Accessing columns first does not follow C memory layout.
• Memory access becomes scattered.
• Causes more page faults.
• Slower execution.
Example:
for(j=0;j<columns;j++)
for(i=0;i<rows;i++)
A[i][j]=0;
3. Algorithm (Easy Steps)
1. Start program.
2. Create a 2D array.
3. Initialize array using column-major order.
4. Observe more page faults.
5. Create another 2D array.
6. Initialize array using row-major order.
7. Observe fewer page faults.
8. End program.
4. C Program
#include<stdio.h>
#include<stdlib.h>
int main()
printf("Demand Paging Performance Demo");
int A[1024][1024];
// Column-major order
for(int j=0; j<1024; j++)
for(int i=0; i<1024; i++)
A[i][j]=0;
printf("\nColumn-major order completed");
int B[1024][1024];
// Row-major order
for(int i=0; i<1024; i++)
for(int j=0; j<1024; j++)
B[i][j]=0;
printf("\nRow-major order completed");
return 0;
5. Code Explanation (Short)
Header Files
#include<stdio.h>
#include<stdlib.h>
Used for input/output functions.
Array Declaration
int A[1024][1024];
Creates a matrix of size 1024 × 1024.
Column-Major Access
for(j=0;j<1024;j++)
for(i=0;i<1024;i++)
• Column is accessed first.
• Memory locations are not continuous.
• More page faults occur.
Row-Major Access
for(i=0;i<1024;i++)
for(j=0;j<1024;j++)
• Row is accessed first.
• Matches C memory storage.
• Fewer page faults occur.
6. Compile and Run
Save file:
lab14.c
Compile:
gcc lab14.c -o lab14
Run:
./lab14
7. Output
Demand Paging Performance Demo
Column-major order completed
Row-major order completed
8. Conclusion
• Demand Paging loads pages when required.
• Program memory access pattern affects performance.
• Row-major order is faster because it causes fewer page faults.
• Column-major order causes more page faults and reduces performance.
Q1. What is Demand Paging?
Loading pages into memory only when required.
Q2. What is a page fault?
A page fault occurs when a required page is not present in RAM.
Q3. Which order is faster in C?
Row-major order.
Q4. Why is row-major faster?
Because C stores arrays row by row, so memory access is continuous.
Q5. What causes more page faults?
Column-major access.
LAB 15: Calculating Bit Map Overhead in Disk Free-Space Management (C)
Objective
To calculate the storage overhead required for a Bit Map (Bit Vector) method used in
disk free-space management.
1. Theory (Short)
Disk Free-Space Management
• The operating system keeps track of free and allocated disk blocks.
• It helps in efficient storage allocation.
Bit Map Method
• Each disk block is represented by 1 bit.
• 1 → Free block
• 0 → Allocated block
Example:
101001
Means some blocks are free and some are allocated.
2. Bit Map Overhead
The space required to store the bit map itself is called overhead.
Formula:
Number of Blocks:
Disk Size / Block Size
Bit Map Size:
(Number of Blocks × 1 bit)
Convert bits into KB:
Overhead = (Disk Size / Block Size) / (8 × 1024)
3. Algorithm (Easy Steps)
1. Start program.
2. Take block size from user.
3. Take disk size from user.
4. Convert disk size from GB to KB.
5. Calculate total number of blocks.
6. Calculate bit map overhead.
7. Display overhead in KB.
8. End program.
4. C Program
#include<stdio.h>
#include<stdlib.h>
int main()
double blockSize, diskSize, overhead;
printf("Calculating Bit Map Overhead\n");
printf("Enter block size (in KB): ");
scanf("%lf",&blockSize);
printf("Enter disk size (in GB): ");
scanf("%lf",&diskSize);
// Convert GB to KB
diskSize = diskSize * 1024 * 1024;
// Calculate overhead
overhead = (diskSize / blockSize) / (8 * 1024);
printf("Total Overhead (in KB): %lf", overhead);
return 0;
5. Code Explanation (Short)
Header Files
#include<stdio.h>
#include<stdlib.h>
• stdio.h → used for printf() and scanf().
• stdlib.h → used for system functions.
Variables
double blockSize, diskSize, overhead;
• blockSize → size of one disk block.
• diskSize → total disk capacity.
• overhead → bit map storage size.
Input
scanf("%lf",&blockSize);
scanf("%lf",&diskSize);
Takes block size and disk size from the user.
Unit Conversion
diskSize = diskSize * 1024 * 1024;
Converts:
GB → KB
Because:
1 GB = 1024 × 1024 KB
Overhead Calculation
overhead = (diskSize / blockSize) / (8*1024);
Steps:
1. Find total number of blocks:
Disk Size / Block Size
2. Each block requires 1 bit.
3. Convert bits → bytes:
Divide by 8
4. Convert bytes → KB:
Divide by 1024
6. Sample Input
Enter block size (in KB): 4
Enter disk size (in GB): 100
7. Sample Output
Total Overhead (in KB): 3.125000
8. Compile and Run
Save file:
lab15.c
Compile:
gcc lab15.c -o lab15
Run:
./lab15
9. Conclusion
• Bit Map is used to manage free disk space.
• Each disk block requires only 1 bit.
• The overhead is the memory required to store this bit map.
• Bit Map provides fast and efficient disk space management.
Q1. What is Bit Map?
A method to represent free and allocated disk blocks using bits.
Q2. What does bit 1 represent?
Free block.
Q3. What does bit 0 represent?
Allocated block.
Q4. Why is Bit Map efficient?
Because it quickly finds free blocks and requires less storage.
Q5. What is Bit Map overhead?
The space required to store the bit map itself.
LAB 16: Disk Space Management Simulation in C
Objective
To simulate disk space allocation and deallocation using a C program.
1. Theory (Short)
Disk Space Management
• The operating system manages free and used disk blocks.
• When a file is created, blocks are allocated.
• When a file is deleted, blocks are released.
Contiguous Allocation
• A file is stored in continuous disk blocks.
• It provides fast access.
• It requires finding consecutive free blocks.
Example:
Disk Blocks:
0001100
0 = Free Block
1 = Allocated Block
2. Program Working
The program uses an array to represent the disk.
• Array element = Disk block
• 0 = Free block
• 1 = Allocated block
Operations:
1. Allocate space for a file.
2. Find consecutive free blocks.
3. Mark blocks as allocated.
4. Delete a file.
5. Mark blocks as free.
3. Algorithm (Easy Steps)
Allocation:
1. Start scanning disk blocks.
2. Find continuous free blocks.
3. If required space is found:
o Mark blocks as 1.
o Display allocation message.
4. Otherwise display error.
Deallocation:
1. Take starting block and file size.
2. Change allocated blocks back to 0.
3. Display deallocation message.
4. C Program
#include <stdio.h>
#define TOTAL_BLOCKS 100
int disk[TOTAL_BLOCKS] = {0};
// Allocate disk space
void allocateSpaceForFile(int fileSize)
int startBlock = -1;
int count = 0;
for(int i = 0; i < TOTAL_BLOCKS; i++)
if(disk[i] == 0)
if(startBlock == -1)
startBlock = i;
count++;
if(count == fileSize)
{
for(int j=startBlock; j<startBlock+fileSize; j++)
disk[j] = 1;
printf("File allocated from block %d\n", startBlock);
return;
else
startBlock = -1;
count = 0;
printf("Not enough space available\n");
// Deallocate disk space
void deallocateSpaceForFile(int startBlock, int fileSize)
for(int i=startBlock; i<startBlock+fileSize; i++)
disk[i] = 0;
printf("File deleted from block %d\n", startBlock);
}
int main()
allocateSpaceForFile(20);
allocateSpaceForFile(30);
deallocateSpaceForFile(0,20);
allocateSpaceForFile(10);
return 0;
5. Code Explanation (Short)
Header File
#include<stdio.h>
Used for printf().
Disk Creation
int disk[TOTAL_BLOCKS]={0};
Creates 100 disk blocks.
Initially all blocks are free.
Allocation Function
void allocateSpaceForFile(int fileSize)
Allocates continuous blocks for a file.
Variables:
startBlock
Stores the first free block.
count
Counts consecutive free blocks.
Searching Free Space
if(disk[i]==0)
Checks whether the block is free.
Allocating Blocks
disk[j]=1;
Marks blocks as occupied.
Deallocation Function
void deallocateSpaceForFile()
Releases file space.
disk[i]=0;
Marks blocks as free again.
Main Function
allocateSpaceForFile(20);
Creates a 20-block file.
allocateSpaceForFile(30);
Creates a 30-block file.
deallocateSpaceForFile(0,20);
Deletes the first file.
allocateSpaceForFile(10);
Uses freed space for a new file.
6. Sample Output
File allocated from block 0
File allocated from block 20
File deleted from block 0
File allocated from block 0
7. Compile and Run
Save file:
lab16.c
Compile:
gcc lab16.c -o lab16
Run:
./lab16
8. Conclusion
• Disk space management keeps track of free and allocated blocks.
• Contiguous allocation stores files in consecutive blocks.
• Allocation marks blocks as 1.
• Deallocation changes blocks back to 0.
• This simulation helps understand how operating systems manage storage.
Q1. What is disk space management?
It is the process of managing free and used disk blocks.
Q2. What is contiguous allocation?
A method where a file is stored in consecutive disk blocks.
Q3. What does 0 represent in the program?
Free block.
Q4. What does 1 represent in the program?
Allocated block.
Q5. What is the disadvantage of contiguous allocation?
It may cause external fragmentation because continuous free space is required.