Programação de Redes Avançada: Processos, I/O Multiplexing e Sincronização

Gerenciamento de Procsesos em Redes

A criação de novos processos é um pilar fundamental para servidores que precisam lidar com múltiplas conexões simultâneas. No ambiente Unix-like, a função principle para essa tarefa é a fork().

#include <unistd.h>

// Retorna 0 no processo filho e o PID do filho no processo pai
// Retorna -1 em caso de erro
pid_t criar_processo = fork();

Tratamento de Processos Zumbis

Um processo entra no estado "zumbi" quando encerra sua execução (via exit() ou return no main), mas seu status de saída ainda não foi coletado pelo processo pai. Para evitar o consumo desnecessário de recursos do sistema, o pai deve capturar esse status.

Aguardando o Término com wait

A função wait bloqueia a execução do pai até que um dos filhos termine.

#include <sys/wait.h>

int status_saida;
pid_t id_filho = wait(&status_saida);

if (WIFEXITED(status_saida)) {
    int codigo = WEXITSTATUS(status_saida);
    // Processamento do código de retorno
}

Uso do waitpid para Não Bloqueio

Para evitar que o pai fique parado, utiliza-se waitpid com a opção WNOHANG.

#include <sys/wait.h>

int info;
// -1 indica espera por qualquer processo filho
pid_t id = waitpid(-1, &info, WNOHANG);

Manipulação de Sinais

Sinais são interrupções de software que permitem ao sistema notificar processos sobre eventos específicos, como o término de um filho ou um alarme de tempo.

A Estrutura sigaction

Embora a função signal() seja simples, a sigaction() é recomendada pela sua robustez e portabilidade.

#include <signal.h>

struct sigaction config;
config.sa_handler = tratador_de_evento; // Função de callback
sigemptyset(&config.sa_mask);
config.sa_flags = 0;

sigaction(SIGCHLD, &config, NULL); // Notifica quando um processo filho para ou termina

O uso de alarm(segundos) agenda um sinal SIGALRM para o futuro, sendo útil para implementar timeouts em operações de rede.

Servidores Simultâneos e Duplicação de Sockets

Ao executar um fork(), o processo filho herda os descritores de arquivo (incluindo sockets) do pai. É crucial gerenciar esses recursos:

  1. O Pai fecha o socket do cliente após o fork, pois o filho cuidará da comunicação.
  2. O Filho fecha o socket de escuta (server socket), pois sua função é apenas atender o cliente específico.

O socket só será efetivamente destruído quando todos os descritores associados a ele em todos os processos forem fechados.

Comunicação entre Processos (IPC) via Pipes

Pipes permitem o fluxo de dados entre processos relacionados. Um pipe possui uma extremidade de leitura e uma de escrita.

#include <unistd.h>

int canais[2];
if (pipe(canais) == 0) {
    // canais[0] -> Leitura
    // canais[1] -> Escrita
}

Multiplexação de I/O com select

A multiplexação permite que um único processo monitore vários descritores de arquivo simultaneamente, reagindo apenas quando os dados estão prontos.

#include <sys/select.h>
#include <sys/time.h>

/*
    maxfd: Maior valor de descritor + 1
    readset: Conjunto de sockets para monitorar leitura
    timeout: Tempo máximo de espera
*/
int resultado = select(maxfd, &readset, NULL, NULL, &timeout);

Operações essenciais para fd_set:

  • FD_ZERO(&set): Limpa o conjunto.
  • FD_SET(fd, &set): Adiciona um descritor.
  • FD_ISSET(fd, &set): Verifica se houve evento no descritor após o select.

Funções de I/O Especializadas

Sinalizadores de send e recv

O comportamento padrão de envio e recebimento pode ser alterado através de flags:

Flag Descrição
MSG_OOB Envia ou recebe dados ugrentes (Out-of-band).
MSG_PEEK Lê os dados do buffer sem removê-los.
MSG_DONTWAIT Torna a operação não bloqueante.

I/O Vetorizado (readv e writev)

Permite ler ou escrever dados de/para múltiplos buffers em uma única chamada de sistema, aumentando a eficiência.

#include <sys/uio.h>

struct iovec segmentos[2];
char cabecalho[] = "Header: ";
char corpo[] = "Dados do payload";

segmentos[0].iov_base = cabecalho;
segmentos[0].iov_len = 8;
segmentos[1].iov_base = corpo;
segmentos[1].iov_len = 16;

writev(socket_fd, segmentos, 2);

Tags: C network-programming Multiprocessing linux-kernel Socket-Programming

Publicado em 6-23 21:37