Sistemas de arquivo em Linux embarcado – Parte 1
- por Sergio Prado
O Linux não é um sistema operacional completo. O Linux é o kernel do sistema operacional, que você pode baixar em http://kernel.org. Mas sem bibliotecas e aplicações você não consegue fazer muita coisa com ele. E estas bibliotecas e aplicações são disponibilizadas através do sistema de arquivo principal, também chamado de rootfs.
Assim que o kernel termina seu processo de inicialização, ele “monta” o rootfs e procura por uma aplicação de inicialização, que será responsável por configurar e inicializar o restante do sistema operacional.
Mas o que significa “montar” o rootfs? Para responder esta pergunta, precisamos antes entender o que é um sistema de arquivo na prática.
Dá uma olhada na listagem do diretório principal “/” na minha máquina de desenvolvimento:
$ ls / bin etc lib opt selinux tftpd vmlinuz boot home lost+found proc srv tmp vmlinuz.old cdrom initrd.img media root sys usr dev initrd.img.old mnt sbin tftpboot var |
Veja que no diretório principal temos um conjunto de arquivos e diretórios, que estão armazenados no HD da minha máquina. Mas como estes arquivos e diretórios são organizados dentro do HD? Quem responde à esta pergunta é o sistema de arquivo usado. No meu caso, estou usando o ext3, que é um tipo de sistema de arquivo bastante utilizado em Linux. É ele que define como as informações de diretórios e arquivos são organizadas e armazenadas no disco.
Portanto, um sistema de arquivo nada mais é do que uma representação dos dados de forma hierárquica, em diretórios e arquivos, dentro de um dispositivo de armazenamento. Este dispositivo de armazenamento pode ser local (HD, pendrive, CD, DVD, memória flash, RAM, etc) ou remoto (protocolo NFS).
“Montar o rootfs” significa acessar este dispositivo de armazenamento, interpretar os dados de diretórios e arquivos e disponibilizar o acesso à estes diretórios e arquivos a partir do diretório “/” do sistema.
E no momento de montar o rootfs, como é que o kernel sabe o tipo de sistema de arquivo usado e em qual dispositivo de armazenamento ele se encontra? Essa informação esta na linha de comandos do kernel, por exemplo:
... root=/dev/sda1 rootfstype=ext3 ... |
A opção “root” indica o dispositivo de armazenamento, que neste exemplo é a primeira partição do meu disco rígido. E a opção “rootfstype” indica o tipo de sistema de arquivo, que neste exemplo é ext3. Para mais informações sobre os parâmetros aceitos na linha de comandos dê uma olhada na documentação do kernel em Documentation/kernel-parameters.txt.
A linha de comandos do kernel pode ser passada pelo bootloader ou pode estar gravada dentro do kernel (configurada no momento da compilação).
Resumindo: no boot da máquina, o kernel verifica os parâmetros root e rootfstype da linha de comandos, monta o rootfs no diretório “/” de acordo com estes parâmetros, e passa o controle para a aplicação de inicialização.
Nosso foco aqui é entender os diferentes tipos de sistemas de arquivo em Linux e quando usar cada um deles.
SISTEMAS DE ARQUIVO E MÓDULOS
O Linux suporta diferentes sistemas de arquivo, incluindo vfat e ntfs (famosos para quem vem do ambiente Windows), ext2, ext3, ext4, squashfs, jffs2, etc. Uma listagem completa encontra-se na documentação do kernel em Documentation/filesystems/.
Todos estes sistemas de arquivo são implementados através de módulos do kernel. Se você quiser saber quais sistemas de arquivo seu kernel suporta, basta listar o arquivo “/proc/filesystems“:
$ cat /proc/filesystems nodev sysfs nodev rootfs nodev proc nodev tmpfs ... ext3 ext2 ext4 nodev ramfs nodev nfs ... vfat |
Todos os módulos que implementam sistemas de arquivo no Linux utilizam uma camada comum de abstração do kernel chamada de VFS (Virtual filesystems).
Essa camada permite que todas as aplicações usem uma API comum de acesso ao sistema de arquivo (open, read, write, close, etc), independentemente do tipo de sistema de arquivo, ou mesmo do tipo de dispositivo de armazenamento. Esta camada possui também algumas funcionalidades comuns à todos os sistemas de arquivo, incluindo um sistema de cache chamado page cache. O objetivo deste cache é minimizar operações de I/O no disco, armazenando em um cache em RAM as operações realizadas em disco, para depois efetivamente atualizar estas informações no dispositivo (hardware), procedimento chamado de page writeback.
Mas por que será que existem diferentes tipos de sistemas de arquivo? Porque cada um possui objetivos específicos, customizados para uma melhor performance, mais segurança ou menos uso de espaço em disco, visando diferentes dispositivos de armazenamento.
DISPOSITIVOS DE ARMAZENAMENTO
No Linux, os dispositivos de armazenamento são classificados em dois tipos: dispositivos de bloco e memórias flash. E dentro do kernel, eles são manipulados por diferentes sub-sistemas.
- Dispositivos de bloco: podem ser lidos ou escritos normalmente sem a necessidade de apagar antes, e ”virtualmente” não possuem limites de escrita quando usados por muito tempo. Exemplos: HDs, floppy disks, etc.
- Memórias flash: trabalham em blocos e para serem escritas precisam ser apagadas antes. Exemplos: flash NAND, flash NOR.
DISPOSITIVOS DE BLOCO
Então os dispositivos de bloco são aqueles que você esta acostumado em um sistema Linux desktop, como por exemplo HDs, CDs e DVDs.
Se você quiser listar os dispositivos de bloco (e suas respectivas partições) presentes no sistema, basta acessar o arquivo “/proc/partitions“:
$ cat /proc/partitions major minor #blocks name 8 0 312571224 sda 8 1 81920000 sda1 8 2 40960000 sda2 8 3 189688832 sda3 8 32 156290904 sdc 8 33 104391 sdc1 8 34 1 sdc2 8 37 40957686 sdc5 ... |
Existem portanto sistemas de arquivo específicos para dispositivos de bloco. E tudo começou com o tradicional sistema de arquivo ext2, presente no Linux desde praticamente as primeiras versões. É um sistema de arquivo bem estável, mas com um grande problema: pode deixar o sistema em um estado inconsistente após um crash ou um reboot não esperado, fazendo com que o sistema precise usar ferramentas de verificação de disco no próximo boot (Ex: fsck.ext2). É o mesmo caso do vfat, a implementação do sistema de arquivo FAT no Linux. Quem nunca precisou executar um Scansdisk depois de um reboot inesperado no DOS ou no Windows? :)
O ext2 foi usado durante um bom tempo no Linux (quase 10 anos). Então começaram a aparecer alguns sistemas de arquivo com uma característica especial chamada “journaling”.
JOURNALING
Os sistemas de arquivo com journaling foram projetados para evitar de deixar o sistema em um estado inconsistente após um crash ou um reboot inesperado.
Antes de escrever no arquivo, todas as alterações em disco são gravadas em um “journal”, uma espécie de log. Após a atualização no arquivo, a entrada no journal é removida. No boot, as entradas no journal são analizadas conforme imagem abaixo:
Todas as entradas completas são executadas, e as entradas incompletas são descartadas. Ou seja, se você desligar o equipamento no meio de uma escrita em disco, no boot o journal vai garantir que esta escrita será executada, mantendo o disco em um estado consistente. Como o journal também é armazenado em disco, existe a possibilidade de desligar o equipamento no meio da gravação de uma entrada no journal. Neste caso, a entrada incompleta no journal é descartada, e os dados serão perdidos, mas continuamos com a garantia de um sistema consistente, já que nenhuma informação foi armazenada “pela metade”.
O Linux suporta diversos sistemas de arquivo com journal. O ext3 foi o padrão durante um bom tempo. Ele é basicamente o ext2 com a funcionalidade de journaling. O ext4 é a nova geração com muitas melhorias, e é o sistema de arquivos padrão das distribuições Linux desktop atuais.
O btrfs (também chamado de ”ButterFS”) é a próxima geração e visa substituir o ext4. Já esta disponível no mainline do kernel, mas ainda em estado experimental. Até a data de hoje, nenhuma das grandes distribuições esta usando o btrfs. A migração do ext4 para o btrfs ainda deve levar um tempo.
Existem muitos outros sistemas de arquivo com journaling, incluindo reiserFS, JFS, XFS, etc. Estes possuem aplicações específicas, como por exemplo trabalhar com arquivos pequenos ou com carga de trabalho muito alta (muito acessos ao disco).
Mas quando usar cada um destes sistemas de arquivo?
O vfat você só usará se quiser manter interoperabilidade com o Windows, por exemplo para gravar e transferir dados em um pendrive. Outros sistemas de arquivo como o reiserFS, JFS e o XFS são mais usados em servidores e máquinas com propósitos específicos, e tem pouca aplicação em Linux embarcado.
Na prática, e atualmente, você irá se limitar a usar ext3 ou ext4 em dispositivos de bloco.
A exceção são dispositivos de bloco que usam memória flash, como por exemplo um cartão SD/MMC ou um pendrive. Estes dispositivos aparecem para o Linux como um dispositivo de bloco, como se fossem um disco. Mas internamente eles usam uma memória flash para armazenar os dados, e possuem uma controladora para emular um dispositivo de bloco. Como precisamos limitar a escrita em memória flash (veremos isso mais adiante), não é aconselhável o uso de sistemas de arquivo como o ext3 ou o ext4, cujo journaling pode limitar a vida útil da flash. Nestes casos, ext2 pode ser a melhor solução.
SISTEMAS DE ARQUIVO COMPRIMIDOS
Sistemas embarcados possuem normalmente poucos recursos de armazenamento. Por este motivo, você pode querer usar um sistema de arquivo que comprima os dados, deixando o tamanho da imagem do sistema de arquivo menor, e ocupando menos espaço em disco.
Um sistema de arquivo comprimido, como o próprio nome diz, armazena os dados de forma comprimida no dispositivo de armazenamento, economizando espaço em disco. É claro que aí existe um trade-off, você ganha em espaço mas pode perder em performance, já que para fazer a leitura é necessário descomprimir os dados antes. E como algoritmos para escrever de forma comprimida são bem mais complicados de implementar sem afetar a performance e a confiabilidade do sistema, a maioria dos sistemas de arquivo comprimidos são de apenas leitura.
O CramFS (Compressed ROM Filesystem) é um exemplo de sistema de arquivo comprimido e de apenas leitura, desenvolvido especialmente para sistemas embarcados ou dispositivos com baixa capacidade de armazenamento. Os dados são comprimidos com o zlib, suportando arquivos de até 16M, e com tamanho máximo total da imagem de até 256MB.
Já o SquashFS é uma espécie de sucessor do CramFS, visando atingir os mesmos objetivos, mas com melhor compressão, melhor performance de leitura e suporte à arquivos e sistemas maiores. Chega a gerar sistemas de arquivo 3 vezes menores em comparação com o ext3. É um sistema de arquivo bastante usado em distribuições que rodam direto de um CD/DVD (Live CD) ou USB. Veja uma comparação do SquashFS com outros sistemas de arquivo aqui.
Portanto, SquashFS deve ser a sua escolha se você quiser poupar espaço no dispositivo de armazenamento ou evitar escritas usando um rootfs read-only. Você só vai precisar do CramFS se seu kernel for muito antigo (anterior à versão 2.6.29).
Agora, se você quer um sistema de arquivo simples, sem compressão, mas que também seja de apenas leitura, você pode tentar o romfs.
SISTEMAS DE ARQUIVO VOLÁTEIS
Quando trabalhamos com Linux embarcado, às vezes precisamos criar arquivos temporários, armazenar informações de processos em execução, fazer log, etc. Fazer isso em um dispositivo de armazenamento pode ser muito custoso, envolve operações de I/O e pode consumir a vida útil do dispositivo no caso de memórias flash. Outro ponto é que estas informações são voláteis, ou seja, não tem mais utilidade após o boot. Portanto, não faz sentido armazenar dados voláteis em dispositivos não-voláteis.
São nestes casos que usamos sistemas de arquivo voláteis. Com um sistema de arquivo volátil, você consegue manter um diretório no sistema montado em RAM. Ou seja, tudo o que você escrever neste diretório vai para a RAM!
Nas primeiras versões, o sistema de arquivo ramfs foi bastante usado. Mas ele tem basicamente dois problemas. Você não consegue limitar a quantidade máxima de RAM a ser usada, ou seja, o controle precisa estar na sua aplicação. O outro problema é que ele não faz swap em disco.
É por isso que apareceu o tmpfs, sistema de arquivo volátil mais usado atualmente. Não gasta muita RAM, cresce e diminui automaticamente conforme o uso, é capaz de fazer swap em disco se necessário, e você consegue limitar a quantidade máxima de RAM que pode ser usada.
E como usar? Basta montar o tmpfs em um diretório qualquer:
$ mount -t tmpfs tmp /tmp |
No exemplo acima, o diretório “/tmp” foi montado com o tmpfs, ou seja, tudo que for escrito no /tmp será salvo em uma região em RAM, e será perdido no boot.
Quando usar o tmpfs? Sempre que você precisar de armazenamento volátil.
NFS
Imagine um produto em desenvolvimento que contenha um dispositivo de armazenamento, seja um HD, cartão SD ou memória flash. E você esta embarcando Linux neste produto. Imagine o trabalho que você teria se precisasse regravar a flash ou o cartão SD toda vez que precisasse testar uma nova versão da sua aplicação.
É por este motivo que existe o NFS (Network Filesystem), um protocolo que permite você montar um sistema de arquivo pela rede.
Você precisa de um kernel com suporte à NFS, uma placa de rede no equipamento, e um servidor NFS na sua máquina de desenvolvimento. Com um sistema de arquivo local, o desenvolvimento fica realmente muito mais fácil. Você não precisa se preocupar com o tamanho do rootfs, pode usar ferramentas que não caberiam no dispositivo, fica fácil atualizar uma aplicação no rootfs, editar um arquivo, etc.
Uns tempos atrás escrevi um artigo sobre como montar o rootfs no Linux via NFS.
E AINDA TEM MAIS
Neste artigo vimos uma introdução à sistemas de arquivo em Linux embarcado, incluindo os principais tipos de sistemas de arquivo para dispositivo de bloco e quando usar cada um deles, além de sistemas de arquivo comprimidos, voláteis e via rede.
Na segunda parte deste artigo vamos explorar os principais sistemas de arquivo para memórias flash.
Se você conhece ou já trabalhou com algum outro sistema de arquivo para dispositivos de bloco, deixe seus comentários aqui!
Um abraço,
Sergio Prado