Sistemas de arquivo em Linux embarcado – Parte 2

- por Sergio Prado

Categorias: Linux Tags: ,

Na parte 1 desta série de artigos, vimos uma intro­dução à sis­temas de arquivo em Linux embar­cado, incluindo os prin­ci­pais tipos de sis­temas de arquivo para dis­pos­i­tivo de bloco, e quando usar cada um deles, além de sis­temas de arquivo com­prim­i­dos, voláteis e via rede.

Nesta segunda parte, estudaremos as memórias flash, o sub-sistema MTD do kernel, os principais sistemas de arquivo para flash, e por fim, veremos como selecionar o melhor sistema de arquivo para o seu produto com Linux embarcado.

AS MEMÓRIAS FLASH

Algumas limitações diferem as memórias flash de dispositivos de bloco tradicionais como HDs e floppy disks.

As memórias flash só podem ser apagadas em blocos. Estes blocos são chamados de erase blocks, e podem variar de algumas dezenas para algumas centenas de kB. Quando você apaga um bloco da flash, todos os bits assumem tipicamente o valor 1. Com um bloco apagado, você pode escrever em qualquer posição da flash. Porém, se você escrever 0 em qualquer um dos bits de qualquer posição da flash, você só consegue fazê-lo voltar para 1 apagando todo o bloco correspondente da flash!

Outra limitação das memórias flash é a quantidade de vezes que você pode apagar e escrever nela (program/erase cycles). Esta limitação, dependendo do modelo e do fabricante da flash, pode variar entre 100.000 e 1.000.000 ciclos. Parece muito, mas basta você deixar sua aplicação fazendo log em memória flash para transformá-la em um peso de papel! É por este motivo que existe uma funcionalidade chamada wear leveling, que pode ser implementada por software ou diretamente em chips controladores de flash.

Imagine um arquivo armazenado em determinado bloco da flash. Agora imagine que este arquivo é atualizado constantemente. Em pouco tempo, aquele bloco da flash será inutilizado. Com o “wear leveling”, este arquivo é remapeado de tempos em tempos para outros blocos da flash, melhorando a vida útil da memória flash consideravelmente.

Mesmo assim, depois de certo tempo de uso da flash, um ou mais blocos serão inutilizados. Por este motivo, existe uma técnica chamada de BBM (Bad Block Management). Quando sai de fábrica, as memórias flash já possuem gravada nelas uma tabela de bad blocks. Toda camada de software que trabalha com memórias flash deve ser capaz de ler e identificar estes bad blocks para não utilizar regiões inválidas da flash.

As memórias flash podem ainda ser divididas em dois tipos principais:

  • As memórias flash NOR, que possuem acesso aleatório para leitura de dados (conseguem endereçar byte a byte), velocidade alta para leitura mas lenta para escrita, baixa densidade (cada byte ocupa mais espaço), menor durabilidade e alto custo por MB. É usada principalmente para armazenar código (substituir as antigas memórias ROM).
  • As memórias flash NAND, que possuem acesso de leitura apenas em blocos (para ler um byte precisa ler o bloco correspondente), velocidade baixa para leitura mas alta para escrita, alta densidade (cada byte ocupa menos espaço), maior durabilidade e baixo custo por MB. É usada principalmente para armazenar dados.

Muito bem, vimos que as memórias flash são bem diferentes quando comparadas com os tradicionais dispositivos de bloco. É por este motivo que dentro do Linux as memórias flash são gerenciadas por um sub-sistema separado, chamado de MTD (Memory Technology Devices).

O SUB-SISTEMA MTD

O sub-sistema MTD é dividido basicamente em duas camadas:

A camada de baixo (MTD chip drivers) conversa diretamente com o hardware, enquanto que a camada de cima (MTD User modules) implementa os diferentes sistemas de arquivo e mecanismos de acesso à flash.

Cada memória deve ter o seu “MTD chip driver”, para possibilitar o acesso ao hardware da flash. Mas cada sistema pode usar um ou mais “MTD User modules”. Cada um deles irá tratar de forma diferentes a flash. É neste contexto que as memórias flash também são chamadas de dispositivos MTD. As partições de todos os dispositivos MTD do sistema podem ser listadas no Linux com o comando abaixo:

$ cat /proc/mtd
dev:  size     erasesize   name
mtd0: 00080000 00020000  "X-Loader"
mtd1: 001e0000 00020000  "U-Boot"
mtd2: 00020000 00020000  "U-Boot Env"
mtd3: 00400000 00020000  "Kernel"
mtd4: 0f980000 00020000  "File System"

O driver mtdchar implementa o módulo “char device” da flash. Ele cria um dispositivo de caractere para cada dispositivo MTD no sistema, normalmente chamado de /dev/mtdX, onde X é o número da partição. Com este módulo, você tem acesso sequencial (byte-a-byte) em toda a flash. Além disso, ele disponibiliza comandos ioctl() para você poder manipular a flash (ler informações, apagar/gravar na flash, etc). A principal utilidade deste módulo é no gerenciamento da flash, quando usamos o pacote mtd-utils. Este pacote possui algumas ferramentas como flash_eraseall para apagar todo o dispositivo, flashcp para escrever em memórias flash NOR e nandwrite para escrever um memórias flash NAND. Para mais informações, visite a página do projeto em http://www.linux-mtd.infradead.org/.

O driver mtdblock implementa o módulo “block device” da flash. Ele cria um dispositivo de bloco para cada dispositivo MTD no sistema, normalmente nomeado /dev/mtdblockX, onde X é o número da partição. Este módulo permite acesso de leitura/escrita por bloco, como se fosse um HD mesmo, mas não gerencia bad blocks e também não trabalha com wear leveling em escritas. Ele pode ser usado para montar um sistema de arquivo read-only como o squashfs.

Já se você quiser um sistema de arquivo que trabalhe com todas as limitações da flash, incluindo bad blocks e wear leveling, você vai precisar de um sistema de arquivo específico para flash.

SISTEMAS DE ARQUIVO PARA FLASH

O JFFS2 é um dos sistemas de arquivo para flash mais antigos ainda em utilização. Ele suporta memórias flash NAND e NOR, trabalha com wear leveling, algoritmo ECC (Error Checking and Correction) e comprime os dados em tempo de execução, economizando espaço na flash. Sua principal deficiência é a performance ruim em memórias flash muito grande (> 64MB), principalmente com relação ao tempo de boot. O JFFS2 não armazena informações da estrutura de diretórios, isso significa que para o kernel identificar a estrutura de diretórios e montar uma partição JFFS2, ele precisa varrer toda a flash. Por este motivo, o tempo de boot fica realmente lento em memórias flash muito grandes. Habilitando no kernel a opção CONFIG_JFFS2_SUMMARY reduz drasticamente este tempo de montagem.

O YAFFS2 é um dos sucessores do JFFS2, com o objetivo de corrigir os problemas de performance em memórias flash muito grandes. Tem um tempo de boot rápido, também trabalha com wear leveling e algoritmo ECC (Error Checking and Correction) para garantir a confiabilidade dos dados, mas suporta apenas memórias flash NAND e não comprime os dados.

Já o UBIFS (Unsorted Block Images Filesystem) é um sistema de arquivo para flash mais recente. É considerado a evolução do JFFS2, e trabalha com volumes lógicos em cima de dispositivos MTD. Tem todas as vantagens do YAFFS2 e do JFFS2. Sua única desvantagem é o overhead de metadados. Ou seja, a quantidade de dados que ele precisa armazenar para controlar as partições pode ser muito grande quando trabalhamos com memórias flash com pouca capacidade de armazenamento. Mas com a capacidade das memórias flash aumentando cada vez mais, deve se tornar nos próximos anos o sistema de arquivo padrão para dispositivos MTD.

CUIDADOS COM MEMÓRIAS FLASH

Mesmo trabalhando com um sistema de arquivo voltado à memórias flash, você precisa tomar alguns cuidados para aumentar a vida útil do dispositivo, dentre eles:

  • Não use a memória flash como região de swap!
  • Não use a memória flash para armazenamento volátil como logs e arquivos temporários (use tmpfs nestes casos).
  • Monte seu rootfs como apenas leitura, ou use o squashfs, quando possível.
  • Use a opção de montagem ”noatime” para evitar a escrita na flash toda vez que você ler um arquivo.
  • Não use a opção de montagem ”sync” (com esta opção habilitada, uma atualização no sistema de arquivos atualiza imediatamente a flash).
  • Você pode decidir se quer ou não usar um sistema de arquivo com journal. Com ele aumenta-se a quantidade de escritas, mas deixa-se o sistema mais resistente à problemas de queda de energia.

EMULANDO DISPOSITIVOS DE BLOCO

Pendrives e cartões SD/MMC trabalham com memórias flash. Mas como é que o kernel os identifica e gerencia como dispositivos de bloco? Como conseguimos montar nestes dispositivos sistemas de arquivo voltados à dispositivos de bloco como o ext3?

Isso é possível porque estes dispositivos possuem uma controladora implementada em hardware/firmware que emula um dispositivo de bloco em cima da memória flash. Esta controladora é que faz o controle de bad blocks e wear leveling. No nível do sistema operacional, não temos nenhum controle sobre esta controladora, nem podemos acessar diretamente a memória flash. Ou seja, como não conhecemos a qualidade desta controladora, é altamente recomendável limitar o número de escritas nestes dispositivos.

ESCOLHENDO UM SISTEMA DE ARQUIVO

Vimos nestes dois artigos diversas soluções de sistemas de arquivo para Linux embarcado. Como escolher o melhor para sua aplicação?

O diagrama abaixo disponibilizado pelo pessoal da Free Electrons ilustra muito bem este processo de decisão, dependendo das necessidades do seu projeto:

Pois é, não tem segredo!

Procurei expor nesta série de artigos os detalhes e as características dos principais sistemas de arquivo usados em Linux embarcado. Agora é só analisar os requisitos do seu produto e escolher o mais adequado para sua aplicação.

Um abraço,

Sergio Prado

Faça um Comentário

Navegue
Creative Commons Este trabalho de Sergio Prado é licenciado pelo
Creative Commons BY-NC-SA 3.0.