Capítulo 18 18 min de leitura

OS

stdlib::os expõe a identidade da máquina hospedeira e do processo em execução: qual sistema operacional e CPU foram usados na compilação, o ID do processo, o diretório de trabalho atual, o caminho do executável, informações de hardware (contagem de CPUs, RAM, tempo de atividade), diretórios padrão nativos da plataforma, detecção de terminal e um atalho para execução de comandos no shell.

As funções se dividem em duas categorias:

  • Fatos de tempo de compilação retornam um valor diretamente (string / bool / i32). Essas funções não podem falhar de forma significativa: os_name, os_arch, os predicados os_is_*, os separadores, os_pid e os_is_tty.
  • Consultas em tempo de execução retornam um Result !T. Cada Err carrega uma razão legível por humanos, e várias retornam Err em plataformas que não expõem a métrica (por exemplo, os_ppid/os_uid/os_gid/os_loadavg_1m no Windows).

Import

import stdlib::os::*;

Catálogo completo

Todos os 36 itens públicos de relance. A coluna Falha? indica quais retornam !T e em qual plataforma a chamada tipicamente vira Err.

Função Assinatura Falha? Descrição
os_name fn os_name() -> string nunca String da família do SO.
os_arch fn os_arch() -> string nunca String da arquitetura da CPU.
os_is_windows fn os_is_windows() -> bool nunca true no Windows.
os_is_posix fn os_is_posix() -> bool nunca true no Linux/macOS/*BSD.
os_is_linux fn os_is_linux() -> bool nunca true apenas no Linux.
os_is_macos fn os_is_macos() -> bool nunca true apenas no macOS.
os_has_async_io fn os_has_async_io() -> bool nunca true quando o reactor epoll/kqueue está ativo.
os_has_reuseport_balance fn os_has_reuseport_balance() -> bool nunca true quando SO_REUSEPORT balanceia accepts (Linux).
os_path_sep fn os_path_sep() -> string nunca \ no Windows, / nos demais.
os_line_sep fn os_line_sep() -> string nunca \r\n no Windows, \n nos demais.
os_path_list_sep fn os_path_list_sep() -> string nunca ; no Windows, : nos demais.
os_pid fn os_pid() -> i32 nunca ID do processo atual.
os_ppid fn os_ppid() -> !i32 Windows ID do processo pai.
os_uid fn os_uid() -> !i32 Windows ID de usuário POSIX.
os_gid fn os_gid() -> !i32 Windows ID de grupo POSIX.
os_username fn os_username() -> !string raro Nome de login.
os_hostname fn os_hostname() -> !string raro Nome do host da máquina.
os_kernel_version fn os_kernel_version() -> !string raro uname -r / build do Windows.
os_cwd fn os_cwd() -> !string raro Diretório de trabalho atual.
os_chdir fn os_chdir(path: string) -> ! sim Muda o diretório de trabalho (sem valor de retorno).
os_exe_path fn os_exe_path() -> !string raro Caminho absoluto do binário.
os_exe_dir fn os_exe_dir() -> !string raro Diretório do binário.
os_cpu_count fn os_cpu_count() -> !i32 raro CPUs lógicas.
os_cpu_count_physical fn os_cpu_count_physical() -> !i32 raro Núcleos físicos (cai para lógico se indisponível).
os_page_size fn os_page_size() -> !i32 raro Tamanho de página de memória virtual em bytes.
os_memory_total fn os_memory_total() -> !u64 raro RAM física total (bytes).
os_memory_free fn os_memory_free() -> !u64 macOS/BSD RAM disponível (bytes).
os_uptime_secs fn os_uptime_secs() -> !u64 raro Segundos desde o boot.
os_loadavg_1m fn os_loadavg_1m() -> !f64 Windows Média de carga em 1 minuto.
os_temp_dir fn os_temp_dir() -> !string raro Diretório temporário por usuário.
os_home_dir fn os_home_dir() -> !string raro Diretório home do usuário.
os_config_dir fn os_config_dir() -> !string raro Diretório base de configuração por usuário.
os_cache_dir fn os_cache_dir() -> !string raro Diretório base de cache por usuário.
os_data_dir fn os_data_dir() -> !string raro Diretório base de dados por usuário.
os_is_tty fn os_is_tty(fd: i32) -> bool nunca true se fd é um terminal.
os_shell fn os_shell(cmd: string) -> !i32 falha no spawn Executa cmd pelo shell do SO.

Identidade da plataforma (tempo de compilação, infalível)

Essas funções refletem o alvo de compilação — a plataforma para a qual o binário foi compilado — e nunca falham.

Função Assinatura Descrição
os_name fn os_name() -> string Família do SO: "linux", "windows", "macos", "freebsd", "openbsd", "netbsd" ou "unknown".
os_arch fn os_arch() -> string Arquitetura da CPU: "x86_64", "aarch64", "arm", "x86", "riscv64" ou "unknown".
os_is_windows fn os_is_windows() -> bool true no Windows.
os_is_posix fn os_is_posix() -> bool true em sistemas POSIX (Linux, macOS, *BSD); false no Windows.
os_is_linux fn os_is_linux() -> bool true apenas no Linux.
os_is_macos fn os_is_macos() -> bool true apenas no macOS / Darwin (os_is_posix() também é true).
os_has_async_io fn os_has_async_io() -> bool true quando o reactor de IO assíncrono (epoll/kqueue) está ativo; false quando o IO cai para bloqueante síncrono.
os_has_reuseport_balance fn os_has_reuseport_balance() -> bool true quando SO_REUSEPORT realmente balanceia accepts (apenas Linux por ora).
os_path_sep fn os_path_sep() -> string Separador de componentes de caminho: \ no Windows, / nos demais.
os_line_sep fn os_line_sep() -> string Terminador de linha: "\r\n" no Windows, "\n" nos demais.
os_path_list_sep fn os_path_list_sep() -> string Separador de lista PATH: ; no Windows, : nos demais.
import stdlib::os::*;

fn main() -> i32 {
    println!("os:", os_name());
    println!("arch:", os_arch());
    if os_is_windows() {
        println!("on windows, sep =", os_path_sep());
    } else {
        println!("on posix, sep =", os_path_sep());
    }
    if os_is_linux() { println!("linux"); }
    if os_is_macos() { println!("macos"); }
    if os_is_posix() { println!("posix"); }
    println!("path list sep:", os_path_list_sep());
    let line: string = format!("hello{}", os_line_sep());
    println!(line);
    if os_has_async_io() { println!("reactor active"); }
    if os_has_reuseport_balance() { println!("reuseport balances"); }
    return 0;
}

Ramificando com os_name

match só combina variantes de enum / some/none / _não literais de string. Para ramificar pela família do SO, compare com .eq em uma cadeia if/else:

import stdlib::os::*;

fn opener() -> string {
    let n: string = os_name();
    if n.eq("macos")   { return "open"; }
    if n.eq("windows") { return "start"; }
    return "xdg-open";
}

fn main() -> i32 {
    println!("url opener for", os_name(), "is", opener());

    let sep: string = os_line_sep();
    let doc: string = format!("line1{}line2{}", sep, sep);
    println!(doc);
    return 0;
}

Ramificação em tempo de execução com base no reactor

os_has_async_io permite que um servidor decida se é seguro lançar uma corrotina por conexão (verdadeiro com um reactor) ou se deve atender de forma síncrona inline (o único comportamento correto quando read/write bloqueiam a thread chamadora):

if os_has_async_io() { spawn handle(c); }
else                 { handle(c); }

os_has_reuseport_balance é o complemento para pools de workers: quando true, cada worker pode usar bind_reuseport e deixar o kernel balancear os accepts; quando false, vincule um único listener compartilhado e tenha N threads chamando accept() nele.

Dividindo o $PATH

Use os_path_list_sep ao dividir $PATH / %PATH% — é ; no Windows mas : no POSIX, e usar qualquer um dos dois fixo quebra a outra plataforma:

import stdlib::os::*;
import stdlib::env::*;

fn main() -> i32 {
    let path: string = env_get("PATH");
    let sep: string = os_path_list_sep();          // ";" on Windows, ":" else
    let dirs: *Vector<string> = path.split(sep);
    println!("PATH has", dirs.len(), "entries");

    let mut i: i32 = 0;
    while i < dirs.len() {
        let d: string = dirs.get(i);
        if !d.eq("") { println!("  ", d); }
        i = i + 1;
    }
    return 0;
}

Identidade do processo

Função Assinatura Descrição
os_pid fn os_pid() -> i32 ID do processo atual. Sempre positivo; nunca falha.
os_ppid fn os_ppid() -> !i32 ID do processo pai. Err("ppid not available") no Windows.
os_uid fn os_uid() -> !i32 ID de usuário POSIX. Err no Windows (sem UID numérico).
os_gid fn os_gid() -> !i32 ID de grupo POSIX. Err no Windows.
os_username fn os_username() -> !string Nome de login ($USER/getpwuid no POSIX, GetUserNameA no Windows).
os_hostname fn os_hostname() -> !string Nome do host da máquina.
os_kernel_version fn os_kernel_version() -> !string uname -r no POSIX; <major>.<minor>.<build> no Windows.

os_pid retorna um i32 direto — sem Result para desempacotar. Os demais retornam !T; leia .ok, .val e .err:

import stdlib::os::*;

fn main() -> i32 {
    println!("pid       :", os_pid());            // bare i32, infallible

    let pp: !i32 = os_ppid();                     // Err on Windows
    if pp.ok { println!("ppid      :", pp.val); }
    else     { println!("ppid      :", pp.err); }

    let u: !i32 = os_uid();                        // Err on Windows
    if u.ok  { println!("uid       :", u.val); }

    let g: !i32 = os_gid();                        // Err on Windows
    if g.ok  { println!("gid       :", g.val); }

    let user: !string = os_username();
    if user.ok { println!("user      :", user.val); }

    let host: !string = os_hostname();
    if host.ok { println!("host      :", host.val); }

    let kv: !string = os_kernel_version();
    if kv.ok   { println!("kernel    :", kv.val); }

    // uid == 0 significa root no POSIX.
    if u.ok && u.val == 0 { println!("(running as root)"); }
    return 0;
}

Diretório atual e executável

Função Assinatura Descrição
os_cwd fn os_cwd() -> !string Diretório de trabalho atual, sem separador no final.
os_chdir fn os_chdir(path: string) -> ! Muda o diretório de trabalho do processo. O sucesso não carrega valor — verifique r.ok.
os_exe_path fn os_exe_path() -> !string Caminho absoluto do executável em execução.
os_exe_dir fn os_exe_dir() -> !string Porção de diretório de os_exe_path, sem separador no final.

os_chdir retorna um Result sem valor (!). Em caso de sucesso não há nada a ler; em caso de falha (caminho inexistente, sem permissão) r.ok é false e r.err contém o motivo.

Um padrão robusto é salvar o cwd, mudar para um diretório temporário e restaurá-lo — o diretório de trabalho é global ao processo, então deixá-lo alterado surpreende o restante do programa:

import stdlib::os::*;

fn main() -> i32 {
    let saved: !string = os_cwd();
    if !saved.ok { println!("no cwd:", saved.err); return 1; }
    println!("start:", saved.val);

    let tmp: !string = os_temp_dir();
    if tmp.ok {
        let c: ! = os_chdir(tmp.val);
        if c.ok {
            let now: !string = os_cwd();
            if now.ok { println!("moved to:", now.val); }
        } else {
            println!("chdir failed:", c.err);
        }
    }

    // Sempre restaurar.
    let back: ! = os_chdir(saved.val);
    if !back.ok { println!("restore failed:", back.err); }
    return 0;
}

Localizando recursos próximos ao binário

os_exe_dir é a maneira confiável de localizar arquivos distribuídos junto ao executável, independentemente do cwd de onde o usuário o iniciou. Construa o caminho com os_path_sep para que funcione tanto no Windows quanto no POSIX:

import stdlib::os::*;

fn main() -> i32 {
    let exe: !string = os_exe_path();
    if exe.ok { println!("running:", exe.val); }

    let dir: !string = os_exe_dir();
    if dir.ok {
        let asset: string = format!("{}{}assets{}logo.png",
            dir.val, os_path_sep(), os_path_sep());
        println!("asset:", asset);
    } else {
        println!("exe dir unavailable:", dir.err);
    }
    return 0;
}

Hardware

Todas as consultas de hardware retornam Results. Contagens de CPU/página são !i32; memória e uptime são !u64; média de carga é !f64.

Função Assinatura Descrição
os_cpu_count fn os_cpu_count() -> !i32 CPUs lógicas disponíveis para o processo.
os_cpu_count_physical fn os_cpu_count_physical() -> !i32 Núcleos físicos; cai para os_cpu_count() quando indisponível.
os_page_size fn os_page_size() -> !i32 Tamanho de página de memória virtual em bytes (tipicamente 4096; 16384 no Apple Silicon).
os_memory_total fn os_memory_total() -> !u64 RAM física total em bytes.
os_memory_free fn os_memory_free() -> !u64 RAM física disponível em bytes (instantâneo; melhor esforço no macOS/BSD).
os_uptime_secs fn os_uptime_secs() -> !u64 Segundos desde o boot do sistema.
os_loadavg_1m fn os_loadavg_1m() -> !f64 Média de carga em 1 minuto. Apenas POSIX — Err no Windows.

Um uso comum é dimensionar um pool de workers a partir da contagem de CPUs com um fallback seguro quando a consulta falha:

import stdlib::os::*;

fn main() -> i32 {
    let cpus: !i32 = os_cpu_count();
    let workers: i32 = if cpus.ok { cpus.val } else { 1 };
    println!("spawning", workers, "workers");

    let phys: !i32 = os_cpu_count_physical();
    if phys.ok { println!("physical cores:", phys.val); }

    let pg: !i32 = os_page_size();
    if pg.ok { println!("page size:", pg.val, "bytes"); }

    let total: !u64 = os_memory_total();
    if total.ok { println!("RAM MiB :", (total.val / 1024 / 1024) as i32); }

    let free: !u64 = os_memory_free();
    if free.ok { println!("free MiB:", (free.val / 1024 / 1024) as i32); }

    let up: !u64 = os_uptime_secs();
    if up.ok { println!("uptime  :", (up.val / 3600) as i32, "h"); }

    let load: !f64 = os_loadavg_1m();      // Err on Windows
    if load.ok { println!("load 1m :", load.val); }
    else       { println!("load 1m :", load.err); }
    return 0;
}

Diretórios padrão (nativos da plataforma)

Cada função retorna um diretório base; acrescente o nome do seu app. A resolução varia por plataforma:

Função Assinatura Linux macOS Windows
os_temp_dir fn os_temp_dir() -> !string $TMPDIR ou /tmp $TMPDIR ou /tmp %TEMP%
os_home_dir fn os_home_dir() -> !string $HOME (fallback getpwuid) igual %USERPROFILE%
os_config_dir fn os_config_dir() -> !string $XDG_CONFIG_HOME ou ~/.config ~/Library/Application Support %APPDATA%
os_cache_dir fn os_cache_dir() -> !string $XDG_CACHE_HOME ou ~/.cache ~/Library/Caches %LOCALAPPDATA%
os_data_dir fn os_data_dir() -> !string $XDG_DATA_HOME ou ~/.local/share ~/Library/Application Support %APPDATA%
import stdlib::os::*;

fn main() -> i32 {
    let tmp: !string = os_temp_dir();
    if tmp.ok { println!("temp:", tmp.val); }

    let home: !string = os_home_dir();
    if home.ok { println!("home:", home.val); }

    let cfg: !string = os_config_dir();
    if cfg.ok { println!("config:", cfg.val); }

    let cache: !string = os_cache_dir();
    if cache.ok { println!("cache:", cache.val); }

    let data: !string = os_data_dir();
    if data.ok { println!("data:", data.val); }

    return 0;
}

Um helper reutilizável de caminho por app

Encapsule a busca pelo diretório base em uma função que retorna !string e propague o erro com ? — o chamador recebe um Result limpo:

import stdlib::os::*;

fn config_path(app: string, file: string) -> !string {
    let base: string = os_config_dir()?;          // propagate Err
    return ok(format!("{}{}{}{}{}", base, os_path_sep(), app, os_path_sep(), file));
}

fn main() -> i32 {
    let p: !string = config_path("myapp", "settings.toml");
    if p.ok { println!("config at:", p.val); }
    else     { println!("could not resolve config dir:", p.err); }
    return 0;
}

Estado de IO

os_is_tty

fn os_is_tty(fd: i32) -> bool

true se o descritor de arquivo for um terminal. fd: 0 = stdin, 1 = stdout, 2 = stderr. Use antes de emitir sequências de escape ANSI de cor — saída redirecionada para um arquivo ou outro processo deve permanecer sem formatação:

import stdlib::os::*;

fn paint(msg: string) -> string {
    if os_is_tty(1) { return format!("\x1b[32m{}\x1b[0m", msg); }
    return msg;
}

fn main() -> i32 {
    println!(paint("build succeeded"));
    if !os_is_tty(0) { println!("(stdin is not interactive)"); }
    return 0;
}

Subprocesso rápido — atalho de shell de uma linha

os_shell

fn os_shell(cmd: string) -> !i32

Executa cmd pelo shell do SO (cmd /c no Windows, /bin/sh -c nos demais). Retorna Ok(exit_code) (0 = sucesso) ou Err apenas se o próprio shell falhou ao ser iniciado. Os streams de saída são herdados; capturar a saída requer redirecionamento a nível de shell dentro de cmd.

import stdlib::os::*;

fn main() -> i32 {
    let r: !i32 = os_shell("exit 3");
    if r.ok {
        if r.val == 0 { println!("command succeeded"); }
        else          { println!("command exited non-zero:", r.val); }
    } else {
        println!("shell did not spawn:", r.err);
    }
    return 0;
}

Veja também

  • stdlib::env — argumentos de linha de comando, get/set de variáveis de ambiente e exit.
  • stdlib::fs — leitura e escrita de arquivos e criação dos diretórios de app resolvidos aqui.
  • stdlib::io — os handles de stdin/stdout/stderr controlados por os_is_tty.
  • stdlib::process — o construtor completo Command do qual os_shell é um atalho.