Home Comunidade Eventos YouTube Networks Desafios // produtos Academy News CTI | CVE Contato

# 02 - SysVinit (System V Initialization)

video explicativo
Video explicativoEspaco reservado para o video desta anotação — adicione video: https://youtu.be/... no frontmatter da nota.

A arquitetura do SysVinit (System V Initialization) representou, por mais de duas décadas, o padrão de facto para a orquestração do espaço de usuário em sistemas operacionais baseados em Linux e Unix. Herdado do UNIX System V Release 4 (SVR4) da AT&T, o SysVinit foi concebido para resolver as limitações estruturais de seu predecessor, o BSD init (que dependia de um único e monolítico script /etc/rc para inicializar a máquina). A revolução introduzida pelo SysVinit foi a modularidade stateful, permitindo que o administrador definisse perfis de execução excludentes (os Runlevels) e delegasse a inicialização de subsistemas a scripts independentes gerenciados de forma centralizada.

Do ponto de vista da engenharia de software, o SysVinit opera sob um paradigma estritamente imperativo e sequencial. Diferente de sistemas modernos orientados a grafos de dependências (como o systemd), o SysVinit não compreende o que um serviço "precisa" para rodar; ele apenas executa instruções shell em uma ordem lexicográfica rígida, presumindo que a estabilidade do sistema emergirá do sequenciamento correto projetado pelo administrador.


1. O Motor de Estados: A Arquitetura do /sbin/init

O binário principal do SysVinit, localizado em /sbin/init, é estruturalmente um programa em C muito simples. Sua responsabilidade não é iniciar serviços de rede ou montar discos diretamente, mas atuar como uma máquina de estados finitos.

Ao ser invocado pelo kernel ao final do boot (assumindo o PID 1), o init executa as seguintes tarefas atômicas:

  1. Apropria-se do papel de "Ceifador" (Reaper) de processos zumbis, estabelecendo um loop baseado nas syscalls da família wait().
  2. Lê compulsivamente seu arquivo de configuração mestre: /etc/inittab.
  3. Determina o estado desejado (default runlevel).
  4. Executa os processos ou scripts mapeados para aquele estado.
  5. Entra em estado de dormência (bloqueado em select() ou ouvindo sinais POSIX e o socket /dev/initctl), aguardando que o administrador solicite uma mudança de estado (transição de runlevel) através do comando telinit.

2. A Anatomia Crítica do /etc/inittab

O arquivo /etc/inittab é o coração arquitetural do SysVinit. Ele não é um script executável, mas uma tabela de diretivas de roteamento de processos. O parser do init analisa este arquivo linha por linha, ignorando comentários (#), buscando um formato posicional estrito delimitado por dois pontos (:).

A sintaxe canônica de uma linha no inittab é:
id:runlevels:action:process

Detalhamento dos Campos

  • id: Um identificador único de 1 a 4 caracteres alfanuméricos. Serve como chave primária para o processo na tabela interna de rastreamento do init.
  • runlevels: Uma string contendo os números (ou letras, como S para Single-User) correspondentes aos estados em que esta linha deve ser ativada. Se vazio, presume-se que a ação se aplica a todos os estados.
  • action: A palavra-chave que dita a política de controle do processo. Esta é a lógica de controle mais sofisticada do SysVinit:
  • sysinit: O processo é executado uma única vez durante o boot, antes de qualquer outra diretiva. Usualmente aponta para o script mestre /etc/rc.d/rc.sysinit, responsável por montar o /proc, /sys, ativar o swap e realizar o fsck na raiz. O init bloqueia sua própria execução até que este processo retorne.
  • boot / bootwait: Executados imediatamente após o sysinit. A variante wait força o init a pausar a leitura do inittab até o término do comando.
  • initdefault: Uma diretiva especial que não possui campo process. Ela instrui o init sobre qual runlevel deve ser assumido ao final do processo de boot (ex: id:3:initdefault:).
  • wait: Executado quando o sistema entra no runlevel especificado. O init invoca o processo e aguarda (espera síncrona) que ele termine antes de processar a próxima linha do inittab. É esta ação que invoca o despachante /etc/rc.d/rc.
  • respawn: A ação mais resiliente. O init cria um fork para rodar o processo, mas não aguarda seu término. Em vez disso, monitora o PID filho. Se o processo morrer, o init o reinicia imediatamente. Historicamente, é usado para manter os processos getty / mingetty vivos nos Terminais Virtuais (tty1 a tty6), garantindo que o prompt de login sempre retorne após um logout.
  • ctrlaltdel: Intercepta o sinal de interrupção de hardware (SIGINT) gerado pela combinação de teclas Ctrl+Alt+Del no console físico, tipicamente invocando /sbin/shutdown -t3 -r now.

  • process: O caminho absoluto do binário ou script shell a ser executado, juntamente com seus argumentos.


A transição de fato entre os serviços do sistema é delegada à diretiva wait do inittab, que tradicionalmente possui linhas similares a esta:
l3:3:wait:/etc/rc.d/rc 3

O script rc (Run Command) atua como o orquestrador do espaço de usuário. O SysVinit puro não sabe o que é um banco de dados ou um servidor web; ele apenas delega ao rc a instrução: "Transite o sistema para o Estado 3".

O Sistema de Diretórios rcX.d

Para implementar a modularidade SVR4, os scripts reais de inicialização de cada serviço (daemons) residem em um repositório centralizado, usualmente /etc/init.d/ ou /etc/rc.d/init.d/. Estes scripts são escritos em Bash ou sh, e devem obrigatoriamente aceitar, no mínimo, os argumentos start e stop.

Para cada runlevel (0 a 6), existe um diretório correspondente, como /etc/rc3.d/ (para o Runlevel 3). O conteúdo destes diretórios não são os scripts em si, mas links simbólicos (symlinks) apontando para o repositório central. A magia da ordem de execução reside na Nomenclatura Posicional desses symlinks, que segue o padrão:
[K ou S][00-99][Nome_do_Servico]

  • A Fase Kill (K): Quando o script rc é chamado para transitar para um novo estado, a primeira coisa que ele faz é listar todos os links no diretório alvo que começam com a letra K (de Kill), ordená-los alfabeticamente e executá-los passando o argumento stop. Isso garante que serviços não desejados no novo estado sejam terminados ordenadamente. Exemplo: K15httpd stop, seguido de K50netfs stop.
  • A Fase Start (S): Após desligar o desnecessário, o rc lista os links que começam com S (de Start), também ordenando alfabeticamente, e os executa com o argumento start. Exemplo: S10network start, seguido de S22sshd start e S99local start.

A ausência de grafos de dependência significa que a carga lógica da inicialização recai integralmente sobre os ombros do administrador e dos empacotadores da distribuição, que precisam garantir manualmente que o symlink do sshd possua um número (ex: 22) estritamente maior que o número do symlink de configuração da placa de rede (ex: 10), garantindo que a ordem lexicográfica respeite a dependência física de execução.


4. O Problema da Daemonização e a Perda de Estado

Para compreender por que o SysVinit foi obsoleto em infraestruturas modernas de alta disponibilidade, é crucial analisar sua maior vulnerabilidade arquitetural: a ignorância de estado decorrente da Daemonização Tradicional Posix.

Quando o script rc invoca um serviço, como /etc/init.d/nginx start, o interpretador shell inicia. O script realiza verificações, define variáveis de ambiente e, eventualmente, chama o binário /usr/sbin/nginx.

Para que o script shell possa terminar (e retornar o controle ao script rc para que ele continue inicializando o próximo serviço), o nginx não pode bloquear o terminal. Ele precisa rodar em background. O padrão de desenvolvimento C para daemons (System V Daemonization) exige que o processo execute a técnica de Double-Fork:

  1. O processo nginx faz um fork(). O pai sai (exit(0)). O shell entende que o comando "terminou".
  2. O filho chama setsid() para se desvincular do terminal controlador (TTY).
  3. O filho faz um segundo fork(). O filho sai.
  4. O neto (o processo real do daemon operante) continua rodando infinitamente em background, reparentado automaticamente pelo PID 1.

A Consequência: O processo pai morreu e o script shell que o SysVinit invocou retornou código 0 (Sucesso). A partir desse momento, o SysVinit perde completamente a capacidade de rastrear a linhagem do processo. O PID 1 não sabe qual é o PID real do nginx. Ele é forçado a confiar que o daemon escreverá seu próprio PID em um arquivo de texto (ex: /var/run/nginx.pid).

Se o processo neto (o daemon) sofrer um crash (Segmentation Fault), o arquivo PID ficará obsoleto (stale). O SysVinit não notará que o serviço caiu, e não há mecanismo nativo de reinicialização automática (como o Restart=always do systemd), exigindo ferramentas auxiliares monstruosas como monit ou supervisord operando no espaço de usuário via polling constante.


5. O Gargalo do Bloqueio Síncrono (I/O Blocking)

A segunda limitação fatal do paradigma SVR4 é a sincronia serializada. O script mestre rc executa as tarefas sequencialmente em uma única thread.

Considere a inicialização do link S10network. Se a placa de rede tentar obter um endereço IP via DHCP e o servidor DHCP da empresa estiver inativo ou lento, o script /etc/init.d/network ficará bloqueado, aguardando um timeout que pode durar 60 segundos.
Durante este minuto inteiro, a CPU da máquina permanece virtualmente em 0% de uso. Todo o processo de boot está paralisado. O servidor SSH (S22sshd), que não tem nenhuma dependência real de um endereço IP externo para iniciar a escuta no socket Unix local, é forçado a aguardar a falha do script anterior. Esta barreira de sincronização linear impede a utilização do paralelismo de processadores multicore modernos, resultando em tempos de inicialização inaceitavelmente longos para ambientes em nuvem (onde o provisioning de instâncias precisa ocorrer em milissegundos).