I'm reading Tanenbaum's Modern Operating Systems and I really can't grasp the following concept: how does a program make a system call? I mean, i got the very basics down (correct me if I'm wrong): the OS is just another program running on the machine (the difference being that it can run in kernel mode having complete access to the machine's hardware) and when an user's program want to have a sort of advanced feature given by the OS, it tries to get it through a system call to the OS itself, writing the call's type and parameters on its stack and making a trap call. Now, I got this down, but the question is, how does a program know that, let's say, the "read" call on Unix is identified by the "ReadFile" call on the Win32 API? For example, in a program written in C, is this info known by the compiler? And let's say in the future a new OS introduces the "foo" system call, that does the exact same thing as the Unix's "read"... Well, how would a user's program know that?
how does a program know that, let's say, the "read" call on Unix is identified by the "ReadFile" call on the Win32 API? For example, in a program written in C, is this info known by the compiler?
Neither the program nor the compiler knows that. The programmer knows that on POSIX-compliant systems, there is an include file called
<unistd.h> that has a function to read from file descriptors with the prototype
ssize_t read(int fd, void *buf, size_t count) and has a certain behavior when called. The compiler doesn't treat
read() any differently than some function in your program.
read() does its business is completely opaque. Under the hood on Unix, it does whatever the kernel expects to happen when you want to make that a system call. On Windows, it uses whatever mechanism Windows makes available for that. In some embedded environment where the process has direct access to the device, it might execute code that reads the file directly.
This is what makes code portable: the libraries implement whatever the standard dictates in a way that's compatible with the local environment. As long as the libraries and callers adhere to the standards, programs work as expected and everyone's happy.
Typically, the information about system calls is made available to C programs by platform-specific header files. That is, the OS provides a wrapper library that contains the actual syscalls and offers a convenient interface to C programmers. For convenience, some of these libraries provide a
syscall() C function that handles the syscall calling conventions (e.g. putting data into the correct registers), but that still requires the programmer to know the platform-specific syscall numbers and argument types. On Linux, man 2 intro provides additional details.
Of course, using this library or invoking syscalls directly is not portable in general. For *nixes, the POSIX standard ensures source compatibility between various Unix flavours (i.e., the wrapper library must provide a consistent interface). If you want a program to work on different operating systems that provide entirely different interfaces for the syscalls, you'll have to use some abstraction layer. The C standard library is one such abstraction layer, and wraps platform-specific functions with a stable interface.
read() example, that C function is portable across POSIX systems. The C standard library offers
fread() instead, which is portable across all C implementations that have a standard library.
On the Linux kernel, the syscalls can be accessed by setting the eax register the corresponding syscall value and calling an interrupt. The values on other registers act like arguments.
The linux kernel, once it starts, binds some interrupts to itself and when a program calls one of those, the CPU jumps to a specific function inside the kernel. Depending of the value of eax and the other registers, the kernel executes the corresponding syscall.
On the C language, the syscalls can be accessed through asm code wrapped as C functions.
Sorry for my writing mistakes.
There are multiple ways a program may interface with the OS.
- Library Call
- Language Features
- Shell, Spawn & execute calls
One important thing to realise is that programs written in compiled languages like C or C++ are only portable as source code. The programmer knows that certain OS provided facilities are guaranteed to be available on complient systems and the compiler and linker provide that information at build time usimg tje system libraries.
Other languages that work as is, i.e. without rebuilding, tend to provide an OS abstraction layer or a virtualization layer that translate the programs instructions to the OS ones.
You hypothetical new OS would have to provide the libraries and/or OSAL for the supported langages.
One of the many problems of using functions such as sys.call() where programs execute OS commands using shell/spawn/etc., is that when such commands change the software stops working until a patch or update is released that detects the system it is running on and issues the correct commands.