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

03 - Sistema de Arquivos (VFS, sistemas de arquivos)

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

1. A Ontologia do VFS: A Abstração Universal

A máxima fundacional do sistema operacional Unix dita que "tudo é um arquivo". No entanto, a implementação física de sistemas de arquivos é caótica e divergente: um disco ext4 possui bitmaps e journaling, uma partição FAT32 utiliza tabelas de alocação primitivas, e um socket de rede sequer possui representação magnética. O Kernel Linux consolida esse caos através de uma camada de abstração colossal chamada VFS (Virtual File System ou Virtual Filesystem Switch).

O VFS é, em sua essência arquitetural, um framework orientado a objetos escrito em linguagem C puro. Ele atua como o grande roteador do kernel, interceptando as chamadas de sistema padronizadas pelo POSIX originadas no espaço do usuário (como open(), read(), write(), close()) e redirecionando-as dinamicamente para o código específico do sistema de arquivos subjacente. Para o processo em Ring 3, ler um dispositivo de bloco criptografado, um arquivo temporário em RAM ou o terminal TTY exige rigorosamente as mesmas instruções, graças à blindagem provida pelo VFS.

2. O Quadrilátero Estrutural de Objetos VFS

O framework do VFS materializa a ilusão de um sistema unificado orquestrando quatro estruturas de dados fundamentais, que juntos formam o metamodelo de qualquer sistema de arquivos no Linux:

I. O Objeto Superbloco (struct super_block)

Representa um sistema de arquivos físico (ou virtual) isolado que foi montado no kernel. O superbloco armazena os metadados macroscópicos do volume: o tamanho dos blocos lógicos (geralmente 4KB), o limite máximo de arquivos suportados, os flags de montagem (como MS_RDONLY para read-only) e o ponteiro para o dentry raiz daquela partição. É a matriz que define as regras do volume.

II. O Objeto Inode (struct inode)

O Index Node é a abstração central do VFS. Um inode representa um arquivo, não o seu nome. Ele contém as coordenadas físicas no disco de onde os blocos de dados reais residem. Seus campos rastreiam o tamanho exato do arquivo em bytes, o ID do proprietário (UID/GID), as permissões de acesso (bits de modo) e os três relógios fundamentais do POSIX: atime (último acesso), mtime (última modificação dos dados) e ctime (última modificação do próprio inode). Dois nomes diferentes apontando para o mesmo inode formam um Hard Link.

III. O Objeto Dentry (struct dentry)

O Directory Entry é o objeto topológico do VFS. Enquanto o inode mapeia metadados, o dentry mapeia hierarquia. Ele resolve nomes de caminhos de texto (/usr/bin/bash) para seus inodes correspondentes. Cada componente do caminho é um dentry distinto (existe um dentry para usr, outro para bin, e outro para bash). Os dentries mantêm ponteiros para o dentry pai e uma lista de filhos, construindo dinamicamente a árvore VFS na memória RAM.

IV. O Objeto File (struct file)

Representa o contexto efêmero de um arquivo aberto por um processo específico. Quando um processo invoca open(), o kernel aloca um struct file. Ele armazena as flags de abertura (O_RDONLY, O_APPEND), o modo de acesso e, criticamente, o f_pos (File Position), o cursor interno de leitura/escrita (offset). Múltiplos processos podem possuir múltiplos objetos file diferentes (com offsets distintos) apontando topologicamente para o mesmo dentry e inode subjacentes.

3. Polimorfismo no Ring 0: Tabelas de Operações

Para suportar múltiplos sistemas de arquivos sem o uso de longos e ineficientes blocos switch/case, o VFS implementa polimorfismo através de tabelas de ponteiros para funções.

Dentro de cada um dos quatro objetos principais do VFS, existe um ponteiro para uma estrutura de operações. A mais crítica é a struct file_operations (frequentemente instanciada como f_op). Esta estrutura contém ponteiros de função para cada ação que pode ser executada em um arquivo.

Por exemplo, quando o VFS monta uma partição ext4, ele preenche o f_op do inode com os ponteiros das rotinas do próprio ext4:

C

struct file_operations ext4_file_operations = {
    .read_iter  = ext4_file_read_iter,
    .write_iter = ext4_file_write_iter,
    .mmap       = ext4_file_mmap,
    .fsync      = ext4_sync_file,
};

Quando o processo emite a syscall write(), o núcleo do VFS apenas extrai o descritor de arquivo, acessa o objeto file e invoca passivamente file->f_op->write_iter(). O kernel ignora completamente a complexidade magnética ou eletrônica subjacente; a delegação é absoluta e o isolamento arquitetural é garantido.

4. O Caminho de Resolução: Path Lookup e RCU-Walk

Converter uma string de espaço do usuário, como /etc/systemd/system.conf, no inode físico correspondente é uma das operações computacionalmente mais custosas do kernel (implementada primariamente em fs/namei.c). Este processo, conhecido como Pathname Resolution, exige ler diretórios no disco, verificar permissões, desviar-se de symlinks (evitando loops infinitos através da constante MAXSYMLINKS) e gerenciar pontos de montagem cruzados.

Para evitar que o disco rígido seja penalizado em cada open(), o VFS utiliza um cache de topologia agressivo: o Dcache (Dentry Cache).

Quando um caminho é resolvido com sucesso, o VFS aloca a árvore de dentries correspondente na memória RAM.

Para garantir performance em sistemas massivamente paralelos, a resolução de caminhos adota a tecnologia RCU (Read-Copy-Update). O algoritmo realiza o chamado RCU-walk, permitindo que múltiplas threads percorram a árvore de dentries no Dcache simultaneamente, de forma totalmente lockless (sem travas ou spinlocks). Somente se o componente não estiver no cache (um cache miss), o kernel transita para o modo mais lento, o REF-walk, adquirindo semáforos, incrementando contadores de referência e emitindo operações de disco reais para ler o inode.

5. Integração com a Memória: address_space e o Page Cache

O VFS não trabalha de forma isolada do Gerenciamento de Memória (MM). Ler diretamente blocos magnéticos do disco para cada instrução I/O destruiria a taxa de transferência do sistema. A ponte entre o VFS e o subsistema de memória virtual é o objeto struct address_space.

Apesar do nome enganoso, o address_space não representa o espaço virtual de um processo; ele representa a memória física em RAM que atua como cache dos dados de um Inode no disco. Ele governa o Page Cache.

Quando o VFS recebe uma solicitação de read(), ele não interage com o driver de bloco imediatamente. Ele consulta a estrutura address_space do inode:

  1. Se a página de dados solicitada já existir na RAM (Page Cache Hit), o VFS copia os bytes da página diretamente para o buffer do espaço do usuário. Nenhuma I/O de disco é gerada.

  2. Se houver falha de cache (Page Fault para páginas file-backed), o VFS invoca a função readpage() ou readahead() do sistema de arquivos subjacente (novamente via polimorfismo). O FS carrega o bloco do disco para a RAM e, em seguida, o VFS o serve ao usuário.

Isso significa que, no Linux, toda leitura regular é uma leitura em cache, e toda gravação é assíncrona. Os dados de um write() são marcados como dirty (sujos) no Page Cache e, posteriormente, as kernel threads de writeback (como as flusher threads) executam a sincronização magnética ou em NAND definitiva, utilizando ordenação de barreiras de bloco (block barriers) para garantir transações atômicas no journaling (como o JBD2 do ext4) e evitar corrupção em falhas elétricas.

6. O Ponto de Montagem (Mount Namespace)

No ecossistema Linux, não existem partições representadas por letras (C:\, D:\). A topologia é unificada em uma única raiz global (/).

Para atrelar um sistema de arquivos recém-descoberto a essa árvore mestre, o VFS invoca a operação de montagem, controlada pela estrutura struct vfsmount.

Um mount point ocorre quando o VFS atrela o super_block de um novo sistema de arquivos a um dentry preexistente de outro sistema de arquivos. Uma vez montado, o dentry original é "sombreado" (shadowed): qualquer lookup naquele diretório é desviado pelo VFS diretamente para o dentry raiz do novo superbloco.

Com o advento dos contêineres, o VFS expandiu essa topologia com os Mount Namespaces. Em vez de existir um único vetor global de montagens, o kernel mantém múltiplas instâncias separadas de árvores de vfsmount. Um processo em um Namespace isolado pode montar arquivos de disco ou criar pseudo-arquivos em /tmp sem que esses pontos de montagem afetem a árvore raiz visualizada pelos processos do sistema hospedeiro principal.

7. Pseudo-Sistemas de Arquivos Virtuais

A flexibilidade do VFS atinge seu apogeu nos Pseudo-Sistemas de Arquivos, estruturas que emulam perfeitamente o comportamento I/O do POSIX, mas que não possuem nenhum dispositivo de bloco por trás. Eles existem puramente em RAM e geram inodes dinamicamente.

  • procfs (/proc): Originalmente desenhado para expor o estado dos processos, evoluiu para uma interface de parâmetros globais. Quando você executa cat /proc/meminfo, não há um arquivo em disco sendo lido. O VFS intercepta a chamada de leitura, invoca uma função no Gerenciamento de Memória que calcula o uso de RAM instantâneo, formata a saída como uma string legível e a devolve ao espaço do usuário.

  • sysfs (/sys): A árvore topológica do hardware. Os inodes e diretórios gerados no sysfs possuem um mapeamento de um-para-um perfeito com estruturas de dados kobject internas do kernel. Quando o udev lê os atributos lógicos de um bus PCI-Express, ele está essencialmente lendo os valores dos ponteiros em Ring 0 serializados pelo sysfs.

  • tmpfs / devtmpfs: Sistemas baseados totalmente no Page Cache e área de Swap. Eles dispensam as complexidades de journaling ou mapeamento de blocos e operam alocando e liberando páginas diretamente através das zonas de memória da MMU, fornecendo armazenamento efêmero operando na velocidade absoluta da RAM (utilizado para /run e criação sob demanda dos nós de dispositivos em /dev).