03 - Unidades (Unit Files)
video: https://youtu.be/... no frontmatter da nota.1. A Ontologia das Unidades (Units)
No paradigma do systemd, uma Unidade (Unit) não é meramente um arquivo de texto; ela é a abstração fundamental de um recurso computacional. Quando o PID 1 lê um arquivo .service ou .socket, ele atua como um compilador Just-In-Time de configurações: o arquivo INI é parseado e convertido em um objeto estruturado (uma struct em C) residente na memória do processo systemd.
A ruptura arquitetural aqui é a eliminação do estado imperativo. O arquivo de unidade não instrui o systemd sobre como iniciar um processo (como os antigos case "$1" in start)), mas declara qual deve ser o estado final daquele recurso e quais as premissas para que ele exista. O motor interno do systemd em Ring 3 — que possui rastreamento atrelado aos cgroups do kernel e ao namespace de processos — calcula a delta entre o estado atual e o estado declarado, executando as chamadas de sistema (fork, execve, setuid, unshare) necessárias para materializar esse estado de forma idempotente.
2. A Taxonomia dos Tipos de Unidades
A modularidade do systemd é evidenciada pela sua tipagem. O sistema não orquestra apenas "serviços" em background, mas unifica quase todas as primitivas do sistema operacional sob a mesma interface D-Bus e o mesmo analisador sintático. Existem 11 tipos nativos de unidades, diferenciadas pelo seu sufixo, cada qual possuindo semânticas e lógicas de transição de estado específicas:
.service(Serviços): O tipo mais prevalente. Encapsula o gerenciamento do ciclo de vida de processos (daemons). Ele define como o binário deve ser invocado, que privilégios deve ter, e qual o mecanismo de rastreamento de seu término (via cgroups, D-Bus ou notificação nativa)..socket(Sockets): Abstrai primitivas de IPC (Inter-Process Communication) e portas de rede (TCP/UDP, Sockets UNIX, Netlink, FIFOs). Osystemdinvocabind()elisten()baseando-se nestes arquivos independentemente do daemon consumidor estar vivo, viabilizando a Ativação por Socket (Socket Activation)..target(Alvos): Unidades puramente lógicas e vazias, destituídas de binários ou configuração de execução. Atuam como nós de agrupamento e sincronização no Grafo Acíclico Dirigido (DAG). Assemelham-se conceitualmente aos antigos Runlevels, mas com granularidade ilimitada..device(Dispositivos): Estas unidades raramente são escritas manualmente; elas são geradas dinamicamente na RAM. Quando o daemonsystemd-udevddetecta um evento de hardware no Ring 0 (ex: conexão de um SSD SATA), ele emite um evento que o PID 1 traduz para uma unidade.device. Isso permite que um.servicedeclare dependência direta à presença de um hardware físico (Requires=dev-sdb1.device)..mount(Pontos de Montagem): Uma representação declarativa da syscallmount(). Na verdade, osystemdconverte automaticamente o legado/etc/fstabem unidades.mountem tempo de boot (via um gerador em/usr/lib/systemd/system-generators/), trazendo o subsistema de arquivos para dentro do mesmo grafo de dependências da rede e dos daemons..automount(Montagem sob Demanda): Opera acoplado a uma unidade.mount. Em vez de montar o disco no boot, osystemdintercepta acessos ao diretório-alvo através do subsistemaautofsdo kernel. Quando um processo tenta ler o diretório (sofre um page fault de VFS), o kernel pausa o processo, avisa osystemd, que por sua vez invoca a montagem real, e então libera o processo..timer(Temporizadores): A obsolescência programada docron. Acionados por relógios monotônicos (baseados no tempo de uptime) ou realtime (relógio de parede/calendário), eles disparam unidades de mesmo nome (ex:backup.timeracionabackup.service) com precisão de milissegundos e suporte a proteção contra perda de disparos durante hibernação (CatchUp)..swap(Paginação): Gerencia arquivos ou partições de swap, com semântica idêntica às unidades.mount..path(Monitoramento de Arquivos): Utiliza a APIinotifydo kernel Linux. O PID 1 escuta eventos de I/O em um diretório ou arquivo (criação, modificação) e dispara uma unidade.serviceassociada. Ideal para processamento de spools (filas) de arquivos assíncronos..slice(Fatias de Recursos): Representações estritas da hierarquia do Control Groups (v1 e v2). Por padrão, o sistema é dividido emsystem.slice(para daemons),user.slice(para sessões logadas) emachine.slice(para contêineres/VMs). Eles gerenciam as cotas agregadas de CPU e RAM para um subconjunto inteiro da árvore de processos..scope(Escopos): Similar aos slices, mas não são criados por arquivos de texto. São gerados programaticamente via chamadas D-Bus para encapsular processos que não foram iniciados diretamente pelosystemd, mas que osystemdfoi instruído a adotar e gerenciar (por exemplo, sessões de terminal interativas ou processos legados ativados via PAM).
3. O Paradigma Declarativo (Formato INI Analítico)
A estrutura sintática dos Unit Files abandona a linguagem shell em favor de um formato inspirado no padrão INI (originário do MS-DOS e Windows). A escolha deste formato não foi estética, mas uma imposição de segurança e determinismo.
Linguagens Turing-completas (como bash) sofrem do problema da parada e possuem efeitos colaterais imprevisíveis: um script pode travar em um laço infinito, alterar variáveis ambientais globais, ou aguardar I/O de um disco corrompido sem nunca retornar o controle ao orquestrador.
O analisador (parser) do systemd, escrito em C robusto, aplica regras rígidas de ingestão sintática:
- As diretivas são chave-valor estritas (ex:
ExecStart=/usr/bin/nginx -g 'daemon off;'). - Se o
systemdnão reconhecer uma diretiva (ex: uma opção introduzida em uma versão mais recente dosystemdrodando em um servidor antigo), ele não falha a inicialização. O parser simplesmente ignora a linha desconhecida, emite um aviso silencioso no journal e continua. Esta tolerância garante a retrocompatibilidade de Unit Files através de múltiplas gerações de distribuições Linux. - Interpolações de código (como
$(comando)) são rigorosamente proibidas. O PID 1 não abre um subshell, prevenindo categorias inteiras de injeção de comandos (Command Injection) em tempo de boot.
4. Instanciação Dinâmica (Templates)
Para evitar a replicação brutal de configurações, a arquitetura do systemd introduziu o conceito de Unidades de Template. Qualquer arquivo de unidade cujo nome contenha o caractere @ antes da extensão atua como um molde polimórfico.
Considere o arquivo openvpn@.service. Ele não pode ser iniciado diretamente. O administrador deve instanciá-lo passando um argumento, como systemctl start openvpn@client1.service.
Quando invocado, o systemd lê o arquivo de template e resolve os Especificadores Nativos (Specifiers) contidos em seu interior em tempo real.
- O especificador
%i(Instância Pura) é substituído dinamicamente pela string que sucede o@. No caso acima,client1. - A diretiva
ExecStart=/usr/sbin/openvpn --config /etc/openvpn/%i.conftorna-se magicamenteExecStart=/usr/sbin/openvpn --config /etc/openvpn/client1.conf.
Este modelo permite que um único arquivo gerencie centenas de túneis VPN, conexões PPP, ou instâncias de bancos de dados isoladas (multitenancy), apenas pela passagem do parâmetro no nome da unidade. Há dezenas de especificadores avançados: %H (nome do host da máquina), %u (nome do usuário logado), e %m (ID da máquina lido de /etc/machine-id), tornando as unidades universais e independentes de ambiente.
5. O Ciclo de Vida em Memória (Garbage Collection)
Ao contrário dos scripts em disco do SysVinit que são inertes até serem chamados, as unidades do systemd possuem um ciclo de vida em memória rigoroso.
O systemd não carrega todos os milhares de .service disponíveis no disco para a RAM durante o boot, pois isso exauriria recursos. Ele aplica uma heurística de Carregamento Demorado (Lazy Loading) acoplado a um Coletor de Lixo (Garbage Collector).
Uma unidade entra na memória (estado loaded) apenas se:
- For requerida por uma transição em andamento (ex: o sistema está alcançando o
multi-user.target). - Tiver recebido uma requisição manual de manipulação via D-Bus (ex: o administrador digitou
systemctl status). - Tiver falhado anteriormente (estado failed), retendo o objeto em memória para que o administrador possa ler o código de saída através do comando de status.
Assim que uma unidade retorna ao estado inactive e não possui mais nenhuma unidade no Grafo que dependa da sua existência, e não há processos residuais atrelados ao seu cgroup, o algoritmo de Garbage Collection do PID 1 limpa silenciosamente a estrutura da memória. Isso permite que sistemas Linux operem por anos, inicializando e destruindo milhões de sessões de usuário efêmeras (user@1000.service), sem sofrer degradação contínua (memory leak) no gerenciador raiz do sistema operacional.