OS Final Lab File [Final][Final][Final]
OS Final Lab File [Final][Final][Final]
Semester- IV
Lab File
Operating System
(BCS451)
Submitted To : Submitted By :
Faculty Name : Name :
Designation : Roll No. :
Section :
Table of Contents
• Vision and Mission Statements of the Institute
• List of Experiments
• Index
Department Vision Statement
To be a recognized Department of Computer Science & Engineering that produces versatile computer
engineers, capable of adapting to the changing needs of computer and related industry.
i. To provide broad based quality education with knowledge and attitude to succeed in Computer
Science & Engineering careers.
ii. To prepare students for emerging trends in computer and related industry.
iii. To develop competence in students by providing them skills and aptitude to foster culture of
continuous and lifelong learning.
iv. To develop practicing engineers who investigate research, design, and find workable solutions to
complex engineering problems with awareness & concern for society as well as environment.
ii. Graduates will possess capability of designing successful innovative solutions to real life problems
that are technically sound, economically viable and socially acceptable.
iii. Graduates will be competent team leaders, effective communicators and capable of working in
multidisciplinary teams following ethical values.
iv. The graduates will be capable of adapting to new technologies/tools and constantly upgrading their
knowledge and skills with an attitude for lifelong learning
Department Program Outcomes (POs)
The students of Computer Science and Engineering Department will be able:
1. Engineering knowledge: Apply the knowledge of mathematics, science, Computer Science &
Engineering fundamentals, and an engineering specialization to the solution of complex engineering
problems.
2. Problem analysis: Identify, formulate, review research literature, and analyze complex
engineering problems reaching substantiated conclusions using first principles of mathematics,
natural sciences, and Computer Science & Engineering sciences.
5. Modern tool usage: Create, select, and apply appropriate techniques, resources, and modern
engineering and IT tools including prediction and modelling to complex Computer Science &
Engineering activities with an understanding of the limitations.
6. The Engineering and Society: Apply reasoning informed by the contextual knowledge to assess
societal, health, safety, legal and cultural issues and the consequent responsibilities relevant to the
professional engineering practice in the field of Computer Science and Engineering.
7. Environment and sustainability: Understand the impact of the professional Computer Science
& Engineering solutions in societal and environmental contexts, and demonstrate the knowledge of,
and need for sustainable development.
8. Ethics: Apply ethical principles and commit to professional ethics and responsibilities and norms
of the Computer Science & Engineering practice.
9. Individual and team work: Function effectively as an individual, and as a member or leader in
diverse teams, and in multidisciplinary settings.
11. Project management and finance: Demonstrate knowledge and understanding of the Computer
Science & Engineering and management principles and apply these to one’s own work, as a member
and leader in a team, to manage projects and in multidisciplinary environments.
12. Life-long learning: Recognize the need for, and have the preparation and ability to engage in
independent and life-long learning in the broadest context of technological change.
Department Program Specific Outcomes (PSOs)
The students will be able to:
2. Understand the processes that support the delivery and management of information systems
within a specific application environment.
Course Outcomes
*Level of Bloom’s Level to be
*Level of Bloom’s Taxonomy Level to be met
Taxonomy met
L1: Remember 1 L2: Understand 2
L3: Apply 3 L4: Analyze 4
L5: Evaluate 5 L6: Create 6
10
EXPERIMENT 1: Unix commands practice.
UNIX Commands:
1. Date Command :
2. Calendar Command :
This command is used to display the calendar of the year or the particular month of calendar year.
Syntax :
$cal<year>
$cal<month><year>
Here the first syntax gives the entire calendar for given year & the second Syntax gives the calendar
of reserved month of that year.
3. Echo Command :
This command is used to print the arguments on the screen .
Syntax : $echo <text>
Multi line echo command :
To have the output in the same line , the following commands can be used.
Syntax : $echo <text\>text
To have the output in different line, the following command can be used.
Syntax : $echo “text
>line2
>line3”
4. ‘who’ Command :
It is used to display who are the users connected to our computer currently.
Syntax : $who – option‟s
Options : -
H–Display the output with headers.
b–Display the last booting date or time or when the system was lastely rebooted.
5. ‘who am i’ Command :
Display the details of the current working directory.
Syntax : $who am i
6. ‘tty’ Command :
It will display the terminal name.
Syntax : $tty
7. ‘CLEAR’ Command :
It is used to clear the screen.
Syntax : $clear
8. ‘MAN’ Command :
It help us to know about the particular command and its options & working. It is like
„help‟ command in windows .
Syntax : $man <command name>
9. MANIPULATION Command :
It is used to manipulate the screen. Syntax :$tput<argument>
Arguments :
1. Clear – to clear the screen.
2. Longname – Display the complete name of the terminal.
3. SMSO – background become white and foreground become black color. 4.rmso – background
become black and foreground becomes white color. 5.Cols – Display the number of columns in our
terminals.
2. MKDIR Command :
To create or make a new directory in a current directory .Syntax :$mkdir<directory name>
3. CD Command :
To change or move the directory to mentioned directory .Syntax :$cd <directory name>
4. RMDIR Command :
To remove a directory in the current directory & not the current directory itself.
Syntax :$rmdir<directory name>
1. CREATE A FILE :
To create a new file in the current directory we use CAT command.
Syntax :
$cat > filename.
The > symbol is redirectory we use cat command.
2. DISPLAY A FILE :
To display the content of file mentioned we use CAT command without „>‟ operator.
Syntax :
$cat <filename.
Options –s = to neglect the warning /error message.
3. COPYING CONTENTS :
To copy the content of one file with another. If file doesnot exist, a new file is created
and if the file exists with some data then it is overwritten.
Syntax :
$ cat<filename source>>><destination filename>
Options : -
-n content of file with numbers included with blank lines.
Syntax :
$cat –n <filename>
4. SORTING A FILE :
To sort the contents in alphabetical order or in reverse order.
Syntax :
$sort <filename >
Option : $ sort –r <filename>
6. MOVE Command :
To completely move the contents from source file to destination file and to remove the source file.
Syntax :
$ mv<source filename><destination filename>
7. REMOVE Command :
To permanently remove the file we use this command .
Syntax :
$rm<filename>
8. WORD Command :
To list the content count of no of lines , words, characters .
Syntax :
$wc<filename>
Options :
-c – to display no of characters.
-l – to display only the lines.
-w – to display the no of words.
9. LINE PRINTER :
To print the line through the printer, we use lp command.
Syntax :
$lp<filename>
10. PAGE Command :
This command is used to display the contents of the file page wise & next page can be
viewed by pressing the enter key.
Syntax :
$pg<filename>
MORE :It also displays the file page by page .To continue scrolling with more command ,press the
space bar key.
Syntax: $more<filename>
GREP :This command is used to search and print the specified patterns from the file.
Syntax: $grep [option] pattern <filename>
PIPE :It is a mechanism by which the output of one command can be channeled into the input of
another command.
Syntax: $who | wc-l
TR :The tr filter is used to translate one set of characters from the standard inputs to another.
Syntax: $tr “[a-z]” “[A-Z]”
EXPERIMENT 2: Implementation of CPU Scheduling Algorithms
a) FCFS b) SJF
a.) FCFS
Algorithm:
(i) We randomly generate the number of jobs. There must be a limit on the number of jobs in a system.
(ii) The execution time of the generated jobs is also not known. Here, we are generating the CPU burst
of each job making use of the past history.
(iii) All the jobs are then arranged in a queue where searching is done to find the one with the least
CPU burst. There may be two jobs in queue with the same execution time then FCFS approach is to be
performed.
Program:
#include <stdio.h>
struct Process {
int id;
int arrival_time;
int burst_time;
int waiting_time;
int turnaround_time;
int completion_time;
};
processes[0].waiting_time = 0;
processes[0].completion_time = processes[0].arrival_time + processes[0].burst_time;
processes[0].turnaround_time = processes[0].burst_time;
printf("\nProcess\tArrival\tBurst\tCompletion\tTurnaround\tWaiting\n");
for (int i = 0; i < n; i++) {
printf("P%d\t%d\t%d\t%d\t\t%d\t\t%d\n",
processes[i].id,
processes[i].arrival_time,
processes[i].burst_time,
processes[i].completion_time,
processes[i].turnaround_time,
processes[i].waiting_time);
}
int main() {
int n;
printf("Enter the number of processes: ");
scanf("%d", &n);
calculateTimes(processes, n);
return 0;
}
b.) SJF
Algorithm:
(i) We randomly generate the number of jobs. There must be a limit on the number of jobs in a system.
(ii) The execution time of the generated jobs is also not known. Here, we are generating the CPU burst
of each job making use of the past history.
(iii) All the jobs are then arranged in a queue where searching is done to find the one with the least
CPU burst. There may be two jobs in queue with the same execution time then FCFS approach is to be
performed.
Note: If the algorithm is non preemptive in nature, then the newly arriving job is to be added to the
job queue even though it is of lesser execution time than the one running on the processor.
Program:
#include <stdio.h>
struct Process {
int id;
int arrival_time;
int burst_time;
int waiting_time;
int turnaround_time;
int completion_time;
};
while (completed != n) {
int idx = -1;
int min_burst = 99999;
if (idx != -1) {
processes[idx].completion_time = current_time + processes[idx].burst_time;
processes[idx].turnaround_time = processes[idx].completion_time - processes[idx].arrival_time;
processes[idx].waiting_time = processes[idx].turnaround_time - processes[idx].burst_time;
total_waiting_time += processes[idx].waiting_time;
total_turnaround_time += processes[idx].turnaround_time;
completed++;
current_time = processes[idx].completion_time;
} else {
current_time++;
}
}
printf("\nProcess\tArrival\tBurst\tCompletion\tTurnaround\tWaiting\n");
int main() {
int n;
printf("Enter the number of processes: ");
scanf("%d", &n);
calculateTimes(processes, n);
return 0;
}
EXPERIMENT 3: Implementation of CPU Scheduling Algorithms
a) Priority (Non-Pre-emptive)
b) Multi-level Queue (Level-1 Time Quantum = 2 ms, Level-2 Time
Quantum = 4 ms, Level-3 FCFS)
a) Priority (Non-Pre-emptive)
Algorithm:
For PRIORITY algorithm,
(i) We again prefer to compute the CPU burst from past history.
(ii) Priority may be assigned on the basis of their CPU burst (simplest approach)
Program:
#include <stdio.h>
struct Process {
int id;
int arrival_time;
int burst_time;
int priority;
int waiting_time;
int turnaround_time;
int completion_time;
};
while (completed != n) {
int idx = -1;
int highest_priority = -1;
total_waiting_time += processes[idx].waiting_time;
total_turnaround_time += processes[idx].turnaround_time;
completed++;
current_time = processes[idx].completion_time;
} else {
current_time++;
}
}
int main() {
int n;
calculateTimes(processes, n);
return 0;
}
b) Multi-level Queue (Level-1 Time Quantum = 2 ms, Level-2 Time Quantum = 4
ms, Level-3 FCFS)
Program:
#include <stdio.h>
#include <stdbool.h>
struct Process {
int id;
int arrival_time;
int burst_time;
int remaining_time;
int waiting_time;
int turnaround_time;
int completion_time;
int queue_level;
bool is_completed;
};
done = false;
if (processes[i].remaining_time > tq) {
*current_time += tq;
processes[i].remaining_time -= tq;
} else {
*current_time += processes[i].remaining_time;
processes[i].remaining_time = 0;
processes[i].completion_time = *current_time;
processes[i].turnaround_time = processes[i].completion_time - processes[i].arrival_time;
processes[i].waiting_time = processes[i].turnaround_time - processes[i].burst_time;
processes[i].is_completed = true;
}
}
}
} while (!done);
}
// FCFS for a specific queue level
void fcfs(struct Process processes[], int n, int queue_level, int *current_time) {
for (int i = 0; i < n; i++) {
if (processes[i].queue_level == queue_level && !processes[i].is_completed) {
if (*current_time < processes[i].arrival_time)
*current_time = processes[i].arrival_time;
*current_time += processes[i].burst_time;
processes[i].completion_time = *current_time;
processes[i].turnaround_time = processes[i].completion_time - processes[i].arrival_time;
processes[i].waiting_time = processes[i].turnaround_time - processes[i].burst_time;
processes[i].is_completed = true;
}
}
}
printf("\nProcess\tArrival\tBurst\tQueue\tCompletion\tTurnaround\tWaiting\n");
for (int i = 0; i < n; i++) {
printf("P%d\t%d\t%d\t%d\t%d\t\t%d\t\t%d\n",
processes[i].id,
processes[i].arrival_time,
processes[i].burst_time,
processes[i].queue_level,
processes[i].completion_time,
processes[i].turnaround_time,
processes[i].waiting_time);
total_waiting_time += processes[i].waiting_time;
total_turnaround_time += processes[i].turnaround_time;
}
int main() {
int n;
printf("Enter the number of processes: ");
scanf("%d", &n);
int current_time = 0;
roundRobin(processes, n, 2, 1, ¤t_time);
roundRobin(processes, n, 4, 2, ¤t_time);
fcfs(processes, n, 3, ¤t_time);
calculateTimes(processes, n);
return 0;
}
EXPERIMENT 4: Implementation of resource allocation graph (RAG).
Program:
#include <stdio.h>
#define MAX 100
int main() {
int processes, resources;
int allocation[MAX][MAX];
int request[MAX][MAX];
// Display RAG
printRAG(allocation, request, processes, resources);
return 0;
}
EXPERIMENT 5: Implementation of Banker’s algorithm for Deadlock
Avoidance.
Theory:
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 allocated ‘k’ instances of resource type Rj
• 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.
Algorithm:
Banker’s algorithm consist of Safety algorithm and Resource request algorithm
Safety Algorithm
The algorithm for finding out whether or not a system is in a safe state can be described as follows:
1. Let Work and Finish be vectors of length ‘m’ and ‘n’ respectively.
Initialize: Work= Available
Finish [i]=false; for i=1,2,……,n
2. Find an i such that both
a) Finish [i]=false
b) Need_i<=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 safe state.
Once the resources are allocated, check to see if the system state is safe. If unsafe, the process must wait
and the old resource-allocated state is restored.
Program:
#include <stdio.h>
#include <stdbool.h>
#define MAX_PROCESSES 10
#define MAX_RESOURCES 10
int need[MAX_PROCESSES][MAX_RESOURCES];
calculateNeed(need, max, allocation, processes, resources);
while (true) {
bool found = false;
if (!found) {
break;
}
}
// Main function
int main() {
int processes, resources;
int allocation[MAX_PROCESSES][MAX_RESOURCES];
int max[MAX_PROCESSES][MAX_RESOURCES];
int available[MAX_RESOURCES];
// Input
printf("Enter the number of processes: ");
scanf("%d", &processes);
printf("Enter the number of resources: ");
scanf("%d", &resources);
return 0;
}
EXPERIMENT 6: Conversion of resource allocation graph (RAG) to wait
for graph (WFG) for each type of method used for storing graph.
Program:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int from, to;
} Edge;
if (choice == 1) {
printf("Enter Adjacency Matrix (%d x %d):\n", total, total);
for (int i = 0; i < total; i++) {
for (int j = 0; j < total; j++) {
scanf("%d", &adjMatrix[i][j]);
}
}
} else if (choice == 2) {
int edges;
printf("Enter number of edges in RAG: ");
scanf("%d", &edges);
printf("Enter edges (from to):\n");
for (int i = 0; i < edges; i++) {
int from, to;
scanf("%d %d", &from, &to);
addEdgeToList(adjList, from, to);
}
convertListToWFG();
} else if (choice == 3) {
printf("Enter number of edges in RAG: ");
scanf("%d", &edgeCount);
convertEdgeListToWFG();
return 0;
}
Experiment7: Implementation of contiguous allocation techniques:
a) Worst-Fit b) Best- Fit c) First- Fit
Theory:
One of the simplest methods for memory allocation is to divide memory into several fixed-sized
partitions. Each partition may contain exactly one process. In this multiple-partition method, when a
partition is free, a process is selected from the input queue and is loaded into the free partition. When the
process terminates, the partition becomes available for another process. The operating system keeps a
table indicating which parts of memory are available and which are occupied. Finally, when a process
arrives and needs memory, a memory section large enough for this process is provided. When it is time to
load or swap a process into main memory, and if there is more than one free block of memory of
sufficient size, then the operating system must decide which free block to allocate. Best-fit strategy
chooses the block that is closest in size to the request. First-fit chooses the first available block that is
large enough. Worst-fit chooses the largest available block.
First Fit:
In the first fit approach is to allocate the first free partition or hole large enough which can
accommodate the process. It finishes after finding the first suitable free partition.
Best Fit:
The best fit deals with allocating the smallest free partition which meets the requirement of the
requesting process. This algorithm first searches the entire list of free partitions and considers the
smallest hole that is adequate. It then tries to find a hole which is close to actual process size needed.
Worst fit:
In worst fit approach is to locate largest available free portion so that the portion left will be big
enough to be useful. It is the reverse of best fit.
Algorithm:
First Fit
1. Get no. of Processes and no. of blocks.
2. After that get the size of each block and process requests.
3. Now allocate processes
if(block size >= process size)
//allocate the process
else
//move on to next block
4. Display the processes with the blocks that are allocated to a respective process.
5. Stop.
Best Fit
1. Get no. of Processes and no. of blocks.
2. After that get the size of each block and process requests.
3. Then select the best memory block that can be allocated using the above definition.
4. Display the processes with the blocks that are allocated to a respective process.
5. Value of Fragmentation is optional to display to keep track of wasted memory.
6. Stop.
Worst Fit
1. Get no. of Processes and no. of blocks.
2. After that get the size of each block and process requests.
3. Then select the largest available memory block that can be allocated using the above definition.
4. Display the processes with the blocks that are allocated to a respective process.
5. Value of Fragmentation is optional to display to keep track of wasted memory.
6. Stop.
Program:
#include <stdio.h>
int block_sizes[num_blocks];
printf("Enter block sizes: ");
for (int i = 0; i < num_blocks; i++) {
scanf("%d", &block_sizes[i]);
}
int process_sizes[num_processes];
printf("Enter process sizes: ");
for (int i = 0; i < num_processes; i++) {
scanf("%d", &process_sizes[i]);
}
int choice;
printf("Enter allocation technique (1-WorstFit, 2-BestFit, 3-FirstFit): ");
scanf("%d", &choice);
printf("\nAllocation results:\n");
switch (choice) {
case 1:
for (int i = 0; i < num_processes; i++) {
worstFit(block_sizes, num_blocks, process_sizes[i]);
}
break;
case 2:
for (int i = 0; i < num_processes; i++) {
bestFit(block_sizes, num_blocks, process_sizes[i]);
}
break;
case 3:
for (int i = 0; i < num_processes; i++) {
firstFit(block_sizes, num_blocks, process_sizes[i]);
}
break;
default:
printf("Invalid choice\n");
}
return 0;
}
Experiment 8: Implement the solution for Bounded Buffer (producer-
consumer) problem using inter process communication techniques-
Semaphores.
Program:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <sys/wait.h>
#define BUFFER_SIZE 5
shared->buffer[shared->rear] = item;
shared->rear = (shared->rear + 1) % BUFFER_SIZE;
sem_post(&shared->mutex);
sem_post(&shared->full);
}
sem_wait(&shared->full);
sem_wait(&shared->mutex);
item = shared->buffer[shared->front];
shared->front = (shared->front + 1) % BUFFER_SIZE;
sem_post(&shared->mutex);
sem_post(&shared->empty);
return item;
}
int main() {
int num_items;
printf("Enter the number of items to produce and consume: ");
scanf("%d", &num_items);
// Allocate shared memory
shared_data_t *shared = mmap(NULL, sizeof(shared_data_t),
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (shared == MAP_FAILED) {
perror("mmap");
exit(1);
}
return 0;
}
Experiment 9: Implement the solutions for Readers-Writers problem
using inter process communication technique –Semaphore.
Program:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <sys/wait.h>
sem_wait(&shared->mutex);
shared->read_count--;
if (shared->read_count == 0) {
sem_post(&shared->w_mutex); // last reader lets writers in
}
sem_post(&shared->mutex);
}
int main() {
int num_writers, num_readers;
// Cleanup
sem_destroy(&shared->mutex);
sem_destroy(&shared->w_mutex);
munmap(shared, sizeof(shared_data_t));
return 0;
Experiment 10: Implementation of System calls.
THEORY:
This code implements a menu-driven program that allows the user to choose from the
following system calls:
- Create Process (fork)
- Exit
Program:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
int choice, status;
pid_t pid;
while (1) {
printf("\nSystem Calls Menu:\n");
printf("1. Create Process (fork)\n");
printf("2. Execute Command (execv)\n");
printf("3. Wait for Process (wait)\n");
printf("4. Exit\n");
printf("Enter your choice: ");
scanf("%d", &choice);
switch (choice) {
case 1:
pid = fork();
if (pid == 0) {
// Child process
printf("Child process (PID: %d) created.\n", getpid());
exit(0); // Ensure child exits
} else if (pid > 0) {
// Parent process
waitpid(pid, &status, 0); // Wait for child to finish
printf("Parent process (PID: %d) continued after child.\n", getpid());
} else {
perror("fork failed");
}
break;
case 2: {
char *args[] = {"/bin/ls", "-l", NULL};
execv("/bin/ls", args);
perror("execv failed"); // only reached if execv fails
break;
}
case 3:
pid = wait(&status);
if (pid > 0) {
printf("Process %d terminated with status %d\n", pid, WEXITSTATUS(status));
} else {
perror("wait failed");
}
break;
case 4:
exit(0);
default:
printf("Invalid choice\n");
}
}
return 0;
}