Operating System Lab Manual
Operating System Lab Manual
OS - Lab - SEM IV
(CSL403)
Practical 1
Aim: Explore basic Linux Commands and System Calls
Theory:
The Linux command line is a text interface to your computer. Often referred to as the
shell,terminal, console, prompt or various other names,it can give the appearance of being
complex and confusing to use but once you understand it, it’s really easy and helpful.
● ls - list directory contentsList information about the FILEs (the current directory by
default). Sort entries alphabetically if none of -cftuvSUX nor --sort isspecified.
● cd - change the working directoryThe cd utility shall change the working directory
of the current shell executionenvironment
grep searches for PATTERNS in each FILE. PATTERNSis one or more patterns
separated by newline characters, and grep prints each line that matches a
pattern.Typically PATTERNS should be quoted when grep is use din a shell command.
setuid() sets the effective user ID of the calling process. If the calling process is
privileged (more precisely: if the process has theCAP_SETUID capability in its user
namespace), the real UID and saved set-user-ID arealso set.
● sort - sort lines of text files
Write sorted concatenation of all FILE(s) to standardoutput. With no FILE, or
whenFILE is -, read standard input.
● | - pipelining
A pipe is a form of redirection (transfer of standardoutput to some other
destination)that is used in Linux and other Unix-like operating systems to send the output
of one command/program/process to another command/program/process for further
processing.
Practical 2
Aim: Linux Shell Script
● Display OS version, release number, kernel version
● Display top 10 processes in descending order
● Display processes with the highest memory usage
● Display current logged in user and log name
Theory:
A shell is a command-line interpreter and typical operations performed by shell script
include file manipulation, program execution, and printing text.
Linux command displays system information like NAME, VERSION, ID and even
VERSION_ID among other things. Since Nix systems like to be command bases, there
are many commands which helps in our need.
This command displays top 10 processes, there are flags that enable display
more precise information like pid, ppid, %mem and we’ve used --sortflag to sort
the output according to%mem i.e memory usage and we’ve pipelined it to head
to display 10 processes.
It displays the highest memory usage process in the system, as you can see from
the below posted image, it's currently firefox browser.
whoami displays current user and other commands have been executed to
display current BASH SHELL and pwd displays current working directory.
Program:
#!/bin/sh
echo "--------------------"
echo "Shell Script OSL Practical 2"
echo "OS INFO: "
cat /etc/os-release
uname -r
uname -a
uname -v
echo "--------------------------------"
whoami
logname
echo "$SHELL"
pwd
uname -srm
echo "--------------------------------"
OUTPUT:
Practical 3
Aim: Linux -API Implementing “CP” command
Theory:
Implementing linux “cp” command to copy one file to another
Description:
Mandatory arguments to long options are mandatory for short options too.
-a, --archive same as -dR --preserve=all
--attributes -only don't copy the file data, just the attributes
--backup make a backup of each existing destination file
-b like --backup but does not accept an argument
Program:
#include<stdio.h>
#include<stdlib.h>
int main(){
FILE *fptr1, *fptr2;
char filename[100],c;
printf("Enter the filename to open for reading\n");
scanf("%s",filename);// Open one file for reading
fptr1=fopen(filename,"r");
if(fptr1==NULL){
printf("Cannot open file %s\n", filename);
exit(0);
}
printf("Enter the filename to open for writing\n");
scanf("%s",filename);
fptr2=fopen(filename,"w");
if(fptr2==NULL){
printf("Cannot open file %s\n",filename);
exit(0);
}
c=fgetc(fptr1);
while(c!=EOF){
fputc(c,fptr2);
c=fgetc(fptr1);
}
printf("\nContents copied to %s",filename);
fclose(fptr1);
fclose(fptr2);
return 0;
}
OUTPUT:
Practical 4
Aim: To create a child process using fork system call. Obtain process ID of both child
and parent using getpid and getppid system call.
Theory:
Fork system call is used for creating a new process, which is called a child process,
which runs concurrently with the process that makes the fork() call (parent process).
After a new child process is created, both processes will execute the next instruction
following the fork() system call. A child process uses the same pc (program counter),
same CPU registers, same open files which are used in the parent process.
It takes no parameters and returns an integer value.Below are different values returned by
fork().
● Negative Value: creation of a child process was unsuccessful.
● Zero: Returned to the newly created child process.
● Positive value: Returned to parent or caller. The value contains the process ID of
the newly created child process.
Syntax for fork() system call is : Int pid=fork(); This is used to create a new process
which becomes the child process of fork call process(parent process) Later both will
execute together.
Syntax for getpid :getpid(); It is used to print the process Id of current process or
childprocess.
Syntax for getppid: getppid(); It is used to print the process Id(PID) of the parent process
Program:
#include <stdio.h>
#include <stdlib.h>
int fork();
int getpid();
int getppid();
int main() {
int pid = fork();
if (pid == 0) {
printf("This is the child process. My pid is %d and my parent's id is
%d.\n",
getpid(),
getppid());
}
else {
printf("This is the parent process. My pid is %d and my parent's id is
%d.\n",
getpid(),
pid);
}
return 0;
}
OUTPUT:
Practical 5
Aim: Process management: Scheduling
● Write a program to demonstrate the concept of non-preemptive scheduling
algorithms.
● Write a program to demonstrate the concept of preemptive scheduling
algorithms.
Theory:
1. Non Preemptive Scheduling:
Preemptive scheduling is used when a process switches from running state to ready
state or from waiting state to ready state. The resources (mainly CPU cycles) are
allocated to the process for a limited amount of time and then are taken away, and the
process is again placed back in the ready queue if that process still has CPU burst time
remaining. That process stays in the ready queue till it gets its next chance to execute.
2. Preemptive Scheduling:
#include<stdio.h>
int main(){
int burst_time[20], process[20], waiting_time[20], turnaround_time[20],
priority[20];
int i, j, limit, sum = 0, position, temp;
float average_wait_time, average_turnaround_time;
printf("Enter Total Number of Processes:\t");
scanf("%d", &limit);
printf("\nEnter Burst Time and Priority For %d Processes\n", limit);
for(i = 0; i < limit; i++) {
printf("\nProcess[%d]\n", i + 1);
printf("Process Burst Time:\t");
scanf("%d", &burst_time[i]);
printf("Process Priority:\t");
scanf("%d", &priority[i]);
process[i] = i + 1;
}
Preemptive Scheduling:
Program:
#include<stdio.h>
#define executed 1
#define not_exec 0
int main() {
int n,i;
float awt,atat;
process pr[20];
printf("\nEnter the no. of processes:");
scanf("%d",&n);
Theory:
A semaphore S is an integer variable that can be accessed only through two standard
operations : wait() and signal().
The wait() operation reduces the value of semaphore by 1 and the signal() operation
increases its value by 1.
We have a buffer of fixed size. A producer can produce an item and can place it in the
buffer. A consumer can pick items and can consume them. We need to ensure that when a
producer is placing an item in the buffer, then at the same time the consumer should not
consume any item. In this problem, the buffer is the critical section.
To solve this problem, we need two counting semaphores – Full and Empty. “Full” keeps
track of the number of items in the buffer at any given time and “Empty” keeps track of
the number of unoccupied slots.
Program:
#include <stdio.h>
#include <stdlib.h>
int mutex = 1;
int full = 0;
int empty = 10, x = 0;
void producer() {
--mutex;
++full;
--empty;
x++;
printf("\nProducer produces" "item %d", x);
++mutex;
}
void consumer() {
--mutex;
--full;
++empty;
printf("\nConsumer consumes "
"item %d", x);
x--;
++mutex;
}
int main() {
int n, i;
printf("\n1. Press 1 for Producer"
"\n2. Press 2 for Consumer"
"\n3. Press 3 for Exit");
for (i = 1; i > 0; i++) {
printf("\nEnter your choice:");
scanf("%d", &n);
switch (n) {
case 1:
Practical 7
Aim: Write a program to demonstrate the concept of deadlock avoidance through
Banker’s Algorithm.
Theory:
When a new process enters the system, it must declare the maximum number of
instances of each resource type that it may need. This number may not exceed the total
number of resources in the system.When a user requests a set of resources, the system must
determine whether the allocation of these resources will leave the system in a safe state. Ifit
will, the resources are allocated; otherwise, the process must wait until some other process
releases enough resources.
● Need. An n × m matrix indicates the remaining resources needed for each process. If
Need[i][j] equals k, then process P imay need k more instances of resource type R j to
complete its task. Note thatNeed[i][j] equals Max[i][j]− Allocation[i][j]
Program:
#include <stdio.h>
int main() {
int n, m, i, j, k;
n = 5; // Number of processes
m = 3; // Number of resources
int alloc[5][3] = { { 0, 1, 0 }, // P0 // Allocation Matrix {
2, 0, 0 }, // P1
{ 3, 0, 2 }, // P2
{ 2, 1, 1 }, // P3
{ 0, 0, 2 } }; // P4
int flag = 0;
for (j = 0; j < m; j++) {
if (need[i][j] > avail[j]){
flag = 1;
break;
}
}
if (flag == 0) {
ans[ind++] = i;
for (y = 0; y < m; y++)
avail[y] += alloc[i][y];
f[i] = 1;
}
}
}
}
printf("Following is the SAFE Sequence\n");
for (i = 0; i < n - 1; i++)
printf(" P%d ->", ans[i]);
printf(" P%d", ans[n - 1]);
return (0);
}
OUTPUT:
Practical 8
Aim: Write a program to demonstrate the concept of dynamic partitioning placement
algorithms i.e., Best Fit, First Fit, Worst-Fit etc.
Theory:
This procedure is a particular instance of the general dynamic storage-allocation problem,
which concerns how to satisfy a request of size n from a list of free holes. There are many
solutions to this problem. The first-fit, best-fit, and worst-fit strategy are the ones most
commonly used to select a freehold from the set of available holes.
● First fit: Allocate the first hole that is big enough.Searching can start either at the
beginning of the set of holes or at the location where the previous first-fit search
ended. We can stop searching as soon as we find a free holethat is large enough.
● Best fit: Allocate the smallest hole that is big enough. We must search the entire list,
unless the list is ordered by size. This Strategy produces the smallest leftover hole.
● Worst fit: Allocate the largest hole. Again, we must search the entire list,unless it is
sorted by size. This strategy produces the largest leftover hole,which may be more
useful than the smaller leftoverhole from a best-fit approach
Simulations have shown that both first fit and best fit are better than worst fit in terms of
decreasing time and storage utilization.Neither first fit nor bestfit is clearly better than the
other in terms of storage utilization, but first fit is generally faster.
Best Fit Algorithm
Program:
#include<stdio.h>
#define max 25
int main(){
int frag[max],b[max],f[max],i,j,nb,nf,temp,lowest=10000;
static int bf[max],ff[max];
printf("\nEnter the number of blocks:");
scanf("%d",&nb);
printf("Enter the number of files:");
scanf("%d",&nf);
printf("\nEnter the size of the blocks:-\n");
for(i=1;i<=nb;i++) {
printf("Block %d:",i);
scanf("%d",&b[i]);
}
printf("Enter the size of the files :-\n");
for(i=1;i<=nf;i++) {
printf("File %d:",i);
scanf("%d",&f[i]);
}
for(i=1;i<=nf;i++) {
for(j=1;j<=nb;j++) {
if(bf[j]!=1) {
temp=b[j]-f[i];
if(temp>=0)
if(lowest>temp) {
ff[i]=j;
lowest=temp;
}
}
}
frag[i]=lowest;
bf[ff[i]]=1;
lowest=10000;
}
printf("\nFile No\tFile Size \tBlock No\tBlock Size\tFragment");
for(i=1;i<=nf && ff[i]!=0;i++)
printf("\n%d\t\t%d\t\t%d\t\t%d\t\t%d",i,f[i],ff[i],b[ff[i]],frag[i]);
}
OUTPUT:
First Fit Algorithm
Program:
#include<stdio.h>
int main() {
// Declare Variables
int nb,blockSize[100],n,jobSize[100],i,j,alloc[100];
for(i=0;i<n;i++) {
alloc[i] = -1;
}
Program:
#include<stdio.h>
int main()
{
int fragments[10], blocks[10], files[10];
int m, n, number_of_blocks, number_of_files, temp, top = 0;
static int block_arr[10], file_arr[10];
printf("\nEnter the Total Number of Blocks:\t");
scanf("%d",&number_of_blocks);
printf("Enter the Total Number of Files:\t");
scanf("%d",&number_of_files);
printf("\nEnter the Size of the Blocks:\n");
for(m = 0; m < number_of_blocks; m++)
{
printf("Block No.[%d]:\t", m + 1);
scanf("%d", &blocks[m]);
}
printf("Enter the Size of the Files:\n");
for(m = 0; m < number_of_files; m++)
{
printf("File No.[%d]:\t", m + 1);
scanf("%d", &files[m]);
}
for(m = 0; m < number_of_files; m++)
{
for(n = 0; n < number_of_blocks; n++)
{
if(block_arr[n] != 1)
{
temp = blocks[n] - files[m];
if(temp >= 0)
{
if(top < temp){
file_arr[m] = n;
top = temp;
}
}
}
fragments[m] = top;
block_arr[file_arr[m]] = 1;
top = 0;
}
}
printf("\nFile Number\tFile Size\tBlock Number\tBlock Size\tFragment");
for(m = 0; m < number_of_files; m++)
{
printf("\n%d\t\t%d\t\t%d\t\t%d\t\t%d", m, files[m], file_arr[m],
blocks[file_arr[m]], fragments[m]);
}
printf("\n");
return 0;
}
OUTPUT:
Practical 9
Aim: Write a program in C demonstrate the concept of page replacement policies for
handling page faults eg: FIFO, LRU etc.
Theory:
LRU replacement associates with each page the times of that page’s last use.When a page
must be replaced, LRU chooses the page that has not been used for the longest period of
time. We can think of this strategy as the optimal page-replacement algorithm looking
backward in time,rather than forward.(Strangely, if we let SR be the reverse of a
reference string S, then the page-fault rate for the OPT algorithm on S is the same as the
page-fault rate for the OPTalgorithm on S R . Similarly, the page-fault rate for the LRU
algorithm on S is the same as the page-fault rate for the LRU algorithm on SR .)
FIFO Page Replacement:
Program:
#include<stdio.h>
#include<string.h>
int main() {
int n,l,i,j,count=0,temp=1;
char page[100],sent[100];
printf("Enter the number of pages: ");
scanf("%d",&n);
printf("Enter then input string: ");
scanf("%s",sent);
l=strlen(sent);
for(i=0;i<l;i++) {
for(j=0;j<n;j++)
if(sent[i] == page[j])
temp=0;
if(temp==1) {
page[count%n] = sent[i];
count++;
}
temp=1;
}
printf("The number of page faults are: %d",count);
printf("\nThe number of page hits are: %d\n",l - count);
}
OUTPUT:
LRU Page Replacement:
Program:
#include<stdio.h>
#include<string.h>
int main() {
char ip[100],page[100];
int n,l,i,j,w,k,count=0,recentNo[100],hit=0,temp=1,min;
printf("Enter the input string: ");
scanf("%s",ip);
l = strlen(ip);
printf("Enter the number of pages: ");
scanf("%d",&n);
for(i=0;i<n;i++) {
page[i]=ip[i];
recentNo[i] = -1;
count++;
}
for(i=n;i<l;i++) {
for(j=0;j<n;j++) {
if(page[j] == ip[i]) {
hit++;
temp=0;
}
}
if(temp == 1) {
for(j=0;j<n;j++) {
for(k=i-1;k>=0;k--) {
if(page[j]==ip[k]) {
recentNo[i] = k;
k=0;
}
}
}
min = 0;
for(j=0;j<n;j++) {
if(recentNo[min]>recentNo[j])
min = j;
}
page[min] = ip[i];
count++;
}
temp=1;
for(j=0;j<n;j++) {
recentNo[i] = -1;
}
}
Practical 10
Aim: Write a program in C to do disk scheduling - FCFS, SCAN, C-SCAN
Theory:
The simplest form of disk scheduling is, of course, the first-come, first-served(FCFS)
algorithm. This algorithm is intrinsically fair, but it generally does not provide the fastest
service.
Example:
Suppose the order of request is- (82,170,43,140,24,16,190)
And current position of Read/Write head is : 50
In the SCAN algorithm, the disk arm starts at one end of the disk and moves toward the
other end, servicing requests as it reaches each cylinder, until it gets to the other end of
the disk. At the other end, the direction of head movement is reversed, and servicing
continues. The head continuously scans back and forth across the disk.
The SCAN algorithm is sometimes called the elevator algorithm, since the disk arm
behaves just like an elevator in a building, first servicing all the requests going up and
then reversing to service requests the other way.
The C-SCAN scheduling algorithm essentially treats the cylinders as a circular list that
wraps around from the final cylinder to the firstone.
Program:
#include<stdio.h>
int main() {
// Variable declaration
int nc,nt,head,track[100],i,j,distance=0;
SCAN:
Program:
#include<stdio.h>
int main() {
Program:
#include<stdio.h>
int main() {