System Calls
We have seen
that operating Systems have two main functions: providing abstractions to user
programs and managing the computer’s resources. For the most part, the
interaction between user programs and the operating system deals with the
former, for example, creating, writing, reading, and deleting files. The resource
- management part is largely transparent to the users and done automatically.
Thus, the interface between user programs and the operating is primarily about
dealing with the abstractions. To really understand what operating system do,
we must examine this interface closely. The system calls available in the
interface vary from one operating system to another (although the underlying
concepts tend to be similar.)
We are thus
forced to make a choice between (1) vague generalities (“operating systems have
system calls for reading files”) and (2) some specific system (”UNIX has a read
system calls with three parameters: one to specify the file, one to tell where
the data are to be put, and one to tell
how many bytes to read”).
We have
chosen the latter approach. The latter approach. It’s more work that way, but
it gives more inside into what operating systems really do. Although this
discussion specifically refers to POSIX (International Standard 9945-1), hence
also to UNIX, System V, BSD, Linux, MINIX 3, and so on, most other modern operating systems operating systems have system calls
that perform the same functions, even if the detail differ. Since the actual mechanics
of issuing a system call are highly machine dependent and often must be
expressed in assembly code, a procedure library is provided to make it
impossible to make system calls C programs and often from other languages as
well.
It is useful
to keep in the following in mind. Any signal-CPU computer can execute only one
instruction at a time. If a process is running a user program in user mode and
needs a system service, such as reading data from a file, it has to execute a
trap instruction to transfer control to the operating system. The operating
system then figures out what the calling process wants by inspecting the
parameters. Then it carries out the system call and returns control to the
instruction following the system call. In a sense, making a system call is like
making a special kind of procedure call, only systems enter the kernel and
procedure calls do not.
To make the
system call mechanism clearer, let us take a quick look at the read the system
call. As mentioned above, it has thee parameters: the first one specifying the
file, the second one pointing to the buffer, and the third one giving the
number of bytes to read. Like nearly all system calls, it is invoked from C
programs by calling a library procedure with the same name as the system call:
read. A call from a C program might look like this:
Count= read
(fd, buffer, nbytes);
The system
call (and the library procedure) return the number of bytes actually read in
count. This value is normally the same as nbytes, but may be smaller, if , for
example, end-of-file is encountered while reading.
If the
system call cannot be carried out owing to an invalid parameter or a disk
error, count is set to -1, and the error number is put in a global variable,
err no. Programs should always check the result of a system call to see if an
error occurred.
System calls
are performed in a series of step. To make this concept clearer, let us examine
the read call discussed above tutorials. In preparation for calling the read
library procedure, which actually makes the read system call, the calling
program first pushes the parameters onto the stack, as shown in steps 1-3 in Figure
1.
C and C++
compilers push the parameters onto the stack in reverse order for historical
reasons (having to do with making the first parameter to printf, the format
string, appear on top of the stack). The first and third parameters are called
by value, but the second parameter is passed by reference, meaning that the
address of the buffer (indicated by &) is passed, not the contents of the
buffer. Then comes the actual call to the library procedure. This instruction
is the normal procedure- call instruction used to call all procedures.
The library
procedure, possibly written in assembly language, typically puts the
system-call number in a place where the operating system expects it, such as a
register (step 5). Then it executes a TRAP instruction to switch from user mode
to kernel mode and start execution at a fixed address within the Kernel
(stepp6).
The TRAP
instruction is actually fairly similar to the procedure-call instruction in the
sense that the instruction following it is taken from a distant location and
the return address is saved on the stack for use later.
Nevertheless,
the TRAP instruction also differs from the procedure-call instruction in two
fundamental ways. First, as a side effect, it switches into kernel mode. The
procedure call instruction does not change the mode. Second, rather than giving
a relative or absolute address where the procedure is located, the TRAP
instruction cannot jump to an arbitrary address. Depending on the architecture,
either it jumps to a single fixed location or there is an 8-bit field in the
instruction giving the index into a table in memory containing jump addresses,
or equivalent.
The kernel
code that starts following the TRAP Examines the system-call number and then
dispatches to the correct system - call handler, usually via a table of
pointers to system-call handlers indexed on system-call number (step 7). At
that point the system-call handler runs (step 8). Once it has completed its
work, control may be returned to the user-space library procedure at the instruction
following the TRAP instruction (step 9). This procedure then returns to the
user program in the usual way procedure call return (step 10).
To finish
the job, the user program has to clean up the stack, as it does after any procedure
call (step 11). Assuming the stack grows downward, as it often does, the
complied code increments the stack pointer exactly enough to remove the
parameters pushed before the call to read. The program is now free to do
whatever it wants to do next.
In step 9
above, we said “may be returned to the user- space library procedure” for good
reason. The system call may block the caller, preventing it from continuing.
For example, if it is trying to read from the keyboard and nothing has been
typed yet, the caller has to be blocked. In this case, the operating will look
around to see if some other process can be run next. Later, when the desired
input is available, this process will get the attention of the system and run
steps 9-11.
In this
following tutorial, we will examine some of the most heavily used POSIX system
calls, or more specifically, the library procedures that make those system
calls. POSIX has about 100 procedure calls. Some of the most important ones are
listed in Figure 2, grouped for convenience in four categories. In the text we will briefly examine each call
to see what it does.
To a large
extent, the services offered by these calls determine most of what the
operating system has to do, since the resource management on personal computers
is minimal (at least compared to big machines with multiple users). The
services include things like creating and terminating processes, creating,
deleting, reading, and writing files, managing directories, and performing
input and output.
As an aside,
it is worth pointing out that mapping of POSIX procedure calls onto system
calls in not one-to-one. The POSIX standard specifies a number of procedures
that a conformant system must supply, but it does not specify whether they are
system calls, library calls, or something else. If a procedure can be carried
out without invoking a system call (i.e., without trapping to the kernel), it
will usually be done in user space for reasons of performance. However, most of
the POSIX procedures do invoke system calls, usually with one procedure mapping
directly onto one system cells, usually with one procedure mapping directly
onto one systems call. In a few cases, especially where several required
procedures are only minor variations of one another, one system call handles
more than one library calls.