Como Lidar com Sinais (Signals) do Sistema em Zig

Introdução

Sinais (signals) são notificações assíncronas enviadas pelo sistema operacional a um processo. Os mais comuns são SIGINT (Ctrl+C), SIGTERM (solicitação de término) e SIGHUP (terminal desconectado). Tratar sinais é essencial para servidores, processos de longa duração e aplicações que precisam realizar limpeza antes de encerrar.

Nesta receita, você aprenderá a capturar e tratar sinais em Zig.

Pré-requisitos

Capturar SIGINT (Ctrl+C)

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

var running = std.atomic.Value(bool).init(true);

fn handler(sig: c_int) callconv(.C) void {
    _ = sig;
    std.debug.print("\nSinal recebido! Encerrando...\n", .{});
    running.store(false, .seq_cst);
}

pub fn main() !void {
    // Instalar handler para SIGINT
    const act = posix.Sigaction{
        .handler = .{ .handler = handler },
        .mask = posix.empty_sigset,
        .flags = 0,
    };

    try posix.sigaction(posix.SIG.INT, &act, null);

    std.debug.print("Programa rodando. Pressione Ctrl+C para encerrar.\n", .{});

    // Loop principal
    var contador: u32 = 0;
    while (running.load(.seq_cst)) {
        contador += 1;
        std.debug.print("\rTrabalhando... ciclo {d}", .{contador});
        std.time.sleep(std.time.ns_per_s);
    }

    std.debug.print("Encerramento limpo após {d} ciclos.\n", .{contador});
}

Múltiplos Sinais

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

var shutdown_requested = std.atomic.Value(bool).init(false);
var reload_requested = std.atomic.Value(bool).init(false);

fn sigintHandler(_: c_int) callconv(.C) void {
    shutdown_requested.store(true, .seq_cst);
}

fn sighupHandler(_: c_int) callconv(.C) void {
    reload_requested.store(true, .seq_cst);
}

pub fn main() !void {
    // Handler para SIGINT/SIGTERM -> encerrar
    const shutdown_act = posix.Sigaction{
        .handler = .{ .handler = sigintHandler },
        .mask = posix.empty_sigset,
        .flags = 0,
    };
    try posix.sigaction(posix.SIG.INT, &shutdown_act, null);
    try posix.sigaction(posix.SIG.TERM, &shutdown_act, null);

    // Handler para SIGHUP -> recarregar
    const reload_act = posix.Sigaction{
        .handler = .{ .handler = sighupHandler },
        .mask = posix.empty_sigset,
        .flags = 0,
    };
    try posix.sigaction(posix.SIG.HUP, &reload_act, null);

    std.debug.print("Servidor simulado rodando (PID: {d})\n", .{std.os.linux.getpid()});
    std.debug.print("  SIGINT/SIGTERM -> encerrar\n", .{});
    std.debug.print("  SIGHUP -> recarregar config\n", .{});

    while (!shutdown_requested.load(.seq_cst)) {
        if (reload_requested.load(.seq_cst)) {
            std.debug.print("Recarregando configuração...\n", .{});
            reload_requested.store(false, .seq_cst);
        }

        std.time.sleep(std.time.ns_per_ms * 500);
    }

    std.debug.print("Realizando shutdown gracioso...\n", .{});
    std.debug.print("Recursos liberados. Bye!\n", .{});
}

Ignorar Sinais

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

pub fn main() !void {
    // Ignorar SIGPIPE (comum em programas de rede)
    const ignore_act = posix.Sigaction{
        .handler = .{ .handler = posix.SIG.IGN },
        .mask = posix.empty_sigset,
        .flags = 0,
    };
    try posix.sigaction(posix.SIG.PIPE, &ignore_act, null);

    std.debug.print("SIGPIPE ignorado. Programa seguro para I/O de rede.\n", .{});
    std.debug.print("Operações de escrita em pipes fechados retornarão erro.\n", .{});
}

Dicas e Boas Práticas

  1. Handlers devem ser simples: Apenas defina flags atômicas no handler. Faça o trabalho pesado no loop principal.

  2. Use std.atomic.Value(bool): Para comunicação segura entre handler e código principal.

  3. Ignore SIGPIPE em servidores: Previne que o programa termine ao escrever em conexões fechadas.

  4. Shutdown gracioso: Ao receber SIGTERM, finalize conexões abertas, salve estado e libere recursos.

  5. Não use alocação em handlers: Funções de sinal não devem chamar funções que alocam memória.

Receitas Relacionadas

Tutoriais Relacionados

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.