Last Updated: 2025-11-09 Sun 19:09

CMSC216 Lab09: Fork/Wait and Stat

CODE DISTRIBUTION: lab09-code.zip

CHANGELOG: Empty

1 Rationale

The fork() and wait() system calls are among the most basic facilities to create and coordinate processes in Unix systems. While their semantics can seem simple at first, use of fork() in particular can be a bit deceptive. This exercise illustrates as much with a simple looking program with somewhat unexpected behavior. It also gives practice on how a parent process can coordinate its execution in a limited fashion with child processes and receive a small amount of information about the child process via collecting their exit codes.

Frequently systems programs must check for the existence of file and interrogate their properties. The first exercise studies two common system calls for this task, access() and stat(). It employs them in a few simple tasks to acquaint students with their use.

Associated Reading

  • Bryant and O'Hallaron CSAPP Ch 8.4 discusses fork() / wait(), their uses in creating and coordinating processes, and gives enough basic coverage to inform on Problem 1.
  • Bryant and O'Hallaron CSAPP Ch 10.6 discusses file metadata and how it is accessed via stat() and related functions. The manual page entry for access() is a good source of information on its use.

Grading Policy

Credit for this exercise is earned by completing the code/asnwers here and submitting a Zip of the work to Gradescope. Students are responsible to check that the results produced locally via make test are reflected on Gradescope after submitting their completed Zip. Successful completion earns 1 Engagement Point.

Lab Exercises are open resource/open collaboration and students are encouraged to cooperate on labs. Students may submit work as groups of up to 5 to Gradescope: one person submits then adds the names of their group members to the submission.

See the full policies in the course syllabus.

2 Codepack

The codepack is linked at the top of this document. Always download it and unzip/unpack it. It should contain the following files which are briefly described.

File Use Description
QUESTIONS.txt EDIT Questions to answer: fill in the multiple choice selections in this file.
fork_me_main.c EDIT PROBLEM 1 C file to edit
stat_demo.c Study PROBLEM 2 demo showing access() / stat() system calls
newer_file.c EDIT PROBLEM 2 code outline to complete
QUESTIONS.txt.bk Backup Backup copy of the original file to help revert if needed
Makefile Build Enables make test and make zip
testy Testing Test running scripts
test_lab09.org Testing Tests for this lab
test_code.org Testing Tests for code problems allowing individual testing
test_order_pids Testing Filter to normalize the PIDs in fork_me
gradescope-submit Misc Allows submission to Gradescope from the command line

3 QUESTIONS.txt File Contents

Below are the contents of the QUESTIONS.txt file for the exercise. Follow the instructions in it to complete the QUIZ and CODE questions for the exercise.

                           _________________

                            LAB09 QUESTIONS
                           _________________


Exercise Instructions
=====================

  Follow the instructions below to experiment with topics related to
  this exercise.
  - For sections marked QUIZ, fill in an (X) for the appropriate
    response in this file. Use the command `make test-quiz' to see if
    all of your answers are correct.
  - For sections marked CODE, complete the code indicated. Use the
    command `make test-code' to check if your code is complete.
  - DO NOT CHANGE any parts of this file except the QUIZ sections as it
    may interfere with the tests otherwise.
  - If your `QUESTIONS.txt' file seems corrupted, restore it by copying
    over the `QUESTIONS.txt.bk' backup file.
  - When you complete the exercises, check your answers with `make test'
    and if all is well, create a zip file with `make zip' and upload it
    to Gradescope. Ensure that the Autograder there reflects your local
    results.
  - IF YOU WORK IN A GROUP only one member needs to submit and then add
    the names of their group.


PROBLEM 1 QUIZ: Questions on fork_me.c
======================================

  Analyze the `fork_me.c' application then compile and run it. Compiling
  can be done via a `make'. Answer the questions below about its
  behavior.

  How many total processes are created by running this program including
  the initial process?
  - ( ) 3
  - ( ) 4
  - ( ) 5
  - ( ) 16

  Which of the following best describes the relationship between the
  processes created by running this program?
  - ( ) There is a "chain" of processes with single parent, single
    child, single grand child, etc.
  - ( ) There is a "fan" of processes with a single parent and multiple
    children
  - ( ) There is a lobsided "tree" of processes with a single parent,
    several children, some of which have grand children, some of which
    have great grand children, etc.
  - ( ) There are several individual parents each of which has a single
    child.

  Concerning when the processes complete and produce their FINISH
  output...
  - ( ) The order that the processes complete is always the same.
  - ( ) The entire output order varies and cannot be predicted
  - ( ) Portions of the output appear predictably but other portions
    vary
  - ( ) Only the process IDs vary but the printing order does not change

  Check ALL that are true about the initial version of he `fork_me.c'
  program
  - ( ) Sometimes when running the program, a process will report its
    Parent PID as 1
  - ( ) All processes report a global_total of 4
  - ( ) Sometimes the START message will appear after some FINISH
    messages
  - ( ) The intended output of child and grandchild processes print more
    "--" symbols does not work and all processes appear at the same
    level


PROBLEM 1 CODE: Create order for fork_me.c
==========================================

  Modify the `fork_me.c' program to fulfill the following requirements
  1. Every run of the program leads to the same ordering of output
     between parent, child, grandchild, etc. processes
  2. Parent processes pause each time a child is created and do not
     proceed until a child process finishes.
  3. The parent process examines the status of the child process and
     extracts its exit code / return value and adds it to the
     `global_total' variable.

  The code modifications required include the use of some of the
  following system calls and associated macros.
  -----------------------------------------------------------------------------------------------------------------------------
   Call/Macro                Effect                                                                                            
  -----------------------------------------------------------------------------------------------------------------------------
   wait(NULL);               Wait for any immediate child process to finish                                                    
   wait(&status);            Wait for any immediate child process to finish AND store info about its completion                
   waitpid(cpid,NULL,0);     Wait specifically for child cpid to finish                                                        
   waitpid(cpid,&status,0);  Wait specifically for child cpid to finish AND store info about its completion                    
   WIFEXITED(status)         Evaluates to "truthy" if status contains info on a process that exited normally                   
   WEXITSTATUS(status)       For processes that exited normally, extracts the exit code / return value for the program         
   WIFSIGNALLED(status)      Evaluates to "truthy" if status contains info on a process that were terminated due to errors     
   WTERMSIG(status)          For processes that were terminated, returns the Signal number related to the cause of termination 
  -----------------------------------------------------------------------------------------------------------------------------
  Not all of these need be used in `fork_me.c' but they may all be
  useful in an upcoming project.

  You can test your code `make test-code testnum=1'.

  NOTE that since the PIDs from run to run are unpredictable, the `make
  test-code' command uses the `test_order_pids' to create consistent
  PIDs for testing purposes.

  EXAMPLE:
  ,----
  | >> ./fork_me
  | START| parent_pid: 1010387 pid: 568951 
  | FINISH| pid: 568951 parent_pid: 1010387 global_total: 4
  | --FINISH| pid: 568953 parent_pid: 568951 global_total: 4
  | ----FINISH| pid: 568954 parent_pid: 568952 global_total: 4
  | ----FINISH| pid: 568957 parent_pid: 568953 global_total: 4
  | ----FINISH| pid: 568956 parent_pid: 568952 global_total: 4
  | ----FINISH| pid: 568960 parent_pid: 568955 global_total: 4
  | ----FINISH| pid: 568961 parent_pid: 568952 global_total: 4
  | --FINISH| pid: 568955 parent_pid: 568951 global_total: 4
  | --FINISH| pid: 568952 parent_pid: 568951 global_total: 4
  | ------FINISH| pid: 568964 parent_pid: 1 global_total: 4
  | ------FINISH| pid: 568965 parent_pid: 1 global_total: 4
  | ----FINISH| pid: 568962 parent_pid: 1 global_total: 4
  | ------FINISH| pid: 568959 parent_pid: 1 global_total: 4
  | --FINISH| pid: 568958 parent_pid: 568951 global_total: 4
  | ------FINISH| pid: 568963 parent_pid: 1 global_total: 4
  | --------FINISH| pid: 568966 parent_pid: 1 global_total: 4
  | 
  | >> ./fork_me | ./test_order_pids
  | START| parent_pid: 101 pid: 100
  | FINISH| pid: 100 parent_pid: 101 global_total: 4
  | --FINISH| pid: 102 parent_pid: 100 global_total: 4
  | --FINISH| pid: 103 parent_pid: 100 global_total: 4
  | ----FINISH| pid: 104 parent_pid: 102 global_total: 4
  | --FINISH| pid: 105 parent_pid: 100 global_total: 4
  | ----FINISH| pid: 106 parent_pid: 105 global_total: 4
  | ------FINISH| pid: 107 parent_pid: 106 global_total: 4
  | ----FINISH| pid: 108 parent_pid: INIT global_total: 4
  | ----FINISH| pid: 109 parent_pid: INIT global_total: 4
  | ------FINISH| pid: 110 parent_pid: INIT global_total: 4
  | ------FINISH| pid: 111 parent_pid: INIT global_total: 4
  | ----FINISH| pid: 112 parent_pid: INIT global_total: 4
  | --------FINISH| pid: 113 parent_pid: 111 global_total: 4
  | ----FINISH| pid: 114 parent_pid: INIT global_total: 4
  | --FINISH| pid: 115 parent_pid: INIT global_total: 4
  | ------FINISH| pid: 116 parent_pid: INIT global_total: 4
  `----


PROBLEM 2 QUIZ: Questions on stat_demo.c
========================================

  Analyze the `stat_demo.c' program. Compile and run it via
  ,----
  | >> make stat_demo
  | ...
  | >> ./stat_demo somefile.txt
  | ...
  | >> ./stat_demo a_dirname/
  `----

  Finally, you can contrast the behavior of `filestats.c' to the shell
  command `stat' which provides similar functionality.

  Answer the following questions about how the system call works.


File Access
~~~~~~~~~~~

  Which of the filling best describes how the `access()' system call is
  used?
  - ( ) It alters read/write access to a file by adjusting its mode bits
  - ( ) It is called to determine if a file exists using the `F_OK' flag
  - ( ) It ensures a file exists and can be accessed by creating if
    necessary
  - ( ) It must be called before `stat()' in order to initialize a
    `struct stat' data structure


File Size
~~~~~~~~~

  How does `stat()' report the Size of a file?
  - ( ) An integer is passed to the calls to be set to the size
  - ( ) The return value is the number of bytes in the file
  - ( ) The size is printed to standard output during the function call
  - ( ) The field `sb.st_size' contains the number of bytes in file


File Kind
~~~~~~~~~

  `stat()' reports the "kind" of file being queried. How is this
  information used in `stat_demo.c'?
  - ( ) The field `sb.st_filetype' is set to a string like "file" or
    "pipe" to indicate the type of a file and that field can be printed
    directly
  - ( ) An integer passed in as an address to the call is set to the
    type of the file
  - ( ) The kind is encoded in the `sb.st_mode' field of the struct and
    macros are used to distinguish the kind and print an appropriate
    message.
  - ( ) The kind is encoded in the `sb.st_mode' field of the struct and
    functions are used to distinguish the kind and print an appropriate
    message.

  Which one of the following is NOT a file "kind" reported by `stat() /
  lstat()'
  - ( ) Directory
  - ( ) Binary
  - ( ) Socket
  - ( ) Symbolic Link (symlink)


mtime / ctime
~~~~~~~~~~~~~

  Do some research and determine the difference between the `ctime' and
  `mtime' fields that are reported by `stat() / lstat()'. Which of the
  following best describes this difference:
  - ( ) `mtime' and `ctime' actually always report the same time and
    their redundancy is a historical oddity.
  - ( ) `mtime' indicates the last time that the file was moved from one
    directory to another while `ctime' indicates the last change to the
    data in the file
  - ( ) `mtime' is the last time of modification when data was altered
    in the file while `ctime' is the last access time when data was read
    from the file
  - ( ) `mtime' is when the actual data of a file changes while `ctime'
    is associated with permissions, links, or other meta data changes
    associated with the file.


  You can test your results for the above quiz questions via `make
  test-quiz' after completing all quiz questions.


Additional Items to Observe
~~~~~~~~~~~~~~~~~~~~~~~~~~~

  The function `ctime()' and `strmode()' both use an interesting
  technique to make it possible to easily produce a printable string for
  situations like those in `filestats.c'.  Time permitting, examine the
  source code for `strmode()' in `strmode.c' and discuss with a TA how a
  string is returned but there is no requirement to free that string.

  Finally, like many system calls, `stat()' has a command-line
  equivalent in the form of the `stat' command which prints to the
  screen many of the statistics of a file and is useful at times.
  ,----
  | >> stat A.txt
  |   File: A.txt
  |   Size: 2         	Blocks: 8          IO Block: 4096   regular file
  | Device: 254,1	Inode: 7080409     Links: 1
  | Access: (0644/-rw-r--r--)  Uid: ( 1000/kauffman)   Gid: (  985/   users)
  | Access: 2024-11-07 14:15:55.332936076 -0500
  | Modify: 2024-11-07 14:10:55.332925991 -0500
  | Change: 2024-11-07 14:15:55.329602657 -0500
  |  Birth: 2024-11-06 16:43:41.436780892 -0500
  `----


PROBLEM 2 CODE: newer_file.c Program
====================================

  Fill in the template code provided in `newer_file.c'. The intent of
  the program is to ensure that two named files exist and then compare
  the modification time of them to print an older vs newer file.  This
  will require use of the `access()' and `stat()' system calls as well
  as a `diff_timespec()' function that is provided in the template.

  You can test your code `make test-code testnum=2'. Below is a
  demonstration of how the complete program should work.

  ,----
  | >> make newer_file              # build
  | gcc -Wall -Werror -g -Og -o newer_file newer_file.c
  | 
  | >> ./newer_file
  | Usage: ./newer_file <file1> <file2>
  | 
  | >> echo A > A.txt               # create 3 files
  | >> echo B > B.txt               
  | >> echo C > C.txt               # newest
  | >> touch -d '-5min' A.txt       # oldest
  | >> touch -d '-3min' B.txt       # middle
  | 
  | >> ./newer_file A.txt B.txt
  | A.txt is OLDER than B.txt
  | 
  | >> ./newer_file C.txt B.txt
  | C.txt is NEWER than B.txt
  | 
  | >> ./newer_file C.txt C.txt
  | C.txt and C.txt are EQUAL in age
  | 
  | >> ./newer_file C.txt X.txt
  | X.txt cannot be accessed
  | 
  | >> ./newer_file Z.txt X.txt
  | Both Z.txt and X.txt cannot be accessed
  `----

4 Submission

Follow the instructions at the end of Lab01 if you need a refresher on how to upload your completed lab zip to Gradescope.


Author: Chris Kauffman (profk@umd.edu)
Date: 2025-11-09 Sun 19:09