AlgoMaster Logo

Linux, Windows, and macOS Compared

15 min readUpdated August 7, 2026
Listen to this chapter
Unlock Audio

Linux, Windows, and macOS solve the same fundamental problems.

They isolate applications, schedule execution, manage memory, organize files, control devices, and provide networking. Most application developers use similar high-level operations on all three systems: start a program, open a file, create a thread, or connect to a server.

The important differences are below that surface.

Each system places kernel components differently, represents processes and resources through different abstractions, organizes storage into a different namespace, and exposes a different stable interface to applications.

PlatformAPI used for that same request
LinuxPOSIX API
WindowsWin32 API
macOSPOSIX and Apple APIs

Below those APIs sit different kernel interfaces and filesystem models.

This chapter focuses on the differences that affect application behavior, deployment, debugging, and portability.

The Common Ground

All three operating systems separate user applications from privileged kernel code.

Each application normally runs in a process with its own virtual address space. The kernel tracks its identity, threads, open resources, permissions, and execution state.

Each system also uses hardware privilege levels and controlled kernel-entry mechanisms:

  1. The application runs in user mode.
  2. It calls an operating-system library.
  3. The library performs a controlled kernel entry.
  4. The kernel validates and performs the request.

The concepts from the previous chapters therefore apply to all three systems. The differences lie in the specific contracts and internal organization.

Kernel Architecture

The textbook labels are:

Operating systemCommon classificationKernel name
LinuxMonolithic and modularLinux kernel
WindowsHybridWindows NT kernel and executive
macOSHybridXNU

These labels are useful starting points, not complete descriptions.

Linux

Linux places process management, memory management, filesystems, networking, and most device drivers in one privileged kernel address space.

Its subsystems can call one another directly. Loadable kernel modules allow drivers and features to be added at runtime, but module code still executes with kernel privilege.

This arrangement supports efficient communication between kernel components. A severe bug in a kernel driver can also corrupt shared kernel state or crash the whole system.

Windows

Windows is based on the Windows NT architecture and is commonly described as hybrid.

Documentation sometimes uses the word kernel narrowly for low-level scheduling, interrupt, and synchronization code. The privileged Windows executive contains higher-level managers for processes, memory, I/O, security, and kernel objects. Together with device drivers and the hardware abstraction layer, these components form what developers often call the Windows kernel.

Windows uses modular layers and user-space services, but substantial operating-system functionality and many drivers run in kernel mode. Calling it hybrid does not mean that its major services receive microkernel-style isolation from one another.

macOS

macOS uses the XNU kernel.

XNU combines components from several traditions:

XNU componentBroad responsibility
Mach-derived coreScheduling, virtual memory, low-level IPC
BSD layerProcesses, POSIX interfaces, filesystems, networking
I/O KitDriver framework

These components are tightly integrated, and much of their implementation executes in one privileged kernel environment.

The Mach heritage influences XNU's internal abstractions, while the BSD layer provides the Unix process, file, socket, and permission model visible to many applications.

What the Architecture Difference Means

Linux and the two hybrid kernels all keep a significant amount of trusted code in kernel space.

Their practical differences cannot be predicted from the labels monolithic and hybrid alone. A developer needs to know where a specific driver or service runs, which interface it uses, and what happens if it fails.

At a broad level:

QuestionLinuxWindowsmacOS
Are major filesystems and networking in kernel space?YesYesYes
Do many drivers run in kernel space?YesYesYes
Can user-space services provide OS functionality?YesYesYes
Can a bad kernel-mode driver crash the system?YesYesYes
Can kernel functionality be modular?YesYesYes

The architectural labels describe lineage and organization. They do not create an absolute reliability or performance ranking.

Process Models

All three systems distinguish a process from the threads that execute within it.

A process owns or references resources such as an address space, credentials, and open objects. Threads are the units that the scheduler runs on CPU cores.

The most visible difference is how a new program starts.

Linux: fork and exec

The traditional Unix model separates process creation from program loading.

fork() creates a child process based on the calling process. An exec operation then replaces the current program image with a new executable.

Creation and program loading are two separate steps here, and the gap between them is where a shell adjusts the child before the new program takes over.

Linux also supports posix_spawn() and lower-level creation mechanisms. The later processes module explains how fork, exec, and Linux's clone mechanism relate.

The fork/exec model influences shell pipelines, server designs, inherited file descriptors, and parent-child relationships throughout Unix software.

Windows: CreateProcess

Windows does not provide general Win32 applications with a direct equivalent of Unix fork.

The primary Win32 operation, CreateProcess, creates a new process and its initial thread while selecting the executable in one operation.

One call produces all four results at once, with no intermediate state in which the child exists but has not yet been given its program.

The caller receives handles for the new process and thread. It can use those handles to wait, inspect status, adjust allowed properties, or terminate the process when permitted.

Handle inheritance is configured explicitly. This differs from the Unix pattern in which a child created by fork initially inherits a copy of the parent's file-descriptor table.

Windows process creation also commonly begins with a command-line string. The target program's runtime parses that string into arguments. Unix exec receives an already separated array of argument strings.

This difference is why cross-platform command quoting can fail even when the visible command appears identical.

macOS: BSD Processes on Mach Tasks

macOS presents the familiar Unix process model through its BSD layer.

Applications can use fork, the exec family, signals, process IDs, and POSIX file descriptors. posix_spawn is also heavily used to create and load a new program through one higher-level operation.

Underneath that Unix-facing model, XNU represents execution using Mach concepts such as tasks and threads. A Mach task is the resource container associated with an address space, while threads execute within it.

Most portable command-line programs can work through the POSIX process interface without using Mach APIs directly.

Identifying and Referencing Resources

Linux and macOS applications commonly use small integers called file descriptors for files, pipes, sockets, and other stream-like resources.

The integer indexes an entry in the process's descriptor table. The kernel entry refers to the actual open object and its access state.

Windows uses handles for many kernel-managed objects:

A handle is also a process-local reference, but the Windows object model applies it to a broad range of object types, including files, processes, threads, events, and synchronization objects. Handles carry access rights and are closed with APIs such as CloseHandle.

Resource exampleLinux and macOSWindows
FileFile descriptorFile handle
ProcessPID, plus other OS interfacesPID and process handle
ThreadThread identifier and library handleThread ID and thread handle
SocketFile descriptorWinsock SOCKET value

A numeric PID identifies a process, but it is not equivalent to an open reference. On Windows, a process handle keeps a reference to the process object and records the access granted to the caller. Unix systems provide other descriptor- or handle-like mechanisms when stronger lifetime tracking is required.

Filesystem Namespace

Linux and macOS expose a single hierarchical file tree rooted at /.

Windows commonly exposes volumes through drive-letter paths and network shares.

SystemExample pathTypical namespace model
Linux/var/log/app/server.logOne tree rooted at /; filesystems mounted into it
WindowsC:\ProgramData\App\server.logDrive letters, volume paths, and UNC network paths
macOS/Users/alex/app/server.logOne tree rooted at /; external volumes commonly under /Volumes

Linux can mount a storage device at any directory:

macOS follows the same Unix-style root model, with its own mount conventions and APFS filesystem features.

Windows supports drive letters such as C: and UNC paths such as:

Internally, Windows has a richer object and volume namespace than drive letters suggest. Drive letters are familiar aliases used by Win32 applications rather than the complete kernel-level model.

Cross-platform code should use its language's path library rather than manually joining path components with / or \.

Case Sensitivity and File Names

Path comparison behavior is a common source of deployment bugs.

System and common defaultCase behavior
Linux with filesystems such as ext4Case-sensitive
Windows with NTFSCase-preserving but usually case-insensitive
macOS with default APFS configurationsCase-preserving but usually case-insensitive

On a typical Linux system, these can be two different files:

On common Windows and macOS installations, those names usually refer to the same directory entry.

These are defaults rather than universal rules. Windows supports case-sensitive directory behavior in some configurations. macOS volumes can be formatted case-sensitive. Linux filesystems can also provide different name-handling features.

A project developed on a case-insensitive laptop can therefore fail after deployment to a case-sensitive Linux server if an import or path uses the wrong capitalization.

Windows also reserves some file names and has path rules that do not exist in the same form on Unix systems. Portable programs should avoid deriving unrestricted file names directly from user input.

Permissions and Identity

Linux and macOS expose Unix identities based on user IDs and group IDs, together with permission bits and optional access-control lists.

Windows represents identities using security identifiers, commonly called SIDs. A process carries an access token describing its identity, groups, and privileges. Windows objects use security descriptors and access-control entries to decide which operations are allowed.

PlatformIdentity carried by the processWhat is checked against it
Linux and macOSUID, GID, and group membershipMode bits and ACLs
WindowsAn access token holding SIDs and privilegesA security descriptor

The models can express many similar policies, but they are not byte-for-byte equivalents. Copying files between platforms or translating container volume permissions can lose or reinterpret security information.

Open Files, Renaming, and Deletion

Unix systems separate a directory name from the underlying open file object.

On Linux and macOS, a process can keep using a file descriptor after another process renames or unlinks that file. Removing the final directory name prevents new opens by that name, but the underlying object remains until its last open reference is closed.

Two separate references point at the same file object. Removing the name does not remove the object, because the descriptor is still holding it.

This behavior supports log rotation and safe replacement patterns.

Windows file opens include sharing modes that declare whether other opens may read, write, or delete the same file. Deletion or renaming can fail when an existing handle was opened without compatible sharing.

Windows can support delete-sharing behavior similar to Unix when applications request the necessary flags. The important difference is that sharing policy is part of opening the handle, so code cannot assume Unix deletion semantics.

Backend software that rotates logs, replaces binaries, or atomically updates configuration must account for this difference.

The Stable Application Interface

All three systems have low-level kernel entry mechanisms, but applications do not treat the same layer as the supported public contract.

Linux: the syscall ABI

Linux exposes a stable, architecture-specific system-call ABI.

Applications normally call glibc, musl, or another library, which invokes operations such as openat, read, write, mmap, and clone.

The path runs from the application, through libc or a language runtime, across the Linux system-call ABI, and into the Linux kernel.

System-call numbers differ across processor architectures, but Linux preserves compatibility within an ABI so existing binaries can continue running on newer kernels.

Windows: Win32 above the native interface

The supported application-facing contract for most Windows software is the Win32 API.

An application may call CreateFileW, CreateProcessW, or VirtualAlloc. User-mode Windows libraries translate those calls through lower-level native interfaces, often involving ntdll.dll, before entering the NT kernel.

The path runs from the application, through the Win32 API, into the system libraries and ntdll, across a native NT system call, and into the Windows kernel.

The low-level syscall numbers are an implementation detail and can differ between Windows releases. Ordinary applications should not hard-code or invoke them directly. The system libraries provide the compatibility layer.

The suffix W identifies the Unicode form of many Win32 functions. Modern Windows code should generally use these Unicode interfaces rather than older narrow-character variants.

macOS: libSystem, frameworks, and XNU

macOS applications commonly use POSIX functions from libSystem, Objective-C or Swift system frameworks, and other documented Apple APIs.

XNU supports BSD-style system calls as well as Mach traps for Mach services.

The path runs from the application, through libSystem or an Apple framework, across a BSD syscall or Mach interface, and into XNU.

Directly hard-coding Darwin syscall numbers is not the normal supported interface. Libraries and frameworks isolate applications from many kernel-level implementation details.

This resembles Windows in one important respect: the documented user-space API is a stronger application contract than a private numeric kernel entry.

Error Reporting

The interface style affects how programs detect failure.

Linux and macOS POSIX functions commonly return -1 or another documented sentinel and set errno.

Win32 functions use operation-specific failure values. Many then provide detailed error information through GetLastError().

Native NT interfaces use status values, while Winsock has its own error-access function. Cross-platform libraries translate these conventions into exceptions, error objects, or language-specific result types.

Code must follow the contract of the API it actually calls rather than assuming that every operating system reports errors through errno.

One File-Open Request on Three Systems

The same high-level action takes a different route on each platform:

PlatformWhat one file-open request passes through
Linuxopen(), libc, the openat syscall, the Linux VFS and filesystem
WindowsCreateFileW(), Win32 libraries, a native NT request, the I/O manager
macOSopen(), libSystem, a BSD syscall, the XNU filesystem layer

All three kernels eventually validate a path, check access, find a filesystem object, and create a process-visible reference.

The differences are in path syntax, object namespace, permission model, sharing behavior, error convention, and the stable API layer.

This is why a portability wrapper does more than rename functions. It must translate semantics.

Executable and Library Formats

The previous chapter used Linux ELF files. Windows and macOS use different binary formats and loaders.

SystemNative executable formatDynamic-loading environment
LinuxELFELF interpreter and libraries such as glibc or musl
WindowsPE/COFFWindows loader and DLLs
macOSMach-Odyld and dynamic libraries/frameworks

macOS can package multiple architecture-specific Mach-O images in a universal binary. The loader selects the image matching the current processor.

A binary is therefore not portable merely because all three machines use 64-bit processors. Its instruction set, executable format, calling convention, library ABI, and kernel-facing interface must all match the target platform.

Differences That Matter to Backend Engineers

Several differences regularly appear in development and production:

SituationWhy the operating system matters
Deploying mixed-case pathsLinux commonly distinguishes names that Windows and macOS treat as equal
Rotating an open log fileUnix unlink behavior differs from Windows sharing-mode behavior
Launching child processesUnix uses argument arrays and often fork/exec; Windows commonly uses CreateProcess and a command-line string
Shipping native dependenciesELF, PE, and Mach-O binaries are not interchangeable
Running Linux containers on macOS or WindowsLinux containers require a Linux kernel, normally supplied by a virtual machine
Implementing high-performance I/OLinux, Windows, and macOS expose different native mechanisms such as epoll, I/O completion ports, and kqueue
Managing long-running servicesLinux commonly uses systemd, Windows uses the Service Control Manager, and macOS uses launchd

High-level runtimes hide many of these differences, but they cannot erase them.

A Java, Go, Python, or Node.js program eventually reaches platform-specific libraries and kernel interfaces. Native extensions, process control, file semantics, permissions, and performance-sensitive I/O often reveal the underlying operating system.

Linux containers are a particularly useful example. A container packages user-space files and processes, but it shares a kernel. Docker Desktop normally uses a Linux virtual machine on Windows or macOS to provide the Linux syscall interface expected by Linux container images.

Loading simulation...

Choosing the Right Mental Model

Avoid treating one operating system as the standard and the others as unusual variations.

Instead, separate the application need from the platform contract:

  1. Start from the application need.
  2. Identify the process, file, memory, or network abstraction it maps to.
  3. Find the platform's documented user-space API for that abstraction.
  4. Below it sits the platform's kernel interface and internal model.

For portable application code, use mature language or platform libraries that deliberately handle the differences.

For systems debugging, identify the layer that failed. A path problem, dynamic-loader error, API misuse, permission denial, and rejected syscall belong to different layers even if each appears as “the program would not start.”

Summary

Linux uses a monolithic, modular kernel. Windows NT and macOS XNU are commonly described as hybrid kernels, although both retain substantial functionality and many drivers in kernel space.

Linux and macOS expose Unix-style processes, file descriptors, and a single filesystem tree. Windows centers its public process model on CreateProcess, uses handles for many kernel objects, and commonly exposes storage through drive letters and UNC paths.

Linux treats its architecture-specific syscall ABI as a stable user-space boundary. Windows applications normally target Win32, while macOS applications target POSIX functions and documented Apple frameworks; their libraries hide lower-level native kernel entries.

The differences that matter most are semantic: process creation, argument passing, case sensitivity, open-file lifetime, permissions, error reporting, binary formats, and the supported API boundary.

The essential mental model is:

The three systems provide similar capabilities, but portability requires translating contracts and behavior, not merely translating function names.

Quiz

Linux, Windows, and macOS Compared Quiz

5 quizzes