CS-220 Spring 2016 Lab 13 - Forking Code

Background

Yesterday, we went over the details of cloning your address spacce into a separate process using the "fork" system call. Today, we get to implement some software that makes use of the fork capability.

I have provided a file search utility. You give this utility a file name, and a null-terminated string of characters, and the utility reads the file to determine how many times the specified string occurs in the file. (For all you hackers out there, this is a very simple minded implementation that doesn't handle overlapped instances of the search string.)

I have also provided a sequential implementation of this search capability which works as follows...

Your job in lab today is to create a parallel command that consists of a rewrite of the top level of the sequential code in which each search runs in its own process.

Getting Started

Download the lab13.tar.gz file and untar it. The resulting lab13 directory will contain the following files:

To start off, make the test target, and see how things work.

Timing your Commands

I have implemented a timing capability based on the shell built-in "time" command. In order to use this in a Makefile, I needed to specify "SHELL=/bin/tcsh" at the top of my Makefile. This tells Make to use the tcsh shell to interpret all of my commands.

The "time" built-in command takes an argument string, starts a timer, executes the argument string, and stops the timer when the command ends. It then prints out a line that tells how much time was used by the commmand.

The timing line printed out by the time command looks like:
0.044u 0.000s 0:00.05 80.0% 0+0k 0+0io 0pf+0w

In this string, the 0.044u measures the total "User" time of all processes associated with your command - this is the time in your C code in seconds. Note that if you run multiple processes, this is the sum of the user time for each process.

The 0.000s measures the total "System" time of all the processes assocaited with your command - this is the time in C library code. (Well actually, it's the time in the "kernel" code, and most C library functions are just wrappers around calls to kernel functions, but we haven't learned about that yet.) Again, if you have multiple processes, this is the sum of the system time in each process.

The 0:00.05 measures the elapsed time that your commmand takes. The format is "mm:ss.hh", where mm is the number of minutes, ss is the number seconds, and hh is the number of hundreths of a second.

The 80% describes what percentage of one of the processor's CPU cores you are using. In this case, our command used 80% of one of the cores. Note that when you run multiple processes on a multi-core machine, you may use 60% of one core for one process, and 60% of a second core for another process, and the time command will report the sum of these two, or that you used 120% of "a core".

The rest of the string (0+0k 0+0io 0pf+0w) measures things like cache misses, page faults, wait times, etc. -- none of which we have discussed yet.

Note that what I have described is the behaviour of the "time" built-in command that is run when you execute commands from the Makefile. If you run the "time" command from the command line, you may get the same results, but the results may be different because different shells have different "time" built-in commands. Some shells don't even have a "time" command. Bottom line... make sure you run all of the timing commands by actually running the "make" command that invokes the "time" command.

Writing Your Parallel Code

You will need to modify the parallel.c file to make it send each search to its own process. The way I did this was to fork a new process for each search. When the search is complete, the forked process prints a messasge with the results, and exits. In my methodology, the parent process, after forking a child for each search, waits for all the children to complete, and then exits. Another alternative is to have the parent process work on one of the searches after forking children for all the other searches.

In order to do this, you will need to invoke fork for each child search, using the invocation of fork as we described it in Monday's lecture. This includes invoking the fork function, and looking at the returned PID to see whether you are running in the parent process or the child process.

I recommend keeping an array of PIDs. I wrote my code to allow a maximum of 10 child processes at one time. Then you can "#define MAXKIDS 10", and declare "int childpid[MAXKIDS]". In my parent process, I first started all the searches based on the argument string. Then, I invoked "waitpid" for each child process. When all the child processes were done, I just exited.

Lab Report

Download and edit the following file: lab13_report.txt. Then submit your edited file on Blackboard in the Lab 13 submission area.