Chapter 5. Signalling Events
Prev | Part II. Interprocess Communication | Next

## Chapter 5. Signalling Events

Processes can receive signals in order to respond to asynchronous requests from software or to unexpected hardware events. There are three different programming interfaces for receiving signals; you must select one and use it consistently throughout a program.

Many programs need access to time data for one of two purposes: to produce timestamps so that data can be ordered by its time of origin, and to define intervals so the program can take action at regular times. (Intervals are presented to the program as signals.)

These two issues are covered in the following topics:

- “Signals” describes signal facilities in general and details the differences between the POSIX, SVR4, and BSD interfaces.

- “Timer Facilities” describes POSIX and IRIX methods of defining timestamps and intervals.

## Signals

A signal is a notification of an event, sent asynchronously to a process. Some signals originate from the kernel in response to hardware traps; for example, the SIGFPE signal that notifies of an arithmetic overflow, or the SIGALRM that notifies of the expiration of a timer interval. Other signals are issued by software. For a detailed, formal discussion of signals, read the `signal(5)` reference page.

A process can block all signals or selected signals, ignore some signals, or request a default system handling for some signals. When a signal that has been sent to a process is blocked by the process, the signal remains pending. When a signal is not blocked, the process receives the signal. In a multithreaded process, signals can be blocked or received by individual threads.

When receiving a signal, a process or thread can handle the signal by an asynchronous call into a signal-handling function. Alternatively, using the POSIX interface, a process or thread can handle signals synchronously, as a stream of event objects.

### Signal Numbers

IRIX supports the following 64 signal numbers:

1-31 | Same meanings as SVR4 and BSD; see Table 5-1.
32 | Reserved by IRIX kernel.
33-48 | Reserved by the POSIX standard for system use.
49-64 | Reserved by POSIX for real-time programming.

Signals with smaller numbers have priority for delivery. The low-numbered BSD-compatible signals, which include all kernel-produced signals, are delivered ahead of real-time signals, and signal 49 takes precedence over signal 64.

Table 5-1 is reproduced from the `signal(5)` reference page for convenience.

Table 5-1. Signal Numbers and Default Actions

Symbolic Name | Numeric Value | Default Action | Normal Meaning
--- | --- | --- | ---
SIGHUP | 1 | Terminate | Controlling terminal disconnect; see `termio(7)`.
SIGINT | 2 | Terminate | Interrupt key signal from controlling terminal; see `termio(7)`.
SIGQUIT | 3 | Terminate and dump | Quit key signal from controlling terminal; see `termio(7)`.
SIGILL | 4 | Terminate and dump | Attempt to execute illegal instruction.
SIGTRAP | 5 | Terminate and dump | Trace/breakpoint reached; see `proc(4)`.
SIGABRT | 6 | Terminate and dump | Abort.
SIGEMT | 7 | Terminate and dump | Emulation trap.
SIGFPE | 8 | Terminate and dump | Arithmetic exception; see `math(3M)`, `sigfpe(3C)`, and `matherr(3M)`.
SIGKILL | 9 | Terminate | Kill request from software or user.
SIGBUS | 10 | Terminate and dump | Bus error (hardware exception).
SIGSEGV | 11 | Terminate and dump | Segmentation fault (illegal address).
SIGSYS | 12 | Terminate and dump | Invalid system call.
SIGPIPE | 13 | Terminate | Read or write to broken pipe; see `pipe(2)`, `read(2)`, `write(2)`.
SIGALRM | 14 | Terminate | Interval timer elapsed; see “Timer Facilities”.
SIGTERM | 15 | Terminate | Process terminated.
SIGUSR1 | 16 | Terminate | Programmer-defined; see also text below.
SIGUSR2 | 17 | Terminate | Programmer-defined.
SIGCHLD or SIGCLD | 18 | Terminate | Child process status change; see `wait(2)` and “Process “Reaping””.
SIGPWR | 19 | Ignore | Power fail/restart.
SIGWINCH | 20 | Ignore | Change in size of window; see `xterm(1)`.
SIGURG | 21 | Ignore | Urgent socket condition; see `socket(2)`.
SIGPOLL | 22 | Terminate | Pollable event from a STREAMS device, see `streamio(7)`.
SIGIO | 22 | Terminate | Input/output possible.
SIGSTOP | 23 | Suspend | Stopped.
SIGTSTP | 24 | Suspend | Stop key signal from controlling terminal; see `termio(7)`.
SIGCONT | 25 | Ignore | Continued.
SIGTTIN | 26 | Suspend | Attempt to read terminal from background process; see `termio(7)`.
SIGTTOU | 27 | Suspend | Attempt to write terminal from background process; see `termio(7)`.
SIGVTALRM | 28 | Terminate | Virtual timer expired; see `getitimer(2)`.
SIGPROF | 29 | Terminate | Profiling timer expired; see `getitimer(2)`.
SIGXCPU | 30 | Terminate and dump | CPU time limit exceeded; see `getrlimit(2)`.
SIGXFSZ | 31 | Terminate and dump | File size limit exceeded; see `getrlimit(2)` and `write(2)`.
(no symbol) | 32-48 | Terminate | Unassigned; do not use.
SIGRTMIN - SIGRTMAX | 49-64 | Terminate | POSIX real-time signal range.

Although SIGUSR1 and SIGUSR2 are nominally defined by the you for your program's purposes, they are also used by different application packages for special signals. For example, if you set a file lock on an NFS mounted file, the NFS lock daemon may send SIGUSR1—see “NFS File Locking”.

### Signal Implementations

There are three UNIX traditions for signals, and IRIX supports all three. They differ in the library calls used, in the range of signals allowed, and in the details of signal delivery. The basic signal operations and the implementing functions are summarized in Table 5-2.(You can click on the reference pages in this table to display them.)

Table 5-2. Signal Handling Interfaces

Function | POSIX Functions | SVR4 Functions | BSD 4.2 Functions
--- | --- | --- | ---
Set and query signal handler | sigaction(2) sigsetops(3) sigaltstack(2) | sigset(2) signal(2) | sigvec(3) signal(3)
Send a signal | sigqueue(2) kill(2) pthread_kill(3P) | sigsend(2) kill(2) | kill(3) killpg(3)
Temporarily block specified signals | sigprocmask(2) pthread_sigmask(3P) | sighold(2) sigrelse(2) | sigblock(3) sigsetmask(3`)`
Query pending signals | sigpending(2) | n.a. | n.a.
Wait for a signal handler to be invoked. | sigsuspend(2) | sigpause(2) | sigpause(3)
Wait for a signal and receive synchronously | sigwait(2) sigwaitinfo(2) sigtimedwait(2) | n.a. | n.a.

It is important to not mix these signal facilities. Your program should use functions from only one column of Table 5-2; otherwise unexpected results can occur.

### Signal Blocking and Signal Masks

Certain ideas are basic to the use of signals. One basic idea is that a program can block the delivery of any signal. When a signal that is sent to a program is blocked, the signal is queued and remains pending until the program unblocks the signal, or terminates. Certain urgent signals—SIGKILL, SIGSTOP, SIGCONT—cannot be blocked.

You specify which signals are blocked using a signal mask, a set of bits in which each bit corresponds to one signal number. When a bit in the mask is set on, the signal is blocked (if it is a signal that can be blocked).

Each process has a signal mask, inherited from its parent process. All three interfaces provide ways to set and clear bits in the current signal mask. The BSD interface, however, only lets you mask the first 32 signal numbers listed in Table 5-1.

Each POSIX thread has a signal mask also (see “Setting Signal Masks”). A multithreaded program (defined as a program that is linked with `libpthread`, so it uses the pthreads version of the standard library) should use the POSIX interface for signal handling.

#### Multiple Signals

In most cases, if a signal of a certain number is pending for a process, and another signal of the same number arrives, the second signal is discarded. In other words, at most one signal of a given number can normally be pending for a process.

In the POSIX interface you can use one particular function, `sigqueue()`, to send a signal that is queued regardless of how many signals of the same number are already pending.

### Signal Handling Policies

You can specify one of three policies for handling an unblocked signal. You set the policy for each signal number individually.

#### Default Handling

Initially, all signals receive default handling. This means that when a signal arrives and is not blocked, it causes the default action listed in Table 5-1. In many cases the default action is to ignore the signal, that is, to silently discard it. In other cases, the default action is to terminate the program, or to terminate it with a dump.

Each signal interface gives you a way to specify non-default handling or a specified signal, or to return a signal to default handling.

#### Ignoring Signals

You can request that a specified signal be ignored. You would do this when the signal is not meaningful to your program and the default action is not what you wish. For example, in a noninteractive program, you might set Ignore handling for SIGHUP (the default action is to terminate).

#### Catching Signals

You can request that a signal be caught and handled asynchronously, at the moment it arrives. You specify that a signal should be caught by specifying the address of a function to be called when the signal is received.

The signal-handling function is entered asynchronously, without regard for what the process was doing at the time the signal was delivered. You cannot be sure what code was executing when the signal handler is called; it could have been any function in your own code, or it could have been code in the C library or in any layer of the X-Windows or Motif support libraries.

All three interfaces provide for passing the signal number as the first argument of the signal-handling function. Other arguments to the handler function depend on the interface used and the options you specify when establishing the handler.

You can create an alternate memory area to be used as a stack when executing the signal handler. Typically a signal handler does not require a great deal of stack space. On the other hand, each POSIX thread has limited stack space, and when you provide an alternate signal-handling stack, you do not have to allow for possible signals in allocating thread stack space (see “Setting Signal Actions”).

#### Synchronous Signal Handling

Using the POSIX signal interface you can process signals in a synchronous way, as a stream of input items to your program. This allows you to design your program so that signals are received when the process is in a known state, without the uncertainties of asynchronous delivery.

### Signal Latency

The time that elapses from the moment a signal is generated until a signal handler begins to execute is the signal latency. Signal latency can be long (as real-time programs measure time) and signal latency has a high variability.

The IRIX kernel normally delivers a pending, unblocked signal the next time the process returns to user code from the kernel domain. In most cases, this occurs

- when the process is dispatched after a wait or preemption

- upon return from a system function

- upon return from the kernel's usual 10-millisecond “tick” (dispatch) interrupt

SIGALRM, which signals the expiration of a real-time timer (see “Timer Facilities”), is given special treatment. It is delivered as soon as the kernel is ready to return to a user process after the timer interrupt, in order to preserve timer accuracy.

When a process is ready to run and is not preempted by a process of higher priority, and is executing in user code, not calling a system function, the latency for other than SIGALRM can be as much as 10 milliseconds. However, when the process is suspended (for example, waiting on a semaphore), or when there are competing processes having higher priorities, the delivery of a signal is delayed until the next time the receiving process is scheduled. This can be many milliseconds.

In general, you should use signals to deliver infrequent messages of high priority. You should not use the exchange of signals as the basis for real-time scheduling.

### Signals Under X-Windows

If you plan to handle signals asynchronously in a program that uses X intrinsics, you must take special steps. Before establishing a signal handler with the operating system, you establish one or more signal callback procedures using `XtAppAddSignal()`. Then, in the asynchronous signal handling function, you call `XtNoticeSignal()`. This function ensures that the established signal callback will be invoked like other callback functions, when it is safe to do so. This process is documented in the `XtAppAddSignal(3Xt)` reference page.

The only X-windows function that can safely be called from a signal handler is `XtNoticeSignal()`.

### POSIX Signal Facility

The POSIX interface to signals is the most functionally complete and robust of the three. It is the recommended interface for all new programs. The functions used in POSIX style signal handling are summarized in Table 5-3.

Table 5-3. Functions for POSIX Signal Handling

Function | Purpose
--- | ---
`kill(2)` | Send a signal to a process or process group. (Discards multiple signals of the same number.)
`sigqueue(3)` | Queue a signal to a specified process, including a sigval for added information about the signal. (Queues multiple signals of the same number.)
`pthread_kill(3P)` | Send a signal to a specified thread.
`sigprocmask(2)` `pthread_sigmask(3P)` | Examine or change the mask of signals allowed and blocked. You must use `pthread_sigmask()` in a program that is linked with `libpthread`.
`sigaction(2)` | Specify or query the signal handling policy for a specified signal.
`sigaltstack(2)` | Specify or query an alternate stack area to be used by a signal handler.
`sigpending(2)` | Return the set of signals pending for the calling process or thread.
`sigsetops(3)` | Manipulate signal mask objects in memory.
`sigsuspend(2)` | Unblock selected signals for the calling process or thread, and wait for a signal to be received asynchronously.
`sigwait(3)` `sigtimedwait(3)` `sigwaitinfo(3)` | Wait for and receive specified signals in a synchronous manner.

In addition to the reference pages listed in Table 5-3, the following have important information about signal handling:

`signal(5)` | Detailed overview of signals and signal handling.
`siginfo(5)` | Description of the information structure passed to a POSIX signal handler.
`ucontext(5)` | Description of machine context structure passed to a POSIX signal handler.

#### Signal Masking

Each process and thread has an active signal mask. A single-thread program sets or queries its signal mask using `sigprocmask()`. A multithreaded program (any program that linked `libpthread`, which provides the pthread version of the standard library) should use `pthread_sigmask()`.

Besides the active signal mask, you may have other signal mask objects (type sigset_t) in memory. The `sigsetops(3)` reference page documents a number of utility functions for setting, clearing, and testing the bits in a signal mask object. Several POSIX signal functions take a signal mask as an argument. For example, `sigsuspend()` takes a new signal mask and swaps it for the current signal mask, establishing which pending signals will be accepted while the process is suspended.

#### Using Synchronous Handling

You can design your program so that it treats arriving signals as a stream of event records to be processed in sequence. For example, you could use one or more signal numbers in the POSIX real-time range to signify events that are meaningful to your application. Your application, or one thread in your application, can receive each signal in turn and act upon it.

To implement this design approach, follow these steps:

1. Block the expected signal numbers in all processes or threads using `sigprocmask()` or `pthread_sigmask()`.

2. Send the signals using `sigqueue()`. This function permits you to augment the signal number with a union sigval (in effect creating an open-ended set of sub-signals), and also assures that multiple signals will be retained until you process them.

3. In the signal-processing loop, wait for the next signal with `sigwaitinfo()` or `sigtimedwait()`. When the signal arrives, act accordingly and wait again.

The `sigwaitinfo()` and `sigtimedwait()` functions accept a new signal mask. They unblock the specified signal or signals and suspend until one such signal arrives. They accept that signal, restore the original signal mask, and return the signal information.

You could construct a very similar work-handling application using a message queue (see Chapter 6, “Message Queues”). However, this design approach allows you to integrate the handling of unplanned signals such as SIGPIPE, and interval-timer signals such as SIGALRM, into the same scheme as planned application events.

#### Using Asynchronous Handling

Using `sigaction()`, you specify a function to be called when a particular signal is received. You have a choice of function prototypes. In each case the signal handler is passed the signal number, additional information about the signal, and information about the machine context at the time the signal was delivered.

Your signal handler can have the POSIX prototype, as follows:

    void name(int sig, siginfo_t *sip, ucontext_t *up)

The second argument, a POSIX information structure siginfo_t, contains these fields:

si_signo | The signal number (again).
si_errno | Either 0 or an error code from `errno.h`.
si_code | An indication of the source of the signal.
si_value | When si_code is SI_QUEUE, the union sigval passed to `sigqueue()`.
si_pid | When si_code is SI_USER, the process ID that called `kill()`.

When the signal is an error reported by the kernel or hardware, si_code is an explanatory number. These values are spelled out in detail in the `siginfo(5)` reference page. The third argument, a pointer to a ucontext_t object, gives the machine state at the time the signal was delivered. The ucontext_t is detailed in the `ucontext(5)` reference page.

Alternatively, your signal handler can have this prototype:

    void name(int sig, int code, struct sigcontext *sc);

The second argument gives some added information about the signal (see `signal(5)` for a list of codes). The third argument, a pointer to a sigcontext_t object, gives the machine state at the time the signal was delivered (in slightly different form from the ucontext_t).

When you use `sigaction()` to set up a signal handler, you pass an argument structure containing option flags that affect the treatment of the signal:

SA_SIGINFO | When set, you are specifying asynchronous handling and your handler uses the POSIX prototype. Its address is passed in the sa_sigaction structure field. When not set, a handler uses the older prototype and its address is passed in sa_handler.
SA_ONSTACK | When set, your handler is called using alternate stack memory you have previously assigned with `sigaltstack()`. Otherwise the handler uses the stack of the process or thread stack executing at the time of the signal.
SA_RESETHAND | When set, the policy for this signal is reset to the default when your handler is called. Your handler is expected to reestablish the action if that is desired.
SA_NODEFER | When not set, the signal is automatically blocked while your handler executes, and unblocked when your handler returns. When set, the same signal could be taken while your handler executes, resulting in multiple entries to the handler.
SA_RESTART | When not set, if this signal interrupts a blocked system function the system function returns EINTR. When set, the system function is restarted.

### System V Signal Facility

The System V signal interface is compatible with code ported from UNIX System V. It includes compatibility for release 3 (SVR3) and release 4 (SVR4). Table 5-4 summarizes the functions you use to manage signals through this interface.

Table 5-4. Functions for SVR4 Signal Handling

Function | Purpose
--- | ---
`kill(2)` | Send a signal to a process or process group. (A duplicate of a pending signal is discarded.)
`sigsend(2)` | Send a signal to a set of processes or process groups, specified in a variety of ways, for example by user ID.
`signal(2)` | SVR3 call to establish handling policy of default, ignore, or catch for a specified signal.
`sigset(2)` | SVR4 call to establish handling policy of default, ignore, or catch for a specified signal.
`sighold(2)` | Hold (block) a specified signal.
`sigignore(2)` | Set the handling for a specified signal to Ignore.
`sigrelse(2)` | Release (unblock) a specified signal.
`sigpause(2)` | Suspend the calling process until a specified signal arrives.

Only asynchronous signal handling is supported by the System V interface. Also, you must block and unblock signals individually; there is no support for setting the entire signal mask in one operation.

The semantics of SVR3-compatible signal established with `signal()` are not desirable for most programs. When control enters a signal handler you established using `signal()`, the handling of that same signal is set to default, and that signal remains unblocked. Your signal handler can use `signal()` to reestablish itself as the handler, or it can use `sighold()` to block the signal. However, even if these actions are the first statements of the handler function, there is a period of time at the beginning of the handler during which a second signal of the same type could be received. If this occurs, the second signal receives default handling and is not seen by your handler.

You can avoid this problem by using the SVR4 function `sigset()` instead of `signal()` to establish a handler. Before a handler established by `sigset()` is called, that signal is blocked until the handler returns, and the signal disposition is not reset to default.

### BSD Signal Facility

The BSD signal facility is compatible with code ported from the BSD 4.2 distribution. Table 5-5 summarizes the functions you use to manage signals with this interface.

| Note: In order to use any of the functions in Table 5-5 you must define one of the compiler variables _BSD_SIGNALS or _BSD_COMPAT prior to the inclusion of the header file `signal.h`. You can do this directly in the source file with #define. More commonly you will include -D_BSD_COMPAT as one of the compiler flags you define in your Makefile.

Table 5-5. Functions for BSD Signal Handling

Function Name | Purpose and Operation
--- | ---
`kill(3B)` | Send a signal to a specified process, or broadcast a signal to a process group or to all processes with the same effective user ID. (A duplicate of a pending signal is discarded.)
`killpg(3B)` | Send a signal to all members of a process group. (A duplicate of a pending signal is discarded.)
`sigvec(3)` | Establish a policy of default, ignore, or catch for a specified signal.
`signal(3B)` | Simplified interface to `sigvec()`.
`sigstack(2B)` | Establish an alternate stack for the use of signal-handling functions.
`sigsetmask(3)` | Set the active signal mask.
`sigblock(3)` | Add blocked signals to the active signal mask.
`sigpause(3B)` | Wait for specified signals to arrive.

Only asynchronous signal handling is supported by the BSD interface. It is possible to set and interrogate the signal mask in a single operation; however, the signal mask type is the integer, so only signal numbers 1-32 can be blocked. The BSD interface does not recognize higher-numbered signals.

## Timer Facilities

You use timer facilities for a number of purposes: to get information about program performance; to make a program pause for a certain time; to program an interval of time; and to create a timestamp value to store with other data.

### Timed Pauses and Schedule Cession

In many instances a program, or a process within a multiprocess program, needs to suspend execution for a period of time. IRIX contains a variety of functions that provide this capability. The functions differ in their precision and in their portability. Table 5-6 contains a summary.

Table 5-6. Functions for Timed Suspensions

Reference Page | Precision | Compatibility | Operation
--- | --- | --- | ---
`sched_yield(2)` | n.a. | POSIX | Defer to any processes eligible to run.
`sginap(2)` | dispatching interval (10ms) | IRIX | Defer to other processes for the specified number of dispatching cycles.
`sleep(3C)` | second | POSIX | Suspend for a number of seconds or until a signal arrives.
`usleep(3C)` | microsecond | IRIX | Suspend for a number of microseconds or until a signal arrives.
`nanosleep(2)` | nanosecond | POSIX | Suspend for a number of seconds and nanoseconds or until a signal arrives.

Sometimes you do not want to suspend for any particular amount of time, but simply want to make the current process defer to other processes, so that any waiting processes receive a chance to run. You can achieve this in two ways. The IRIX unique function `sginap()` accepts an argument of 0, meaning to defer for the minimum amount of time. However, `sched_yield()` is a POSIX compliant function for this purpose.

### Time Data Structures

The include files `time.h` and `sys/time.h` define several data types and data structures related to time. Some of these are used in POSIX time functions and others in BSD-based functions; and there are somewhat confusing similarities between them. Features of these structures are summarized in Table 5-7.

Table 5-7. Time Data Structures and Usage

Data Type | Declared In | Contains | Some Functions Using This Type
--- | --- | --- | ---
time_t | `time.h` | long int with time in seconds since 00:00:00 UTC, January 1, 1970 | `time(2)`, `ctime(3C)`, `cftime(3C)`, `difftime(3C)`
timeval | `sys/time.h` | structure of time_t giving seconds and a long int giving microseconds | `adjtime(2)`, `getitimer(2)`, `getrusage(3C)`, `gettimeofday(3C)`, `select(2)`, `utimes(3B)`
itimerval | `sys/time.h` | structure of two timeval fields for first interval and repeat interval | `getitimer(2)` and `setitimer(2)`
timespec_t | `time.h` | structure of time_t giving seconds and a long int giving nanoseconds | `clock_gettime(2)`, `nanosleep(2)`, `aio_suspend(3)`, `sigtimedwait(3C)`
itimerspec | `time.h` | structure of two timespec_t fields for first interval and repeat interval | `timer_settime(3C)`, `timer_gettime(3C)`
tm | `time.h` | structure of int fields for seconds, minutes, hours, day, month, etc. | `localtime(2)`, `gmtime(2)`, `strftime(3C)`

### Time Signal Latency

It takes time for the kernel to deliver the SIGALRM that notifies your program at the end of an interval. (The issue of signal latency in general is discussed under “Signal Latency”.) The signal latency is less for SIGALRM than for other signals, since the kernel initiates a scheduling cycle immediately after the timer interrupt, without waiting for the end of a fixed time slice. When the receiving process or thread is running or ready to run, the latency is fairly short and consistent from one signal to the next. (Even so, it is not advisable to use a repeating itimer as the time base for a real-time program). Under less favorable conditions, signal latency can be variable and sometimes lengthy (tens of milliseconds) relative to a fast timer frequency.

### How Timers Are Managed

The IRIX kernel can be asked to implement itimers for many processes at once, each interval having a different length and starting at a different time. The kernel's method differs depending on the hardware architecture (this issue is discussed at length in the `timers(5)` reference page).

- Some obsolete Silicon Graphics systems have no hardware support for interval timers, so the kernel had to rely on frequent, periodic interrupts as a time base.

  In those systems, the precision of timer interrupts was controlled by a kernel tuning variable, fasthz, which determined the rate at which the kernel was interrupted to poll for an expired timer.

- In all current architectures, each CPU has a clock comparator that the kernel can program to cause an interrupt after a specific interval has elapsed.

  In these systems, timer interrupts have sub-microsecond precision and do not impose overhead for timer-polling interrupts.

In earlier versions of IRIX, in order to minimize the overhead of polling for elapsed timers, the kernel did not allow normal processes to ask for timer intervals with fine granularity (sub-millisecond precision). Only processes that executed under real-time scheduling priority could ask for precise timer intervals.

Starting with IRIX 6.2, any process can request a timer interval with any precision. If this support is misused, it is possible to cause performance problems. For example, a process can set up a repeating timer at an interval so short that one CPU is monopolized by setting and handling that timer.

### POSIX Timers

IRIX supports the time and timer facilities specified by IEEE standard 1003.1b-1993, commonly called POSIX timers. This timer interface is the most complete, robust, and portable, and is recommended for all new applications. The functions it includes for time measurement are summarized in Table 5-8.

Table 5-8. POSIX Time Management Functions

Function Name | Purpose and Operation
--- | ---
`time(2)` | Return a time_t value containing the count of seconds elapsed since 00:00:00 UTC, January 1, 1970.
`times(2)` | Return user and system execution time consumption for the calling process and its terminated child processes.
`clock_gettime(2)` | Return the instantaneous reading of one of two clocks: the system time (CLOCK_REALTIME), or the hardware cycle counter (CLOCK_SGI_CYCLE).
`clock_getres(2)` | Return the precision of the system time (CLOCK_REALTIME), the hardware cycle counter in this system (CLOCK_SGI_CYCLE) or the high-resolution timer base (CLOCK_SGI_FAST).

The POSIX functions for interval timers are summarized in Table 5-9.

Table 5-9. POSIX Time Management Functions

Function Name | Purpose and Operation
--- | ---
`alarm(2)` | Cause a SIGALRM signal after a specified number of whole seconds.
`timer_create(3C)` | Create a POSIX timer and specify its time base (CLOCK_REALTIME or CLOCK_SGI_FAST) and the signal number it can generate.
`timer_delete(3C)` | Remove a timer created with `timer_create()`.
`timer_settime(3C)` | Set expiration and reload times of a timer, or disarm it.
`timer_gettime(3C)` | Query the time remaining in a timer.
`timer_getoverrun(3C)` | Query the number of overrun events generated by a timer.

#### Getting Program Execution Time

The `times()` function returns counts of accumulated user-process and system execution time. These counts have a resolution of the system dispatching interval, 10 milliseconds.

#### Creating Timestamps

The `time()` function returns a timestamp with a resolution of 1 second. A timestamp with a resolution this coarse can be used only for infrequent events.

You can use the `clock_gettime()` function to sample the system time with a resolution of 0.01 second, or you can use it to read the hardware cycle counter—a free-running binary counter with an update frequency near the machine clock rate. The `clock_getres()` function returns the resolution of either of these clocks.

The program in Example 5-1 demonstrates the use of `clock_gettime()` and `clock_getres()`. The following is an example of the output of this program, `ptime`, as executed on an Indy workstation:

    $ ptime
    CLOCK_REALTIME value: sec 835660711, ns 465330000 [8.35661e+08 sec]
    CLOCK_REALTIME units: sec 0, ns 10000000 [0.01 sec]
    CLOCK_SGI_CYCLE value: sec 83, ns 449744360 [83.4497 sec]
    CLOCK_SGI_CYCLE units: sec 0, ns 40 [4e-08 sec]
    CLOCK_SGI_FAST units: sec 0, ns 1000000 [0.001 sec]

Example 5-1. Example of POSIX Time Functions

    /*
    || Program to exercise POSIX clock_gettime() and clock_getres() functions.
    ||
    ||  ptime [-r -c -R -C -F]
    ||      -r  display CLOCK_REALTIME value
    ||      -R  display CLOCK_REALTIME resolution
    ||      -c  display CLOCK_SGI_CYCLE value
    ||      -C  display CLOCK_SGI_CYCLE resolution
    ||      -F  display CLOCK_SGI_FAST resolution (cannot get time from this)
    || Default is display everything (-rRcC).
    */
    #include <time.h>
    #include <unistd.h>     /* for getopt() */
    #include <errno.h>      /* errno and perror */
    #include <stdio.h>
    void showtime(const timespec_t tm, const char *caption)
    {
        printf("%s: sec %ld, ns %ld [%g sec]\n",
            caption, tm.tv_sec, tm.tv_nsec,
            ((double)tm.tv_sec) + ((double)tm.tv_nsec / 1e9));
    }
    main(int argc, char **argv)
    {
        int opta = 1;
        int optr = 0;
        int optR = 0;
        int optc = 0;
        int optC = 0;
        int optF = 0;
        timespec_t sample, res;
        int c;
        while ( -1 != (c = getopt(argc,argv,"arRcCF")) )
        {
            switch (c)
            {
            case 'a': opta=1; break;
            case 'r': optr=1; opta=0; break;
            case 'R': optR=1; opta=0; break;
            case 'c': optc=1; opta=0; break;
            case 'C': optC=1; opta=0; break;
            case 'F': optF=1; opta=0; break;
            default: return -1;
            }
        }
        if (opta || optr)
        {
            if (!clock_gettime(CLOCK_REALTIME,&sample))
                showtime(sample,"CLOCK_REALTIME value");
            else
                perror("clock_gettime(CLOCK_REALTIME)");
        }
        if (opta || optR)
        {
            if (!clock_getres(CLOCK_REALTIME,&res))
                showtime(res,"CLOCK_REALTIME units");
            else
                perror("clock_getres(CLOCK_REALTIME)");
        }
        if (opta || optc)
        {
            if (!clock_gettime(CLOCK_SGI_CYCLE,&sample))
                showtime(sample,"CLOCK_SGI_CYCLE value");
            else
                perror("clock_gettime(CLOCK_SGI_CYCLE)");
        }
        if (opta || optC)
        {
            if (!clock_getres(CLOCK_SGI_CYCLE,&res))
                showtime(res,"CLOCK_SGI_CYCLE units");
            else
                perror("clock_getres(CLOCK_SGI_CYCLE)");
        }
        if (opta || optF)
        {
            if (!clock_getres(CLOCK_SGI_FAST,&res))
                showtime(res,"CLOCK_SGI_FAST units");
            else
                perror("clock_getres(CLOCK_SGI_FAST)");
        }
    }

The real-time clock (CLOCK_REALTIME) can shift backward or jump forward under the influence of adjustments to the system time by a time daemon. The Silicon Graphics hardware cycle counter always increases at a steady rate. However, the cycle counter has a limited precision that depends on the hardware. You can use the `syssgi()` system function to find out the precision of the cycle counter (see `syssgi(2)` and look for the SGI_CYCLECNTR_SIZE option).

#### Using Interval Timers

You create an interval timer object by calling `timer_create()`. To this function you pass codes that specify the time base to use and the signal to send upon timer expiration. It returns an ID value to identify the timer to other functions.

The time base for a timer is either CLOCK_REALTIME or CLOCK_SGI_FAST (the latter is a nonportable request). Typically CLOCK_SGI_FAST has finer resolution, but you can verify that using the `clock_getres()` function, as shown in Example 5-1.

You also pass a sigevent_t object to `timer_create()`. In it you would normally set the following values:

sigev_notify | SIGEV_SIGNAL to have the timer generate a signal on expiration.
sigev_signo | The signal number you want sent, possibly selected from the POSIX real-time range, for example, SIGRTMIN+1.
sigev_value.sival_int sigev_value.sival_ptr | An extra value to be passed to the signal-handling function or to `sigwait()` when the signal is delivered.

You can pass a NULL instead of the address of a sigevent_t. In that case, the timer signals with a SIGALRM.

Initially, a timer is disarmed (inactive). You start a timer by calling `timer_settime()`. The principal argument to this function is an itimerspec_t object, which contains two times. One, it_value, specifies when the timer next expires. The other, it_interval, is the value to be loaded into the timer when it expires. You can call `timer_settime()` to accomplish any of three different operations:

- With it_value nonzero and it_interval zero, arm the timer and initiate a one-time interval.

- With it_value nonzero and it_interval nonzero, arm and initiate a repeating timer.

- With it_value zero, disarm the timer, preventing it from expiring (if it has not expired already).

You can also use `timer_settime()` to reprogram the intervals in a timer while it runs.

A timer can be programmed in terms of relative time (you pass an it_value that represents increments past the present time) or absolute time (you pass an it_value that represents actual future times when the timer should expire).

You can interrogate the time remaining in a timer by calling `timer_gettime()`. After a timer has expired—for example, in the signal handling function—you can call `timer_getoverrun()` to find out how many additional intervals it would have signalled, but could not signal because the first signal was pending.

### BSD Timers

IRIX supports the BSD UNIX feature of interval timers or “itimers.” Table 5-10 summarizes the functions you use to manage itimers.

Table 5-10. BSD Functions for Interval Timers

Function Name | Purpose and Operation
--- | ---
`setitimer(2)` | Set the expiration and repeat interval of a timer.
`getitimer(2)` | Return the current value of a timer.

Each process has three itimers available to it, as summarized in Table 5-11.

Table 5-11. Types of itimer

Kind of itimer | Interval Measured | Resolution | Signal Sent
--- | --- | --- | ---
ITIMER_REAL | Elapsed clock time | 1 millisecond or less | SIGALRM
ITIMER_VIRTUAL | User time (process execution time) | 1 second | SIGVTALRM
ITIMER_PROF | User+system time | 1 second | SIGPROF

The ITIMER_VIRTUAL and ITIMER_PROF have a relatively coarse precision. Their intervals vary depending on when and how often the process is dispatched. The ITIMER_REAL timer is comparable to the POSIX time base CLOCK_SGI_FAST.

In order to use an itimer, you establish a signal handler for the appropriate signal as shown in Table 5-11, then issue the `setitimer()` call. The principal argument to this function is a `struct itimerval`, an object containing two incremental time values. The it_value field specifies the time until the timer should expire. The it_interval field, when nonzero, gives the time that should be loaded into the timer after it expires.

| Tip: One excellent reason not to mix BSD and POSIX timer support in the same program is that the POSIX struct itimerspec, used to set a POSIX timer, and the BSD struct itermval, used to set a BSD itimer, have fields with identical names, but these fields have different data types and precisions.

You can use `setitimer()` for any of three operations:

- With it_value nonzero and it_interval zero, initiate a one-time interval.

- With it_value nonzero and it_interval nonzero, initiate a repeating timer.

- With it_value zero, disarm the timer, preventing it from expiring (if it has not expired already).

### Hardware Cycle Counter

All current Silicon Graphics systems have a hardware “cycle counter,” a free-running binary counter that is incremented at a high, regular frequency. You can use the cycle counter as a high-precision timestamp.

The precision of the cycle counter is different in different system types; for example, it is a 24-bit counter in the Indy workstation, but a 64-bit counter in CHALLENGE and Onyx systems. The rate at which the timer increments is its resolution, and this also varies with the hardware type.

The cycle counter is an addressable hardware device that you can map into the address space of your process (see “Mapping Physical Memory”). When this is done you can sample the cycle counter as if it were a program variable. The code to do this mapping is discussed in the `syssgi(2)` reference page under SGI_QUERY_CYCLECNTR.

However, the use of the hardware cycle counter has been integrated into the POSIX timer support beginning in IRIX 6.2, and this makes access to the cycle counter much simpler than before:

- In order to sample the cycle counter, call `clock_gettime()` passing CLOCK_SGI_CYCLE.

- In order to find out the resolution (update frequency) of the cycle counter, call `clock_getres()` passing CLOCK_SGI_CYCLE.

- In order to find out the precision of the cycle counter, call `syssgi()` passing SGI_CYCLECNTR_SIZE. The returned value is the number of bits in the counter.

The first two operations are illustrated in Example 5-1.

Prev | Table of Contents | Next
Chapter 4. Mutual Exclusion |  | Chapter 6. Message Queues