CS31 Weekly Lab: Week 11

using a C library, signals, circular arrays

Week 11 lab topics:

  1. using a C library
  2. signals and signal handler functions
  3. circular arrays (to implement a fixed-size queue)

Create a week11 subdirectory and copy over some files:
    $ cd cs31/inclass		
    $ cp -r ~mauskop/public/cs31/week11 .

Using a C library
C libraries consist of two parts:
  1. A header file (libraryname.h). The header file is an ascii file that is readable in your favorite editor. It contains definitions exported by the library, including global variable definitions, function prototypes, type definitions (like structs), and constant defintions (#defines). The header file is #include'ed at the top of any .c file that use the library. It also contains detailed comments written for users of the library.
  2. The library binary file (.o, .a, or .so). It contains compiled versions of library functions. The binary is built from one or more C source files (.c files). Often times the library source code (its .c files) is not distributed with the library.

To use a library, you need to:

  1. #include it in a C source file (we will look at how this is done in testparsecmd.c).
    #include <stdio.h>        // use this syntax for standard header files
                              // (most located in /usr/include/)
    #include "parsecommand.h"     // use this syntax for other header files
    
  2. link in the library file (parsecommand.o) as part of the gcc command to build the executable (this is included in the Makefile).

Here is some more information about C libraries (see "Using and Linking C Library Code").

Signals and Signal Handlers
The program signals.c has some examples of registering a signal handler function on a signal, and some examples of ways you can send signals to processes (for example, alarm can be used to send a SIGALRM).

Try running this and use the kill command to send the process signals:

ps             # get the process' pid, let's say its 345

kill -CONT 345    # sends a SIGCONT signal to process 345
kill -18   345    # or -18 is another way to specify the SIGCONT signal 

kill -INT 345    # sends a SIGINT signal to process 345
kill -2   345    # or -2 is another way to specify SIGINT signal 

The man page for signal lists the signals on this system and describes the signal system call in more detail.

You can also try changing some of the handler code to see what happens. Try changing the SIGINT handler to not call exit, and to print out some other message. Then see what happens when you type (assume 345 is the pid):

kill -INT 345
To kill the process now, you need to send it a SIGKILL signal:
kill -9 345


Circular Arrays in C
A queue is a data structure whose values are ordered in FIFO order (first-in-first-out). New items are added to the end of the queue and removed from the begining of the queue (FIFO semantics for adding and removing).

Together we are going to implement a queue as an fixed-size circular array of int values where each time a new value is added to a full array, it replaces the currently first item in the array (the oldest value added to the queue).

As values are added and removed the first and last bucket index values of the queue change. When the very last bucket is filled the first time, the next value added will replace the value in bucket 0 (the new end of the list) and the first bucket in the list now becomes bucket 1 (the next bucket to replace). When the next value after that is added, it is added to bucket 1 (the new end of the list) and the new start of the list becomes bucket 2. And, so on. This is a circular array implementation of a queue because the first and last bucket indices cycle around the array indexes (0, 1, ..., 4, 0, 1, ..., 4, 0, 1, ...)

For example, for a circular queue of int values of size 5:

after adding values: 3, 6, 17: the queue has 3 elements, and the
                               next bucket to insert into is bucket 3
0   1    2    3    4    
---------------------          first value in the queue is in bucket 0
 3 | 6 | 17 |   |   |
----------------------         last value in the queue is in bucket 2

                                                
after adding values: 10, 4:    the queue has 5 elements, and the
                               next bucket to insert into is bucket 0
0   1    2    3    4   
----------------------         the first value in the queue is in bucket 0
 3 | 6 | 17 | 10 | 4 |
----------------------         the last value in the queue is in bucket 4


after adding the value 7:      the list has 5 elements, and the
                               next bucket to insert into is bucket 1
0   1    2    3    4    
----------------------         the first value in the queue is in bucket 1
 7 | 6 | 17 | 10 | 4 | 
----------------------         the last value in the queue is in bucket 0

after adding the value 9:      the list has  5 elements, and the
                               next bucket to insert into is bucket 2
0   1    2    3    4    
----------------------         the first value in the queue is in bucket 2
 7 | 9 | 17 | 10 | 4 | 
----------------------         the last value in the queue is in bucket 1

printing out the queue from first to last value is:  17  10  4  7  9