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

  • Gustavo H M Silva

    Ola sério Prado! Obrigado por todas as informações. Eu comprei um mini2440 e finalmente consegui botar o linux para rodar nele (embora não o que eu queria, queria o ubuntu server). Queria saber, há como rodar um servidor WAMP nessa maquininha ou é uma coisa bem além do possível

    • http://sergioprado.org/ Sergio Prado

      Olá Gustavo,

      Realmente um servidor WAMP é muita coisa para a friendlyARM (talvez rodasse na Beagleboard).

      Em embarcados normalmente usamos soluções que consomem menos recursos como o HTTP do Busybox ou o servidor Boa no lugar do Apache e o Sqlite no lugar do MySql.

      Um abraço. 

    • http://cleitonbueno.wordpress.com/ Cleiton Bueno

      Gustavo vou expor minhas experiencias com Linux Embarcado.

      Atualmente estou rodando o Busybox em um Raspberry PI e após tempos testando o melhor custo x beneficio focando desempenho foi a combinação:

      Lighttpd 1.4.32
      PHP 5.4.13
      SQLite 3.7.16.2

      Python 2.7.4 (esse é a parte)

      Fiz como todo fazem com compilação cruzada, tenho meu toolchain para ARM configurado, e peguei o código-fonte de todos no site e olha olhada rápida no README peguei as flash importantes para compilar e gerar minha imagem rootfs do meu embarcado, e é a configuração final do meu projeto que estou fazendo para a Faculdade.

      O mais trabalhoso foi o lighttpd que usei bastante parâmetros na linha, e Python devido a alguns serviços que iram rodar que escrevi em Python, já que Perl não tive um bom feedback com cross-compiling.

  • Onéssimo Falconi

    Boa tarde Sergio Prado,

    Procurei por diversos sites e não consegui encontrar uma forma de testar a memória flash com especificação: 512MB, HIGH SECTOR LF SPANSION S29GL512N

    Toda vez que inicio o sistema após atualizar o kernel versão 2.6.22 aparece as linhas abaixo:(…)Log JFFS2 notice:
    (788) jffs2_get_inode_nodes: Node header CRC failed at 0x145c7
    58.
    {ffff,ffff,ffffffff,d22144a5}
    Returns — [0]
    JFFS2 notice:
    (788) jffs2_get_inode_nodes: Node header CRC failed at 0x145bd64.
    {ffff,ffff,ffffffff,ffffffff}
    PowerFail_InitiaJFFS2
    notice: (788) jffs2_get_inode_nodes: Node header CRC faile
    d at 0x145a998.
    {ffff,ffff,ffffffff,d22144a5}
    lize Returns —
    JFFS2 notice: (788) jffs2_get_inode_nodes: Node header CRC faile
    d at 0x1459fb4.
    {ffff,ffff,ffffffff,ffffffff}
    [0]
    JFFS2 notice:
    (788) jffs2_get_inode_nodes: Node header CRC failed at 0x14595cc.
    {ffff,ffff,ffffffff,ffffffff}
    JFFS2 notice:
    (788) jffs2_get_inode_nodes: Node header CRC failed at 0x1458bf0.
    {ffff,ffff,ffffffff,ffffffff}
    JFFS2 notice:
    (788) jffs2_get_inode_nodes: Node header CRC failed at 0x1458128.
    {ffff,ffff,ffffffff,ffffffff}
    Memory_Initialize
    Returns — [0]
    Memory_OpenLocalFile
    Returns — [0]
    Memory_Initialize
    Returns — [0]
    Memory FRAM
    Returns — [0](…)Como posso testar se a flash está com problema? Existe algum software para isto ou alguma forma de testar este tipo de erro na inicialização do sistema? Obrigado e parabéns pelo seu blog que é o  melhor conteúdo que encontrei sobre o assunto. Os demais sites contém muitas informações e não detalham tão bem sobre o assunto.

    • http://sergioprado.org/ Sergio Prado

      Olá Onéssimo,

      Você pode usar o U-Boot para recriar a tabela de bad blocks da flash:

      MINI2440 # nand scrub
      MINI2440 # nand createbbt

      Um abraço.

  • Onéssimo Falconi

    Boa tarde Sergio Prado,

    O problema é que estou trabalhando com MIPS (YAMON) e não com U-Boot. Preciso testar toda a memória flash. Tem como me ajudar com este MIPS?  Agradeço desde já a sua atenção. 

    Abraço.

    • http://sergioprado.org/ Sergio Prado

      Aí complicou. Se o fabricante não fornece nenhuma ferramenta para formatar a flash e criar a tabela de bad blocks (via JTAG ou outro mecanismo de hardware), e se o seu bootloader também não suporta esta feature, fica mais complicado. Não conheço nenhuma ferramenta que rode em userspace para fazer isso.

      Um abraço.

  • Onéssimo Falconi

    Bom dia Sergio Prado,

    Consegui entender os avisos emetidos pelo jffs2 lendo sobre o assunto no seguinte link:
    http://www.linux-mtd.infradead.org/faq/jffs2.html

    Abraço.

    • http://sergioprado.org/ Sergio Prado

      Obrigado pela dica Onéssimo! 

  • E.Grau

    Muitoo bom mesmo, a tempos procurava um material tal completo e objetivo sobre o desenvolvimento de Linux embarcado… já estou interessado em seus cursos uhuu!

  • Bruna

    Olá Sérgio!

    Utilizamos a placa Cubieboard com cartão SD e sistema de arquivos EXT4. No entanto, estamos preocupados com a durabilidade (max write cycles) do cartão. Estudamos a possibilidade de comprar cartões com implementação de static wear levelling e afins na controladora mas também estamos interessados em utilizar um outro sistema de arquivos, tais como JFFS2 e UBIFS. Sabemos que eles não são para block devices diretamente (SD card no nosso caso) mas gostaríamos de saber a tua opinião sobre essa possível abordagem.

    Abraço

    • http://sergioprado.org/ Sergio Prado

      Olá Bruna,

      Utilizar um sdcard com jffs2 ou ubifs não vai dar muito certo. Eles implementam FTL (flash translation layer), também implementado nos sdcards, então o resultado pode ser até pior. As opções que temos hoje para dispositivos do tipo flash com FTL são: ext4, ext4 sem journaling (diminui a quantidade de escritas em disco porém aumenta o risco de corromper o sistema de arquivos) e F2FS (um sistema de arquivos criado pela Samsung exatamente para dispositivos flash com FTL integrado). Como o F2FS é um sistema de arquivos novo, sua adoção ainda não é grande, mas sei que a Motorola e a Samsung tem usado em alguns smartphones.

      Um abraço!

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