Skip to main content

OS Interface

The std.os module provides thin wrappers around OS-specific APIs with the following goals:
  • Convert “errno”-style error codes into Zig errors
  • Provide slice-based APIs alongside null-terminated APIs
  • Cross-platform abstraction for POSIX-like systems
  • Use libc when linked, otherwise use direct syscalls

Platform Modules

linux

pub const linux = @import("os/linux.zig");
Linux-specific system calls and constants.

windows

pub const windows = @import("os/windows.zig");
Windows-specific APIs and structures.

wasi

pub const wasi = @import("os/wasi.zig");
WebAssembly System Interface (WASI) support.

Other Platforms

pub const freebsd = @import("os/freebsd.zig");
pub const plan9 = @import("os/plan9.zig");
pub const uefi = @import("os/uefi.zig");
pub const emscripten = @import("os/emscripten.zig");

Environment Variables

environ

pub var environ: [][*:0]u8 = undefined;
Array of environment variable strings. Populated by startup code before main(). Warning: The value will be undefined when using zig build-lib.

argv

pub var argv: [][*:0]u8 = undefined;
Array of command-line argument strings. Populated by startup code before main(). Availability: Not available on WASI or Windows without libc. Use std.process.argsAlloc for cross-platform code.

File Descriptor Operations

getFdPath

pub fn getFdPath(fd: std.posix.fd_t, out_buffer: *[max_path_bytes]u8) std.posix.RealPathError![]u8
Returns the canonical path of a file descriptor.
fd
std.posix.fd_t
required
File descriptor to query
out_buffer
*[max_path_bytes]u8
required
Buffer to store the path
return
[]u8
Slice of the buffer containing the canonical path
  • Windows: WTF-8 encoded
  • Other: Opaque byte sequence
Platform Support:
  • ✅ Windows, macOS, Linux, FreeBSD, illumos, serenity
  • ✅ DragonFly BSD 6.0+, NetBSD 10.0+
  • ❌ WASI
Warning: Calling this function is usually a bug. Use it only when absolutely necessary. Example:
const file = try std.fs.cwd().openFile("data.txt", .{});
defer file.close();

var buf: [std.fs.max_path_bytes]u8 = undefined;
const path = try std.os.getFdPath(file.handle, &buf);
std.debug.print("Path: {s}\n", .{path});
Platform-Specific Implementation:
macOS
F.GETPATH
Uses fcntl with F.GETPATH command
Linux
/proc/self/fd/{fd}
Reads symlink from /proc/self/fd/{fd}
FreeBSD
F.KINFO
Uses fcntl with F.KINFO to get kinfo_file structure
Windows
GetFinalPathNameByHandle
Uses Win32 API GetFinalPathNameByHandle

isGetFdPathSupportedOnTarget

pub fn isGetFdPathSupportedOnTarget(os: std.Target.Os) bool
Checks if getFdPath is supported on a target OS.
os
std.Target.Os
required
Target operating system
return
bool
true if getFdPath is supported on the target

File Statistics

fstat_wasi

pub fn fstat_wasi(fd: posix.fd_t) FstatError!wasi.filestat_t
Gets file statistics on WASI.
fd
posix.fd_t
required
File descriptor
return
wasi.filestat_t
File statistics structure

FstatError

pub const FstatError = error{
    SystemResources,
    AccessDenied,
    Unexpected,
};

Platform Detection

The std.os module is primarily used through std.posix, which provides a more portable interface. For platform-specific functionality:

Windows Specific

const std = @import("std");
const windows = std.os.windows;

// Access Windows-specific functionality
const peb = windows.peb();
const handle = windows.kernel32.CreateFileW(...);

Linux Specific

const std = @import("std");
const linux = std.os.linux;

// Direct Linux syscalls
const tid = linux.gettid();
const result = linux.syscall(.getrandom, ...);

Error Handling

The std.os module converts OS error codes to Zig errors:
// errno-style errors are converted
const file = std.posix.open("/path/to/file", .{}, 0) catch |err| switch (err) {
    error.FileNotFound => {
        // Handle missing file
    },
    error.AccessDenied => {
        // Handle permission error
    },
    else => return err,
};

System Information

Getting System Paths

// Get path for a file descriptor
var buf: [std.fs.max_path_bytes]u8 = undefined;
if (std.os.isGetFdPathSupportedOnTarget(builtin.os)) {
    const path = try std.os.getFdPath(fd, &buf);
    std.debug.print("FD path: {s}\n", .{path});
}

Platform Constants

Each platform module provides OS-specific constants:
// Linux
const CLONE_VM = std.os.linux.CLONE.VM;
const SYS_getrandom = std.os.linux.SYS.getrandom;

// Windows
const GENERIC_READ = std.os.windows.GENERIC_READ;
const FILE_SHARE_READ = std.os.windows.FILE_SHARE_READ;

// WASI
const RIGHTS_FD_READ = std.os.wasi.RIGHTS.FD_READ;

Usage Patterns

Cross-Platform File Operations

const std = @import("std");
const builtin = @import("builtin");

pub fn openFile(path: []const u8) !std.fs.File {
    return if (builtin.os.tag == .windows)
        // Use Windows-specific path handling
        try std.fs.cwd().openFile(path, .{})
    else
        // Use POSIX path handling
        try std.fs.cwd().openFile(path, .{});
}

Platform-Specific Code

const builtin = @import("builtin");

pub fn getProcessId() u32 {
    return switch (builtin.os.tag) {
        .windows => std.os.windows.GetCurrentProcessId(),
        .linux => @as(u32, @bitCast(std.os.linux.getpid())),
        .wasi => 0, // WASI doesn't have process IDs
        else => @compileError("Unsupported OS"),
    };
}

Error Code Conversion

const std = @import("std");
const posix = std.posix;

pub fn readFully(fd: posix.fd_t, buffer: []u8) !usize {
    var index: usize = 0;
    while (index < buffer.len) {
        const n = posix.read(fd, buffer[index..]) catch |err| switch (err) {
            // Convert EINTR to retry
            error.Interrupted => continue,
            // Other errors are returned
            else => return err,
        };
        if (n == 0) break; // EOF
        index += n;
    }
    return index;
}

Design Philosophy

The std.os module follows these principles:
  1. Thin Wrappers: Minimal abstraction over OS APIs
  2. Error Translation: Convert OS error codes to Zig errors
  3. Type Safety: Use Zig’s type system for safer APIs
  4. Cross-Platform: Abstract common operations while allowing platform-specific code
  5. Libc Optional: Work with or without libc when possible

Best Practices

  1. Prefer std.posix: Use std.posix for portable POSIX operations instead of std.os directly.
  2. Platform checks: Use builtin.os.tag for compile-time platform detection.
  3. Error handling: Always handle platform-specific errors appropriately.
  4. Direct syscalls: On Linux without libc, use std.os.linux for direct syscalls.
  5. Windows APIs: Access Windows-specific functionality through std.os.windows.
  6. Avoid getFdPath: Only use getFdPath when absolutely necessary, as it’s often a code smell.
  • std.posix - Portable POSIX interface (recommended)
  • std.fs - File system operations
  • std.process - Process management
  • std.Thread - Threading support

Build docs developers (and LLMs) love