CS2257
CS2257
(Implement the following on LINUX or other Unix like platform. Use C for high level
language implementation)
1. Write programs using the following system calls of UNIX operating system:
fork, exec, getpid, exit, wait, close, stat, opendir, readdir
2. Write programs using the I/O system calls of UNIX operating system (open, read,
write, etc)
4. Given the list of processes, their CPU burst times and arrival times, display/print
the Gantt chart for FCFS and SJF. For each of the scheduling policies, compute
and print the average waiting time and average turnaround time. (2 sessions)
5. Given the list of processes, their CPU burst times and arrival times, display/print
the Gantt chart for Priority and Round robin. For each of the scheduling policies,
compute and print the average waiting time and average turnaround time. (2
sessions)
Free space is maintained as a linked list of nodes with each node having the starting byte
address and the ending byte address of a free block. Each memory request consists of the
process-id and the amount of storage space required in bytes. Allocated memory space is
again maintained as a linked list of nodes with each node having the process-id, starting
byte address and the ending byte address of the allocated space. When a process finishes
(taken as input) the appropriate node from the allocated list should be deleted and
this free disk space should be added to the free space list. [Care should be taken to merge
contiguous free blocks into one single block. This results in deleting more than one node
from the free space list and changing the start and end address in the appropriate node]. For
allocation use first fit, worst fit and best fit.
TOTAL: 45
1
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
HARDWARE:
30 Personal Computers
SOFTWARE:
Linux:
2
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
Aim :
To write C programs for the fork, exec, getpid, exit, wait, stat, opendir, readdir
system calls of UNIX operating system.
Description
A system call is just what its name implies - a request for the operating system to do
something on behalf of the user's program. The system calls are functions used in the kernel
itself. To the programmer, the system call appears as a normal C function call. However
since a system call executes code in the kernel, there must be a mechanism to change the
mode of a process from user mode to kernel mode. The C compiler uses a predefined library
of functions (the C library)
that have the names of the system calls. The library functions typically invoke an instruction
that changes the process execution mode to kernel mode and causes the kernel to start
executing code for system calls. The instruction that causes the mode change is often
referred to as an "operating system trap" which is a software generated interrupt.
The library routines execute in user mode, but the system call interface is a special case of
an interrupt handler. The library functions pass the kernel a unique number per system call in
a machine dependent way
getpid, getppid
To get the process and parent process identification number.
General Syntax:
pid_t getpid(void);
pid_t getppid(void);
Description
getpid returns the process ID of the current process. (This is often used by routines that
generate unique temporary file names.)
getppid returns the process ID of the parent of the current process.
fork()
causes the UNIX system to create a new process, called the "child process", with a new
process ID. The contents of the child process are identical to the contents of the parent
process.
int fork()
3
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
The new process inherits several characteristics of the old process. Among the
characteristics inherited are:
The environment.
All signal settings.
The set user ID and set group ID status.
The time left until an alarm clock signal.
The current working directory and the root directory.
The file creation mask as established with umask().
The child process begins executing and the parent process continues executing at the
return from the fork() system call. This is difficult to understand at first because you only
call fork() once, yet it returns twice -- once per process. To differentiate which process is
which, fork() returns zero in the child process and non-zero (the child's process ID) in the
parent process.
The UNIX system calls that transform a executable binary file into a process are the
"exec" family of system calls. The prototypes for these calls are:
wait()
You can control the execution of child processes by calling wait() in the parent. wait()
forces the parent to suspend execution until the child is finished. wait() returns the process
ID of a child process that finished.
If the child finishes before the parent gets around to calling wait(), then when wait() is
called by the parent, it will return immediately with the child's process ID. (It is possible to
have more that one child process by simply calling fork() more than once.). The prototype
for the wait() system call is:
int wait(status)
int *status;
where status is a pointer to an integer where the UNIX system stores the value returned by
the child process. wait() returns the process ID of the process that ended.
exit()
The exit() system call ends a process and returns a value to it parent. The prototype for
the exit() system call is:
void exit(status)
int status;
where status is an integer between 0 and 255.
4
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
Note: since wait() returns the exit status multiplied by 256 (contained in
the upper 8 bits), the status value is shifted right 8 bits (divided by 256)
to obtain the correct value.
File Status
The i-node data structure holds all the information about a file except the file's name and
its contents. Sometimes your programs need to use the information in the i-node structure to
do some job. You can access this information with the stat(), lstat() and fstat() system calls.
SYNOPSIS
#include <sys/types.h>
#include <dirent.h>
DESCRIPTION
The opendir() function opens a directory stream corresponding to the directory name,
and returns a pointer to the directory stream. The stream is positioned at the first entry in
the directory.
RETURN VALUE
The opendir() function returns a pointer to the directory stream or NULL if an error
occurred.
readdir reads one dirent structure from the directory pointed at by fd into the memory area
pointed to by dirp. The parameter count is ignored; at most one dirent structure is read.
int readdir(unsigned int fd, struct dirent *dirp, unsigned int count);
DESCRIPTION
The closedir() function closes the directory stream associated with dir. The directory
stream descriptor dir is not available after this call.
5
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
Exercises:
Algorithm:
1. Include necessary header files for using systems calls.
2. Make necessary declaration.
3. Get the process identification number and parent process identification number using
getpid() and getppid() system calls
4. Display the process id’s
#include <sys/types.h>
#include <unistd.h>
#include<stdio.h>
main()
{
int pid,ppid;
pid=getpid();
ppid=getppid();
main()
{
printf("\n This is to demonstrate the fork()");
fork();
printf("\nAfter fork()");
}
OUTPUT
this is to demonstrate fork()
after fork()
main()
6
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
{
int pid;
pid=fork();
if(pid==0)
{
printf("\n I am the child, my process ID is %d ",getpid());
printf("\n I am the child's parent process ID is %d ",getppid());
}
else
{
printf("\n I am the parent, my process ID is %d ",getpid());
printf("\n I am the parent's parent process ID is %d ",getppid());
}
}
OUTPUT
i am the parent, my process ID is 5273
i am the parent's parent,process ID is 5129
I am the child, my process ID is 5274
I am the child's parent, process ID is 1
/* 4. orphan process */
#include <sys/types.h>
#include <unistd.h>
#include<stdio.h>
main()
{
int pid;
pid=fork();
if(pid==0)
{
printf("\n I am the child, my process ID is %d ",getpid());
printf("\n I am the child's parent process ID is %d ",getppid());
sleep(10);
printf("\n I am the child, my process ID is %d ",getpid());
printf("\n I am the child's parent process ID is %d ",getppid());
}
else
{
printf("\n I am the parent, my process ID is %d ",getpid());
printf("\n I am the parent's parent process ID is %d ",getppid());
}
}
OUTPUT
7
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
main()
{
execl("/bin/date", "date", NULL);
}
OUTPUT
Mon Jan 11 11:08:50 UTC 2010
8
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
main()
{
int pid,i=0;
printf("\n Ready to fork");
pid=fork();
if(pid==0)
{
printf("\n Child starts ");
for(i=0;i<1000;i++);
9
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
}
if(p<0)
{
printf("\n Cannnot create child ");
exit(-1);
printf("\n Process ended ");
}
}
OUTPUT
child created
#include <sys/types.h>
#include <unistd.h>
#include<stdddio.h>
main()
{
10
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
Result :
Thus C program was written programs using the fork, exec, getpid, exit, wait, stat,
opendir, readdir system calls of UNIX operating system.
11
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
Aim :
To write a C program using open(), read(), write and close system calls.
Description
open()
open() lets you open a file for reading, writing, or reading and writing. The prototype for the
open() system call is:
int open(char *file_name, int option_flags int [, mode])
where file_name is a pointer to the character string that names the file, option_flags represent
the type of channel, and mode defines the file's access permissions if the file is being created.
read() write()
The read() system call does all input and the write() system call does all output.When used
together, they provide all the tools necessary to do input and output sequentially.
Both read() and write() take three arguments. Their prototypes are:
where file_descriptor identifies the I/O channel, buffer_pointer points to the area in memory
where the data is stored for a read() or where the data is taken for a write(), and transfer_size
defines the maximum number of characters transferred between the file and the buffer. read()
and write() return the number of bytes transferred.
close()
To close a channel, use the close() system call. The prototype for the close() system call is:
int close(int file_descriptor)
where file_descriptor identifies a currently open channel. close() fails if file_descriptor does
not identify a currently open channel.
Exercises
/* 1. using creat system call */
#include <stdio.h>
#include <sys/types.h> /* defines types used by sys/stat.h */
#include <sys/stat.h> /* defines S_IREAD & S_IWRITE */
int main()
{
int fd;
fd = creat("datafile.dat", S_IREAD | S_IWRITE);
if (fd == -1)
printf("Error in opening datafile.dat\n");
else
12
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
{
printf("datafile.dat opened for read/write access\n");
printf("datafile.dat is currently empty\n");
}
close(fd);
exit (0);
}
13
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
14
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
OUTPUT
enter source file name write.c
enter destination file name read.c
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
main()
{
char buf[100],fn[10];
int fd,n;
printf("enter file name");
scanf("%s",fn);
printf("\n enter text\n");
scanf("%s",buf);
fd=open(fn,O_CREAT|O_WRONLY,0666);
n=write(fd,buf,strlen(buf));
close(fd);
}
Result :
Thus C program was written using open(), read(), write() and close() system calls.
15
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
Description :
UNIX commands can be simulated in the high level language using Unix system calls
and APIs available in the language. In addition, programming language construct can also
be used to avail the UNIX commands.
Exercises :
1. Siumulation of ls command
Algorithm :
1. Include necessary header files for manipulating directory.
2. Declare and initialize required objects.
3. Read the directory name form the user
4. Open the directory using opendir() system call and report error if the directory is
not available
5. Read the entry available in the directory
6. Display the directory entry (ie., name of the file or sub directory.
7. Repeat the step 6 and 7 until all the entries were read.
/* 1. Siumulation of ls command */
#include<dirent.h>
#include<stdio.h>
main()
{
char dirname[10];
DIR *p;
struct dirent *d;
printf("Enter directory name ");
scanf("%s",dirname);
p=opendir(dirname);
if(p==NULL)
{
perror("Cannot find dir.");
exit(-1);
}
while(d=readdir(p))
printf("%s\n",d->d_name);
}
OUTPUT:
enter directory name iii
.
..
f2
f1
16
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
3. Simulation of cp command
Algorithm:
1. Include required header file
2. Make necessary declarations
3. Read the source and destination file names from the user.
4. Using read() system call, read the content of the file to the buffer.
5. Uing write() system call, write the buffer content to the destination file.
6. Close the opened files.
17
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
OUTPUT:
enter source file name
ls.c
enter destination file name
file8
[it2-20@localhost ~]$ cat file8
#include<dirent.h>
#include<stdio.h>
main()
{
char dirname[10];
DIR *p;
struct dirent *d;
printf("enter directory name");
scanf("%s",dirname);
p=opendir(dirname);
if(p==NULL)
{
perror("cannot find directory");
exit(-1);
}
while(d=readdir(p))
printf("%s\n",d->d_name);
}
18
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
#include<stdio.h>
#include<fcntl.h>
main()
{
char fn[10];
printf("Enter source file name ");
scanf("%s",fn);
if(remove(fn)==0)
printf("\nFile/Dir removed");
else
printf("\nCannot be removed");
}
OUTPUT:
enter source file name
file10
file/directory removed
Result :
Thus C program was written to simulate some Unix Commands
19
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
Aim :
To write C program to simulate FCFS and SJF scheduling algorithms.
CPU/Process Scheduling
The assignment of physical processors to processes allows processors to accomplish work.
The problem of determining when processors should be assigned and to which processes is
called processor scheduling or CPU scheduling.
When more than one process is runable, the operating system must decide which one first.
The part of the operating system concerned with this decision is called the scheduler, and
algorithm it uses is called the scheduling algorithm.
FirstComeFirstServed (FCFS) Scheduling
Other names of this algorithm are:
● FirstInFirstOut (FIFO)
● RuntoCompletion
● RunUntilDone
Perhaps, FirstComeFirstServed algorithm is the simplest scheduling algorithm is the
simplest scheduling algorithm. Processes are dispatched according to their arrival time on the
ready queue. Being a nonpreemptive discipline, once a process has a CPU, it runs to
completion. The FCFS scheduling is fair in the formal sense or human sense of fairness but it
is unfair in the sense that long jobs make short jobs wait and unimportant jobs make
important jobs wait.
FCFS is more predictable than most of other schemes since it offers time. FCFS scheme is
not useful in scheduling interactive users because it cannot guarantee good response time.
The code for FCFS scheduling is simple to write and understand. One of the major drawback
of this scheme is that the average time is often quite long.
ShortestJobFirst (SJF) Scheduling
Other name of this algorithm is ShortestProcessNext (SPN).
ShortestJobFirst (SJF) is a nonpreemptive discipline in which waiting job (or process) with
the smallest estimated runtimetocompletion is run next. In other words, when CPU is
available, it is assigned to the process that has smallest next CPU burst.
The SJF scheduling is especially appropriate for batch jobs for which the run times are
known in advance. Since the SJF scheduling algorithm gives the minimum average time for a
given set of processes, it is probably optimal.
The SJF algorithm favors short jobs (or processors) at the expense of longer ones.
The obvious problem with SJF scheme is that it requires precise knowledge of how long a job
or process will run, and this information is not usually available.
The best SJF algorithm can do is to rely on user estimates of run times.
20
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
In the production environment where the same jobs run regularly, it may be possible to
provide reasonable estimate of run time, based on the past performance of the process. But in
the development environment users rarely know how their program will execute.
Like FCFS, SJF is non preemptive therefore, it is not useful in timesharing environment in
which reasonable response time must be guaranteed.
FCFS SCHEDULING
#include<stdio.h>
struct process
{
char pName[10];
int ex_time,wt_time,st_time,end_time;
}p[10];
main()
{
int n,i,j,k;
float avgwaittime=0.0,avgTurnAroundTime=0.0;
float totalWaitTime=0.0;
int totalExecTime=0,totalTurnAroundTime=0;
printf("\n enter number of process");
scanf("%d",&n);
p[0].st_time=0;
p[0].wt_time=0;
printf("\n enter process name");
scanf("%s",p[i].pName);
printf("enter Burst time");
scanf("%d",&p[i].ex_time);
if(i==0) p[i].end_time=p[i].ex_time;
else
{
p[i].wt_time=p[i-1].end_time;
p[i].st_time=p[i-1].end_time;
p[i].end_time=p[i].st_time+p[i].ex_time;
}
}
for(j=0;j<n;j++)
{
totalExecTime+=p[j].ex_time;
totalWaitTime+=p[j].wt_time;
}
totalTurnAroundTime=totalExecTime+totalWaitTime;
avgwaittime=(float)totalWaitTime/n;
avgTurnAroundTime=(float)totalTurnAroundTime/n;
printf("\n\n Name Burst Start End Wait Time\n");
for(k=0;k<n;k++)
printf("\n%s \t%d \t%d \t%d \t%d",p[k].pName,p[k].ex_time,p[k].st_time,p[k
].end_time,p[k].wt_time);
21
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
p1 3 0 3 0
p2 2 3 5 3
p3 1 5 6 5
p4 4 6 10 6
p5 2 10 12 10
Average Waiting Time 4.800000
Average Turn Around Time 7.200000
#include<stdio.h>
struct process
{
char pname[10];
int ex_time,wt_time,st_time,end_time;
}p[10],temp;
main()
{
int n,i,j,k;
float avgwaittime=0.0,avgturnaroundtime=0.0;
22
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
float totalwaittime=0.0;
int totalexectime=0,totalturnaroundtime=0;
printf("\n enter the number of process");
scanf("%d",&n);
p[0].st_time=0;
p[0].wt_time=0;
for(i=0;i<n;i++)
{
printf("\n enter process name");
scanf("%s",p[i].pname);
printf("enter burst time");
scanf("%d",&p[i].ex_time);
}
for(i=0;i<n;i++)
for(j=0;j<n;j++)
{
temp=p[i];p[i]=p[j];p[j]=temp;
}
}
p[0].wt_time=0;
p[0].st_time=0;
p[0].end_time=p[0].ex_time;
for(j=0;j<n;j++)
{
if(j>0)
{
p[j].wt_time=p[j-1].end_time;
p[j].st_time=p[j-1].end_time;
p[j].end_time=p[j].st_time+p[j].ex_time;
}
totalexectime+=p[j].ex_time;
totalwaittime+=p[j].wt_time;
}
avgwaittime=(float)totalwaittime/n;
totalturnaroundtime=totalexectime+totalwaittime;
avgturnaroundtime=(float)totalturnaroundtime/n;
printf("\n\nname brust start endwaittime\n");
for(k=0;k<n;k++)
printf("\n%s\t%d\t%d\t%d\t%d",p[k].pname,p[k].ex_time,p[k].st_time,p[k].end_time
,p[k].wt_time);
printf("\naverage waiting time %f",avgwaittime);
printf("\n average turnaroundtime %f",avgturnaroundtime);
}
23
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
p4 2 0 2 0
p1 3 2 5 2
p3 4 5 9 5
p2 6 9 15 9
Result :
Thus C program was written for simulating FCFS and SJF scheduling algorithms.
24
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
Aim :
To write a C program for simulating Priority and Round Robin scheduling algorithms.
Priority Scheduling
The basic idea is straightforward: each process is assigned a priority, and priority is allowed
to run. EqualPriority processes are scheduled in FCFS order. The shortestJobFirst (SJF)
algorithm is a special case of general priority scheduling algorithm.
An SJF algorithm is simply a priority algorithm where the priority is the inverse of the
(predicted) next CPU burst. That is, the longer the CPU burst, the lower the priority and vice
versa.
Priority can be defined either internally or externally. Internally defined priorities use some
measurable quantities or qualities to compute priority of a process.
Examples of Internal priorities are
● Time limits.
● Memory requirements.
● File requirements,
for example, number of open files.
● CPU Vs I/O requirements.
Externally defined priorities are set by criteria that are external to operating system such as
● The importance of process.
● Type or amount of funds being paid for computer use.
● The department sponsoring the work.
● Politics.
Priority scheduling can be either preemptive or non preemptive
● A preemptive priority algorithm will preemptive the CPU if the priority of the newly
arrival process is higher than the priority of the currently running process.
● A nonpreemptive priority algorithm will simply put the new process at the head of
the ready queue.
A major problem with priority scheduling is indefinite blocking or starvation. A solution to
the problem of indefinite blockage of the lowpriority process is aging. Aging is a technique
of gradually increasing the priority of processes that wait in the system for a long period of
time.
Round Robin Scheduling
One of the oldest, simplest, fairest and most widely used algorithm is round robin (RR).
In the round robin scheduling, processes are dispatched in a FIFO manner but are given a
limited amount of CPU time called a timeslice or a quantum.
25
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
If a process does not complete before its CPUtime expires, the CPU is preempted and given
to the next process waiting in a queue. The preempted process is then placed at the back of
the ready list.
Round Robin Scheduling is preemptive (at the end of timeslice) therefore it is effective in
timesharing environments in which the system needs to guarantee reasonable response times
for interactive users.
The only interesting issue with round robin scheme is the length of the quantum. Setting the
quantum too short causes too many context switches and lower the CPU efficiency. On the
other hand, setting the quantum too long may cause poor response time and appoximates
FCFS.
In any event, the average waiting time under round robin scheduling is often quite long.
Priority Scheduling
#include<stdio.h>
struct process
{
char pname[10];
int ex_time,wt_time,st_time,end_time,turn_time,priority;
}p[10],temp;
main()
{
int n,i,j,k;
float avgwaittime=0.0,avgturnaroundtime=0.0;
float totalwaittime=0.0;
int totalexectime=0,totalturnaroundtime=0;
printf("\nenter number of process");
scanf("%d",&n);
p[0].st_time=0;
p[0].wt_time=0;
for(i=0;i<n;i++)
{
printf("\nenter process name");
scanf("%s",p[i].pname);
printf("\nenter process priority 0 with highest priority");
scanf("%d",&p[i].priority);
printf("enter burst time");
scanf("%d",&p[i].ex_time);
}
for(i=0;i<n;i++)
for(j=0;j<n;j++)
{
if(p[i].priority<p[j].priority)
{
temp=p[i];
p[i]=p[j];
p[j]=temp;
}
}
26
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
if(j==0)
{
p[j].wt_time=0;
p[j].st_time=0;
p[j].end_time=p[j].ex_time;
p[j].turn_time=p[j].ex_time+p[j].wt_time;
}
if(j>0)
{
p[j].wt_time=p[j-1].end_time;
p[j].st_time=p[j-1].end_time;
p[j].end_time=p[j].st_time+p[j].ex_time;
p[j].turn_time=p[j].ex_time+p[j].wt_time;
}
totalexectime+=p[j].ex_time;
totalwaittime+=p[j].wt_time;
totalturnaroundtime+=p[j].turn_time;
}
avgwaittime=(float)totalwaittime/n;
avgturnaroundtime=(float)totalturnaroundtime/n;
printf("\n\nname burst start end waittime\n");
for(k=0;k<n;k++)
printf("\n%s\t%d\t%d\t%d\t%d\t%d",p[k].pname,p[k].ex_time,p[k].st_time,p[k].end_
time,p[k].wt_time); 37,1 87%
printf("\naveragewaitingtime%f",avgwaittime);
printf("\naverageturnaroundtime%f",avgturnaroundtime);
}
OUTPUt:
27
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
p3 4 0 4 0
p1 3 4 7 4
p4 2 7 9 7
p2 6 9 15 9
for(i=0;i<n;i++)
{
printf("\nEnter process name");
scanf("%s",p[i].PName);
printf("Enter Burst time ");
scanf("%d",&p[i].ex_time);
p[i].rem_time=p[i].ex_time;
p[i].completed_time=0;
}
printf("\nEnter quantum time ");
scanf("%d",&quant_time);
printf("\n name start end busrt rem compl");
/* for first process start and the wait time to be 0 */
j=0;
fs=p[j].st_time=0;
p[j].wt_time=0;
if(p[j].ex_time>quant_time)
{ p[j].end_time=quant_time;
28
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
p[j].completed_time=quant_time;
}
else
{p[j].end_time=p[j].ex_time;
p[j].completed_time=p[j].ex_time;
p[j].wt_time=p[j].end_time-p[j].rem_time;
p[j].turn_time=p[j].wt_time+p[j].ex_time;
}
p[j].rem_time=p[j].ex_time-p[j].completed_time;
printf("\n%s\t%d\t%d\t%d\t%d\t
%d",p[j].PName,p[j].st_time,p[j].end_time,p[j].ex_time,p[j].rem_time,p[j].completed_time);
fe=p[j].end_time;
j++;
while(j<n)
{
p[j].st_time=fe;
if(p[j].rem_time>quant_time)
{
p[j].end_time=p[j].st_time+quant_time;
p[j].rem_time-=quant_time;
p[j].completed_time+=quant_time;
fe+=quant_time;
printf("\n%s\t%d\t%d\t%d\t%d\t
%d",p[j].PName,p[j].st_time,p[j].end_time,p[j].ex_time,p[j].rem_time,p[j].completed_time);
}
else if(p[j].rem_time>0)
{
p[j].end_time=p[j].st_time+p[j].rem_time;
p[j].wt_time=fe-p[j].completed_time;
p[j].completed_time+=p[j].rem_time;
p[j].turn_time=p[j].wt_time+p[j].ex_time;
fe+=p[j].rem_time;
p[j].rem_time=0;
printf("\n%s\t%d\t%d\t%d\t%d\t
%d",p[j].PName,p[j].st_time,p[j].end_time,p[j].ex_time,p[j].rem_time,p[j].completed_time);
}
j++;
if(j==n)
{
for(k=0;k<n;k++)
{
if(p[k].rem_time>0) /*break for the inner for loop */
{ j=k;
break; }
}
if(k>n) /* breaks from the outer while loop */
break;
29
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
}
}
printf("\n Process Name Waiting Time Turn Around Time");
for(i=0;i<n;i++)
{ printf("\n%s\t\t%d\t\t%d",p[i].PName,p[i].wt_time,p[i].turn_time);
totalWaitTime+=p[i].wt_time;
totalTurnAroundTime+=p[i].turn_time;
}
avgWaitTime=(float)totalWaitTime/n;
avgTurnAroundTime=(float)totalTurnAroundTime/n;
printf("\nAverage waiting time %f",avgWaitTime);
printf("\nAverage turn around time %f",avgTurnAroundTime);
}
OUTPUT:
enter number of process4
Result :
Thus C program was written for simulating FCFS and SJF scheduling algorithms.
30
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
Aim
To write a C program to implement inter-process communication using pipes, shared memory
and message queues.
Description
The Linux IPC (Inter-process communication) facilities provide a method for multiple
processes to communicate with one another.
1. Signals - Sent by other processes or the kernel to a specific process to indicate various
conditions.
2. Pipes - Unnamed pipes set up by the shell normally with the "|" character to route
output from one program to the input of another.
3. FIFOS - Named pipes operating on the basis of first data in, first data out.
4. Message queues - Message queues are a mechanism set up to allow one or more
processes to write messages that can be read by one or more other processes.
5. Semaphores - Counters that are used to control access to shared resources. These
counters are used as a locking mechanism to prevent more than one process from
using the resource at a time.
6. Shared memory - The mapping of a memory area to be shared by multiple processes.
Message queues, semaphores, and shared memory can be accessed by the processes if they
have access permission to the resource as set up by the object's creator. The process must
pass an identifier to the kernel to be able to get the access.
PIPES
The Linux IPC (Inter-process communication) facilities provide a method for multiple
processes to communicate with one another. There are several methods of IPC available to
Linux.
Simply put, a pipe is a method of connecting the standard output of one process to the
standard input of another. Pipes are the eldest of the IPC tools, having been around since the
earliest incarnations of the UNIX operating system. They provide a method of one-way
communications (hence the term half-duplex) between processes.
To create a simple pipe with C, we make use of the pipe() system call. It takes a single
argument, which is an array of two integers, and if successful, the array will contain two new
file descriptors to be used for the pipeline. After creating a pipe, the process typically spawns
a new process (remember the child inherits open file descriptors).
PROTOTYPE: int pipe( int fd[2] );
31
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
RETURNS: 0 on success
-1 on error
Shared Memory
Shared memory allows one or more processes to communicate via memory that appears in all
of their virtual address spaces. The pages of the virtual memory is referenced by page table
entries in each of the sharing processes' page tables. It does not have to be at the same address
in all of the processes' virtual memory.
SYNOPSIS
#include <sys/ipc.h>
#include <sys/shm.h>
The function shmat attaches the shared memory segment identified by shmid to the
address space of the calling process. The attaching address is specified by shmaddr with
one of the following criteria:
int shmdt(const void *shmaddr);
The function shmdt detaches the shared memory segment located at the address
specified by shmaddr from the address space of the calling process.
Message Queues
Message queues can be best described as an internal linked list within the kernel's addressing
space. Messages can be sent to the queue in order and retrieved from the queue in several
different ways. Each message queue (of course) is uniquely identified by an IPC identifier.
Message queues allow one or more processes to write messages, which will be read by one or
more reading processes. Linux maintains a list of message queues, the msgque vector; each
element of which points to a msqid_ds data structure that fully describes the message queue.
When message queues are created a new msqid_ds data structure is allocated from system
memory and inserted into the vector.
NAME
msgget - get a message queue identifier
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
32
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
DESCRIPTION
The function returns the message queue identifier associated with the value of the key
argument.
int msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg);
The msgsnd system call appends a copy of the message pointed to by msgp to the
message queue whose identifier is specified by msqid.
ssize_t msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msg-
typ, int msgflg);
The system call msgrcv reads a message from the message queue specified by msqid
into the msgbuf pointed to by the msgp argument, removing the read message from the
queue.
Exercise Programs :
#include<stdio.h>
main()
{
int fd[2],child;
char a[10];
printf("\nEnter the string to enter into the pipe ");
scanf("%s",a);
pipe(fd);
33
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
child=fork();
if(!child)
{
close(fd[0]);
write(fd[1],a,5);
wait(0);
}
else
{
close(fd[1]);
read(fd[0],a,5);
printf("\nThe string received from the pipe is %s\n",a);
}
return 0;
}
OUTPUT:
enter the string to enter into the pipe hello
the string received from the pipe is hello
main()
{
int child, shmid,i;
char *shmptr;
child=fork();
if(!child)
{
shmid=shmget(2041,32,0666|IPC_CREAT);
shmptr=shmat(shmid,0,0);
printf("\nParent writing.........\n");
for(i=0;i<10;i++)
{
shmptr[i]='a'+i;
putchar(shmptr[i]);
}
printf("\n%s",shmptr);
wait(NULL);
}
else
{
shmid=shmget(2041,32,0666|IPC_CREAT);
shmptr=shmat(shmid,0,0);
34
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
main()
{
int msqid;
int msgflg = IPC_CREAT | 0666;
key_t key;
message_buf sbuf;
size_t buf_length;
/* Get the message queue id for the "name" 1234, which was created by
* the server. */
key = 1234;
msqid = msgget(key, msgflg );
sbuf.mtype = 1;
(void) strcpy(sbuf.mtext, "Did you get this?");
buf_length = strlen(sbuf.mtext) ;
35
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
/* Send a message. */
msgsnd(msqid, &sbuf, buf_length, IPC_NOWAIT);
printf("Message: \"%s\" Sent\n", sbuf.mtext);
}
OUTPUT:
message:"did you get there?" sent
main()
{
int msqid;
key_t key;
message_buf rbuf;
/* Get the message queue id for the "name" 1234, which was created by the server. */
key = 1234;
Result :
36
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
Thus C program was program written to implement IPC using pipes, shared memory and
message queues.
Semaphores
Semaphore is a location in memory whose value can be tested and set by more than one
process. The test and set operation is, so far as each process is concerned, uninterruptible or
atomic; once started nothing can stop it. The result of the test and set operation is the addition
of the current value of the semaphore and the set value, which can be positive or negative.
Depending on the result of the test and set operation one process may have to sleep until the
semphore's value is changed by another process. Semaphores can be used to implement
critical regions, areas of critical code that only one process at a time should be executing.
Say you had many cooperating processes reading records from and writing records to a single
data file. You would want that file access to be strictly coordinated. You could use a
semaphore with an initial value of 1 and, around the file operating code, put two semaphore
operations, the first to test and decrement the semaphore's value and the second to test and
increment it. The first process to access the file would try to decrement the semaphore's value
and it would succeed, the semaphore's value now being 0. This process can now go ahead and
use the data file but if another process wishing to use it now tries to decrement the
semaphore's value it would fail as the result would be 1. That process will be suspended until
the first process has finished with the data file. When the first process has finished with the
data file it will increment the semaphore's value, making it 1 again. Now the waiting process
can be woken and this time its attempt to increment the semaphore will succeed.
A semaphore is represented by an anonymous structure including the following members:
The function semop performs operations on selected members of the semaphore set
indicated by semid. Each of the nsops elements in the array pointed to by sops specifies an
operation to be performed on a semaphore by a struct sembuf including the following
members:
37
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
DESCRIPTION
This function returns the semaphore set identifier associated with the
argument key.
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
DESCRIPTION
The function semctl performs the control operation specified by cmd on the semaphore
set identified by semid, or on the semnum-th semaphore of that set. (Semaphores are
numbered starting at 0.)
38
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
return 0;
}
OUTPUT:
Semaphore set created ,semaphore set id '2588751'
Producer:0
Consumer:0
Producer:1
Producer:2
Consumer:1
Consumer:2
39
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
Producer:3
Consumer:3
Producer:4
Consumer:4
Producer:5
Producer:6
Producer:7
Consumer:5
Consumer:6
Consumer:7
Producer:8
Producer:9
Consumer:8
Consumer:9
Result :
40
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
Aim :
Description :
In an environment that supports dynamic memory allocation, the memory manager must keep
a record of the usage of each allocatable block of memory. This record could be kept by using
almost any data structure that implements linked lists. An obvious implementation is to
define a free list of block descriptors, with each descriptor containing a pointer to the next
descriptor, a pointer to the block, and the length of the block. The memory manager keeps a
free list pointer and inserts entries into the list in some order conducive to its allocation
strategy. A number of strategies are used to allocate space to the processes that are competing
for memory.
Best Fit
The allocator places a process in the smallest block of unallocated memory in which it will
fit.
Problems:
It requires an expensive search of the entire free list to find the best
hole.
More importantly, it leads to the creation of lots of little holes that are
not big enough to satisfy any requests. This situation is called fragmentation, and is a
problem for all memory-management strategies, although it is particularly bad for
best-fit.
Solution: One way to avoid making little holes is to give the client a bigger block than it
asked for. For example, we might round all requests up to the next larger multiple of 64
bytes. That doesn't make the fragmentation go away, it just hides it.
Worst Fit
41
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
The memory manager places process in the largest block of unallocated memory available.
The idea is that this placement will create the largest hole after the allocations, thus
increasing the possibility that, compared to best fit, another process can use the hole created
as a result of external fragmentation.
First Fit
Another strategy is first fit, which simply scans the free list until a large enough hole is
found. Despite the name, first-fit is generally better than best-fit because it leads to less
fragmentation.
Problems:
Small holes tend to accumulate near the beginning of the free list,
making the memory allocator search farther and farther each time.
Next Fit
The first fit approach tends to fragment the blocks near the beginning of the list without
considering blocks further down the list. Next fit is a variant of the first-fit strategy. The
problem of small holes accumulating is solved with next fit algorithm, which starts each
search where the last one left off, wrapping around to the beginning when the end of the list
is reached (a form of one-way elevator)
Result:
Thus C program was written to implement first fit and best fit allocation strategies.
Paging
• Basic idea: allocate physical memory to processes in fixed size chunks called page
frames. Present abstraction to application of a single linear address space. Inside machine,
break address space of application up into fixed size chunks called pages. Pages and page
frames are same size. Store pages in page frames. When process generates an address,
dynamically translate to the physical page frame which holds data for that page.
42
Lab Manual Meenakshi College of Engineering, Chennai
CS 2257 - Operating Systems Laboratory
• So, a virtual address now consists of two pieces: a page number and an offset within
that page. Page sizes are typically powers of 2; this simplifies extraction of page numbers
and offsets. To access a piece of data at a given address, system automatically does the
following:
o Extracts page number.
o Extracts offset.
o Translate page number to physical page frame id.
o Accesses data at offset in physical page frame.
43
Lab Manual Meenakshi College of Engineering, Chennai