Question: Process synchronization using Peterson's algorithms. Consider the C program ( twoupdate ) distributed in class to demonstrate race condition. In that program, a file named
Process synchronization using Peterson's algorithms.
Consider the C program (twoupdate) distributed in class to demonstrate race condition. In that program, a file named data was shared by two processes. The file contained a single integer initialized to zero, and the integer was incremented by the two processes repeatedly. Because there was no code in the lockfile and unlockfile functions to coordinate the processes, the final value in the file varied from one run to another. In this assignment, we will implement Peterson's algorithm (Section 5.3, p. 207 in the textbook) to ensure mutual exclusion in the respective critical sections of the two processes, and thereby eliminate the race condition.
In order to implement Peterson's Algorithm, the two processes should share a boolean array called flag with two components and an integer variable called turn, all initialized suitably. We will create and access these shared variables using UNIX system calls relating to shared memory shmget, shmat, shmdt and shmctl. The necessary include files and function prototypes for these system calls are as follows:
#include
#include
#include
int shmget(key_t key, size_t size, int shmflag);
void *shmat(int shmid, const void *shmaddr, int shmflag);
int *shmdt(const void *shmaddr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
Spool out the man files for these system calls and study them carefully.
First create an include file called sharevar.h as shown. The file sharevar.h contains necessary constant and type definitions to create the shared memory segment for this assignment. In order to create a shared memory segment, the processes must first agree on a unique key. Edit this file to change the nine digit number 800123456 with your own Banner ID number. (There are other ways to obtain a unique key in UNIX. For example you may use the ftok system call.)
The shared memory segment is created, or an existing one is accessed with the shmget system call. The value returned by shmget is the shared memory identifier, shmid. In this system call, the size argument specifies the size of the shared memory segment in bytes, and the shmflag argument is a combination of constants indicating the access permission and a request to create the shared segment if it does not exist. The shmat system call returns the starting address of the shared memory segment and can be controlled, if desired, using the shmaddr and shmflag arguments. The shmdt system call detaches the shared memory segment and the shmctl system call removes it.
Study the attached listing of the files named sharevar.h and twoupdate.c, which show the use of these system calls. There is no need to modify twoupdate.c. But you have to suitably modify process0.c and process1.c. Both processes should make shmget, shmat and shmdt system calls in their respective main functions. Further, the lockfile and unlockfile functions in these two processes should implement the Peterson's algorithm. But, note that the shmctl to remove the shared memory segment should be called only by twoupdate.c. Be sure to create the file data with an integer 0 in it before you run the program every time. If the program works correctly, the final value left in the file should be the same every time the program is run, as long as the count for update is not changed.
Create a script output with a listing of all source files sharevar.h, twoupdate.c, process0.c, and process1.c. Show the output of four or five runs of twoupdate for small values of count, say below 30. If you use a C Shell script file for repeated testing, provide a listing of the same. Exit from script.
Warning: Do not edit the source file inside the script. The control characters will create a messy script output. Edit out the print statements in both processes and recompile them.
Warning: The program runs for this part may take several minutes. Create a new script file with three runs of twoupdate for count = 1000, 5000, and 10000, as further proof of proper file access coordination between the two processes. Cleary we do not want lengthy printouts. Hence, we have edited out the print statements inside process0.c and process1.c.
Listing of sharevar.h
/*Constant and type definitions needed for creating the*/
/*shared memory segment to implement Peterson's Algorithm*/
#define PERMS 0666 /*Access rights*/
#define SHMKEY ((key_t) 800123456) /*Use your Banner ID#*/
#define FALSE 0
#define TRUE 1
typedef struct {
int flag[2];
int turn;
} syncvars;
Source code for twoupdate.c
#include
#include
#include
int main (int argc, char *argv[])
{
FILE *fp; int initial, final; int status;
/*Check for command line arguments*/
if (argc != 3) {
printf("usage: twoupdate filename count ");
return -1;
}
/*Determine initial value in file before update*/
/*Uses standard C library functions for input/output*/
fp = fopen(argv[1], "r");
fscanf(fp, "%d", &initial);
fclose(fp);
/*Launch the two processes*/
if (fork() == 0) {
execlp("process0", "process0", argv[1], argv[2], (char *) NULL);
}
if (fork() == 0) {
execlp("process1", "process1", argv[1], argv[2], (char *) NULL);
}
/*Wait for the two processes to terminate*/
wait(&status); wait(&status);
/*Determine final value in file after update*/
fp = fopen(argv[1], "r");
fscanf(fp, "%d", &final);
fclose(fp);
/*Print value in file before and after two-process update*/
printf(" ****Initial value in file %d ", initial);
printf("****Final value in file %d ", final);
return 0;
}
Source code for process0.c
#include
#include
#include
#include
#include
#include
#include
void waste_time(void);
#define MAXSIZE 80
int main(int argc, char *argv[])
{
/*Uses UNIX input/output system calls for file access */
/*for no reason other than educational value*/
/*function prototype for file access*/
void fileaccess (int fd, int count);
int fd, count;
/*Check for command line arguments*/
if (argc != 3) {
printf("usage: process0 filename count ");
return -1;
}
count = atoi(argv[2]);
/*Open file and update*/
fd = open(argv[1], O_RDWR, 0);
fileaccess(fd, count);
return 0;
}
/*Access the file with the given fd and increment*/
/*the only integer value in that file count times*/
void fileaccess(int fd, int count)
{
/*function prototypes for locking and unlocking file*/
void lockfile (void);
void unlockfile (void);
int i, k, value; pid_t pid;
char buff[MAXSIZE];
/*Initialize the seed for random number generator*/
srand(time(NULL));
pid = getpid();
for (i = 0; i < count; i++)
{
lockfile(); /*lock the file*/
/*Read value from file*/
lseek(fd, 0L, 0);
k = read(fd, buff, MAXSIZE); buff[k] = '\0';
sscanf(buff, "%d ", &value);
/*Increment value*/
value++;
/*Slow down*/
waste_time();
/*Write back into file*/
sprintf(buff, "%10d ", value);
lseek(fd, 0L, 0);
k = strlen(buff); write(fd, buff, k);
printf("pid = %d, new value = %d ", pid, value);
unlockfile(); /*unlock the file*/
}
}
void waste_time (void)
{
/*Slow down; waste time in loop*/
int randNum = rand() % 100000; int x;
for (x = 0; x < randNum; x++) {
}
}
void lockfile(void)
{
}
void unlockfile(void)
{
}
Compile the code process0.c and name the executable file process0. Make a copy of process0.c as process1.c. Edit the file and replace every occurrence of the string process0 in it by process1. Compile the code process1.c and name the executable file process1.
Step by Step Solution
There are 3 Steps involved in it
Get step-by-step solutions from verified subject matter experts
