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
- Zig instalado (versão 0.13+). Veja o guia de instalação
- Conhecimento básico de Zig. Consulte a introdução ao Zig
- Sistema operacional POSIX (Linux/macOS)
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
Handlers devem ser simples: Apenas defina flags atômicas no handler. Faça o trabalho pesado no loop principal.
Use
std.atomic.Value(bool): Para comunicação segura entre handler e código principal.Ignore SIGPIPE em servidores: Previne que o programa termine ao escrever em conexões fechadas.
Shutdown gracioso: Ao receber SIGTERM, finalize conexões abertas, salve estado e libere recursos.
Não use alocação em handlers: Funções de sinal não devem chamar funções que alocam memória.
Receitas Relacionadas
- Criar e Gerenciar Threads - Threads em Zig
- TCP Server - Servidor de rede
- Time e Timestamps - Tempo
- Informações do Sistema - Info do OS