---
title: "Processos e Signals em Zig: Fork, Exec, IPC e Tratamento de Sinais"
url: "https://ziglang.com.br/tutoriais/zig-processos-signals/"
markdown_url: "https://ziglang.com.br/tutoriais/zig-processos-signals.MD"
description: "Aprenda a criar e gerenciar processos em Zig. Fork, exec, pipes, comunicacao entre processos (IPC), tratamento de signals como SIGINT e SIGTERM. Tutorial completo em portugues."
date: "2026-02-21"
author: ""
---

# Processos e Signals em Zig: Fork, Exec, IPC e Tratamento de Sinais

Aprenda a criar e gerenciar processos em Zig. Fork, exec, pipes, comunicacao entre processos (IPC), tratamento de signals como SIGINT e SIGTERM. Tutorial completo em portugues.


Processos sao a unidade fundamental de execucao em sistemas Unix/Linux. Entender como cria-los, gerencia-los e coordena-los e essencial para qualquer programador de sistemas. Neste terceiro artigo da serie, exploramos como Zig facilita o trabalho com processos, pipes e signals, trazendo seguranca e clareza para operacoes que em C seriam complexas e propensas a erros.

> Pre-requisito: Leia os artigos anteriores sobre [syscalls](/tutoriais/zig-sistemas-operacionais/artigo-1-syscalls-linux/) e [file system](/tutoriais/zig-sistemas-operacionais/artigo-2-file-system-operations/).

## Executando Processos Filhos

A forma mais comum e segura de executar processos externos em Zig e atraves de `std.process.Child`. Esta API abstrai as complexidades de fork/exec e gerenciamento de pipes.

### Execucao Simples de Comandos

```zig
const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // Executar um comando e capturar a saida
    const resultado = try std.process.Child.run(.{
        .allocator = allocator,
        .argv = &.{ "uname", "-a" },
    });
    defer {
        allocator.free(resultado.stdout);
        allocator.free(resultado.stderr);
    }

    std.debug.print("Saida: {s}\n", .{resultado.stdout});

    if (resultado.stderr.len > 0) {
        std.debug.print("Erros: {s}\n", .{resultado.stderr});
    }

    std.debug.print("Codigo de saida: {}\n", .{resultado.term});
}
```

### Processo com Stdin Interativo

```zig
const std = @import("std");

pub fn main() !void {
    var child = std.process.Child.init(.{
        .argv = &.{ "cat", "-n" },
        .stdin_behavior = .pipe,
        .stdout_behavior = .pipe,
    }, std.heap.page_allocator);

    try child.spawn();

    // Escrever no stdin do processo filho
    if (child.stdin) |stdin| {
        try stdin.writeAll("Linha 1\n");
        try stdin.writeAll("Linha 2\n");
        try stdin.writeAll("Linha 3\n");
        stdin.close();
        child.stdin = null;
    }

    // Aguardar o processo terminar e capturar saida
    const resultado = try child.wait();
    std.debug.print("Processo terminou com: {}\n", .{resultado});
}
```

### Pipeline de Processos

Podemos encadear processos como pipes no shell (equivalente a `ls -la | grep zig | wc -l`):

```zig
const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // Primeiro comando: ls -la
    const ls_result = try std.process.Child.run(.{
        .allocator = allocator,
        .argv = &.{ "ls", "-la" },
    });
    defer allocator.free(ls_result.stdout);
    defer allocator.free(ls_result.stderr);

    // Processar a saida em Zig (em vez de chamar grep)
    var linhas_zig: usize = 0;
    var iter = std.mem.splitScalar(u8, ls_result.stdout, '\n');
    while (iter.next()) |linha| {
        if (std.mem.indexOf(u8, linha, "zig") != null) {
            linhas_zig += 1;
            std.debug.print("{s}\n", .{linha});
        }
    }

    std.debug.print("\nTotal de linhas contendo 'zig': {d}\n", .{linhas_zig});
}
```

## Fork e Exec Diretos

Para casos que exigem controle total, Zig permite usar fork e exec diretamente atraves das APIs POSIX.

### Fork Basico

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

pub fn main() !void {
    const pid = try posix.fork();

    if (pid == 0) {
        // Processo filho
        std.debug.print("[Filho PID {d}] Executando tarefa...\n", .{
            std.os.linux.getpid(),
        });
        std.time.sleep(1 * std.time.ns_per_s);
        std.debug.print("[Filho] Tarefa concluida\n", .{});
        posix.exit(0);
    } else {
        // Processo pai
        std.debug.print("[Pai PID {d}] Filho criado com PID {d}\n", .{
            std.os.linux.getpid(),
            pid,
        });

        // Aguardar o filho terminar
        const resultado = posix.waitpid(pid, 0);
        std.debug.print("[Pai] Filho terminou com status: {}\n", .{resultado});
    }
}
```

### Fork com Exec

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

pub fn main() !void {
    const pid = try posix.fork();

    if (pid == 0) {
        // No processo filho, substituir com novo programa
        const err = posix.execvpeZ(
            "/usr/bin/python3",
            &.{ "/usr/bin/python3", "-c", "print('Ola do Python!')" },
            std.c.environ,
        );
        // Se exec retornar, houve erro
        std.debug.print("Erro no exec: {}\n", .{err});
        posix.exit(1);
    } else {
        // Pai aguarda
        _ = posix.waitpid(pid, 0);
        std.debug.print("Processo filho concluiu\n", .{});
    }
}
```

## Comunicacao Entre Processos (IPC)

### Pipes

Pipes sao a forma mais simples de IPC no Unix. Eles criam um canal unidirecional entre dois processos.

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

pub fn main() !void {
    // Criar pipe: [0] = leitura, [1] = escrita
    const pipe_fds = try posix.pipe();

    const pid = try posix.fork();

    if (pid == 0) {
        // Filho: escreve no pipe
        posix.close(pipe_fds[0]); // Fechar lado de leitura

        const mensagem = "Mensagem do filho para o pai!";
        _ = try posix.write(pipe_fds[1], mensagem);
        posix.close(pipe_fds[1]);
        posix.exit(0);
    } else {
        // Pai: le do pipe
        posix.close(pipe_fds[1]); // Fechar lado de escrita

        var buffer: [256]u8 = undefined;
        const bytes_lidos = try posix.read(pipe_fds[0], &buffer);
        posix.close(pipe_fds[0]);

        std.debug.print("Pai recebeu: {s}\n", .{buffer[0..bytes_lidos]});

        _ = posix.waitpid(pid, 0);
    }
}
```

### Variaveis de Ambiente

Variaveis de ambiente sao outra forma de comunicacao (unidirecional, de pai para filho):

```zig
const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // Ler variavel de ambiente
    const home = std.posix.getenv("HOME") orelse "/tmp";
    std.debug.print("HOME: {s}\n", .{home});

    // Executar processo com variaveis de ambiente customizadas
    var env_map = std.process.EnvMap.init(allocator);
    defer env_map.deinit();

    try env_map.put("MEU_APP_CONFIG", "/etc/meuapp.conf");
    try env_map.put("MEU_APP_DEBUG", "true");
    try env_map.put("PATH", std.posix.getenv("PATH") orelse "/usr/bin");

    const resultado = try std.process.Child.run(.{
        .allocator = allocator,
        .argv = &.{ "env" },
        .env_map = &env_map,
    });
    defer allocator.free(resultado.stdout);
    defer allocator.free(resultado.stderr);

    std.debug.print("Ambiente do filho:\n{s}\n", .{resultado.stdout});
}
```

## Tratamento de Signals

Signals sao notificacoes assincronas enviadas a processos pelo kernel ou por outros processos. Os mais comuns sao:

| Signal | Numero | Descricao |
|--------|--------|-----------|
| SIGINT | 2 | Ctrl+C no terminal |
| SIGTERM | 15 | Pedido de terminacao |
| SIGKILL | 9 | Terminacao forcada (nao pode ser tratado) |
| SIGHUP | 1 | Terminal desconectado |
| SIGUSR1 | 10 | Sinal definido pelo usuario |
| SIGCHLD | 17 | Processo filho terminou |

### Configurando um Signal Handler

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

var executando: bool = true;

fn handleSignal(sig: c_int) callconv(.C) void {
    if (sig == posix.SIG.INT) {
        std.debug.print("\nRecebido SIGINT (Ctrl+C). Encerrando graciosamente...\n", .{});
        executando = false;
    } else if (sig == posix.SIG.TERM) {
        std.debug.print("\nRecebido SIGTERM. Encerrando...\n", .{});
        executando = false;
    }
}

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

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

    std.debug.print("Programa rodando. Pressione Ctrl+C para encerrar.\n", .{});
    std.debug.print("PID: {d}\n", .{std.os.linux.getpid()});

    // Loop principal
    while (executando) {
        std.debug.print(".", .{});
        std.time.sleep(500 * std.time.ns_per_ms);
    }

    // Limpeza graceful
    std.debug.print("Limpeza concluida. Ate logo!\n", .{});
}
```

### Shutdown Gracioso

Um padrao muito comum em servidores e daemons:

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

const Estado = struct {
    rodando: std.atomic.Value(bool),
    conexoes_ativas: std.atomic.Value(u32),
};

var estado_global = Estado{
    .rodando = std.atomic.Value(bool).init(true),
    .conexoes_ativas = std.atomic.Value(u32).init(0),
};

fn signalHandler(_: c_int) callconv(.C) void {
    estado_global.rodando.store(false, .release);
}

pub fn main() !void {
    // Instalar handlers
    const act = posix.Sigaction{
        .handler = .{ .handler = signalHandler },
        .mask = posix.empty_sigset,
        .flags = 0,
    };
    try posix.sigaction(posix.SIG.INT, &act, null);
    try posix.sigaction(posix.SIG.TERM, &act, null);

    std.debug.print("Servidor iniciado. PID: {d}\n", .{std.os.linux.getpid()});

    while (estado_global.rodando.load(.acquire)) {
        // Simular processamento
        _ = estado_global.conexoes_ativas.fetchAdd(1, .monotonic);
        std.time.sleep(100 * std.time.ns_per_ms);
        _ = estado_global.conexoes_ativas.fetchSub(1, .monotonic);
    }

    // Aguardar conexoes ativas terminarem
    std.debug.print("Aguardando conexoes ativas...\n", .{});
    while (estado_global.conexoes_ativas.load(.acquire) > 0) {
        std.time.sleep(100 * std.time.ns_per_ms);
    }

    std.debug.print("Shutdown completo.\n", .{});
}
```

## Evitando Processos Zumbis

Um processo zumbi e um processo filho que terminou mas cujo pai ainda nao chamou `wait()`. Eles consomem entradas na tabela de processos do kernel.

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

fn ignorarSIGCHLD() !void {
    const act = posix.Sigaction{
        .handler = .{ .handler = posix.SIG.DFL },
        .mask = posix.empty_sigset,
        .flags = posix.SA.NOCLDWAIT, // Evita zumbis automaticamente
    };
    try posix.sigaction(posix.SIG.CHLD, &act, null);
}

pub fn main() !void {
    try ignorarSIGCHLD();

    // Agora podemos criar filhos sem preocupacao com zumbis
    var i: usize = 0;
    while (i < 5) : (i += 1) {
        const pid = try posix.fork();
        if (pid == 0) {
            // Filho executa e termina
            std.debug.print("Filho {d} executando\n", .{i});
            posix.exit(0);
        }
    }

    std.debug.print("Pai: criou 5 filhos, nenhum sera zumbi\n", .{});
    std.time.sleep(2 * std.time.ns_per_s);
}
```

## Exercicios

1. **Gerenciador de tarefas:** Crie um programa que execute multiplos comandos em paralelo (como processos filhos) e exiba o status de cada um ao terminar.

2. **Daemon simples:** Implemente um daemon que rode em background, escreva logs periodicamente e responda a SIGUSR1 recarregando configuracao.

3. **Watchdog:** Crie um processo pai que monitore um processo filho e o reinicie automaticamente se ele falhar.

---

## Proximo Artigo

No proximo artigo, mergulhamos em [networking com sockets raw](/tutoriais/zig-sistemas-operacionais/artigo-4-networking-sockets-raw/), construindo servidores TCP, trabalhando com protocolos customizados e explorando sockets raw.

### Conteudo Relacionado

- [Artigo anterior: File System Operations](/tutoriais/zig-sistemas-operacionais/artigo-2-file-system-operations/)
- [Concorrencia em Zig](/tutoriais/concorrencia-em-zig/) — Threads e paralelismo
- [Servidor HTTP em Zig](/tutoriais/zig-http-server/) — Aplicacao pratica de processos e I/O
- [Zig Debugging](/tutoriais/zig-debugging/) — Depuracao de programas

---

*Duvidas sobre processos e signals em Zig? Junte-se a comunidade Zig Brasil!*
