OSrecdwithout Output
OSrecdwithout Output
OBJECTIVE
This lab complements the operating systems course. Students will gain practical experience with
designing and implementing concepts of operating systems such as
1. Learn different types of CPU scheduling algorithms.
2. Demonstrate the usage of semaphores for solving synchronization problem.
3. Understand memory management techniques and different types of fragmentation.
4. That occur in them and various page replacement policies.
5. Understand Banker‘s algorithm used for deadlock avoidance.
6. Learn various disk scheduling algorithms.
using C language in Linux environment.
OUTCOMES
Upon the completion of Operating Systems practical course, the student will be able to:
1
BASIC COMMANDS:
1. Date Command :
This command is used to display the current data and time.
$date
2. Calender Command :
This command is used to display the calendar of the year or the particular month of
calendar year.
Syntax :
a. $cal <year>
b. $cal <month> <year>
Here the first syntax gives the entire calendar for given year & the second Syntax gives
the calendar of reserved month of that year.
3. Echo Command :
This command is used to print the arguments on the screen .
Syntax : $echo <text>
4. ’who’ Command :
It is used to display who are the users connected to our computer currently.
Syntax : $who
5. ’who am i’ Command :
Display the details of the current working directory.
Syntax : $who am i
6. ’CLEAR’ Command :
It is used to clear the screen.
Syntax : $clear
7. ’MAN’ Command :
It help us to know about the particular command and its options & working. It is like
„help‟ command in windows .
Syntax : $man <command name>
8. LIST Command :
It is used to list all the contents in the current working directory.
Syntax : $ ls – options <arguments>
If the command does not contain any argument means it is working in the Current directory.
2
Options :
a– used to list all the files including the hidden files.
c– list all the files columnwise.
d- list all the directories.
m- list the files separated by commas.
p- list files include „/‟ to all the directories.
r- list the files in reverse alphabetical order.
f- list the files based on the list modification date.
x-list in column wise sorted order.
1. CREATE A FILE :
To create a new file in the current directory we use CAT command.
Syntax : $cat > <filename>
The > symbol is redirectory we use cat command.
2. DISPLAY A FILE :
To display the content of file mentioned we use CAT command without „>‟ operator.
Syntax : $cat <filename>
Options –s = to neglect the warning /error message.
3
3. Concatinate two files :
$cat file1 file2 >file3 /*Concatenate file1 and file2 to file3 */
4. SORTING A FILE :
To sort the contents in alphabetical order in reverse order.
Syntax :
$sort <filename >
Option : $ sort –r <filename>
5. copying contents from one file to another :
To copy the contents from source to destination file . so that both contents are same.
Syntax :
$cp <source filename> <destination filename>
$cp <source filename path > <destination filename path>
Wen both are ordinary files, the first is copied into second. If destination file doesn’t exit, it will
be created before copying takes places. If it does exit, it will simply be overwritten without any
warning from the system.
6. MOVE Command :
To completely move the contents from source file to destination file and to remove the source file.
When both are ordinary files, the first is moved into second.
If destination file doesn’t exit , it will be created before copying takes place.
If it does exit, it will simply be overwritten without any warning from system
Syntax :
$ mv <source filename> <destination filename>
On execution of this command FileName1 no longer exit at its original location, but the
contents of FileName1 is moved to FileName2.
7. REMOVE Command :
To permanently remove the file we use this command .
Syntax :
$rm <filename>
8. WORD Command :
To list the content count of no of lines , words, characters .
Syntax :
$wc<filename>
4
Options :
-c – to display no of characters.
-l – to display only the lines.
-w – to display the no of words.
FILTERS AND PIPES
HEAD : It is used to display the top ten lines of file.
Syntax: $head<filename>
TAIL : This command is used to display the last ten lines of file.
Syntax: $tail<filename>
5
EXPERIMENT 1
OBJECTIVE
Write a C programs to implement UNIX system calls
(a) fork() (b)sleep() ( c)wait() (d)execv() (e)execlp()
DESCRIPTION: system callsork (): The fork function creates a copy of the running process. The
copy ( the child) has a copy of parent process stack, data area, heap and starts after fork statement.
The fork function is available in unistd.h
wait (): The wait function suspends the execution of current process until a child has exited
or until a signal is delivered whose action is to terminate the current process.
sleep (): It suspends the execution of a process for a specified amount of time.
getpid (): This function returns the ID of the process from where it is called.
getppid (): This function returns the ID of the parent’s process from where it is called.
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
pid_t p;
p = fork();
if (p == 0)
{
printf("I am child having PID %d\n", getpid());
printf("My parent PID is %d\n", getppid());
}
else
{
printf("I am parent having PID %d\n", getpid());
printf("My child PID is %d\n", p);
}
return 0;
}
6
Compile:
Run:
Output:
#include<stdio.h>
#include<unistd.h>
int main()
{
pid_t ret_value;
printf(“\n The process id is %d\n”,getpid());
ret_value=fork();
if(ret_value<0)
{
//fork has failed
printf(“\n Fork Failure\n”);
}
elseif(ret_value==0)
{
//child process
sleep(20);
printf(“\n child process\n”);
printf(“the process id is %d\n”,getpid());
else
{
//parent process
printf(“parent process\n”);
printf(“the process id is %d\n”,getpid());
}
return 0;
}
Compile:
Run:
Output:
7
(c )AIM : To demonstrate usage of wait() System calls
#include<stdio.h>
#include<unistd.h>
int main()
{
pid_t ret_value;
printf(“\n The process id is %d\n”,getpid());
ret_value=fork();
if(ret_value<0)
{
//fork has failed
printf(“\n Fork Failure\n”);
}
elseif(ret_value==0)
{
//child process
printf(“\n child process\n”);
printf(“the process id is %d\n”,getpid());
else
{
//parent process
wait();
printf(“parent process\n”);
printf(“the process id is %d\n”,getpid());
}
return 0;
}
Compile:
Run:
Output:
8
DESCRIPTION: exec system call
execl (): This function initiates a new program in the same environment in which it is
operating. An executable program with fully qualified path, i.e, /bin/ls and arguments are
passed to the function.
execlp (): The routine execlp () will perform the same routine except that it will use
environment variable path to determine which is executable to process. Thus, a fully
qualified name would not have to be used. The first argument to the function could be “ls”.
This function can also take the fully qualified name as it also resolves explicitly.
execv (): This is same as exec () except that the arguments are passed as null terminated
array of pointers to char. The first element “argv[0]” is the command name.
execvp (): The routine execvp () will perform the same purpose except that it will use
environment variable path to determine which is executable to process. The first argument
to the function could be “ls”. This function can also take the fully qualified name as it also
resolves explicitly.
#include<stdio.h>
#include<unistd.h>
main()
{
char *temp[3];
temp[0]="ls";
temp[1]="-l";
temp[2]=(char *)0;
execv("/bin/ls",temp);
printf("this will not print\n");
}
Compile:
Run:
Output:
9
(e)AIM: Program to implement execlp function
#include<stdio.h>
#include<sys/types.h>
main()
{
int pid;
pid=fork();
if(pid==0)
{
printf("\n fork program started");
execlp("/bin/ls","ls",NULL);
}
else
{
printf("\nend");
}
}
Compile:
Run:
Output:
10
EXPERIMENT 2
OBJECTIVE
Write a C programs to implement file management system calls
DESCRIPTION:
The file structure related system calls available in the UNIX system let us create, open, and
close files, read and write files, randomly access files, alias and remove files, get information
about files, check the accessibility of files, change protections, owner, and group of files, and
control devices. These operations either use a character string that defines the absolute or relative
path name of a file, or a small integer called a file descriptor
#include <unistd.h>
#include <fcntl.h>
int main() {
int n, fd;
char buf[30];
fd = open("fork.c", O_RDONLY);
n = read(fd, buf, 10);
write(1, buf, 10);
return 0;
}
Compile:
Output:
11
AIM: Read from one file and write to another (create if not exists)
#include <unistd.h>
#include <fcntl.h>
int main() {
int n, fd, fd1;
char buf[50];
fd = open("fork.c", O_RDONLY);
n = read(fd, buf, 50);
fd1 = open("target", O_CREAT | O_WRONLY, 0642);
write(fd1, buf, n);
return 0;
}
Compile:
Run:
Output:
#include <unistd.h>
#include <fcntl.h>
int main()
{
int n, fd1;
char buf[50];
n = read(0, buf, 50);
fd1 = open("target", O_WRONLY | O_APPEND);
write(fd1, buf, n);
return 0;
}
Compile:
Run:
Output:
12
//Reads and displays the first 10 bytes of seeking.c.
//Skips next 10 bytes using lseek().
//Reads and displays the next 10 bytes (i.e., bytes 21–30).
offset: Number of bytes to move
whence:
SEEK_SET → from beginning
SEEK_CUR → from current position
SEEK_END → from end
13
AIM: C program to demonstrate Reading, writing, Seeking in Files
int main() {
int fd[2];
char buf1[50] = "Just a sample\n";
char buf2[50];
lseek(fd[0], 0, SEEK_SET);
printf("\n%s", buf2);
14
EXPERIMENT 3
OBJECTIVE
Write C programs to demonstrate various process related concepts.
(a) zombie process (b)orphan process
On UNIX operating system, a zombie process is a process that has completed the execution
but still has an entry in the process table, allowing the process that started it to read its exit
status.
In other words, the child process has died but has not been reaped.
When a process ends, all of the memory and resources associated with it are deallocated so
that they can be used by other processes. However, the processes entry in the process table
remains.
The parent is sent a signal indicating that the child has died; the handler for this signal will
typically execute the system call, which reads the exit state and removes the zombie.
15
else {
sleep(1);
printf("In a parent process\n");
printf("pid=%d \n",getpid());
printf("parent's pid=%d \n",getppid());
printf("child's pid=%d \n",pid);
system("ps -l");
}
}
Compile:
Run:
Output:
16
DESCRIPTION: Orphan process
An orphan process is a computer process whose parent process has finished or terminated.
A process can become an orphan during remote invocation when the client process crashes
after making a request of the server.
A process can also be orphaned during run time on the same machine as its parent process.
In a UNIX-like operating system, any orphaned process will be immediately adopted by the
special “init” system process.
This operation is called re-parenting and occurs automatically. Even though, technically, the
process has the “init” process as its parent, it is still called an orphan process since the process
which originally created it, no longer exists.
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/stat.h>
#include<stdlib.h>
int main(){
int pid;
pid=fork();
if(pid<0){
perror("ERROR");
exit(1);
}
else if(pid==0){
sleep(1);
printf("In a child process\n");
printf("pid=%d \n",getpid());
printf("parent's pid=%d \n",getppid());
}
else{
printf("In a parent process\n");
printf("pid=%d \n",getpid());
printf("parent's pid=%d \n",getppid());
printf("child's pid=%d \n",pid);
}
17
Compile:
Run:
Output:
EXPERIMENT 4
OBJECTIVE
Write C programs to demonstrate various thread related concepts
(a) Thread Creation and Termination (b) Thread Creation and Termination using semaphore
( c) Multithreading
DESCRIPTION:
A thread is an independent stream of instructions that can be scheduled to run as such by the OS.
From the developers point of view a thread is a procedure that runs independently from its main
program. A program containing several procedures being able to be scheduled to run
simultaneously and/or independently by the OS is said to be a multithreaded program. Threads
exist within a process and use process resources .A thread is “lightweight” because most of the
overhead has already been accomplished through the creation of its process
Since threads within the same process share resources
Changes made by one thread will be seen by other threads
Two pointers having the same value point to the same data
Reading and writing to the same memory location is possible and therefore require
explicit synchronization by the programmer
Pthreads
Pthreads are defined as a set of C language programming types and procedure calls, implemented
with a <pthread.h> header/include file and a thread library. Original Pthreads API was in the
ANSI/IEEE POSIX 1003.1-1995 standard. But continued to evolve and undergo revisions
Subroutines comprising the Pthreads API informally grouped into 4 major groups:
Thread management
• Work directly on thread (creation, detaching, joining etc.)
18
Mutexes
• Deal with synchronization, called a “mutex” (mutual exclusion)
• Functions include for crating, destroying, locking and unlocking mutexes
• Supplemented by mutex attribute functions that set or modify attributes associated with
mutexes
Condition variables
• Address communication between threads that share a mutex
• Based on the programmer
• Include functions to create, destroy, wait and signal based on specified variable values
• Functions to set/query condition variable attributes are also included.
Synchronization
• These routines manage read/write locks and barriers
arguments:
thread: unique identifier for the new thread returned by the subroutine
attr: attribute object, that may be set thread attributes.. you can specify a thread
atttribute obejct or NULL. For default values
start_routine: the C routine that the thread will execute once it is created
arg: a single argument that may be passed to start_routine. It must be passed by
reference as a pointer cast of ype void. NULL may be used if no argument is to be
passed.
o Maximum number of threads that can be created by a process is implementation dependent.
Thread attributes:
19
o By default, a thread is created with certain attributes. Some of the attributes can be changed by
the programmer via the thread attribute object.
Thread joining:
Syntax:
pthread_join (threadid, status)
20
/* wait we run the risk of executing an exit which will terminate */
21
pthread_t thread1, thread2;
/* Create independent threads each of which will execute functionC */
if( (rc1=pthread_create( &thread1, NULL, &functionC, NULL)) )
{
printf("Thread creation failed: %d\n", rc1);
}
if( (rc2=pthread_create( &thread2, NULL, &functionC, NULL)) )
{
printf("Thread creation failed: %d\n", rc2);
}
/* Wait till threads are complete before main continues. Unless we */
pthread_join( thread1, NULL);
pthread_join( thread2, NULL);
exit(0);
}
void *functionC()
{
pthread_mutex_lock( &mutex1 );
counter++;
printf("Counter value: %d\n",counter);
pthread_mutex_unlock( &mutex1 );
}
Compile:
Run:
Output:
22
AIM: To Demonstrate Multithreading
#include<pthread.h>
#include<stdio.h>
#define NUM_THREADS 3
int je,jo,evensum=0,sumn=0,oddsum=0,evenarr[50],oddarr[50];
void *Even(void *threadid)
{
int i,n;
je=0;
n=(int)threadid;
for(i=1;i<=n;i++)
{
if(i%2==0)
{
evenarr[je]=i;
evensum=evensum+i;
je++;
}}}
void *Odd(void *threadid)
{
int i,n;
jo=0;
n=(int)threadid;
for(i=0;i<=n;i++)
{
if(i%2!=0)
{
oddarr[jo]=i;
oddsum=oddsum+i;
jo++;
}}}
23
void *SumN(void *threadid)
{
int i,n;
n=(int)threadid;
for(i=1;i<=n;i++)
{
sumn=sumn+i;
}}
int main()
{
pthread_t threads[NUM_THREADS];
int i,t;
printf("enter a number\n");
scanf("%d",&t);
pthread_create(&threads[0],NULL,Even,(void *)t);
pthread_create(&threads[1],NULL,Odd,(void *)t);
pthread_create(&threads[2],NULL,SumN,(void *)t);
for(i=0;i<NUM_THREADS;i++)
{
pthread_join(threads[i],NULL);
}
printf("the sum of first N natural nos is %d\n",sumn);
printf("the sum of first N even natural nos is %d\n",evensum);
printf("the sum of first N odd natural nos is %d\n",oddsum);
printf("the first N even natural nos is --- \n");
for(i=0;i<je;i++)
printf("%d\n",evenarr[i]);
printf("the first N odd natural nos is --- \n");
for(i=0;i<jo;i++)
printf("%d\n",oddarr[i]);
pthread_exit(NULL); }
24
Compile:
Run:
Output:
EXPERIMENT 5
OBJECTIVE
Write C programs to simulate CPU scheduling algorithms:
(a) FCFS (b) SJF (c) priority (d)Round Robin
DESCRIPTION:
CPU scheduling is a process which allows one process to use the CPU while the execution of
another process is on hold(in waiting state) due to unavailability of any resource like I/O etc,
thereby making full use of CPU. The aim of CPU scheduling is to make the system efficient, fast
and fair.
Whenever the CPU becomes idle, the operating system must select one of the processes in
the ready queue to be executed. The selection process is carried out by the short-term scheduler
(or CPU scheduler). The scheduler selects from among the processes in memory that are ready to
execute, and allocates the CPU to one of them.
Dispatcher
The dispatcher is the module that gives control of the CPU to the process selected by the short-
term scheduler.
CPU Scheduling: Scheduling Criteria
There are many different criterias to check when considering the "best" scheduling algorithm,
they are:
CPU Utilization
To make out the best use of CPU and not to waste any CPU cycle, CPU would be working most of
the time(Ideally 100% of the time). Considering a real system, CPU usage should range from 40%
(lightly loaded) to 90% (heavily loaded.)
25
Throughput
It is the total number of processes completed per unit time or rather say total amount of work
done in a unit of time. This may range from 10/second to 1/hour depending on the specific
processes.
Turnaround Time
It is the amount of time taken to execute a particular process, i.e. The interval from time of
submission of the process to the time of completion of the process(Wall clock time).
Turnaround Time=Completion Time - Arrival Time
Waiting Time
The sum of the periods spent waiting in the ready queue amount of time a process has been
waiting in the ready queue to acquire get control on the CPU.
Waiting Time=Turnaround Time - Burst Time
Load Average
It is the average number of processes residing in the ready queue waiting for their turn to get
into the CPU.
Response Time
Amount of time it takes from when a request was submitted until the first response is produced.
Remember, it is the time till the first response and not the completion of process execution(final
response).
In general CPU utilization and Throughput are maximized and other factors are reduced for
proper optimization.
Response Time=First instance of CPU(for any process) – Arrival Time(of that process)
The First Come First Serve (FCFS) CPU Scheduling is by far the simplest algorithm.
As the name indicates, the process that requests the CPU first, is allocated the CPU.
The implementation of the FCFS policy is easily managed by a FIFO queue. When a
process enters the queue (ready queue), its PCB is linked onto the tail of the queue. When
the CPU is free, it is allocated to the process at the head of the queue. The running process
is then removed from the queue.
The average waiting time under the FCFS policy, however, is often quite long.
The FCFS Scheduling Algorithm is non-preemptive. Once the CPU has been allocated to
a process, that process keeps the CPU until it releases the CPU, either by terminating or
by requesting I/O.
Data structures: A simple FIFO queue is used.
26
AIM:To Implement First Come, First Serve (FCFS) Cpu Scheduling Algorithm
#include<stdio.h>
int main(){
int n,i;
int wt[20],bt[20],tat[20],Twt=0,Ttat=0;
float Avgwt=0,Avgtat=0;
system("clear");
printf("Enter the number of processes\n");
scanf("%d",&n);
printf("Enter the burst time for each process\n");
for(i=0;i<n;i++){
scanf("%d",&bt[i]);
}
wt[0]=0;
for(i=1;i<n;i++){
wt[i]=wt[i-1]+bt[i-1];
Twt+=wt[i];
}
for(i=0;i<n;i++){
tat[i]=wt[i]+bt[i];
Ttat+=tat[i];
}
Avgwt=(float)Twt/n;
Avgtat=(float)Ttat/n;
printf("P no\tB.T\tW.T\tT.A.T\n");
for(i=0;i<n;i++)
{
printf("%d\t%d\t%d\t%d\n",i+1,bt[i],wt[i],tat[i]);
}
printf(" \t \t%d\t%d\n",Twt,Ttat);
printf(" \t \t%.2f\t%.2f\t\n",Avgwt,Avgtat);
}
27
Compile:
Run:
Output:
28
(a) AIM:To Implement Shortest Job First(SJF) Cpu Scheduling Algorithm
#include<stdio.h>
#include<stdlib.h>
int main(){
int n,i=0,temp=0,j=0;
system("clear");
int
wt[20],bt[20],tat[20],pno[20],Twt=0,Ttat=0;
float Avgwt=0,Avgtat=0;
system("clear");
printf("Enter the number of processes\n");
scanf("%d",&n);
printf("Enter the burst time for each process\n");
for(i=0;i<n;i++){
scanf("%d",&bt[i]);
pno[i]=i;
}
for(i=0;i<n;i++){
for(j=i+1;j<n;j++){
if(bt[j]<bt[i]){
temp=bt[j];
bt[j]=bt[i];
bt[i]=temp;
temp=0;
temp=pno[j];
pno[j]=pno[i];
pno[i]=temp;
}}
}
wt[0]=0;
for(i=1;i<n;i++){
wt[i]=wt[i-1]+bt[i-1];
Twt+=wt[i];
}
for(i=0;i<n;i++){
tat[i]=wt[i]+bt[i];
Ttat+=tat[i];
}
Avgwt=(float)Twt/n;
Avgtat=(float)Ttat/n;
printf("P no\tB.T\tW.T\tT.A.T\n");
for(i=0;i<n;i++){
printf("%d\t%d\t%d\t%d\n",pno[i],bt[i],wt[i],tat[i]);
}
printf(" \t \t%d\t%d\n",Twt,Ttat);
printf(" \t \t%.2f\t%.2f\t\n",Avgwt,Avgtat);
}
29
Compile:
Run:
Output:
30
DESCRIPTION: Round-Robin Cpu scheduling algorithm
Round robin scheduling is a preemptive version of first-come, first-served scheduling. Processes
are dispatched in a first-in-first-out sequence but each process is allowed to run for only a limited
amount of time. This time interval is known as a time-slice or quantum. If a process does not
complete or get blocked because of an I/O operation within the time slice, the time slice expires
and the process is preempted. process gets blocked because of an I/O operation), it is then
preempted. This preempted process is placed at the back of the run queue where it must wait for
the processes that were already on the list to cycle through the CPU.
#include<stdio.h>
#include<sys/stat.h>
int main(){
int n,i,count=0,t=0,ts, wt[20],bt[20],tat[20],Twt=0,Ttat=0,RT[15];
float Avgwt=0,Avgtat=0;
system("clear");
printf("Enter the number of processes\n");
scanf("%d",&n);
printf("Enter the time slice\n");
scanf("%d",&ts);
printf("Enter the burst time for each process\n");
for(i=1;i<=n;i++){
scanf("%d",&bt[i]);
RT[i]=bt[i];
}
while(1){
for(i=1;i<=n;i++){
if(RT[i]>0){
if(RT[i]>ts){
RT[i]-=ts;
t=t+ts;
}
31
else{
t=t+RT[i];
RT[i]=0;
tat[i]=t;
count++;
}
}
}
if(count==n){
break;
}
}
for(i=1;i<=n;i++){
wt[i]=tat[i]-bt[i];
Twt+=wt[i];
}
for(i=1;i<=n;i++){
Ttat+=tat[i];
}
Avgwt=(float)Twt/n;
Avgtat=(float)Ttat/n;
printf("P
no\tB.T\tW.T\tT.A.T\n");
for(i=1;i<=n;i++){
printf("%d\t%d\t%d\t%d\n",i,bt[i],wt[i],tat[i]);
}
printf(" \t \t%d\t%d\n",Twt,Ttat);
printf(" \t \t%.2f\t%.2f\t\n",Avgwt,Avgtat);
}
Compile:
Run:
Output:
32
EXPERIMENT 6
OBJECTIVE
Write C programs to simulate Intra & Inter-Process Communication (IPC) techniques:
(A) Pipes (B) Messages Queues (C) Shared Memory.
DESCRIPTION:
One of the mechanisms that allow related-processes to communicate is the pipe. A pipe is a one-
way mechanism that allows two related processes (i.e. one is an ancestor of the other) to send a
byte stream from one of them to the other one. The system assures us of one thing: The order in
which data is written to the pipe is the same order as that in which data is read from the pipe. The
system also assures that data won't get lost in the middle, unless one of the processes (the sender
or the receiver) exits prematurely. An echo server is usually an application which is used to test
if the connection between a client and a server is successful. It consists of a server which sends
back whatever text the client sends. The program shows communication between 2 processes
using the IPC mechanism – pipes.
System Calls Used: pipe()
This system call is used to create a read-write pipe that may later be used to communicate with a
process we'll fork off. The call takes as an argument an array of 2 integers that will be used to save
the two file descriptors used to access the pipe. The first one to read from the pipe and the second
one to write to the pipe.
Syntax:
#include <unistd.h>
int pipe (int fd[2]);
Return: 0 on success
-1 on errors
fd[0] is set up for reading and fd[1] is set up for writing. The first element in the array (element 0) is
set up and opened for reading while the second element (element 1) is setup and opened for writing.
33
(A) AIM : To implement two-process communication through using unnamed pipes.
34
AIM: Program to demonstrate IPC using Unnamed Pipes
DESCRIPTION: One of the mechanisms that allow processes to communicate is the named
pipe (fifo). A FIFO special file (a named pipe) is similar to a pipe, except that it is accessed as
part of the filesystem. It can be opened by multiple processes for reading or writing. When
processes are exchanging data via the FIFO, the kernel passes all data internally
without writing it to the filesystem. fifo is a two-way mechanism that allows two unrelated
processes to send/ receive a byte stream between them. The program shows communication
between 2 processes using the IPC mechanism – fifo.
System Calls Used: mkfifo()
mkfifo() makes a FIFO special file with name pathname. Mode specifies the FIFO's permissions.
Syntax:
#include <sys/types.h> #include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
Return: 0 on success
-1 on errors
35
//Program 2 Reads First
#include<stdio.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
int main()
{
int fd;
char str1[100],str2[100];
while(1)
{
fd=open("fifo1",O_RDONLY);
read(fd,str1,sizeof(str1));
printf("user1:%s\n",str1);
close(fd);
fd=open("fifo1",O_WRONLY);
fgets(str2,100,stdin);
write(fd,str2,strlen(str2)+1);
close(fd);
}
Compile:
Run:
Output:
36
(B) AIM: Program to demonstrate IPC using message queues
Description: In message queue, a mail box is created. The OS should read and write program. A
message queue is created and owned by one process. A message queue is created by a msgget call
with the following syntax
msgget( key, flag )
The OS maintains an array of message queues and their keys. The first msgget call results in creation
of new message queue.
The position of the message queue in the system array (called the message queue id ) is returned by
the msgget call. This id’s are used in a send or receive call.
The send and receive calls have the following syntax:
msgsnd( msgqid, msg, count, flag );
msgrcv( msgqid, msg-struct-ptr, maxcount, type, flag );
The count and flag parameters of msgsnd specify number of bytes in a message and the actions it
should take if sufficient space is not available in message queue (e.g. Block the sender).
In msgrcv call, msg-struct-ptr is the address of the structure to receive the message, maxcount is
maximum length of message, type indicates the type of message to be received. When a message
is sent to a message queue on which many processes are waiting, the operating system wakes all
processes. When type parameter in msgrcv is positive, the operating system returns the first
message with a matching type and if it is negative, the operating system returns the lowered
number message with the type value less than the absolute value specified in the call.
An echo server is usually an application which is used to test if the connection between a client
and a server is successful. It consists of a server which sends back whatever text the client sends.
The program shows communication between 2 processes using the IPC mechanism: message
queues
37
// msgQueue
#include<stdio.h>
#include<string.h>
#include<sys/msg.h>
#include<sys/ipc.h>
#include<unistd.h>
int main()
{
int pid,msqid;
char buff[20],buff1[20],a[20];
msqid=msgget((key_t)151,IPC_CREAT|0600);
pid=fork();
if(pid==0)
{ strcpy(a,"160424733161");
msgsnd(msqid,a,12,0);
sleep(2);
msgrcv(msqid,buff1,18,0,0);
printf(" echoed message is %s\n",buff1);
}
else
{
sleep(1);
msgrcv(msqid,buff,12,0,0);
printf("message received is %s\n",buff);
strcat(buff," pass");
msgsnd(msqid,buff,18,0);
}}
Compile:
Run:
Output:
38
( C) DESCRIPTION: IPC using shared memory
Shared Memory is an efficient means of passing data between programs. One program will
create a memory portion which other processes (if permitted) can access. A process creates a
shared memory segment using shmget. Once created, a shared segment can be attached to a
process addressspace using shmat(). It can be detached using shmdt(). Once attached, the process
can read or write to the segment, as allowed by the permission requested in the attach operation.
A shared memory segment is described by a control structure with a unique ID that point to an
area of physical memory. The identifier of the segment is called the shmid. The structure
definition for the shared memory segment control structures and prototype can be found in
<sys/shm.h>
shmget () - allocates a System V shared memory segment
System Calls used: shmget (),shmat(), shmdt()
Syntax:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);.
The argument key is the value of the IPC key to use. The size argument specifies the minimum
size of the shared memory region required. The flag option must contain the permission bits if
shared memory is being created. Additional flags that may be used include IPC_CREAT and
IPC_EXCL, when shared memory is being created.
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
Return: On success shmat() returns the address of the attached shared memory segment; on
error (void *) -1 is returned.
On success shmdt() returns 0; on error -1 is returned, and errno is set to indicate the cause of the
error.
39
( C )AIM : Write a program to illustrate communication between processes using shared memory
// sharedmemory
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<string.h>
#include<sys/shm.h>
int main(){
int pid,shmid;
char *p;
shmid=shmget((key_t)224,20,IPC_CREAT|0600);
p=(char*)shmat(shmid,0,0);
pid=fork();
if(pid==0){
strcpy(p,"data in shared memory\n");
}
else if(pid>0)
{
sleep(1);
printf("%s\n",p);
}
else{
perror("error");
}
}
Compile:
Run:
Output:
40
EXPERIMENT 7
OBJECTIVE
Write C programs to simulate solutions to Classical Process Synchronization Problems:
(A) Dining Philosophers (B) Producer-Consumer (C) Readers-Writers
41
(7.A)AIM: To implement Dining Philosopher’s Problem using semaphore
// dining philosophers
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<semaphore.h>
# define N 5
# define THINKING 0
# define HUNGRY 1
# define EATING 2
# define LEFT (phnum+N-1)%N
# define RIGHT (phnum+1)%N
int phnum;
int state[N];
int phil[N]={0,1,2,3,4};
sem_t mutex;
sem_t s[N];
void test(int phnum){
if(state[phnum]==HUNGRY && state[LEFT]!=EATING &&
state[RIGHT]!=EATING){ state[phnum]=EATING;
sleep(2);
printf("phil %d takes fork %d and %d\n",phnum+1,LEFT+1,phnum+1);
printf("phil %d is eating\n",phnum+1);
sem_post(&s[phnum]);
}
}
void takefork(int phnum){
sem_wait(&mutex);
state[phnum]=HUNGRY;
printf("phil %d is hungry\n",phnum+1);
test(phnum);
sem_post(&mutex);
sem_wait(&s[phnum]);
sleep(1); }
42
void putfork(int phnum)
{
sem_wait(&mutex);
state[phnum]=THINKING;
printf("phil %d putsdown fork %d and %d\n",phnum+1,LEFT+1,phnum+1);
printf("phil %d is in thinking state\n",phnum+1);
test(LEFT);
test(RIGHT);
sem_post(&mutex);
}
void * philosopher(void * num)
{
while(1)
{
int * i=num;
sleep(1);
takefork(* i);
sleep(1);
putfork(* i);
}
}
int main(){
int i;
pthread_t tid[N];
sem_init(&mutex,0,1);
for(i=0;i<N;i++)
sem_init(&s[i],0,0);
for(i=0;i<N;i++){
pthread_create(&tid[i],NULL,philosopher,&phil[i]);
printf("phil %d is thinking\n",phil[i]+1);
}
for(i=0;i<N;i++){
pthread_join(tid[i],NULL);
}
}
43
Compile:
Run:
Output:
44
OBJECTIVE: C Program to implement Producer Consumer problem
DESCRIPTION:
Producer consumer problem is also known as bounded buffer problem. In this problem we have
two processes, producer and consumer, who share a fixed size buffer. Producer work is to produce
data or items and put in buffer. Consumer work is to remove data from buffer and consume it. We
have to make sure that producer do not produce data when buffer is full and consumer do not
remove data when buffer is empty.
The producer should go to sleep when buffer is full. Next time when consumer removes data it
notifies the producer and producer starts producing data again. The consumer should go to sleep
when buffer is empty. Next time when producer add data it notifies the consumer and consumer
starts consuming data. This solution can be achieved using semaphores.
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.
Initialization of semaphores– mutex=1, Full = 0 // Initially, all slots are empty. Thus full slots
are 0 Empty = n // All slots are empty initially
Producer – When producer produces an item then the value of “empty” is reduced by 1 because
one slot will be filled now. The value of mutex is also reduced to prevent consumer to access the
buffer. Now, the producer has placed the item and thus the value of “full” is increased by 1. The
value of mutex is also increased by 1 beacuse the task of producer has been completed and
consumer can access the buffer.
Consumer – As the consumer is removing an item from buffer, therefore the value of “full” is
reduced by 1 and the value is mutex is also reduced so that the producer cannot access the buffer
at this moment. Now, the consumer has consumed the item, thus increasing the value of “empty”
by 1. The value of mutex is also increased so that producer can access the buffer now.
45
(7.B)AIM: To implement Producer &Consumer problem using semaphore
#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)
46
{
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);
}
Compile:
Run:
Output:
47
DESCRIPTION: Readers-Writer problem
Readers writer problem is another example of a classic synchronization problem.
There is a shared resource which should be accessed by multiple processes. There are two types
of processes in this context. They are reader and writer. Any number of readers can read from the
shared resource simultaneously, but only one writer can write to the shared resource. When
a writer is writing data to the resource, no other process can access the resource. A writer cannot
write to the resource if there are non zero number of readers accessing the resource at that time.
Solution when Reader has the Priority over Writer
Here priority means, no reader should wait if the share is currently opened for reading.
Three variables are used: mutex, wrt, readcnt to implement solution
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
Functions for sempahore :
– wait() : decrements the semaphore value.
– signal() : increments the semaphore value.
Writer process:
1. Writer requests the entry to critical section.
2. If allowed i.e. wait() gives a true value, it enters and performs the write. If not allowed, it
keeps on waiting.
3. It exits the critical section.
The code for the writer process:
While(True)
{
// writer requests for critical section
wait(w);
// performs the write
// leaves the critical section
signal(w);
}
48
Reader process:
1. Reader requests the entry to critical section.
2. If allowed:
it increments the count of number of readers inside the critical section. If this reader
is the first reader entering, it locks the wrtsemaphore to restrict the entry of writers
if any reader is inside.
It then, signals mutex as any other reader is allowed to enter while others are already
reading.
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.
And, the code for the reader process
while(TRUE)
{
//acquire lock
wait(m);
read_count++;
if(read_count == 1)
wait(w);
//release lock
signal(m);
/* perform the reading operation */
// acquire lock
wait(m);
read_count--;
if(read_count == 0)
signal(w);
// release lock
signal(m);
}
49
(7.C)AIM: To implement Readers and Writer problem using 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);
}
main()
{
int i,b;
pthread_t rtid[5],wtid[5];
sem_init(&mutex,0,1);
50
sem_init(&writeblock,0,1);
for(i=0;i<=2;i++)
{
pthread_create(&wtid[i],NULL,writer,(void *)i);
pthread_create(&rtid[i],NULL,reader,(void *)i);
}
for(i=0;i<=2;i++)
{
pthread_join(wtid[i],NULL);
pthread_join(rtid[i],NULL);
}
}
Compile:
Run:
Output:
EXPERIMENT 8
OBJECTIVE
Write C programs to simulate Page Replacement Algorithms:
(A)FIFO (B) LRU
DESCRIPTION
Page replacement is basic to demand paging. It completes the separation between logical memory
and physical memory. With this mechanism, an enormous virtual memory can be provided for
programmers on a smaller physical memory. There are many different page-replacement
algorithms. Every operating system probably has its own replacement scheme. A FIFO
replacement algorithm associates with each page the time when that page was brought into
memory. When a page must be replaced, the oldest page is chosen. We replace the page at the head
of the queue. When a page is brought into memory, we insert it at the tail of the queue. If the
recent past is used as an approximation of the near future, then the page that has not been used for
the longest period of time can be replaced. This approach is the Least Recently Used (LRU)
algorithm. LRU replacement associates with each page the time 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.
51
Optimal page replacement algorithm has the lowest page-fault rate of all algorithms and will never
suffer from Belady's anomaly. The basic idea is to replace the page that will not be used for the
longest period of time. Use of this page-replacement algorithm guarantees the lowest possible page
fault rate for a fixed number of frames. Unfortunately, the optimal page-replacement algorithm is
difficult to implement, because it requires future knowledge of the reference string.
52
Compile:
Run:
Output:
53
(8.B) AIM: To implement LRU page replacement algorithms
54
printf("Total pf is %d\n",pf);
mr=(float)pf/n;
hr=(float)(n-pf)/n;
printf("hr,mr %.2f %.2f\n",hr,mr);
}
Compile:
Run:
Output:
55
EXPERIMENT 9
OBJECTIVE
Write a C program to simulate Bankers algorithm for the purpose of deadlock avoidance.
DESCRIPTION:
Banker's algorithm is a deadlock avoidance algorithm. It is named so because this algorithm is
used in banking systems to determine whether a loan can be granted or not.
Consider there are n account holders in a bank and the sum of the money in all of their accounts
is S. Every time a loan has to be granted by the bank, it subtracts the loan amount from the total
money the bank has. Then it checks if that difference is greater than S. It is done because, only
then, the bank would have enough money even if all the n account holders draw all their money
at once.
Banker's algorithm works in a similar way in computers.
Whenever a new process is created, it must specify the maximum instances of each
resource type that it needs, exactly.
Let us assume that there are n processes and m resource types. Some data structures
that are used to implement the banker's algorithm are:
1. Available
It is an array of length m. It represents the number of available resources of each type.
If Available[j] = k, then there are k instances available, of resource type R(j).
2. Max
It is an n x m matrix which represents the maximum number of instances of each
resource that a process can request. If Max[i][j] = k, then the process P(i) can request
at most k instances of resource type R(j).
3. Allocation
It is an n x m matrix which represents the number of resources of each type currently
allocated to each process. If Allocation[i][j] = k, then process P(i) is currently
allocated k instances of resource type R(j).
4. Need
It is an n x m matrix which indicates the remaining resource needs of each process.
If Need[i][j] = k, then process P(i) may need k more instances of resource type R(j) to
complete its task.
Need[i][j] = Max[i][j] - Allocation [i][j]
56
// bankers Algorithm
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(){
int
max[10][10],need[10][10],alloc[10][10],avail[10],fin[10],safe[10];
int pr_cnt,res_cnt,i,j,process,count=0;
system("clear");
printf(" Enter the system state information\n");
printf("Enter the number of processes\n");
scanf("%d",&pr_cnt);
for(i=0;i<pr_cnt;i++){
fin[i]=0;
}
printf("Enter the number of resources\n");
scanf("%d",&res_cnt);
printf("Enter the max matrix for all processes:\n");
for(i=0;i<pr_cnt;i++)
{
for(j=0;j<res_cnt;j++)
{
printf("Enter max info for process[%d][%d]\n",i,j);
scanf("%d",&max[i][j]);
}
}
for(i=0;i<pr_cnt;i++){
for(j=0;j<res_cnt;j++){
printf("Enter allocation info for process[%d][%d]\n",i,j);
scanf("%d",&alloc[i][j]);
}
}
printf("Enter the available resources in the system:\n");
for(i=0;i<res_cnt;i++){
printf("available[%d]=\n",i);
scanf("%d",&avail[i]);
}
for(i=0;i<pr_cnt;i++){
for(j=0;j<res_cnt;j++){
need[i][j]=max[i][j]-alloc[i][j];
} }
57
do{
printf("Available resources are:\n");
for(j=0;j<res_cnt;j++)
printf("%d\t",avail[j]);
printf("\n max matrix:\t allocation matrix:\n");
for(i=0;i<pr_cnt;i++){
for(j=0;j<res_cnt;j++){
printf("%d\t",max[i][j]);
}
printf("\t");
for(j=0;j<res_cnt;j++){
printf("%d\t",alloc[i][j]);
}
}
printf("\n");
}
process=-1;
for(i=0;i<pr_cnt;i++){
if(fin[i]==0){
process=i;
for(j=0;j<res_cnt;j++){
if(avail[j]<need[i][j]){
process=-1;
break;
}
}
}
if(process!=-1)
break;
}
if(process!=-1){
safe[count]=process+1;
count++;
for(j=0;j<res_cnt;j++){
avail[j]+=alloc[process][j];
alloc[process][j]=0;
max[process][j]=0;
fin[process]=1;
}
}
}
58
while(count!=pr_cnt && process!=-1);
if(count==pr_cnt)
{
printf("the system is in a safe state\n");
printf("Safe sequence:<");
for(i=0;i<pr_cnt;i++)
printf("%d",safe[i]);
printf(">\n");
}
else{
printf("the system is in unsafe state!!\n");
}
}
Compile:
Run:
Output:
59
60
EXPERIMENT 10
OBJECTIVE
To Write a C program to simulate disk scheduling algorithms
(A)FCFS (B)SSTF
(A)DESCRIPTION
One of the responsibilities of the operating system is to use the hardware efficiently. For the disk
drives, meeting this responsibility entails having fast access time and large disk bandwidth. Both
the access time and the bandwidth can be improved by managing the order in which disk I/O
requests are serviced which is called as disk scheduling. The simplest form of disk scheduling is,
of course, the first-come, first-served (FCFS) algorithm. FCFS (First-Come, First-Served) Disk
Scheduling Algorithm is a simple and easy-to-implement algorithm used in operating systems to
manage input/output (I/O) requests from processes to access disk blocks. In this algorithm, the
operating system processes the I/O requests in the order in which they arrive in the queue,
without any reordering or prioritization.
When a process generates an I/O request, it is added to the end of the queue, and the operating
system services the requests in the same order. The requests are serviced one by one until the
entire queue is empty. This algorithm has the advantage of being fair, predictable, and requiring
low overhead. However, it also has limitations, such as long waiting times for requests that arrive
later and potential starvation of requests that are stuck behind long-running requests.
(B) DESCRIPTION: Shortest Seek Time First (SSTF) is a disk scheduling algorithm that
prioritizes processing I/O requests that are closest to the current position of the disk head. The
algorithm selects the I/O request with the shortest seek time to the next request, in order to
reduce disk head movement and improve disk access times.
When an I/O request is generated, the SSTF algorithm selects the request that is closest to the
current position of the disk head. The algorithm processes the request with the shortest seek time
first and then selects the next closest request. This process continues until all requests are
processed.
SSTF is a more efficient algorithm than FCFS, as it prioritizes processing requests with the
shortest seek time. This reduces the amount of time the disk head spends moving across the disk,
resulting in faster access times and improved system performance. SSTF also provides better
response times for time-critical requests and can be useful in real-time systems.
61
(10.A)AIM: C program to simulate FCFS Disk scheduling Algorithm
#include<stdio.h>
#include<stdlib.h>
int main(){
int req[20],n,hdpos,tseek=0,i;
float avgmv;
printf("Enter the number of requests\n");
scanf("%d",&n);
printf("Enter the disk read requests\n");
for(i=0;i<n;i++){
//temp[i]=req[i];
scanf("%d",&req[i]);
}
printf("Enter head position\n");
scanf("%d",&hdpos);
printf("FCFS sequence is\n");
for(i=0;i<n;i++){
printf("%d->",req[i]);
tseek=tseek+abs(req[i]-hdpos);
hdpos=req[i];
}
avgmv=(float)tseek/n;
printf("\nTST is %d\n",tseek);
printf("Avgmv is %.2f\n",avgmv);
}
Compile:
Run:
Output:
62
(10.B)AIM: C program to simulate SSTF Disk scheduling Algorithm
#include<stdlib.h>
#include<limits.h>
int main(){
int
req[20],n,hdpos,tseek=0,i,temp[20],min,dist,index,cpos,cnt=0;
float avgmv;
printf("Enter the number of requests\n");
scanf("%d",&n);
printf("Enter the disk read requests\n");
for(i=0;i<n;i++){
temp[i]=req[i];
scanf("%d",&req[i]);
}
printf("Enter head position\n");
scanf("%d",&hdpos);
printf("SSTF sequence is\n");
cpos=hdpos;
while(cnt<n){
min=INT_MAX;
for(i=0;i<n;i++){
if(temp[i]!=-1){
dist=abs(req[i]-cpos);
if(min>dist){
min=dist;
index=i; }
}
}
tseek+=min;
temp[index]=-1;
cnt++;
cpos=req[index];
printf("%d->",req[index]);
if(cnt==n)
break;
}
avgmv=(float)tseek/n;
printf("\nTST is %d\n",tseek);
printf("Avgmv is %.2f\n",avgmv);
}
63
Compile:
Run:
Output:
64