Operating System Lab File
Operating System Lab File
S. Experiments
No
1 Study of hardware and software requirements of different operating systems
(UNIX,LINUX,WINDOWS XP, WINDOWS7/8
Execute various UNIX system calls for
2 i. Process management
ii. File management
iii. Input/output Systems calls
Implement CPU Scheduling Policies:
i. SJF
3 ii. Priority
iii. FCFS
iv. Multi-level Queue
Implement file storage allocation technique:
4 i. Contiguous(using array)
ii. Linked –list(using linked-list)
iii. Indirect allocation (indexing)
Implementation of contiguous allocation techniques:
i. Worst-Fit
5
ii. Best- Fit
iii. First- Fit
Calculation of external and internal fragmentation
6 i. Free space list of blocks from system
ii. List process file from the system
9 Implement the solution for Bounded Buffer (producer-consumer) problem using inter
process communication techniques-Semaphores.
10 Implement the solutions for Readers-Writers problem using inter process
communication technique –Semaphore.
Beyond Syllabus
#include<stdio.h>
#include<unistd.h> for fork() and sleep() functions
int main()
{
int i =10;
int pid;
pid=fork();
if(pid==0)
{
for(i=0; i < 999999; i++)
{
sleep(2);
printf(" from Child process %d\n", i);
}
}
else
{
for(i=0; i < 999999; i=i+2)
{
sleep(2);
printf(" from Parent process %d\n", i);
}
}
return 0;
}
Output 2.(i).a
user@node1:~/oslab$ ./chp
from Parent process 0
from Child process 0
from Parent process 2
from Child process 1
from Parent process 4
from Child process 2
…………………..
……………………
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
int i =10;
char *p[]={"./hello", NULL};
int pid;
pid=fork();
if(pid==0)
{
for(i=0; i < 999999; i++)
{
sleep(2);
printf(" from Child process %d\n", i);
}
}
else
{
for(i=0; i < 999999; i=i+2)
{
sleep(2);
printf(" from parent process %d\n", i);
execv(p[0], p);
}
}
return 0;
}
Output 2.(i).b
user@node1:~/oslab$ ./chp2
from parent process 0
hello world
from Child process 0
from Child process 1
from Child process 2
from Child process 3
………
Experiment 2 (ii)
Objective: Execute various UNIX system calls for: File Management
Solution:
There are four system calls for file management,
1. open (): system call is used to know the file descriptor of user-created files. Since read and
write use file descriptor as their 1st parameter so to know the file descriptor open() system
call is used.
2. read ()system call is used to read the content from the file. It can also be used to read the
input from the keyboard by specifying the 0 as file descriptor.
3. write (): system call is used to write the content to the file.
4. close ():system call is used to close the opened file, it tells the operating system that you are
done with the file and close the file.
int main()
{
int n,fd;
char buff[50];
printf("Enter text to write in the file:\n");
n= read(0, buff, 50);
fd=open("file",O_CREAT | O_RDWR, 0777);
write(fd, buff, n);
write(1, buff, n);
int close(int fd);
return 0;
}
Output 2.(ii)
Enter text to write in the file:
Hello world, welcome @ IncludeHelp
Hello world, welcome @ IncludeHelp
Experiment 2 (iii)
Objective: Execute various UNIX system calls for: Input / output system calls
Solution:
Standard File Descriptors: When any process starts, then that process file descriptors table’s
fd(file descriptor) 0, 1, 2 open automatically, (By default) each of these 3 fd references file table
entry for a file named /dev/tty
Read from stdin => read from fd 0: Whenever we write any character from keyboard, it read from
stdin through fd 0 and save to file named /dev/tty
Write to stdout => write to fd 1: Whenever we see any output to the video screen, it’s from the
file named /dev/tty and written to stdout in screen through fd 1
#include<stdio.h>
struct process
{
int id;
int brust;
};
typedef struct process proc;
int main()
{
int n;
printf("enter the number of processs: ");
scanf("%d", &n);
proc p[n], temp;
int i,j;
for(i=0;i<n;i++)
{
p[i].id=i;
printf("enter the brust time of process P %d :", i);
scanf("%d", &p[i].brust);
}
for(i=0;i<n-1;i++)
{
for(j=i+1;j<n;j++)
{
if(p[j].brust < p[i].brust)
{
temp.id = p[j].id;
temp.brust = p[j].brust;
p[j].id = p[i].id;
p[j].brust = p[i].brust;
p[i].id = temp.id;
p[i].brust = temp.brust;
}
}
}
int wt=0;
int twt=0;
printf("\n scheduling information \n");
for(i=0;i<n;i++)
{
twt = twt+wt;
printf("process id: %d \t wating time: %d \n",p[i].id, wt );
wt=wt+p[i].brust;
}
printf("\n avg waiting time: %1.2f :", ((float)twt/n));
return 0;
}
Input: Program 3(i):
Process Brust Time
P0 5
P1 3
P2 6
P3 4
Output:
scheduling information
process id: 1 wating time: 0
process id: 3 wating time: 3
process id: 0 wating time: 7
process id: 2 wating time: 12
avg waiting time: 5.50 :
Experiment 3 (ii)
Objective: Implement CPU Scheduling Policies: Priority Scheduling
Solution:
Algorithm:
Step 1. First input the processes with their arrival time, burst time and priority.
Step 2. Sort the processes, according to arrival time if two process arrival times is same then
sort according process priority if two process priority are same then sort according to
process number.
Step 3. Now simply apply FCFS algorithm.
Example: Sorting the process according to priority. (Array sorting flowchart can be used for logic
understanding)
#include<stdio.h>
struct process
{
int id;
int brust;
int pr;
};
typedef struct process proc;
int main()
{
int n;
printf("enter the number of processs: ");
scanf("%d", &n);
proc p[n], temp;
int i,j;
for(i=0;i<n;i++)
{
p[i].id=i;
printf("enter the brust time of process P %d :", i);
scanf("%d", &p[i].brust);
printf("enter the priority of process P %d :", i);
scanf("%d", &p[i].pr);
}
for(i=0;i<n-1;i++)
{
for(j=i+1;j<n;j++)
{
if(p[j].pr < p[i].pr)
{
temp.id = p[j].id;
temp.brust = p[j].brust;
temp.pr = p[j].pr;
p[j].id = p[i].id;
p[j].brust = p[i].brust;
p[j].pr = p[i].pr;
p[i].id = temp.id;
p[i].brust = temp.brust;
p[i].pr = temp.pr;
}
}
}
int wt=0;
int twt=0;
printf("\n scheduling information \n");
for(i=0;i<n;i++)
{
twt = twt+wt;
printf("process id: %d priority %d \t wating time: %d \n",p[i].id,p[i].pr, wt );
wt=wt+p[i].brust;
}
printf("\n avg waiting time: %1.2f :", ((float)twt/n));
return 0;
}
Input: Program 3(ii):
Process Brust Time Priority
P0 5 3
P1 3 1
P2 6 2
P3 4 4
Output:
Scheduling information
process id: 1 priority 1 wating time: 0
process id: 2 priority 2 wating time: 3
process id: 0 priority 3 wating time: 9
process id: 3 priority 4 wating time: 14
avg waiting time: 6.50
Experiment 3 (iii)
Objective: Implement CPU Scheduling Policies: FCFS (First come first serve) Scheduling
Solution:
Algorithm:
Step 1. First input the processes with their arrival time, burst time.
Step 2. Queue them in their arrival order i.e. the process that comes first then process it first.
Example:
#include<stdio.h>
struct process
{
int id;
int brust;
int at;
};
typedef struct process proc;
int main()
{
int n;
printf("enter the number of processs: ");
scanf("%d", &n);
proc p[n], temp;
int i,j;
for(i=0;i<n;i++)
{
p[i].id=i;
printf("enter the brust time of process P %d :", i);
scanf("%d", &p[i].brust);
printf("enter the arival time of process P %d :", i);
scanf("%d", &p[i].at);
}
for(i=0;i<n-1;i++)
{
for(j=i+1;j<n;j++)
{
if(p[j].at < p[i].at)
{
temp.id = p[j].id;
temp.brust = p[j].brust;
temp.at = p[j].at;
p[j].id = p[i].id;
p[j].brust = p[i].brust;
p[j].at = p[i].at;
p[i].id = temp.id;
p[i].brust = temp.brust;
p[i].at = temp.at;
}
}
}
int wt=0;
int twt=0;
printf("\n scheduling information \n");
for(i=0;i<n;i++)
{
twt = twt+wt;
printf("process id: %d arrival-time %d \t wating time: %d \n",p[i].id,p[i].at, wt );
wt=wt+p[i].brust;
}
printf("\n avg waiting time: %1.2f :", ((float)twt/n));
return 0;
}
Input: Program 3(iii):
Process Brust Time Arival time
P0 5 3
P1 3 1
P2 6 2
P3 4 4
Output:
Scheduling information
process id: 1 arrival-time 1 wating time: 0
process id: 2 arrival-time 2 wating time: 3
process id: 0 arrival-time 3 wating time: 9
process id: 3 arrival-time 4 wating time: 14
avg waiting time: 6.50
Experiment 3 (iv)
Objective: Implement CPU Scheduling Policies: Multi level Queue Scheduling
Solution:
Algorithm:
Let, us suppose we have three level queue such that queue 1, 2 and 3. Queue 1 for system
process, queue 2 for interactive process and queue 3 for batch process. Input process
parameters from user
Step 1. If process is system process then insert this process in queue 1.
Step 2. If process is interactive process the insert this process in queue 2.
Step 3. If process is batch process then insert this process in queue 3.
Step 4. Sort all queues using FCFS.
Step 5. Process Queue 1
Step 6. Process Queue 2
Step 7. Process Queue 3
Step 8. Repeat from step 1 for further processing.
Example:
#include<stdio.h>
#include<stdlib.h>
typedef struct process
{
int num;
int arrive;
int brust;
int pt;
int f;
int cpu;
int wait;
} process;
typedef struct queue
{
process p[10];
int front;
int rear;
} queue;
void insert(queue *q,process t)
{
q->p[q->rear]=t;
q->rear++;
}
void pstack(queue *que)
{
int i,j;
for(i=0;i<3;i++)
{
printf("\n queue %d is \n",i+1);
printf("Process\tarive\tbrust\t type\t I/O\t cpu \twait\n");
for(j=0;j<que[i].rear;j++)
{
printf(" %d \t %d \t %d \t %d \t %d \t %d
\t %d\n",que[i].p[j].num,que[i].p[j].arrive,que[i].p[j].brust,que[i].p[
j].pt,que[i].p[j].f,que[i].p[j].cpu,que[i].p[j].wait);
}
}
}
void fcfs_sort(queue *que)
{
process *temp;
int i,j,k,n;
for(k=0;k<3;k++)
{
n=que[k].rear;
for(i=1;i<n;i++)
{
for(j=0;j<n-i;j++)
{
if(que[k].p[j].arrive > que[k].p[j+1].arrive)
{
temp=&que[k].p[j];
que[k].p[j]=que[k].p[j+1];
que[k].p[j+1]=*temp;
}
}
}
}
}
void pque(queue *que)
{
int c=0,cp=0;
int w=0;
int j=0,k=0,l=0,t=0,i;
process *temp;
int max=0;
for(i=0;i<3;i++)
{
max=max+que[i].rear;
}
for(i=0;i<max;i++)
{
if(que[0].p[j].arrive <= cp && j< que[0].rear)
{
que[0].p[j].cpu=c;
que[0].p[j].wait=c-que[0].p[j].arrive;
if(que[0].p[j].wait < 1)
que[0].p[j].wait=0;
c=c+que[0].p[j].brust;
j++;
cp=c;
continue;
}
else if(que[1].p[k].arrive < cp && k < que[1].rear )
{
que[1].p[k].cpu=c;
que[1].p[k].wait=c-que[1].p[k].arrive;
if(que[1].p[k].wait < 1)
que[1].p[k].wait=0;
c=c+que[1].p[k].brust;
k++;
cp=c;
continue;
}
else if(que[2].p[l].arrive < cp && l < que[2].rear)
{
que[2].p[l].cpu=c;
que[2].p[l].wait=c-que[2].p[l].arrive;
if(que[2].p[l].wait < 1)
que[2].p[l].wait=0;
c=c+que[2].p[l].brust;
l++;
}
else
c=0;
}
}
int main()
{
queue que[3];
int j;
for(j=0;j<3;j++)
{
que[j].front=-1;
que[j].rear=0;
}
process temp;
int n;
printf("enter the number of process");
scanf("%d",&n);
int i;
for(i=0;i<n;i++)
{
temp.num=i+1;
temp.cpu=0;
temp.wait=0;
printf("\n enter the ariive time of process %d =",i+1);
scanf("%d",&temp.arrive);
printf("\n enter the brust time of process %d =",i+1);
scanf("%d",&temp.brust);
printf("\n enter the type of(1-back,2-forground,3-other) process %d =",i+1);
scanf("%d",&temp.pt);
printf("\n enter the feedback of process(0-for normal,1-I/O) %d =",i+1);
scanf("%d",&temp.f);
if(temp.pt>=2 && temp.f==1)
insert(&que[temp.pt-2],temp);
else if(temp.f==0)
insert(&que[temp.pt-1],temp);
else
insert(&que[temp.pt-1],temp);
}
pstack(que);
fcfs_sort(que);
printf("\n\nafter sorting of process list is \n\n");
pstack(que);
pque(que);
printf("\n\nafter processing cpu and wait is \n\n");
pstack(que);
return 0;
}
Input: Program 3(iv):
Process Arival Time Brust Time Process_Type I/O
P1 0 2 1 0
P2 2 5 1 0
P3 1 3 1 0
P4 1 3 2 0
P5 0 4 2 0
P6 2 4 2 0
P7 1 3 3 0
P8 3 4 3 0
P9 2 6 3 0
Output:
queue 1 is
Process arive brust type I/O cpu wait
1 0 2 1 0 0 0
2 2 5 1 0 0 0
3 1 3 1 0 0 0
queue 2 is
Process arive brust type I/O cpu wait
4 1 3 2 0 0 0
5 0 4 2 0 0 0
6 2 4 2 0 0 0
queue 3 is
Process arive brust type I/O cpu wait
7 1 3 3 0 0 0
8 3 4 3 0 0 0
9 2 6 3 0 0 0
queue 2 is
Process arive brust type I/O cpu wait
5 0 4 2 0 0 0
5 0 4 2 0 0 0
6 2 4 2 0 0 0
queue 3 is
Process arive brust type I/O cpu wait
7 1 3 3 0 0 0
9 2 6 3 0 0 0
9 2 6 3 0 0 0
queue 2 is
Process arive brust type I/O cpu wait
5 0 4 2 0 8 8
5 0 4 2 0 12 12
6 2 4 2 0 16 14
queue 3 is
Process arive brust type I/O cpu wait
7 1 3 3 0 20 19
9 2 6 3 0 23 21
9 2 6 3 0 29 27
Experiment 4 (i)
Objective: Implement file storage allocation technique: Contiguous (using array)
Solution:
About Contiguous allocation technique:
In this scheme, each file occupies a contiguous set of blocks on the disk. For example, if a file
requires n blocks and is given a block b as the starting location, then the blocks assigned to the
file will be: b, b+1, b+2,……b+n-1. This means that given the starting block address and the length
of the file (in terms of blocks required), we can determine the blocks occupied by the file.
The directory entry for a file with contiguous allocation contains
Address of starting block
Length of the allocated portion.
The file ‘mail’ in the following figure starts from the block 19 with length = 6 blocks.
Therefore, it occupies 19, 20, 21, 22, 23, 24 blocks.
4(i). Example program for Contiguous (using array) file storage allocation technique
#include<stdio.h>
struct filep
{
int id;
int start;
int count;
};
typedef struct filep files;
int main()
{
int disk[100];
int i,j,k;
for(i=0;i<100;i++)
disk[i]=-1;
files f[3];
for(i=0;i < 3;i++)
{
f[i].id=i;
printf("enter the number of blocks for file : %d", i);
scanf("%d", &f[i].count);
}
int flag = 0;
for(i=0;i<3;i++)
{
flag=0;
for(j=0;j<100;j++)
{
if(disk[j] == -1)
{
f[i].start = j;
disk[k]=f[i].id;
}
j=j+f[i].count;
break;
}
else
{
j=j+flag;
flag=0;
}
}
if(j==99 && flag == 0)
printf("\n disk is full \n");
}
}
printf("\n here is the disk allocation details \n");
for(i=0;i<100;i++)
printf("%d ", disk[i]);
return 0;
}
#include<stdio.h>
#include<stdlib.h>
struct file
{
int id;
int block;
struct file *next_block;
};
typedef struct file f;
int main()
{
f *file1, *temp, *temp2;
int i=1,j, count;
printf("enter the number of blocks for file : %d", i);
scanf("%d", &count);
for(j=0;j<count;j++)
{
if(j==0)
{
temp=(f*)malloc(sizeof(f));
temp->id=i;
temp->block=j;
temp->next_block=NULL;
file1=temp;
printf("%d\t", i);
}
else
{
temp=file1;
while ( temp->next_block != NULL)
temp=temp->next_block;
temp2=(f*)malloc(sizeof(f));
temp2->id=i;
temp2->block=j;
temp2->next_block=NULL;
temp->next_block = temp2;
printf("%d\t", i);
}
}
printf("\n Here is the disk allocation for file : \n");
temp=file1;
while(temp!=NULL)
{
printf("file id: %d\t block number: %d\t block address %d\n", temp->id,
temp->block,(int)temp);
temp=temp->next_block;
}
return 0;}
Input: Program 4(ii):
File ID Block_Count
File 1 5
Output:
Here is the disk allocation for file :
file id: 1 block number: 0 block address 38287408
file id: 1 block number: 1 block address 38287440
file id: 1 block number: 2 block address 38287472
file id: 1 block number: 3 block address 38287504
file id: 1 block number: 4 block address 38287536
file id: 1 block number: 5 block address 38287568
Experiment 4 (iii)
Objective: Implement file storage allocation technique: Indirect allocation (indexing)
Solution:
About Indirect allocation (indexing) technique:
In this scheme, a special block known as the Index block contains the pointers to all the blocks
occupied by a file. Each file has its own index block. The ith entry in the index block contains the
disk address of the ith file block. The directory entry contains the address of the index block as
shown in the image:
#include<stdio.h>
#include<stdlib.h>
struct file
{
int id;
int block;
struct file *next_block;
};
typedef struct file f;
int main()
{
f *files[3], *temp, *temp2;
int count, i, j, id;
for(i=0;i<3;i++)
{
printf("enter the number of blocks for file : %d", i);
scanf("%d", &count);
for(j=0;j<count;j++)
{
if(j==0)
{
temp=(f*)malloc(sizeof(f));
temp->id=i;
temp->block=j;
temp->next_block=NULL;
files[i]=temp;
printf("%d\t", i);
}
else
{
temp=files[i];
while ( temp->next_block != NULL)
temp=temp->next_block;
temp2=(f*)malloc(sizeof(f));
temp2->id=i;
temp2->block=j;
temp2->next_block=NULL;
temp->next_block = temp2;
printf("%d\t", i);
}
}
}
temp=files[id];
while(temp!=NULL)
{
printf("file id: %d\t block number: %d\t block address %d\n", temp->id,
temp->block,(int)temp);
temp=temp->next_block;
}
return 0;
}
Input: Program 4(iii):
File ID Block_Count
File 0 5
File 1 4
File 2 6
Output:
disk allocation using index as file id
file id: 0 block number: 0 block address 35997744
file id: 0 block number: 1 block address 35997776
file id: 0 block number: 2 block address 35997808
file id: 0 block number: 3 block address 35997840
file id: 0 block number: 4 block address 35997872
#include<stdio.h>
#include<stdlib.h>
int disk[20];
struct blocks
{
int bid;
int size;
int index;
}block;
struct processs
{
int pid;
int size;
int flag;
struct blocks b;
int fragment;
}process;
int main()
{
int nb, np, i , j;
int disk_index=0;
printf(" Total disk size is 50 \n");
printf(" enter numebr of blocks: ");
scanf("%d", &nb);
struct blocks bls[nb], temp;
for(i=0;i<nb;i++)
{
bls[i].bid=i;
printf("enter the size of block %d ", i);
scanf("%d", &bls[i].size);
bls[i].index=disk_index;
disk_index=disk_index+bls[i].size;
}
printf(" enter numebr of process: ");
scanf("%d",&np);
struct processs ps[np];
for(i=0;i<np;i++)
{
ps[i].pid=i;
printf("enter the size of process %d ", i);
scanf("%d", &ps[i].size);
ps[i].flag=-1;
}
for(i=0;i<nb-1;i++)
{
for(j=i+1;j<nb;j++)
{
if(bls[j].size > bls[i].size)
{
temp.bid = bls[j].bid;
temp.size = bls[j].size;
temp.index = bls[j].index;
bls[j].bid = bls[i].bid;
bls[j].size = bls[i].size;
bls[j].index = bls[i].index;
bls[i].bid = temp.bid;
bls[i].size = temp.size;
bls[i].index = temp.index;
}
}
}
for(i=0;i<np;i++)
{
if(i >= nb || bls[i].size < ps[i].size)
printf("\n no block is available for the process \n");
else
ps[i].b=bls[i];
}
printf(" \n now process block allocation list is \n");
for(i=0;i<np;i++)
{
printf(" process id: %d process size %d block id %d free space %d\n", ps[i].pid,
ps[i].size, ps[i].b.bid, ps[i].b.size-ps[i].size);
}
return 0;
}
Process ID Process_size
Process 0 3
Process 1 2
Process 2 4
Output:
now process block allocation list is
process id: 0 process size 3 block id 2 free space 5
process id: 1 process size 2 block id 0 free space 3
process id: 2 process size 4 block id 1 free space 0
Experiment 5 (ii)
Objective: Implementation of contiguous allocation techniques: Best-Fit
Solution:
Best Fit Allocate the process to the partition which is the first smallest sufficient partition among
the free available partition.
Algorithm
Step 1. Input the total number of blocks and their size.
Step 2. Input the total number of processes and their size.
Step 3. For each process in list
Find free block where, block_size - process _size = minimum
Allocate block to process
#include<stdio.h>
#include<stdlib.h>
int disk[20];
struct blocks
{
int bid;
int size;
int index;
}block;
struct processs
{
int pid;
int size;
int flag;
struct blocks b;
int fragment;
}process;
int main()
{
int nb, np, i , j;
int disk_index=0;
printf(" Total disk size is 20 \n");
printf(" enter numebr of blocks: ");
scanf("%d", &nb);
struct blocks bls[nb];
for(i=0;i<nb;i++)
{
bls[i].bid=i;
printf("enter the size of block %d ", i);
scanf("%d", &bls[i].size);
bls[i].index=disk_index;
disk_index=disk_index+bls[i].size;
}
printf(" enter numebr of process: ");
scanf("%d",&np);
struct processs ps[np];
for(i=0;i<np;i++)
{
ps[i].pid=i;
printf("enter the size of process %d ", i);
scanf("%d", &ps[i].size);
ps[i].flag=-1;
}
int temp,temp2=20, bindex, pindex;
for(i=0; i<nb;i++)
{
for(j=0;j<np;j++)
{
temp = bls[i].size - ps[j].size;
if(temp < 0)
{
continue;
}
else if(temp < temp2)
{
if(ps[j].flag == -1)
{
pindex=j;
temp2=temp;
}
}
}
ps[pindex].b = bls[i];
ps[pindex].flag = 1;
temp2=20;
}
printf(" \n now process block allocation list is \n");
for(i=0;i<np;i++)
{
printf(" process id: %d process size %d block id %d free space %d\n", ps[i].pid,
ps[i].size, ps[i].b.bid, ps[i].b.size-ps[i].size);
}
return 0;
}
Input: Program 5(ii):
Block ID Block_size
Block 0 5
Block 1 4
Block 2 8
Block 3 3
Process ID Process_size
Process 0 3
Process 1 2
Process 2 4
Output:
now process block allocation list is
process id: 0 process size 3 block id 1 free space 1
process id: 1 process size 2 block id 3 free space 1
process id: 2 process size 4 block id 0 free space 1
Experiment 5 (iii)
Objective: Implementation of contiguous allocation techniques: First-Fit
Solution:
First Fit: In the first fit, the partition is allocated which is first sufficient block from the top of Main
Memory.
Algorithm
Step 4. Input the total number of blocks and their size.
Step 5. Input the total number of processes and their size.
Step 6. For each process in list
Find free block where, block_size > process _size
Allocate block to process
#include<stdio.h>
#include<stdlib.h>
int disk[20];
struct blocks
{
int bid;
int size;
int index;
int status;
}block;
struct processs
{
int pid;
int size;
int flag;
struct blocks b;
int fragment;
}process;
int main()
{
int nb, np, i , j;
int disk_index=0;
printf(" Total disk size is 50 \n");
printf(" enter numebr of blocks: ");
scanf("%d", &nb);
struct blocks bls[nb], temp;
for(i=0;i<nb;i++)
{
bls[i].bid=i;
printf("enter the size of block %d ", i);
scanf("%d", &bls[i].size);
bls[i].index=disk_index;
disk_index=disk_index+bls[i].size;
bls[i].status = -1;
}
printf(" enter numebr of process: ");
scanf("%d",&np);
struct processs ps[np];
for(i=0;i<np;i++)
{
ps[i].pid=i;
printf("enter the size of process %d ", i);
scanf("%d", &ps[i].size);
ps[i].flag=-1;
}
for(i=0;i<np;i++)
{
for(j=0;j<nb;j++)
{
if(bls[j].size > ps[i].size && bls[j].status ==-1)
{
ps[i].b=bls[j];
bls[j].status = 1;
break;
}
}
}
printf(" \n now process block allocation list is \n");
for(i=0;i<np;i++)
{
printf(" process id: %d process size %d block id %d free space %d\n", ps[i].pid,
ps[i].size, ps[i].b.bid, ps[i].b.size-ps[i].size);
}
return 0;
}
Process ID Process_size
Process 0 3
Process 1 2
Process 2 4
Output:
now process block allocation list is
process id: 0 process size 4 block id 0 free space 1
process id: 1 process size 2 block id 1 free space 2
process id: 2 process size 3 block id 2 free space 5
Experiment 6 (i)
Objective: Calculation of external and internal fragmentation: Free space list of blocks from
system
Solution:
External Fragmentation:
External fragmentation happens when there’s a sufficient quantity of area within the memory to
satisfy the memory request of a method. However, the process’s memory request cannot be
fulfilled because the memory offered is during a non-contiguous manner. Either you apply first-
fit or best-fit memory allocation strategy it’ll cause external fragmentation.
#include<stdio.h>
#include<stdlib.h>
int disk[20];
struct blocks
{
int bid;
int size;
int index;
int status;
}block;
struct processs
{
int pid;
int size;
int flag;
struct blocks b;
int fragment;
}process;
int main()
{
int nb, np, i , j;
int disk_index=0;
printf(" Total disk size is 50 \n");
printf(" enter numebr of blocks: ");
scanf("%d", &nb);
struct blocks bls[nb], temp;
for(i=0;i<nb;i++)
{
bls[i].bid=i;
printf("enter the size of block %d ", i);
scanf("%d", &bls[i].size);
bls[i].index=disk_index;
disk_index=disk_index+bls[i].size;
bls[i].status = -1;
}
printf(" enter numebr of process: ");
scanf("%d",&np);
struct processs ps[np];
for(i=0;i<np;i++)
{
ps[i].pid=i;
printf("enter the size of process %d ", i);
scanf("%d", &ps[i].size);
ps[i].flag=-1;
}
for(i=0;i<np;i++)
{
for(j=0;j<nb;j++)
{
if(bls[j].size > ps[i].size && bls[j].status ==-1)
{
ps[i].b=bls[j];
bls[j].status = 1;
break;
}
}
}
printf(" \n now Free space list of blocks is \n");
int free=0;
for(i=0;i<nb;i++)
{
if(bls[i].status == -1)
{
printf(" Block id: %d Block size %d block index %d \n", bls[i].bid, bls[i].size,
bls[i].index);
free= free + bls[i].size;
}
}
printf(" \n Total external free space is: %d \n", free);
return 0;
}
Input: Program 6(i):
Disk size = 20
Block ID Block_size
Block 0 5
Block 1 4
Block 2 8
Block 3 3
Process ID Process_size
Process 0 4
Process 1 6
Process 2 2
Output:
now Free space list of blocks is
Block id: 2 Block size 3 block index 9
Total external free space is: 3
Experiment 6 (ii)
Objective: Calculation of external and internal fragmentation: List process file from the system
Solution:
Internal Fragmentation:
Internal fragmentation happens when the memory is split into mounted sized blocks. Whenever,
a method request for the memory, the mounted sized block is allotted to the method. Just in
case, the memory allotted to the method is somewhat larger than the memory requested, then
the distinction between allotted and requested memory is that the internal fragmentation.
#include<stdio.h>
#include<stdlib.h>
int disk[20];
struct blocks
{
int bid;
int size;
int index;
int status;
}block;
struct processs
{
int pid;
int size;
int flag;
struct blocks b;
int fragment;
}process;
int main()
{
int nb, np, i , j;
int disk_index=0;
printf(" Total disk size is 50 \n");
printf(" enter numebr of blocks: ");
scanf("%d", &nb);
for(i=0;i<nb;i++)
{
bls[i].bid=i;
printf("enter the size of block %d ", i);
scanf("%d", &bls[i].size);
bls[i].index=disk_index;
disk_index=disk_index+bls[i].size;
bls[i].status = -1;
}
printf(" enter numebr of process: ");
scanf("%d",&np);
struct processs ps[np];
for(i=0;i<np;i++)
{
ps[i].pid=i;
printf("enter the size of process %d ", i);
scanf("%d", &ps[i].size);
ps[i].flag=-1;
}
for(i=0;i<np;i++)
{
for(j=0;j<nb;j++)
{
if(bls[j].size > ps[i].size && bls[j].status ==-1)
{
ps[i].b=bls[j];
bls[j].status = 1;
break;
}
}
}
printf(" \n now process block allocation list is \n");
int t_free=0;
for(i=0;i<np;i++)
{
printf(" process id: %d process size %d block id %d free space %d\n", ps[i].pid,
ps[i].size, ps[i].b.bid, ps[i].b.size-ps[i].size);
t_free = t_free + (ps[i].b.size-ps[i].size);
}
printf(" \n Total internal fragmentation space is: %d \n", t_free);
return 0;
}
Input: Program 6(ii):
Block ID Block_size
Block 0 5
Block 1 4
Block 2 8
Block 3 3
Process ID Process_size
Process 0 3
Process 1 2
Process 2 4
Output:
now process block allocation list is
process id: 0 process size 4 block id 0 free space 1
process id: 1 process size 2 block id 1 free space 2
process id: 2 process size 3 block id 2 free space 5
#include<stdio.h>
int main()
{
int np, nr, temp, temp1;
printf("enter number of resources: ");
scanf("%d", &nr);
printf("enter number of processs: ");
scanf("%d", &np);
int rag[nr+np][nr+np];
int i, j;
for(i=0;i<np+nr;i++)
{
for(j=0; j<np+nr;j++)
{
rag[i][j]=0;
}
}
for(i=0;i<np;i++)
{
printf("enter the number of resources process %d, holding", i);
scanf("%d", &temp);
for(j=0; j<temp;j++)
{
printf("enter the ressorce number process %d holding: ", j);
scanf("%d", &temp1);
rag[np+temp1][i]=1;
}
printf("enter the number of resources process %d, requesting", i);
scanf("%d", &temp);
for(j=0; j<temp;j++)
{
printf("enter the ressorce number process %d requesting: ", i);
scanf("%d", &temp1);
rag[i][np+temp1]=1;
}
}
for(i=0;i<np+nr;i++)
{
for(j=0; j<np+nr;j++)
{
printf("%d ", rag[i][j]);
}
printf("\n ");
}
return 0;
}
Input: Program 8:
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.
Safety Algorithm
1) Let Work and Finish be vectors of length ‘m’ and ‘n’ respectively.
Initialize: Work = Available
Finish[i] = false; for i=1, 2, 3, 4….n
2) Find an i such that both
a) Finish[i] = false
b) Needi <= Work
if no such i exists goto step (4)
3) Work = Work + Allocation[i]
Finish[i] = true
goto step (2)
4) if Finish [i] = true for all i
then the system is in a safe state
Resource-Request Algorithm
Let Requesti be the request array for process Pi. Requesti [j] = k means process Pi wants k
instances of resource type Rj. When a request for resources is made by process Pi, the following
actions are taken:
return 0;
}
Input: Program 9:
Output:
Following is the SAFE Sequence
P1 -> P3 -> P4 -> P0 -> P2
Experiment 9
Objective: Implement the solution for Bounded Buffer (producer-consumer)problem using
inter process communication techniques-Semaphores
Solution:
Producer consumer problem is a classical synchronization problem. We can solve this problem
by using semaphores.
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.
Problem Statement – We have a buffer of fixed size. A producer can produce an item and can
place 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 consumer should not
consume any item. In this problem, buffer is the critical section.
To solve this problem, we need two counting semaphores – Full and Empty. “Full” keeps track of
number of items in the buffer at any given time and “Empty” keeps track of number of
unoccupied slots.
11. Example program for Implement the solution for Bounded Buffer (producer-consumer)
problem using inter process communication techniques-Semaphores
#include<stdio.h>
#include<stdlib.h>
int mutex=1,full=0,empty=3,x=0;
int main()
{
int n;
void producer();
void consumer();
int wait(int);
int signal(int);
printf("\n1.Producer\n2.Consumer\n3.Exit");
while(1)
{
printf("\nEnter your choice:");
scanf("%d",&n);
switch(n)
{
case 1: if((mutex==1)&&(empty!=0))
producer();
else
printf("Buffer is full!!");
break;
case 2: if((mutex==1)&&(full!=0))
consumer();
else
printf("Buffer is empty!!");
break;
case 3:
exit(0);
break;
}
}
return 0;
}
int wait(int s)
{
return (--s);
}
int signal(int s)
{
return(++s);
}
void producer()
{
mutex=wait(mutex);
full=signal(full);
empty=wait(empty);
x++;
printf("\nProducer produces the item %d",x);
mutex=signal(mutex);
}
void consumer()
{
mutex=wait(mutex);
full=wait(full);
empty=signal(empty);
printf("\nConsumer consumes item %d",x);
x--;
mutex=signal(mutex);
}
Output: Program 11
1.Producer
2.Consumer
3.Exit
Enter your choice:1
● If one of the people tries editing the file, no other person should be reading or writing at
the same time, otherwise changes will not be visible to him/her.
● However if some person is reading the file, then others may read it at the same time.
Precisely in OS we call this situation as the readers-writers problem
Problem parameters:
● One set of data is shared among a number of processes
● Once a writer is ready, it performs its write. Only one writer may write at a time
● If a process is writing, no other process can read it
● If at least one reader is reading, no other process can write
● Readers may not write and only read
Here priority means, no reader should wait if the share is currently opened for reading.
1. semaphore mutex, wrt; // semaphore mutex is used to ensure mutual exclusion when
readcnt is updated i.e. when any reader enters or exit from the critical section and
semaphore wrt is used by both readers and writers
2. int readcnt; // readcnt tells the number of processes performing read in the critical
section, initially 0
Writer process:
Reader process:
1. Reader requests the entry to critical section.
2. If allowed:
o it increments the count of number of readers inside the critical section. If this
reader is the first reader entering, it locks the wrt semaphore to restrict the
entry of writers if any reader is inside.
o It then, signals mutex as any other reader is allowed to enter while others are
already reading.
o After performing reading, it exits the critical section. When exiting, it checks if no
more reader is inside, it signals the semaphore “wrt” as now, writer can enter
the critical section.
3. If not allowed, it keeps on waiting.
12. Example program for Implement the solutions for Readers-Writers problem using inter
process communication technique –Semaphore
#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>
sem_t mutex,writeblock;
int data = 0,rcount = 0;
void *reader(void *arg)
{
int f;
f = ((int)arg);
sem_wait(&mutex);
rcount = rcount + 1;
if(rcount==1)
sem_wait(&writeblock);
sem_post(&mutex);
printf("Data read by the reader%d is %d\n",f,data);
sleep(1);
sem_wait(&mutex);
rcount = rcount - 1;
if(rcount==0)
sem_post(&writeblock);
sem_post(&mutex);
}
Output: Program 12
Data writen by the writer0 is 1
Data read by the reader0 is 1
Data read by the reader1 is 1
Data read by the reader2 is 1
Data writen by the writer1 is 2
Data writen by the writer2 is 3
Experiment 1(i)
Objective: Implement the Implement disk scheduling algorithms: FCFS
Solution:
First Come First Serve (FCFS)
FCFS is the simplest disk scheduling algorithm. As the name suggests, this algorithm entertains
requests in the order they arrive in the disk queue. The algorithm looks very fair and there is no
starvation (all requests are serviced sequentially) but generally, it does not provide the fastest
service.
Algorithm:
Step 1. Let Request array represents an array storing indexes of tracks that have been
requested in ascending order of their time of arrival. ‘head’ is the position of disk head.
Step 2. Let us one by one take the tracks in default order and calculate the absolute distance
of the track from the head.
Step 3. Increment the total seek count with this distance.
Step 4. Currently serviced track position now becomes the new head position.
Step 5. Go to step 2 until all tracks in request array have not been serviced.
Figure: chart shows the sequence in which requested tracks are serviced using FCFS.
#include<stdio.h>
int main()
{
int queue[20],n,head,i,j,k,seek=0,max,diff;
float avg;
printf("Enter the max range of disk\n");
scanf("%d",&max);
printf("Enter the size of queue request\n");
scanf("%d",&n);
printf("Enter the queue of disk positions to be read\n");
for(i=1;i<=n;i++)
scanf("%d",&queue[i]);
printf("Enter the initial head position\n");
scanf("%d",&head);
queue[0]=head;
for(j=0;j<=n-1;j++)
{
diff=abs(queue[j+1]-queue[j]);
seek+=diff;
printf("Disk head moves from %d to %d with seek
%d\n", queue[j],queue[j+1],diff);
}
printf("Total seek time is %d\n",seek);
avg=seek/(float)n;
printf("Average seek time is %f\n",avg);
return 0;
}
Output 13 (i):
Enter the max range of disk
200
Enter the size of queue request
8
Enter the queue of disk positions to be read
90 120 35 122 38 128 65 68
Enter the initial head position
50
Disk head moves from 50 to 90 with seek
Disk head moves from 90 to 120 with seek
Disk head moves from 120 to 35 with seek
Disk head moves from 35 to 122 with seek
Disk head moves from 122 to 38 with seek
Disk head moves from 38 to 128 with seek
Disk head moves from 128 to 65 with seek
Disk head moves from 65 to 68 with seek
Total seek time is 482
Average seek time is 60.250000
Experiment 1(ii)
Objective: Implement the Implement disk scheduling algorithms: SCAN
Solution:
SCAN (Elevator) algorithm
In SCAN disk scheduling algorithm, head starts from one end of the disk and moves towards the
other end, servicing requests in between one by one and reach the other end. Then the
direction of the head is reversed and the process continues as head continuously scan back and
forth to access the disk. So, this algorithm works as an elevator and hence also known as the
elevator algorithm. As a result, the requests at the midrange are serviced more and those
arriving behind the disk arm will have to wait.
Algorithm-
Step 1. Let Request array represents an array storing indexes of tracks that have been
requested in ascending order of their time of arrival. ‘head’ is the position of disk head.
Step 2. Let direction represents whether the head is moving towards left or right.
Step 3. In the direction in which head is moving service all tracks one by one.
Step 4. Calculate the absolute distance of the track from the head.
Step 5. Increment the total seek count with this distance.
Step 6. Currently serviced track position now becomes the new head position.
Step 7. Go to step 3 until we reach at one of the ends of the disk.
Step 8. If we reach at the end of the disk reverse the direction and go to step 2 until all tracks
in request array have not been serviced.
Step 9.
Figure: chart shows the sequence in which requested tracks are serviced using SCAN.
Algorithm:
Step 1. Let Request array represents an array storing indexes of tracks that have been
requested in ascending order of their time of arrival. ‘head’ is the position of disk
head.
Step 2. The head services only in the right direction from 0 to size of the disk.
Step 3. While moving in the left direction do not service any of the tracks.
Step 4. When we reach at the beginning(left end) reverse the direction.
Step 5. While moving in right direction it services all tracks one by one.
Step 6. While moving in right direction calculate the absolute distance of the track from the
head.
Step 7. Increment the total seek count with this distance.
Step 8. Currently serviced track position now becomes the new head position.
Step 9. Go to step 6 until we reach at right end of the disk.
Step 10. If we reach at the right end of the disk reverse the direction and go to step 3 until all
tracks in request array have not been serviced.
Figure: chart shows the sequence in which requested tracks are serviced using SCAN.
13 (iii). Example program for Implement disk scheduling algorithms: C-SCAN
#include<stdio.h>
int main()
{
int queue[20],n,head,i,j,k,seek=0,max,diff,temp,queue1[20],queue2[20],
temp1=0,temp2=0;
float avg;
printf("Enter the max range of disk\n");
scanf("%d",&max);
printf("Enter the initial head position\n");
scanf("%d",&head);
printf("Enter the size of queue request\n");
scanf("%d",&n);
printf("Enter the queue of disk positions to be read\n");
for(i=1;i<=n;i++)
{
scanf("%d",&temp);
if(temp>=head)
{
queue1[temp1]=temp;
temp1++;
}
else
{
queue2[temp2]=temp;
temp2++;
}
}
for(i=0;i<temp1-1;i++)
{
for(j=i+1;j<temp1;j++)
{
if(queue1[i]>queue1[j])
{
temp=queue1[i];
queue1[i]=queue1[j];
queue1[j]=temp;
}
}
}
for(i=0;i<temp2-1;i++)
{
for(j=i+1;j<temp2;j++)
{
if(queue2[i]>queue2[j])
{
temp=queue2[i];
queue2[i]=queue2[j];
queue2[j]=temp;
}
}
}
for(i=1,j=0;j<temp1;i++,j++)
queue[i]=queue1[j];
queue[i]=max;
queue[i+1]=0;
for(i=temp1+3,j=0;j<temp2;i++,j++)
queue[i]=queue2[j];
queue[0]=head;
for(j=0;j<=n+1;j++)
{
diff=abs(queue[j+1]-queue[j]);
seek+=diff;
printf("Disk head moves from %d to %d with seek %d\n",queue[j],queue[j+1],diff);
}
printf("Total seek time is %d\n",seek);
avg=seek/(float)n;
printf("Average seek time is %f\n",avg);
return 0;
}
Output 13 (iii):