First Things First
Before we delve into the definition of a Linux system call and examine the details of its execution, it is best to start with defining the various software layers of a typical Linux system.
The Linux kernel is a specialized program that boots and runs at the lowest available level on your hardware. It has the task of orchestrating everything that runs on the computer, including handling keyboard, disk, and network events to providing time slices for executing multiple programs in parallel.
When the kernel executes a user-level program, it virtualizes the memory space so that programs believe they are the only process running in memory. This protective bubble of hardware and software isolation increases security and reliability. An unprivileged application cannot access memory belonging to other programs, and if that program crashes, the kernel terminates so that it cannot harm the rest of the system.
Breeching the Barrier with Linux System Calls
This layer of isolation between unprivileged applications provides an excellent boundary to protect other applications and users on the system. However, without some way to interface with the other elements in the computer and the outside world, programs wouldn’t be able to accomplish much of anything.
To facilitate interaction, the kernel designates a software gate that allows the running program to request that the kernel act on its behalf. This interface is known as a system call.
Since Linux follows the UNIX philosophy of “everything is a file”, many functions can be performed by opening and reading or writing to a file, which could be a device. On Windows, for example, you might use a function called CryptGenRandom to access random bytes. But on Linux, this can be done by simply opening the “file” /dev/urandom and reading bytes from it using standard file input/output system calls. This crucial difference allows for a simpler system call interface.
In most applications, system calls are not made directly to the kernel. Virtually all programs link in the standard C library, which provides a thin but important wrapper around Linux system calls. The library makes sure that the function arguments are copied into the correct processor registers then issues the corresponding Linux system call. When data is received from the call, the wrapper interprets the results and returns it back to the program in a consistent way.
Behind the Scenes
Every function in a program that interacts with the system is eventually translated into a system call. To see this in action, let’s start with a basic example.
This is probably the most trivial C program you will ever see. It simply gains control via the main entry point and then exits. It doesn’t even return a value since main is defined as void. Save the file as ctest.c and let’s compile it:
Once it’s compiled, we can see the file size as 8664 bytes. It may vary slightly on your system, but it should be around 8k. That’s a lot of code just to enter and exit! The reason it’s 8k is that the libc runtime is being included. Even if we strip the symbols, it’s still a tad over 6k.
In an even simpler example, we can make the Linux system call to exit rather than depending on the C runtime to do that for us.
Here we move 1 into the EAX register, clear out the EBX register (which would otherwise contain the return value) then call the Linux system call interrupt 0x80 (or 128 in decimal). This interrupt triggers the kernel to process our call.
If we compile our new example, called asmtest.c, and strip out the symbols and exclude the standard library:
we’ll produce a binary less than 1k (on my system, it yields 984 bytes). Most of this code is executable headers. We now are calling the direct Linux system call.
For All Practical Purposes
In nearly all cases, you won’t ever have to make direct system calls in your C programs. If you use assembly language, however, the need may arise. However, in optimization, it would be best to let the C library functions make the system calls and have only your performance-critical code embedded in the assembly directives.