Systems Programming

System Calls and Error Handling

(Much of this information is contained in Chapter 1 of Rochkind.)

What is a System Call

Every program will require some operating systems service, if for nothing else to do I/O.

A user process has its own virtual address space, and when it is not making use of OS services, it is said to be executing in user space. It cannot directly address operating system memory (or another process's memory), so you cannot make an ordinary function call into the kernel. There are only two ways to get the Unix operating system to do something for a process: it must either raise an exception or perform a system call. When one of these things happens, the process enters kernel space. At this point, the kernel is said to be executing on behalf of the process. A context switch occurs whenever the actual processor changes virtual address spaces, and context switches are much more expensive then a function call.

A deliberate invocation of the kernel is called a system call, and the exact mechanism is processor/architecture dependent. Most processors hava trap or syscall machine instruction. Intel processors have an int (interrupt) machine instruction, for example. In C, system calls are handled through the normal function call mechanism; that is, you call a C function (or use a macro) that will produce the assembly language code to make the actual kernel request. This makes them convenient to use and portable (the C libraries have to change from system to system, but C programs don't).

When we refer to something like readdir() as a system call, we mean to imply that the function (or macro) does relatively little work beyond making the one appropriate system call, assembling the results, and returning.

We think of system calls as different from regular library functions, such as round() or strcmp(), that do all their work in user space. (There are some library routines that involve quite a bit of user-level code plus one or more system calls, most notably those of the standard I/O library).

To use a system call, you should include the appropriate header file(s) (just as with regular library routines). The headers will define the necessary datatypes, function prototypes, and handy constants and macros.

To see what system calls a program runs, you can use the Linux program strace.

Error Handling

C does not have any standard exception signaling/handling mechanism, so errors are handled by normal function return values (or by side affecting some data). The hundreds of system calls available in Unix/Linux handle errors in a variety of ways, and, just as with library functions, you should study their documentation carefully so that you can catch and handle errors appropriately.

System calls that return a value often designate some special value as indicative of an error. -1 is a common error return value, but some return 0 or a NULL pointer or EOF.

Many system calls and system-level functions use the errno facility. To deal with these calls, you will need to include errno.h, which will provide something that looks like a global integer variable called errno. There are a standard set of predefined constants corresponding to common error conditions (see errno.h for details).

You will see system call documentation that will say, for example, that the call returns EOF on error, and the precise reason for the error will be left in errno. To use the errno facility, you:

  1. first make a system call, then
  2. check to see if it gave an error indication, then and only then
  3. use errno to determine and respond to the cause of the error.
CAUTION: Some system calls will alter errno even if there is not an error, so you must check for an error indication first.

There are two useful functions you may want to use in conjunction with errno: perror() and strerror(). strerror() takes an integer argument (errno) and returns a string that, one hopes, gives a hint about what the error means. perror() takes an argument string which it prints to standard error followed by the result of calling strerror() on the current value of errno.

Here is an example of a program that uses errno and perror(): And here is a script of the errno_example program in action:

% errno_example
Usage: errno_example <filename>
% errno_example errno_example.c
% errno_example non-existent-file
Open: No such file or directory
% echo $?
2
%



Author: Mark A. Sheldon
Modified: 14 February 2008