CMSC 421: Principles of Operating Systems

Homework 2: This Homework Ain't Over Till It's Over

This homework is due on Thursday, September 29, at 11:59:59 PM (Eastern daylight time). You must use submit to turn in your homework like so: submit cs421_jtang hw2 team.c player.c

Your programs must be named team.c and player.c, and they will be compiled on Ubuntu 16.04 as follow:

  gcc --std=c99 -Wall -O2 -o team team.c
  gcc --std=c99 -Wall -O2 -o player player.c
There must not be any compilation warnings in your submission; warnings will result in grading penalties. In addition, each code file must be properly indented and have a file header comment, as described on the coding conventions page.

Due to UMBC policy, you may not use the GL systems for this assignment, because you will be using the fork() system call.

In this homework, you will be simulating a baseball game. The first file, team.c, is the main driver for this assignment, and it is responsible for handling the user interface. At startup, it spawns multiple players, each represented as instances of player.c. The process team then simulates a baseball being thrown between the various player processes, by sending POSIX signals.

Part 1: Create Processes

The team.c program will be divided into several parts as follows. When the process begin, inspect its command line arguments. There must be exactly four arguments (plus the program name itself); if not then display an error message and quit. These four arguments are the names of your infield players: first base (1B), second base (2B), short stop (SS), and third base (3B), in that order.

Second, create four unnamed pipes. Each pipe will be associated with a player (first pipe to 1B, second pipe to 2B, and so forth). Next, call fork() four times, to create four child processes. The first child process will be 1B, second process will be 2B, and so forth. Store the child processes' PIDs somewhere in memory.

In each child process, call dup2() to both close stdin and duplicate the reading end of the pipe associated with that child process. Next, close all pipes' file descriptors. (The pipe associated with the process can still be read via the file descriptor that now occupies stdin.) Third, call execlp() to execute the player program. While executing the program, pass the associated player name (from the original command line) and its position (1B, 2B, SS, or 3B). Ensure the child process does not continue forking additional processes!

In player, display its player number (i.e., the child process's PID), name, and its position. Use the %ld specifier when printing the PID. Next, set up signal handlers for the signals SIGUSR1 and SIGUSR2. Initialize a global variable to zero; this number will hold the number of times that player has handled the baseball. Then, open the file /dev/urandom using open(). The process will obtain pseudo-random numbers from that virtual file. Finally, read from stdin (which is really its unnamed pipe); this will cause the child process to block.

Back in the parent process, after it has spawned the four child processes, close the reading end of all four pipes. Then write the four player numbers (i.e., child processes' PIDs) to the first pipe, in the order 1B, 2B, SS, then 3B. Repeat writing PIDs to the other three pipes. You may find the dprintf() function useful here. Store in memory the pipes' writing end file descriptors along with the child processes' PIDs. In the end the parent must track all four file descriptors and all four child PIDs.

Because player was reading from the pipe, it will be unblocked. Store the other three player numbers in an array. Read from stdin to make the process block a second time.

Part 2: Main Menu

Within the parent process, in team, display the following menu:

Main Menu:
1. Throw ball directly to a player
2. Field ball
3. Show player statistics
4. End game

Read from the user a menu option, and then perform the requested action as below.

Part 3: Throw Ball Directly to a Player

In the parent process, in team, display the four players' names in a menu. Then read from the user a number, one through four. Send a SIGUSR1 signal to the player process with that name.

In the child process, in player, in its signal handler for SIGUSR1, print a message including the player's name and that he caught the ball. Increment by one the number of times that player has handled the baseball.

Afterwards, in the parent process, return to the main menu.

Part 4: Field Ball

In the parent process, in team, display the four players' names in a menu. Then read from the user a number, one through four. Send a SIGUSR2 signal to the player process with that name.

In the child process, in player, in its signal handler for SIGUSR2, print a message stating giving that player's name and that he caught the ball. Increment by one the number of times that player has handled the baseball. Then, if that player is the first baseman, do nothing else. Otherwise, read a single byte from its previously opened handle to /dev/urandom, via read(). Treat the lower two bits as a random number from 0 through 3. Print a message stating to which position the ball is being thrown (where zero is 1B, one is 2B, two is SS, and three is 3B) and the target player number. Then send a SIGUSR2 signal to that player number (i.e., process ID). If the random number matches the player's position, pick another random number. In other words, a player will never throw the ball to himself. Again, once the first baseman has caught SIGUSR2, then cease sending signals.

Afterwards, in the parent process, return to the main menu.

Part 5: Show Player Statistics

In the parent process, in team, write a byte to all four unnamed pipes. It does not matter which byte is written.

Because player was reading from the pipe, it will be unblocked. Display that player's name, position, and the number of times that player has caught the ball (i.e., sum of SIGUSR1 and SIGUSR2 signals receieved).

Afterwards, in the parent process, return to the main menu.

Part 6: End Game

In the parent process, in team, close all four unnamed pipes. Then use waitpid() to wait for each child process to terminate. After all children have ended, end the program.

Because player was reading from the pipe, it will be unblocked. Upon detecting that the pipe was closed, the process terminates itself.

Part 7: Required Documentation

For this assignment, you are required to provide the following documentation. In your team.c file, add a comment block near the top that answers the following questions:

Question 1: Back in Part 4, players could indefinitely throw the baseball between themselves without ever throwing to 1B. As currently described, there is no way for team to know when 1B has caught the ball. Describe how you would modify your program files so that team will wait for 1B to catch the ball prior to returning to the main menu. Do not actually write the code; just describe how using pseudocode.

Question 2: Research Linux's virtual file /proc/PID/stat. List all of the possible process states and their one letter abbreviations. Then identify which state(s) the 1B process is in when it is waiting for the ball, and then which state(s) it is in when while handling a SIGUSR2 signal.

Sample Output

Here is a sample output from running team. Observe that because multiple processes are trying to write to the terminal simultaneously, their outputs are interleaved in non-obvious ways.

$ ./team Davis Schoop Hardy Machado
Main Menu:
1. Throw ball directly to a player
2. Field ball
3. Show player statistics
4. End game
#11765: Hardy (SS)
#11764: Schoop (2B)
#11763: Davis (1B)
#11766: Machado (3B)
Choose an option> (User enters 1)
1. Davis
2. Schoop
3. Hardy
4. Machado
Choose a player> (User enters 2)
Main Menu:
1. Throw ball directly to a player
2. Field ball
3. Show player statistics
4. End game
Schoop caught the ball.
Choose an option> (User enters 1)
1. Davis
2. Schoop
3. Hardy
4. Machado
Choose a player> (User enters 3)
Main Menu:
Hardy caught the ball.
1. Throw ball directly to a player
2. Field ball
3. Show player statistics
4. End game
Choose an option> (User enters 1)
1. Davis
2. Schoop
3. Hardy
4. Machado
Choose a player> (User enters 3)
Main Menu:
Hardy caught the ball.
1. Throw ball directly to a player
2. Field ball
3. Show player statistics
4. End game
Choose an option> (User enters 3)
Main Menu:
Hardy (SS): 2
1. Throw ball directly to a player
2. Field ball
3. Show player statistics
4. End game
Davis (1B): 0
Schoop (2B): 1
Machado (3B): 0
Choose an option> (Users enters 2)
1. Davis
2. Schoop
3. Hardy
4. Machado
Choose a player> (User enters 4)
Main Menu:
1. Throw ball directly to a player
2. Field ball
3. Show player statistics
4. End game
Machado caught the ball.
Throwing the ball to #11765 SS.
Hardy caught the ball.
Throwing the ball to #11766 3B.
Machado caught the ball.
Throwing the ball to #11764 2B.
Schoop caught the ball.
Throwing the ball to #11763 1B.
Davis caught the ball.
Choose an option> (User enters 3)
Main Menu:
Hardy (SS): 3
1. Throw ball directly to a player
2. Field ball
3. Show player statistics
4. End game
Davis (1B): 1
Schoop (2B): 2
Machado (3B): 2
Choose an option> (User enters 4)

Other Hints and Notes

Extra Credit

Sorry, there is no extra credit available for this assignment.