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
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.
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.
out_buffer
*[max_path_bytes]u8
required
Buffer to store the path
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:
Uses fcntl with F.GETPATH command
Reads symlink from /proc/self/fd/{fd}
Uses fcntl with F.KINFO to get kinfo_file structure
Uses Win32 API GetFinalPathNameByHandle
isGetFdPathSupportedOnTarget
pub fn isGetFdPathSupportedOnTarget(os: std.Target.Os) bool
Checks if getFdPath is supported on a target OS.
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.
File statistics structure
FstatError
pub const FstatError = error{
SystemResources,
AccessDenied,
Unexpected,
};
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,
};
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});
}
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
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, .{});
}
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:
- Thin Wrappers: Minimal abstraction over OS APIs
- Error Translation: Convert OS error codes to Zig errors
- Type Safety: Use Zig’s type system for safer APIs
- Cross-Platform: Abstract common operations while allowing platform-specific code
- Libc Optional: Work with or without libc when possible
Best Practices
-
Prefer std.posix: Use
std.posix for portable POSIX operations instead of std.os directly.
-
Platform checks: Use
builtin.os.tag for compile-time platform detection.
-
Error handling: Always handle platform-specific errors appropriately.
-
Direct syscalls: On Linux without libc, use
std.os.linux for direct syscalls.
-
Windows APIs: Access Windows-specific functionality through
std.os.windows.
-
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