Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Download as pdf or txt
Download as pdf or txt
You are on page 1of 75

DEPARTMENT OF

COMPUTER SCIENCE & ENGINEERING


B.E- II Year IV Semester
Lab Record for
Operating Systems Lab
Subject Code: PC452CS
Academic Year: 2022-23

Name

Roll No

Branch/Section

Year

Semester
2
INDEX

Exp. Experiment Page Date Date Signature


No. No. of of of the
Experiment submission Faculty
Linux Commands
1 9

C programs to implement UNIX


system calls
2 13
(a) fork() (b)sleep()&wait()
(c)execv() (c)execlp()
file management:
(a) command line argument
3 18
(b) Recursively decend a
directory hierarchy
process related concepts:
4 (a) Zombie process 21
(b) orphan process
(a) Thread Creation and
Termination
(b) Thread Creation and
5 Termination Using 26
semaphore
(c) Demonstrate of
Multithreading
programs to simulate CPU
scheduling algorithms:
6 33
(a) FCFS (b) SJF
(c) priority (d)Round Robin
programs to simulate IPC
techniques:
7 42
(A) Pipes (B) Messages Queues
(C) Shared Memory
C programs to simulate
Classical Synchronization
Problems:
8 52
(a) Dining Philosophers
(b) Producer-Consumer
(c) Readers-Writers
Programs to implement Page
Replacement algorithms
9 63
a) First In First Out (FIFO)
b) Least Recently Used (LRU)
Program to simulate Bankers
10 algorithm for the purposeof 68
deadlock avoidance.
program to simulate FCFS disk
11 74
scheduling algorithms
OPERATING SYSTEMS LABORATORY

OBJECTIVE:

This lab complements the operating systems course. Students will gain practical experience with
designing and implementing concepts of operating systems such as system calls, CPU scheduling,
process management, memory management, file systems and deadlock handling using C language
in Linux environment.

OUTCOMES:
Upon the completion of Operating Systems practical course, the student will be able to:
1. Understand and implement basic services and functionalities of the operating system
using system calls.
2. Use modern operating system calls and synchronization libraries in software/ hardware
interfaces.
3. Understand the benefits of thread over process and implement synchronized programs
using multithreading concepts.
4. Analyze and simulate CPU Scheduling Algorithms like FCFS, Round Robin, SJF, and
Priority.
5. simulate Intra & Inter-Process Communication (IPC) techniques: Pipes, Messages
Queues, Shared Memory.
6. Implement page replacement schemes.

7. simulate solutions to Classical Process Synchronization Problems

8. Understand the concepts of deadlock in operating systems and implement them in

multiprogramming system.

3
INTRODUCTION TO OPERATING SYSTEM
A program that act as an intermediary between a user of a computer and the computer
hardware.
An operating system can be define as a software that control the hardware resources of the
computer and provide an environment under which program can run. Generally, we call
this software as the KERNEL.
OPERATING SYSTEM GOALS
Execute user programs and make solving user problem easier.
Make the computer system convenient to use.
Operating system is a software program that enables the computer hardware to
communicate & operate with the computer software.
Without operating system a computer would be useless.

COMPUTER SYSTEM COMPONENTS


A computer system can be divided roughly into four components; the hardware, the operating
system, the application program and the user.
Hardware :
Provide basic computing resources (CPU, memory, Input/output devices).
Operating System :
Control & coordinates the use of the hardware among the various application program for the
various users.
Application Program :
Define the way in which the system resources are used to solve the computing problems of
the user (compiler, database system, video games, business program).
Users :
(People, machines, other computers)

4
INTRODUCTION TO LINUX OPERTATING SYSTEM

Linux is one of popular version of UNIX operating System. It is open source as its source code is
freely available. It is free to use. Linux was designed considering UNIX compatibility. Its
functionality list is quite similar to that of UNIX.
Components of Linux System
Linux Operating System has primarily three components
 Kernel − Kernel is the core part of Linux. It is responsible for all major activities of this
operating system. It consists of various modules and it interacts directly with the underlying
hardware. Kernel provides the required abstraction to hide low level hardware details to
system or application programs.
 System Library − System libraries are special functions or programs using which
application programs or system utilities accesses Kernel's features. These libraries
implement most of the functionalities of the operating system and do not requires kernel
module's code access rights.
 System Utility − System Utility programs are responsible to do specialized, individual
level tasks.
Architecture
The following illustration shows the architecture of a Linux system −

5
TELNET
The telnet command is used to communicate with another host using the telnet protocol. Where
hostname is the host you want to connect to & port, indicates a port number. If a number is not
specified, the default telnet is used.
Example:
Operating system Server Internet Protocol
Unix Telnet 192.168.0.2
Linux Telnet 192.168.0.6

The Linux file system is the structure in which all the information on your computer is stored.
Files are organized within a hierarchy of directories. Each directory can contain files, as well as
other directories.
If you were to map out the files and directories in Linux, it would look like an upside-down tree.
At the top is the root directory, which is represented by a single slash ( / ). Below that is a set of
common directories in the Linux system, such as bin, dev, home, lib , and tmp , to name a few.
Each of those directories, as well as directories added to the root, can contain subdirectories.

Some of the Linux directories that may interest you include the following:
.• /bin - Contains common Linux user commands, such as ls, sort , date , and chmod .
• /boot - Has the bootable Linux kernel and boot loader configuration files (GRUB).
• /dev - Contains files representing access points to devices on your systems. These include
terminal devices ( tty* ), floppy disks ( fd* ), hard disks ( hd* or sc* ),RAM ( ram* ), and CD-
ROM ( cd* ).

6
• /etc - Contains administrative configuration files.
• /home - Contains directories assigned to each user with a login account.
• /media - Provides a location for mounting devices, such as remote file systems and removable
media (with directory names of cdrom , floppy , and so on). In Fedora and RHEL, many removable
media are mounted automatically in this directory when the media is inserted (CD or DVD) or
connected (USB pen drives or cameras ).
• /proc - Provides a mechanism for the kernel to send information to processes.
• /root - Represents the root user's home directory.
• /sbin - Contains administrative commands and daemon processes.
• /tmp - Contains temporary files used by applications.
• /usr - Contains user documentation, games , graphical files (X11), libraries (lib), and a variety
of other user and administrative commands and files.
• /var - Contains directories of data used by various applications. In particular, this is where you
would place files that you share as an FTP server ( /var/ftp ) or a Web server ( /var/www ). It also
contains all system log files ( /var/log ). In time, FTP, HTTP, and similar services will move to the
/srv directory to adhere to the Linux Standards Base ( www.freestandards.org/spec ).

7
The Vi editor is a visual editor used to create and edit text, files, documents and
programs. It displays the content of files on the screen and allows a user to add, delete or
change part of text . There are three modes available in the Vi editor , they are
1. Command mode
2.Input (or) insert mode.
Starting Vi :
The Vi editor is invoked by giving the following commands in LINUX prompt.
Syntax : $vi <filename> (or) $vi
This command would open a display screen with 25 lines and with tilt (~) symbol at the
start of each line. The first syntax would save the file in the filename mentioned and for the
next the filename must be mentioned at the end.
Options :
1.vi +n <filename> - this would point at the nth line (cursor pos).
2.vi –n <filename> - This command is to make the file to read only to change from one mode to
another press escape key.
To move editor from command node to edit mode, you have to press the <ESC> key.
This command is used to insert the text before the current cursor position.
Syntax : <ESC>
SAVING AND QUITING FROM vi :-
1. <ESC> w Command :
To save the given text present in the file.
Syntax : <ESC> : w
2. <ESC> q! Command :
To quit the given text without saving.
Syntax : <ESC> :q!
3. <ESC> wq Command :
This command quits the vi editor after saving the text in the mentioned file.
Syntax : <ESC> :wq
5.<ESC> q Command :
This command would quit the window but it would ask for again to save the file.
Syntax : <ESC> : q

8
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.

9
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.

DIRECTORY RELATED COMMANDS :

1.Present Working Directory Command :


To print the complete path of the current working directory.
Syntax : $pwd
2.MKDIR Command :
To create or make a new directory in a current directory .
Syntax : $mkdir <directory name>
3.CD Command :
To change or move the directory to the mentioned directory .
Syntax : $cd <directory name>.
4.RMDIR Command :
To remove a directory in the current directory & not the current directory itself.
Syntax : $rmdir <directory name>

FILE RELATED COMMANDS :

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.

10
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>

11
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>

12
EXPERIMENT 1
OBJECTIVE
Write a C programs to implement UNIX system calls
(a) fork() (b)sleep()&wait() (c)execv() (c)execlp()

DESCRIPTION: system calls

 fork (): 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.

(a) AIM : To demonstrate usage of fork(), System

calls#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
main()
{
int pid;
printf("\n Demonstration of fork system call \n");
pid=fork();
if(pid<0)
{
printf("\n not possible \n");
exit(0);
}
else
if(pid==0)
{

13
sleep(5);
printf("child processor ID is %d \n",getpid());
printf("parent processor ID is %d \n",getppid());
}
else
{
wait();
printf("parent id is %d",getpid());
printf("\nparent of parent processor ID is %d\n",getppid());
}
}

Compile: cc fork.c
Run: ./a.out
Output: Demonstration of fork system call
child processor ID is 3597
parent processor ID is 3596
parent id is 3596
parent of parent processor ID is 3201

14
(b) AIM : To demonstrate usage of wait(),sleep() 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”);
}
Else if(ret_value==0)
{
//child process
printf(“\n child process\n”);
printf(“the process id is %d\n”,getpid());
sleep(20);
}
else
{
//parent process
wait()
printf(“parent process\n”);
printf(“the process id is %d\n”,getpid());
sleep(30);
}
return 0;
}

15
Compile: cc Wait_Sleep.c
Run: ./a.out
Output: The process id is 4163
child process
the process id is 4164
parent process
the process id is 4163

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.

( c)AIM: Program to implement execv system calls


#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");
}

16
Compile: cc execv.c
Run: ./a.out
Output:
Total 3
-rwxr-xr-x 1be322 group 4716 feb 26 11:30 a.out
-rw-r-r-x 1be322 group 688 feb 26 11:30 fork.c
-rw-r-r-x 1be322 group 925 feb 28 11:30 wait_sleep.c

(d)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: cc execlp.c
Run: ./a.out
Output: fork.c
Wait_Sleep.c
execv.c

17
EXPERIMENT 2

OBJECTIVE
Write a C programs to implement file management

DESCRIPTION:

 A system call is just what its name implies, i.e, a result for the operating system to do
something on behalf of users program. The system calls are functions used in Kernel
itself.

-> File Related System Calls:


open () —> For opening an existing file or creating a new file.
read () —> For reading data from a file.
write () —> For writing data to a file.
close () —> For closing an open file.

-> Commonly Used Flags:


O_RDONLY —> open for reading only.
O_WRONLY —> open for writing only.
O_RDWR —> open for reading and writing.
O_APPEND —> append on each write.
O_CREAT —> create a file if it does not exist.

(a) AIM: Program to implement command line argument

#include<stdio.h>
#include<sys/stat.h>
#include<time.h>
#include<grp.h>
#include<pwd.h>
main(int argc,char *argv[])
{
struct stat thebuf;
char *path;
int i;
for(i=1;i<argc;i++)
{
path=argv[i];

18
if(!stat(path,&thebuf))
{
printf("\n file mode of %s %d of which is ",argv[1],thebuf.st_mode);
switch(thebuf.st_mode&S_IFMT)
{
case S_IFDIR:
printf("\n A directory file");
break;
case S_IFCHR:
printf("\n A character special file");
break;
case S_IFREG:
printf(" \n an ordinary file");
break;
case S_IFBLK:
printf("\n a block special file");
break;
case S_IFIFO:
printf("\n a fifo file");
break;
case S_IFSOCK:
printf("\n a socket");
break;
}
}
else
printf("\n cant get state on %s ",path);
}}
Compile: cc Command.c
Run: ./a.out fork.c
Output: file mode of fork.c 33188 of which is an ordinary file.

19
(b) AIM: Recurssively decend a directory hierarchy pointing a file

#include<stdio.h>
#include<dirent.h>
#include<errno.h>
#include<fcntl.h>
main(int argc,char *argv[])
{
struct dirent *direntp;
DIR *dirp;
if(argc!=2)
{
printf("ussage %s directory name \n",argv[0]);
return 1;
}
if((dirp=opendir(argv[1]))==NULL)
{
perror("Failed to open directory \n");
return 1;
}
while((direntp=readdir(dirp))!=NULL)
printf("%s\n",direntp->d_name);
while((closedir(dirp)==-1)&&(errno==EINTR));
return 0;
}
Compile: cc directory.c
Run: ./a.out ./
Output: fork.c
Wait.c
Execv.c
Execlp.c
Command.c

20
EXPERIMENT 3

OBJECTIVE
Write C programs to demonstrate various process related concepts.
(a) zombie process (b)orphan process

DESCRIPTION: Zombie 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.
(a) AIM:To show existence of a zombie process
#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){
printf(“\n Fork Failure\n”);
}
elseif(ret_value==0)
{
//child process
printf(“\n child process\n”);
printf(“the process id is %d\n”,getpid());
//sleep(20);
}
Else{

21
//parent process
printf(“parent process\n”);
printf(“the process id is %d\n”,getpid());
sleep(30);
}
return 0;}
Compile:cc Zombie.c
Run: ./a.out
Output: The process id is 4394
parent process
the process id is 4394
child process
the process id is 4395

22
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.

(b) AIM: To show existence of an orphan process


#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
main()
{
int pid;
printf("Demonstration of orphan process");
pid=fork();
if(pid==0)
{
sleep(10);
printf("child ID is %d \n ",getpid());
printf("child parent ID is %d \n",getppid());
}
else
{
printf("parent ID is %d \n",getpid());
printf("parent of parent process is %d \n",getppid());
}
}

23
Compile:cc orphan.c
Run: ./a.out
Output: Demonstration of orphan process
parent ID is 7035
parent of parent process is 3201
child ID is 7036
child parent ID is 1

EXPERIMENT 4
OBJECTIVE
Write C programs to demonstrate various thread related concepts

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.)
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

24
• 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

Naming convention :
• All routines begin with pthread_
• Pthreads API contains around 100 subroutines
• For portability, the pthread.h header file should be includes in each source file using the
Pthreads library
• Current POSIX standard is defined only for C language.

Thread management APIs:

Thread creation pthread_create() :


o Initially main() program comprises a single, default thread. All other threads must be explictly
created by the programmer.
o pthread_create() creates a new thread and makes it executable. It can be called any no. Of
times within the program.
Syntax:
pthread_create (thread, attr, start_routine, arg)

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:
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.
Terminating Threads - pthread_exit():
o There are several ways in which a thread may be terminated:
Threads returns normally from its starting routine. It's work is done.
Threads can make a call to the pthread_exit subroutine - whether its work is done or not.
The thread is canceled by another thread via the pthread_cancel routine.
The entire process is terminated due to making a call to either the exec() or exit()

25
If main() finishes first, without calling pthread_exit explicitly itself
o Cleanup: the pthread_exit () routine does not close files; any files opened inside the thread
will remain open after the thread is terminated.
Thread joining:
Syntax:
pthread_join (threadid, status)

"Joining" is one way to accomplish synchronization between threads.


o The pthread_join () subroutine blocks the calling thread until the specified thread id thread
terminates.
o The programmer is able to obtain the target thread's termination return status if it was
specified in the target thread's call to pthread_exit().
o A joining thread can match one pthread_join() call. It is a logical error to attempt multiple
joins on the same thread.

(a) AIM: Thread Creation and Termination


#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

void *print_message_function( void *ptr );


main()
{
pthread_t thread1, thread2;
char *message1 = "Thread 1";
char *message2 = "Thread 2";
int iret1, iret2;

/* Create independent threads each of which will execute function */

iret1 = pthread_create( &thread1, NULL, print_message_function, (void*) message1);


iret2 = pthread_create( &thread2, NULL, print_message_function, (void*) message2);

/* Wait till threads are complete before main continues. Unless we */


/* wait we run the risk of executing an exit which will terminate */
/* the process and all threads before the threads have completed. */

pthread_join( thread1, NULL);


pthread_join( thread2, NULL);

printf("Thread 1 returns: %d\n",iret1);


printf("Thread 2 returns: %d\n",iret2);

26
exit(0);
}

void *print_message_function( void *ptr )


{
char *message;
message = (char *) ptr;
printf("%s \n", message);
}

Compile: cc pthread1.c -lpthread


Run: ./a.out
Output: Thread 1
Thread 2
Thread 1 returns: 0
Thread 2 returns: 0

27
(b) AIM: Thread Creation and Termination Using semaphore
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void *functionC();
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
int counter = 0;
main()
{
int rc1, rc2;
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 */
/* wait we run the risk of executing an exit which will terminate */
/* the process and all threads before the threads have completed. */
pthread_join( thread1, NULL);
pthread_join( thread2, NULL);
exit(0);
}
void *functionC()
{
pthread_mutex_lock( &mutex1 );
counter++;

28
printf("Counter value: %d\n",counter);
pthread_mutex_unlock( &mutex1 );
}
Compile: cc Threadmutex1.c -lpthread
Run: ./a.out
Output: Counter value: 1
Counter value: 2

(c) 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;

29
for(i=0;i<=n;i++)
{
if(i%2!=0)
{
oddarr[jo]=i;
oddsum=oddsum+i;
jo++;
}}}
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);

30
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);
}

Compile: cc multithread.c -lpthread


Run: ./a.out
Output: enter a number
3
the sum of first N natural nos is 6
the sum of first N even natural nos is 2
the sum of first N odd natural nos is 4
the first N even natural nos is----
2
the first N odd natural nos is----
1
3

31
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.)
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

32
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)

DESCRIPTION: The First Come First Serve(FCFS) Scheduling algorithms:

 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.
What is Convoy Effect?
Convoy Effect is a situation where many processes, who need to use a resource for short time are
blocked by one process holding that resource for a long time.
This essentially leads to poor utilization of resources and hence poor performance.
Here we have simple formulae for calculating various times for given processes:

(a) AIM:To Implement First Come, First Serve (FCFS) Cpu Scheduling Algorithm
#include<stdio.h>
main()
{
int WT[10],BT[10],TA[10],TTA=0,TWT=0,AWT,ATA,n,i;
printf("enter the value of n");
scanf("%d",&n);

33
printf("enter BT of n process \n");
for(i=1;i<=n;i++)
scanf("%d",&BT[i]);
WT[1]=0;
TA[1]=BT[1]+WT[1];
for(i=2;i<=n;i++)
{
WT[i]=BT[i-1]+WT[i-1];
TA[i]=BT[i]+WT[i];
TWT=TWT+WT[i];
TTA=TTA+TA[i];
}
AWT=TWT/n;
ATA=TTA/n;
printf("Process no.\t Burst time\t Waiting time\t Turnaround time\n");
for(i=1;i<=n;i++)
{
printf("%d\t %d\t %d\t %d\t",i,BT[i],WT[i],TA[i]);
printf("\n");
}}
Compile: cc fcfs.c
Run: ./a.out
Output: Enter the value of n 3
Enter BT of n process
5
4
6
Process no. Burst time Waiting time Turnaround time
1 5 0 5
2 4 5 9
3 6 9 15

34
DESCRIPTION: Shortest Job First(SJF)
Shortest Job First scheduling works on the process with the shortest burst time of a process
or duration first.
 This is the best approach to minimize waiting time.
 This is used in Batch Systems.
 It is of two types:
1. Non Pre-emptive
2. Pre-emptive
 To successfully implement it, the burst time/duration time of the processes should be
known to the processor in advance, which is practically not feasible all the time. 
 This scheduling algorithm is optimal if all the jobs/processes are available at the same time.
(either Arrival time is 0 for all, or Arrival time is same for all) 
 Data structures used: A priority queue is used
(b) AIM:To Implement Shortest Job First(SJF) Cpu Scheduling Algorithm
#include<stdio.h>
main()
{
int WT[10],BT[10],TA[10],TTA=0,TWT=0,AWT,pn[10],ATA,n,j,temp,i;
printf("enter the value of n");
scanf("%d",&n);
printf("enter BT of n process \n");
for(i=1;i<=n;i++)
{
scanf("%d",&BT[i]);
pn[i]=i;
}
for(i=1;i<n;i++)
for(j=i;j<=n;j++)
{
if(BT[i]>BT[j])
{
temp=BT[i];
BT[i]=BT[j];
BT[j]=temp;
temp=pn[i];

35
pn[i]=pn[j];
pn[j]=temp;
}
]
WT[1]=0;
TA[1]=BT[1]+WT[1];
for(i=2;i<=n;i++)
{
WT[i]=BT[i-1]+WT[i-1];
TA[i]=BT[i]+WT[i];
TWT=TWT+WT[i];
TTA=TTA+TA[i];
}
AWT=TWT/n;
ATA=TTA/n;
printf("Process no.\t Burst time\t Waiting time\t Turnaround time\n");
for(i=1;i<=n;i++)
{
printf("%d\t %d\t %d\t %d\t",i,BT[i],WT[i],TA[i]);
printf("\n");
}}
Compile: cc sjf.c
Run: ./a.out
Output: Enter the value of n 3
Enter BT of n process
5
4
7
Process no. Burst time Waiting time Turnaround time
1 4 0 4
2 5 4 9
3 7 9 16

36
DESCRIPTION: Priority Scheduling
 Priority is assigned for each process.
 Process with highest priority is executed first and so on.
 Processes with same priority are executed in FCFS manner.
 Priority can be decided based on memory requirements, time requirements or any other
resource requirement.
(c) AIM:To Implement PRIORITY Cpu Scheduling Algorithm

#include<stdio.h>
main()
{
int p[20],bt[20],pri[20], wt[20],tat[20],i, k, n, temp;
float wtavg, tatavg;
printf("Enter the number of processes --- ");
scanf("%d",&n);
for(i=0;i<n;i++)
{ p[i] = i;
printf("Enter the Burst Time & Priority of Process %d --- ",i);
scanf("%d %d",&bt[i], &pri[i]);
}
for(i=0;i<n;i++)
for(k=i+1;k<n;k++)
if(pri[i] > pri[k])
{
temp=p[i];
p[i]=p[k];
p[k]=temp;
temp=bt[i];
bt[i]=bt[k];
bt[k]=temp;
temp=pri[i];
pri[i]=pri[k];
pri[k]=temp
}
wtavg = wt[0] = 0;

37
tatavg = tat[0] = bt[0];
for(i=1;i<n;i++)
{
wt[i] = wt[i-1] + bt[i-1];
tat[i] = tat[i-1] + bt[i];
wtavg = wtavg + wt[i];
tatavg = tatavg + tat[i];
}
printf("\nPROCESS\t\tPRIORITY\tBURST TIME\tWAITING TIME\tTURNAROUND
TIME");
for(i=0;i<n;i++)
printf("\n%d \t\t %d \t\t %d \t\t %d \t\t %d ",p[i],pri[i],bt[i],wt[i],tat[i]);
printf("\nAverage Waiting Time is --- %f",wtavg/n);
printf("\nAverage Turnaround Time is --- %f",tatavg/n);
}
Compile: cc priority.c
Run: ./a.out
Output: Result: Enter the number of processes --- 5
Enter the Burst Time & Priority of Process 0 --- 10 3
Enter the Burst Time & Priority of Process 1 --- 1 1
Enter the Burst Time & Priority of Process 2 --- 2 4
Enter the Burst Time & Priority of Process 3 --- 1 5
Enter the Burst Time & Priority of Process 4 --- 5 2

PROCESS PRIORITY BURST TIME WAITING TIME TURNAROUND TIME


1 1 1 0 1
4 2 5 1 6
0 3 10 6 16
2 4 2 16 18
3 5 1 18 19
Average Waiting Time is --- 8.200000
Average Turnaround Time is --- 12.000000

38
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.

(d) AIM: Simulation of Round-Robin CPU scheduling algorithm


#include<stdio.h>
void main()
{
int n,i,q,x=0,count=0,temp;
int bt[10],temp_bt[10],tat[10],wt[10];
float twt=0,ttat=0;
system("clear");
printf("***** RR (ROUND ROBIN) SCHEDULING ******\n");
printf("\nEnter the number of processes : ");
scanf("%d",&n);
for(i=0;i<n;i++)
{
printf("Enter the burst time of process %d : ",i);
scanf("%d",&bt[i]);
temp_bt[i]=bt[i];
}
printf("\nEnter the time quantum : ");
scanf("%d",&q);
while(1)
{
for(i=0;i<n;i++)
{

39
temp=q;
if(temp_bt[i]==0)
{
count++;
continue;
}
if(temp_bt[i]>q)
temp_bt[i]=temp_bt[i]-q;
else if(temp_bt[i]>=0)
{
temp=temp_bt[i];
temp_bt[i]=0;
}
x=x+temp;
tat[i]=x;
}
if(n==count)
break;
}
for(i=0;i<n;i++)
{
wt[i]=tat[i]-bt[i];
twt=twt+wt[i];
ttat=ttat+tat[i];
}
printf("\nProcess\t|Burst Time\t|Wait Time\t|Turn-Around Time");
for(i=0;i<n;i++)
{
printf("\n%d\t|%d\t\t|%d\t\t|%d",i,bt[i],wt[i],tat[i]);
}
printf("\n\nTotal waiting time is %f",twt);

40
printf("\nAverage waiting time is %f",twt/n);
printf("\n\nTotal turn around time is %f",ttat);
printf("\nAverage turn around time is %f\n\n",ttat/n);
}

Compile: cc roundrobin.c
Run: ./a.out
Output: ***** RR (ROUND ROBIN) SCHEDULING ******
Enter the number of processes 3
Enter the burst time of process0:6
Enter the burst time of process1:4
Enter the burst time of process2:2
Enter the time quantum
Process Burst Time Wait Time Turn-Around Time
0 6 6 12
1 4 6 10
2 2 0 6

Total waiting time is 16.00


Average waiting time is 5.333
Total turn around time is 28.00
Average turn around time is 9.333

41
EXPERIMENT 6
OBJECTIVE
Write C programs to simulate Intra & Inter-Process Communication (IPC) techniques:
(A) Pipes (B) Messages Queues (C) Shared Memory.

DESCRIPTION: pipes

 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. (Also, the child inherits
the open file descriptors)

System Call: pipe()


Prototype: 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. Visually speaking, the output of d1 becomes the input for fd().
Once again, all the data travelling through pipe moves through the kernel.

(A) AIM : To demonstrate basic operations with pipes


#include<stdio.h>
main()
{
int pid,a[2],i=5;
char str[3],buff[3];
pipe(a);
pid=fork();
if(pid==0)
{
i=i+10;
sprintf(str,"%d",i);
write(a[1],str,3);
printf("child process message to parent is %d \n",i);

42
}
else
{
read(a[0],buff,3);
printf("parent received the message from child is %s \n",buff);

}
}

Compile: cc Pipes.c
Run: ./a.out
Output:child process message to parent is 15
Parent received the message from child is 15

43
DESCRIPTION: echo server using pipes
 Pipe acts as a medium to transfer output of one process as input to another process.
 The process will do the work and generate output. If the same output is required by the
second process, then we can repeat the process. But it is time consuming. So we prefer to
give output of first to second through the Inter Process Communication technique.
(a) AIM: To demonstrate echo server using pipes
#include<stdio.h>
main()
{
int pid,a[2],b[2],i=5;
char str[3],buff[3];
pipe(a);
pipe(b);
pid=fork();
if(pid==0)
{
i=i+10;
sprintf(str,"%d",i);
write(a[1],str,3);
sleep(5);
read(b[0],str,3);
printf("response from server is %s \n",str);
}
else
{
read(a[0],buff,3);
printf("request from client is %s \n",buff);
i=i+15;
sprintf(buff,"%d",i);
write(b[1],buff,3);
wait();}}

44
Compile: cc E_Pipes.c
Run: ./a.out
Output: request from client is 15
response from client is 15
request from server is 20

DESCRIPTION: message queues


->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 is 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 n a message and the actions
as 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.

45
(B) AIM : To demonstrate basic operations with message queues
#include<stdio.h>
#include<sys/msg.h>
#include<sys/ipc.h>
#include<string.h>
main()
{
int pid,msgid;
char a[20],buf[20];
msgid=msgget((key_t)59,IPC_CREAT|0600);
pid=fork();
if(pid==0)
{
strcpy(a,"HELLO_CSE");
msgsnd(msgid,&a,10,0);
}
else
{
msgrcv(msgid,&buf,15,0,0);
printf("message from child is %s \n",buf);
}
}

Compile: cc MessageQ.c
Run: ./a.out
Output: message from child is HI

46
DESCRIPTION: echo server using message queues
Echo server is any program that provides some service or work. In message queues, a mail box is
created. OS should read and write program. Context switches makes program longer and therefore,
shared memory is used. In message queues, the syntax for creating and receiving is as follows:
Create:
int msgid = msgget( (key_t) 21, IPC_CREAT|0600);
Sending Message:
msgsnd( msqid, &mbuff, length, 0);
struct mbuff
{
char[10];
int type;
};
struct mbuff a1;
strcpy(a1.a, “CSE_OSLAB”);
a1.type = 1;
Receiving Message:
msgrcv( msqid, &buffer, length, type, 0);

(b)AIM : To demonstrate echo server using message queues


#include<stdio.h>
#include<sys/msg.h>
#include<sys/ipc.h>
#include<string.h>
main()
{
int pid,msgid;
char a[20],buf[20];
msgid=msgget((key_t)59,IPC_CREAT|0600);
pid=fork();
if(pid==0)

47
{
strcpy(a,"1604-13-733-059");
msgsnd(msgid,&a,15,IPC_NOWAIT);
sleep(1);
msgrcv(msgid,&a,15,0,0);
printf("response from server is %s \n",a);
}
else
{
msgrcv(msgid,&buf,15,0,0);
printf("response from client is %s \n",buf);
strcpy(buf,"pass");
msgsnd(msgid,&buf,15,IPC_NOWAIT);
wait();
}}
Compile: cc E_MessageQ.c
Run: ./a.out
Output: response from client is1603-18-733-044
response from server is pass

48
DESCRIPTION: IPC using shared memory
Shared memory (shm) is also one of IPC (inter process communication) technique like pipes, but
pipes are use full to communicate parent and child process.
But here shared memory concept is supporting communication between any two independent
processes. Generally every shared memory is associated with a key. if process knows the key of
that shared memory, then it can attach to that shared memory and it will return a pointer to that
shared memory and we can read or write in to that shared memory using that pointer .
First Function:
int shmget(key_t key, size_t size, int flag);
First argument is the KEY of the shared memory.
Second argument is SIZE of the shared memory.
The flag is two types
IPC_CREAT| 0666,if we have to create the shared memory for the first time.
0666,if we have to just get the key of the shared memory which has been already created by any
other process.
if return value is > 0, success and return value = id of desired shm
= -1, failure
and we can not create shm with that key (already another shm is created with that key) or we can
not get the id of desired shm.
Second Function:
char* shmat(int shmid,char* shmaddr, int flag);
shmid is the return value of the shmget() function, here we have to pass it as first argument.
Generally shmaddr will be passed as NULL, and flag = 0.
Return value is pointer to desired shared memory segment. Thus we can read or write in to that
shared memory.

49
(C) AIM : Write a program to illustrate communication between processes using shared memory
//program to write data at the shared memory location
#include<stdio.h>
#include<sys/shm.h>
#include<sys/ipc.h>
int main()
{
char *s;
int shmid=shmget(1234,20,IPC_CREAT|0666);
s=shmat(shmid,NULL,0);
printf("Enter a msg: \n");
scanf("%s",s);
}
Compile: cc shared.c
Run: ./a.out
Output:Enter a msg: hello

//program to read data from the shared memory location


#include<stdio.h>
#include<sys/shm.h>
#include<sys/ipc.h>
int main()
{
char *s;
int shmid=shmget(1234,20,IPC_CREAT|0666);
s=shmat(shmid,NULL,0);
printf("Entered msg: %s \n",s);
}
Compile: cc shared1.c
Run: ./a.out
Output: Entered msg hello

50
EXPERIMENT 7
OBJECTIVE
Write C programs to simulate solutions to Classical Process Synchronization Problems:
(a) Dining Philosophers (b) Producer-Consumer (c) Readers-Writers

DESCRIPTION: Dining Philosopher’s problem


The dining-philosophers problem is considered a classic synchronization problem because it is an
example of a large class of concurrency-control problems. It is a simple representation of the need
to allocate several resources among several processes in a deadlock-free and starvation-free
manner. Consider five philosophers who spend their lives thinking and eating. The philosophers
share a circular table surrounded by five chairs, each belonging to one philosopher. In the center
of the table is a bowl of rice, and the table is laid with five single chopsticks. When a philosopher
thinks, she does not interact with her colleagues. From time to time, a philosopher gets hungry and
tries to pick up the two chopsticks that are closest to her (the chopsticks that are between her and
her left and right neighbors). A philosopher may pick up only one chopstick at a time. Obviously,
she cam1ot pick up a chopstick that is already in the hand of a neighbor. When a hungry
philosopher has both her chopsticks at the same time, she eats without releasing her chopsticks.
When she is finished eating, she puts down both of her chopsticks and starts thinking again. The
dining-philosophers problem may lead to a deadlock situation and hence some rules have to be
framed to avoid the occurrence of deadlock.
 One simple solution is to represent each chopstick by a semaphore. A philosopher tries to
grab the chopstick by executing a wait operation on that semaphore, and releases her
chopsticks by executing the signal operation on the appropriate semaphores.
A semaphore S is an integer variable that can be accessed only through two standard operations :
wait()andsignal().
The wait() operation reduces the value of semaphore by 1 and the signal() operation increases its
value by 1.
wait(S){
while(S<=0); // busy waiting
S--;
}
signal(S){
S++;
}

51
Semaphores are of two types:
1. Binary Semaphore – This is also known as mutex lock. It can have only two values – 0
and 1. Its value is initialized to 1. It is used to implement solution of critical section problem
with multiple processes.

2. Counting Semaphore – Its value can range over an unrestricted domain. It is used to
control access to a resource that has multiple instances.

Semaphore Solution to Dining Philosopher –

Each philosopher is represented by the following pseudocode:

process P[i]
while true do
{ HUNGRY;
PICKUP(CHOPSTICK[i], CHOPSTICK[i+1 mod 5]);
EAT;
PUTDOWN(CHOPSTICK[i], CHOPSTICK[i+1 mod 5])
}
(a) AIM: To implement Dining Philosopher’s Problem using semaphore

#include<stdio.h>
#include<pthread.h>
pthread_mutex_t chopstick[5];
pthread_t philosopher[5];
void* runner(void* arg)
{
int i=*(int*)arg;
printf("Philosopher %d is thinking \n",i);
sleep(2);
pthread_mutex_lock(&chopstick[i]);
pthread_mutex_lock(&chopstick[(i+1)%5]);
printf("philosopher %d is eating \n",i);
sleep(3);
pthread_mutex_unlock(&chopstick[i]);
pthread_mutex_unlock(&chopstick[(i+1)%5]);

52
printf("Philosopher %d finished eating \n",i);
}
int main()
{
int i;
for(i=0;i<5;i++)
{
pthread_create(&philosopher[i],NULL,runner,&i);
sleep(1);
}
for(i=0;i<5;i++)
pthread_join(philosopher[i],NULL);
}

Compile: cc Dining.c -lpthread


Run: ./a.out
Output: philosopher 0 is hungry
philosopher 1 is hungry
philosopher 1 is eating
philosopher 4 is hungry
philosopher 3 is hungry
philosopher 2 is hungry
philosopher 0 is eating
philosopher 1 is thinking
philosopher 0 is thinking
philosopher 4 is eating
philosopher 3 is eating
philosopher 4 is thinking

53
DESCRIPTION: Producer Consumer problem
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.

54
(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)

55
{
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: cc ProducesConsumer.c
Run: ./a.out
Output: 1.Producer
2.Consumer
3.Exit

56
Enter your choice:1
Producer produces the item 1
Enter your choice:1
Producer produces the item 2
Enter your choice:1
Producer produces the item 3
Enter your choice:1
Buffer is full!!
Enter your choice:2
Consumer consumes item 3
Enter your choice:2
Consumer consumes item 2
Enter your choice:2
Consumer consumes item 1
Enter your choice:2
Buffer is empty!!
Enter your choice:3

DESCRIPTION: Readers-Writer problem


Readers writer problem is another example of a classic synchronization problem.

The Problem Statement


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

57
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);
}
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)

58
{
//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);
}

59
(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);
}

void *writer(void *arg)


{
int f;
f = ((int) arg);
sem_wait(&writeblock);
data++;
printf("Data writen by the writer%d is %d\n",f,data);
sleep(1);
sem_post(&writeblock);
}

main()
{
int i,b;
pthread_t rtid[5],wtid[5];
sem_init(&mutex,0,1);

60
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: cc ReadersWriter.c -lpthread


Run: ./a.out
Output: 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

61
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.
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.

62
(a) AIM: To implement FIFO page replacement algorithms

#include<stdio.h>
main()
{
nt i, j, k, f, pf=0, count=0, rs[25], m[10], n;
printf("\n Enter the length of reference string -- ");
scanf("%d",&n);
printf("\n Enter the reference string -- ");
for(i=0;i<n;i++)
scanf("%d",&rs[i]);
printf("\n Enter no. of frames -- ");
scanf(“%d”,&f);
for(i=0;i<f;i++)
m[i]=-1;
printf("\n The Page Replacement Process is \n");
for(i=0;i<n;i++)
{
for(k=0;k<f;k++)
{
if(m[k]==rs[i])
break;
}
if(k==f)
{
m[count++]=rs[i];
pf++;
}
for(j=0;j<f;j++)
printf("\t%d",m[j]);
if(k==f)
printf("\tPF No. %d",pf);
printf("\n");
if(count==f)

63
count=0;
}
printf("\n The number of Page Faults using FIFO are %d",pf);
}

Compile: cc FIFO.c
Run: ./a.out
Output: Enter the length of reference string -- 20
Enter the reference string -- 7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1
Enter no. of frames -- 3
The Page Replacement Process is
7 -1 -1 PF No. 1
7 0 -1 PF No. 2
7 0 1 PF No. 3
2 0 1 PF No. 4
2 0 1
2 3 1 PF No. 5
2 3 0 PF No. 6
4 3 0 PF No. 7
4 2 0 PF No. 8
4 2 3 PF No. 9
0 2 3 PF No. 10
0 2 3
0 2 3
0 1 3 PF No. 11
0 1 2 PF No. 12
0 1 2
0 1 2
7 1 2 PF No. 13
7 0 2 PF No. 14
7 0 1 PF No. 15

The number of Page Faults using FIFO are 15

64
(b) AIM: To implement LRU page replacement algorithms

#include<stdio.h>
main()
{
int i, j , k, min, rs[25], m[10], count[10], flag[25], n, f, pf=0, next=1;
printf("Enter the length of reference string -- ");
scanf("%d",&n);
printf("Enter the reference string -- ");
for(i=0;i<n;i++)
{
scanf("%d",&rs[i]);
flag[i]=0;
}
printf("Enter the number of frames -- ");
scanf("%d",&f);
for(i=0;i<f;i++)
{
count[i]=0; m[i]=-1;
}
printf("\nThe Page Replacement process is -- \n");

for(i=0;i<n;i++)
{
for(j=0;j<f;j++)
{
if(m[j]==rs[i])
{
flag[i]=1;
count[j]=next;
next++;}
}
if(flag[i]==0)
{
if(i<f)
{
m[i]=rs[i];
count[i]=next;
next++;
}
else
{
min=0;

65
for(j=1;j<f;j++)
if(count[min] > count[j]) min=j;

m[min]=rs[i];
count[min]=next;
next++;
}
pf++;
}
for(j=0;j<f;j++) printf("%d\t", m[j]);
if(flag[i]==0)
printf("PF No. -- %d" , pf);
printf("\n");
}
printf("\nThe number of page faults using LRU are %d",pf); }

Compile: cc LRU.c
Run: ./a.out
Output: Enter the length of reference string -- 20
Enter the reference string -- 7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1
Enter the number of frames – 3
The Page Replacement process is --
7 -1 -1 PF No. -- 1
7 0 -1 PF No. -- 2
7 0 1 PF No. -- 3
2 0 1 PF No. -- 4
2 0 1
2 0 3 PF No. -- 5
2 0 3
4 0 3 PF No. -- 6
4 0 2 PF No. -- 7
4 3 2 PF No. -- 8
0 3 2 PF No. -- 9
0 3 2
0 3 2
1 3 2 PF No. -- 10
1 3 2
1 0 2 PF No. -- 11
1 0 2
1 0 7 PF No. -- 12
1 0 7
1 0 7
The number of page faults using LRU are 12

66
EXPERIMENT 9

OBJECTIVE

Write a C program to simulate Bankers algorithm for the purpose of deadlock avoidance.
DESCRIPTION:
In a multiprogramming environment, several processes may compete for a finite number of
resources. A process requests resources; if the resources are not available at that time, the process
enters a waiting state. Sometimes, a waiting process is never again able to change state, because
the resources it has requested are held by other waiting processes. This situation is called a
deadlock. Deadlock avoidance is one of the techniques for handling deadlocks. This approach
requires that the operating system be given in advance additional information concerning which
resources a process will request and use during its lifetime. With this additional knowledge, it can
decide for each request whether or not the process should wait. To decide whether the current
request can be satisfied or must be delayed, the system must consider the resources currently
available, the resources currently allocated to each process, and the future requests and releases of
each process. Banker’s algorithm is a deadlock avoidance algorithm that is applicable to a system
with multiple instances of each resource type.

67
AIM: To implement Bankers algorithm for the purpose of deadlock avoidance.
#include<stdio.h>
struct file
{
int all[10];
int max[10];
int need[10];
int flag;
};
void main()
{
struct file f[10];
int fl;
int i, j, k, p, b, n, r, g, cnt=0, id, newr;
int avail[10],seq[10];
clrscr();
printf("Enter number of processes -- ");
scanf("%d",&n);
printf("Enter number of resources -- ");
scanf("%d",&r);
for(i=0;i<n;i++)
{
printf("Enter details for P%d",i);
printf("\nEnter allocation\t -- \t");
for(j=0;j<r;j++)
scanf("%d",&f[i].all[j]);
printf("Enter Max\t\t -- \t");
for(j=0;j<r;j++)
scanf("%d",&f[i].max[j]);
f[i].flag=0;
}

68
printf("\nEnter Available Resources\t -- \t");
for(i=0;i<r;i++)
scanf("%d",&avail[i]);
printf("\nEnter New Request Details -- ");
printf("\nEnter pid \t -- \t");
scanf("%d",&id);
printf("Enter Request for Resources \t -- \t");
for(i=0;i<r;i++)
{
scanf("%d",&newr);
f[id].all[i] += newr;

avail[i]=avail[i] - newr;
}
for(i=0;i<n;i++)
{
for(j=0;j<r;j++)
{
f[i].need[j]=f[i].max[j]-f[i].all[j];
if(f[i].need[j]<0)
f[i].need[j]=0;
}
}
cnt=0;
fl=0;
while(cnt!=n)
{
g=0;
for(j=0;j<n;j++)
{
if(f[j].flag==0)

69
{
b=0;
for(p=0;p<r;p++)
{
if(avail[p]>=f[j].need[p])
b=b+1;
else
b=b-1;
}
if(b==r)
{
printf("\nP%d is visited",j);
seq[fl++]=j;
f[j].flag=1;
for(k=0;k<r;k++)
avail[k]=avail[k]+f[j].all[k];
cnt=cnt+1;
printf("("); for(k=0;k<r;k++)
printf("%3d",avail[k]);
printf(")");
g=1;
} }}
if(g==0)
{
printf("\n REQUEST NOT GRANTED -- DEADLOCK OCCURRED");
printf("\n SYSTEM IS IN UNSAFE STATE");
goto y;
}}
printf("\nSYSTEM IS IN SAFE STATE");
printf("\nThe Safe Sequence is -- (");
for(i=0;i<fl;i++)

70
printf("P%d ",seq[i]);
printf(")");
y: printf("\nProcess\t\tAllocation\t\tMax\t\t\tNeed\n");
for(i=0;i<n;i++)
{
printf("P%d\t",i);
for(j=0;j<r;j++)
printf("%6d",f[i].all[j]);
for(j=0;j<r;j++)
printf("%6d",f[i].max[j]);
for(j=0;j<r;j++)
printf("%6d",f[i].need[j]);
printf("\n");
}
getch();}
Compile: cc Bankers.c
Run: ./a.out
Output: Enter number of processes – 5
Enter number of resources – 3
Enter details for P0
Enter allocation -- 0 1 0
Enter Max -- 7 5 3
Enter details for P1
Enter allocation -- 2 0 0
Enter Max -- 3 2 2
Enter details for P2
Enter allocation -- 3 0 2
Enter Max -- 9 0 2
Enter details for P3
Enter allocation -- 2 1 1
Enter Max -- 2 2 2

71
Enter details for P4
Enter allocation -- 0 0 2
Enter Max -- 4 3 3
Enter Available Resources -- 3 3 2
Enter New Request Details –
Enter pid -- 1
Enter Request for Resources -- 1 0 2
P1 is visited( 5 3 2)
P3 is visited( 7 4 3)
P4 is visited( 7 4 5)
P0 is visited( 7 5 5)
P2 is visited( 10 5 7)
SYSTEM IS IN SAFE STATE

The Safe Sequence is -- (P1 P3 P4 P0 P2 )


Process Allocation Max Need
P0 010 753 743
P1 302 322 020
P2 302 902 600
P3 211 222 011
P4 002 433 431

72
EXPERIMENT 10

OBJECTIVE
Write a C program to simulate FCFS disk scheduling algorithms

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. This algorithm is intrinsically fair, but it
generally does not provide the fastest service.

#include<stdio.h>
main()
{
int t[20], n, I, j, tohm[20], tot=0;
float avhm;
clrscr();
printf(“enter the no.of tracks”);
scanf(“%d”,&n);
printf(“enter the tracks to be traversed”);
for(i=2;i<n+2;i++)
scanf(“%d”,&t[i]);
for(i=1;i<n+1;i++)
{
tohm[i]=t[i+1]-t[i];
if(tohm[i]<0)
tohm[i]=tohm[i]*(-1);
}
for(i=1;i<n+1;i++)
tot+=tohm[i];
avhm=(float)tot/n;

73
printf(“Tracks traversed\tDifference between tracks\n”);
for(i=1;i<n+1;i++)
printf(“%d\t\t\t%d\n”,t[i],tohm[i]);
printf("\nAverage header movements:%f",avhm);
getch();
}
Compile: cc fcfs_disk.c
Run: ./a.out
Output: Enter no.of tracks:9
Enter track position:55 58 60 70 18 90 150 160 184
OUTPUT
Tracks traversed difference between tracks
55 45
58 3
60 2
70 10
18 52
90 72
150 60
160 10
184 24
Average header movements:30.888889

74

You might also like