Simon, what you are experiencing is the exact situation I have mention I've observed: that it is more difficult for a Windows power user to learn Linux (or Mac, or any other OS) than it is for a complete newbie. One has to first "unlearn", or learn to understand that some of their built-in assumptions are not universal and are instead specific to Windows only, before they can "learn the new stuff".
I've never said or intended it in any kind of derogatory way, it is just how it is. If Linux was the popular one, it would be the exact other way around. It is the same thing when learning a language when an adult: the first foreign language (not related to your native language) is the hardest, because you have to both learn how to learn a new language, and then learn the new language first. The following ones, even if not related to the languages you already know, are comparably much easier, because you've already learned how to learn a new language.
(This does not apply to learning languages as a kid, because kids' brains have a specific language acquisition "mode" that adults don't tend to have; but looking at some language prodigies, their acquisition skills do seem awfully like what kids have, augmented with adult reasoning, logical capabilities, and memory management skills.)
I've never heard of a syscall interface.
Others have already talked about this above, so I should shut up.
However, I think I can describe the situation well starting back in the 8086 MS-DOS era, with references to connect to other stuff. Apologies for repeating what has already been said; I just find this era, and how it affected what we have now in Linux, fascinating.
Syscall interface is another name for the Application Binary Interface between applications ("userspace") and the OS (kernel part, specifically). We do use the term
Application Binary Interface, or ABI for any interface between binary/machine code things, but for the specific one between an operating system and its applications, "syscall interface" is the most commonly used name.
Intel 8086 and compatibles have a dedicated instruction,
int n, for software to trigger an interrupt.
n is an 8-bit value, with the smallest 8 or 16 reserved for specific interrupts, and with a couple of related instructions for breakpoints and such. The interrupt table has 256 entries, so most of these were utterly unused – and were perfect for programs to call standardized routines in BIOS. In fact, these interrupts, and the parameters they took in registers, were the DOS syscall interface.
My own introduction to these was via
Ralf Brown's Interrupt List in 1988 or 1989. It described all the interrupts available on MS-DOS and IBM compatible 8086/80186/80286/80386 BIOSes, plus many extensions like VESA VBE (which you could use in DOS to get SuperVGA graphics modes).
At the same time, in the Unix world, efforts for interoperability standards were heavily underway. POSIX started in 1988. Intel created
iBCS so that the same 80386 binaries could run on different Unix variants. RMS started the GNU project in 1983, founded Free Software Foundation in 1985, and the first release of GCC was in 1987.
The late 1980s and early 1990s were the era of the
Unix Wars. While BSD was much more popular at universities, many considered System V to be the "proper Unix"; but the USL-vs-BSD lawsuit meant BSD was still replacing the proprietary AT&T parts of it with its own during these times. In the end, System V, especially
System V Release 4.0 became the basis for interoperability among Unix variants.
Thus, when Linus developed Linux, he used the
System V ABI for calling conventions, because it had just become the standard among Unix variants by 1991, and the GNU toolchain he used already supported that.
I believe the
OSDev Wiki System V ABI page is the easiest collection point for various hardware details of that ABI. On x86-64 aka AMD64 (
PDF at gitlab.com), for example, it specifies that pointer and up to 64-bit integer arguments are passed (in order of left to right) in
rdi,
rsi,
rdx,
rcx,
r8, and
r9 registers, and any left over on the stack, and that
r11 is a scratch register whose value is not saved or retained over a function call (or a syscall). On AMD64, instead if
int n, a dedicated
syscall instruction is used for "calling" OS/kernel "functions". (These functions are also called "syscalls". So, it means both the act of calling a function provided by the OS/kernel, and such functions themselves.) Return values are passed in
rax and optionally in rdx; that is, two separate integer or pointer values can be returned trivially from any function or syscall when using the System V ABI on AMD64. (In comparison, on the original 8086/80386 System V ABI, all parameters were passed on stack, which was definitely not optimal.)
In Linux, the userspace-kernel ABI, the syscall interface, is
very stable. It is the internal kernel structures and function interfaces that they refuse to set in stone, because the kernel is much more flexible for development this way. For out-of-tree driver developers and e.g. kernel Rust developers, this is a huge cause of annoyance. Hell, even the excellent
Linux Device Drivers, 3rd Edition book about Linux device driver development by Jonathan Corbet, Alessandro Rubini, and Greg Kroah-Hartman is out a bit wrt. in-kernel interfaces, as it refers to the state of the art for 2.6.10, and right now we're at 6.11.
When incompatible changes are done to the syscall interface – for example, a new parameter is added –, the existing versions are kept as-is, and a new syscall allocated for the new purpose. I recommend looking at
man 2 syscalls for a list with links to the C interface description, and the kernel version each syscall was introduced in.
It is quite possible to create applications using the syscall interfaces only and not use the standard C library at all. As with embedded development, this then relies on the
freestanding environment part of the C standards. For the syscalls, I've written some very trivial syscall wrappers in extended inline assembly for GCC and Clang; the only downside is that they're both OS and architecture-specific. There is much that I personally dislike about the standard C library, but like about the C language, so one of my long-term hobbies is examining what kind of a library replacement for the standard/POSIX C library would work better in practice. With C23 and parameter array/length notation, it might even help programmers avoid buffer overrun bugs.
Because of Linux being developed using GNU toolchain and developers preferring the open standards like X/Open and Single Unix Specification (
at the Open Group) and POSIX (also
2024 edition at the Open Group), it is very natural –– but possibly confusing! –– that many of the syscalls the Linux kernel provides match the
POSIX System Interface functions (but see
Introduction and
General Information first).
And this finally ties into the
Linux man pages project at
man7.org by Michael Kerrisk. He maintains these pages actively participating in the Linux kernel mailing lists, plus collects them from other sources (like the GNU C library).
Section 2 contains system calls and C functions that are simple wrappers around them, like
open(),
read(),
write(), and
close().
Section 3 contains the rest of the standard C library calls, like
fopen(),
fread(),
fwrite(), and
fclose(). (The former are the low-level I/O functions where one must deal with e.g.
short counts and interrupts, and the latter are the Standard C I/O functions, implemented by the C library using the former.)
Unlike many other man page collections, the Linux man pages list the
Standard each function or syscall wrapper is based on, usually a short
History list of related past standards that implemented the same, plus notes with e.g. any known Linux-specific details. These are reliable; and when bugs or issues are found, Kerrisk is very friendly and appreciates fixes and contributions – I can say that from personal experience.
Side note: One of the things that really, really bug me about systems-level C (low-level libraries or services) programming outside Windows, is how few understand and use the POSIX interfaces. A particular annoyance is whenever someone writes code using
opendir(),
readdir(),
closedir() when
nftw(),
scandir(), and
glob() are not only free for use (baked in to the base C library, no size cost or anything), but are also more robustly written than self-coded tree traversals, and should deal things like file and directory renames and moves during traversal. A second one is arbitrary line length limitation due to the use of
fgets(), when
getline() and
getdelim() automagically manage a dynamic memory buffer for you.
Simply put, there is a TON of extremely useful POSIX C functions baked in to the standard C libraries outside Windows that are underutilized, because MS never implemented them.
Anyway, a question at the end:
If you already know C, why do you go to C++ for GUI applications, instead of using
GTK?
GTK has a pure C interface. Granted, because it is object-oriented as UI toolkits tend to be, it can feel quite odd at first. I recommend creating user interfaces either by hand or using
Glade, and saving the interface as an
.ui XML file. In the C code, one uses GtkBuilder to instantiate the objects from the
.ui file. There are two separate approaches for attaching the callbacks –– the functions that e.g. clicking on a button causes to be called ––: relying on GModule to automatically match the names to same-named functions in the global scope, or using
gtk_builder_add_callback_symbol() (or
gtk_builder_add_callback_symbols()) to explicitly associate names with callback functions.
I'm not averse to using dedicated ELF sections to collect callback function name-to-function mappings, but it will not work for native Windows GTK apps.
Some dislike GTK, but to me, it is just another option for creating UIs. Gtk and Qt are my go-to ones, but I'm not tied to either one.
Note that while GTK is associated with the GNOME project, you don't need GNOME to be able to use GTK. You can easily run GTK apps on any desktop environments, including KDE (which uses Qt), by just installing the support libraries (and none of the desktop applications). The same is true of all the toolkits, really.
If you want an example of GTK UI application, just say something simple it should do/have. Because I haven't bothered to upgrade my Mint 20.3 yet, I'm using GTK 3, not GTK 4 yet.