Use of lockf C library function for process synchronization PLEASE DO NOT HANDWR
ID: 3803459 • Letter: U
Question
Use of lockf C library function for process synchronization
PLEASE DO NOT HANDWRITE THE ANSWER UNLESS HANDWRITTING IS EXTREMELY CLEAR, THANK YOU!
The shared resource responsible for race condition in the twoupdate program is an external file, and is not an arbitrary in-memory data structure. Hence, it is possible to use file and record locking mechanisms available in UNIX to ensure mutual exclusion of the respective critical sections of the two processes, and thereby eliminate the race condition. The required system call in UNIX is fcntl. It is simpler to use a C library function called lockf built on top of fcntl. Spool out the man page for this library function and study it. The function prototype is as follows:
int lockf(int fd, int action, long size);
Two valid actions, defined as symbolic constants in , of interest to us are:
F_LOCK
Lock a section of a file for exclusive use, if it is available; if the section is already locked, the process blocks until the lock can be established
F_ULOCK
Unlock a previously locked section of a file
The third parameter size indicates the number of contiguous bytes to lock or unlock from the current position in the file. A value of zero for size indicates the entire section from the present position to the end of the file. Thus, by using a separate lseek system call to position at the beginning of the file before invoking lockf, the entire file can be locked or unlocked.
Suitably modify process0.c and process1.c to use the lockf function. Specifically, modify the function prototypes and invocations for both lockfile and unlockfile to allow passing the file descriptor as a parameter. Then modify the lockfile and unlockfile functions. Each requires exactly two lines of code.
Create a script output with a listing of all modified source files -- 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, and three fairly large values as specified for Part 2 of the assignment.
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] = '';
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.
PLEASE DO NOT HANDWRITE THE ANSWER UNLESS HANDWRITTING IS EXTREMELY CLEAR, THANK YOU!
F_LOCK
Lock a section of a file for exclusive use, if it is available; if the section is already locked, the process blocks until the lock can be established
F_ULOCK
Unlock a previously locked section of a file
Explanation / Answer
To make a class fully thread-safe, first add the appropriate synchronization class to the shared classes as a data member. In the previous account-management example, a CSemaphore data member would be added to the view class, a CCriticalSection data member would be added to the linked-list class, and a CEvent data member would be added to the data storage class.
Next, add synchronization calls to all member functions that modify the data in the class or access a controlled resource. In each function, you should create either a CSingleLock or CMultiLock object and call that object's Lock function. When the lock object goes out of scope and is destroyed, the object's destructor calls Unlock for you, releasing the resource. Of course, you can call Unlock directly if you want.
Designing your thread-safe class in this fashion allows it to be used in a multithreaded application as easily as a non-thread-safe class, but with a higher level of safety. Encapsulating the synchronization object and synchronization access object into the resource's class provides all the benefits of fully thread-safe programming without the drawback of maintaining synchronization code.
The following code example demonstrates this method by using a data member, m_CritSection (of type CCriticalSection), declared in the shared resource class and a CSingleLock object. The synchronization of the shared resource (derived from CWinThread) is attempted by creating a CSingleLock object using the address of the m_CritSection object. An attempt is made to lock the resource and, when obtained, work is done on the shared object. When the work is finished, the resource is unlocked with a call to Unlock.
Related Questions
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.