Operating systems and portability

To better understand the portability of C programs, I inventoried some relevant differences between different operating systems.

Families of Operating systems

To understand the relations between different operating systems, it is useful to start with a bit of history.

UNIX

UNIX is an operating system from the 70’s that included, amongst other things, the C programming language and the sh-shell to interact with the operating system. Newer operating systems that provide these same set of APIs still can get certified for the “Single UNIX Specification”.

POSIX

POSIX (Portable Operating System Interface) is the most important part of the Single UNIX Specification. It stands for Portable Operating System Interface. It is a set of specifications maintained by a working group including IEEE, The Open Group, and the ISO/IEC. Note that POSIX is not an OS, it is just a set of criteria that an operating systems needs to meet to be POSIX-compliant.

Some main elements of POSIX: (docs)

Note that POSIX guarantees source code portability, but not necessarily binary code portability. I.e. the executable might not be transferable, but POSIX-compatible operating systems should all have a C compiler that can produce an executable from the source code.

Unix-like: Linux, BSD, MacOS

Unix-like operating systems aim to be POSIX-compatible, which means they have implemented (almost) all the POSIX specifications, and therefore contain the elements listed above. They may or may not be officially POSIX or UNIX certified.

Examples of Unix-like operating systems:

  • most Linux distributions
  • BSD/OS (discontinued, but its derivatives still exit):
    • FreeBSD – (docs)
    • OpenBSD – (docs)
    • MacOS – based on BSD/FreeBSD; note that MacOS is also UNIX certified

Windows

Windows is not POSIX-compliant. This means, for example, that the Windows Powershell is completely different from the sh-shell and it’s derivatives like bash. Also, Windows does not come with a C compiler out of the box.

Operating systems and libc implementations

Linux – glibc

Linux distributions typically contain the GNU C Library a.k.a. ‘glibc’, which is an implementation of the C standard library as well as the additional POSIX libraries. Linux distributions on embedded systems might contain a different, smaller implementation that might not cover all functions from libc. Shell command man 3 intro can be used to get more info on the local implementation of libc. Similarly, man 2 intro can be used for more info on the POSIX wrapper functions in C that can be used to execute system calls like ‘open()’.

MacOs – BSD libc

MacOs uses the BSD libc implementation which has some additional libraries on top of the POSIX C library. The shell commands mentioned above are also available. (wikipedia)

Windows – MinGW

Windows does not come with a C compiler out of the box. A popular C compiler for Windows is MinGW. While MinGW supports the standard libc functions, the additional functions defined in the POSIX specifications are not available. Wrapper functions that execute system calls like ‘open()’, for example, are not available.

Android – Bionic libc

Android is based on Linux but has a different implementation of the C programming language (Bionic libc). While bionic aims to implement all standard C and POSIX libraries, it is missing a number and is therefore not fully compatible with glibc.

iOS

C is not really suitable to develop for ios. Instead objective-C or the more modern language Swift should be used.

Embedded C

The ISO standard for Embedded C provides a few additional libraties and data types on top of standard C. Depending on the hardware, there might also be controller specific extensions. On the other hand, on embedded systems a number of non-relevant libraries might not be available to save memory. uClibc-ng is a popular linux based example of a libc implementations for embedded systems.

Hardware considerations

x86 / x64 architecture

Servers, desktops and laptops typically use chips that support the “x86 architecture”. Operating systems like Windows, Linux, and MacOS are built upon this architecture and are therefore all in the family of x86 operating systems. From 2021 onward, MacOS is transitioning away from x86 to their own ARM-based chips. While Linux was originally developed for x86 operating systems, there are also many Linux distributions that work on other architectures. x86 architectures exist with 16, 32, or 64 bit CPUs. The 64 bit version (x86-64) is often referred to as just “x64”.

ARM

Tablets and mobile phones typically use ARM chips, which are more energy efficient. Android and iOS are the dominating operating systems in the mobile market. Because they utilize a different hardware architecture, software is hardly ever compatible across ARM and x86 systems. Android is based on Linux.

Other / Embedded

Embedded systems may use x86, ARM, or one of many other architectures. Note that many embedded systems don’t really have an operating system, in the sense that there are no log-in options for users. Some embedded operating systems are POSIX compliant.