System calls, FFI, and cross-platform abstractions 51 If you wanted to present a readiness-based API instead, you have to create an illusion of having two separate operations when doing I/O on Windows. First, request a notification when the data is ready to be read on a socket, and then actually read the data. While possible to do, you'll most likely find yourself having to create a very complex API or accept some inefficiencies on Windows platforms due to having intermediate buffers to keep the illusion of having a readiness-based API. We'll leave the topic of event queues for when we go on to create a simple example showing how exactly they work. Before we do that, we need to become really comfortable with FFI and syscalls, and we'll do that by writing an example of a syscall on three different platforms. We'll also use this opportunity to talk about abstraction levels and how we can create a unified API that works on the three different platforms. System calls, FFI, and cross-platform abstractions We' ll implement a very basic syscall for the three architectures: BSD/mac OS, Linux, and Windows. We'll also see how this is implemented in three levels of abstraction. The syscall we'll implement is the one used when we write something to the standard output (stdout ) since that is such a common operation and it's interesting to see how it really works. We'll start off by looking at the lowest level of abstraction we can use to make system calls and build our understanding of them from the ground up. The lowest level of abstraction The lowest level of abstraction is to write what is often referred to as a “raw” syscall. A raw syscall is one that bypasses the OS-provided library for making syscalls and instead relies on the OS having a stable syscall ABI. A stable syscall ABI means it guarantees that if you put the right data in certain registers and call a specific CPU instruction that passes control to the OS, it will always do the same thing. To make a raw syscall, we need to write a little inline assembly, but don't worry. Even though we introduce it abruptly here, we'll go through it line by line, and in Chapter 5, we'll introduce inline assembly in more detail so you become familiar with it. At this level of abstraction, we need to write different code for BSD/mac OS, Linux, and Windows. We also need to write different code if the OS is running on different CPU architectures.