O que poderia explicar essa manipulação estranha de arquivos esparsos de / in tmpfs?

12

Na partição do sistema de arquivos ext4 , posso executar o seguinte código:

fs="/mnt/ext4"

#create sparse 100M file on ${fs}
dd if=/dev/zero \
   of=${fs}/sparse100M conv=sparse seek=$((100*2*1024-1)) count=1 2> /dev/null

#show its actual used size before
echo "Before:"
ls ${fs}/sparse100M -s

#setting the sparse file up as loopback and run md5sum on loopback
losetup /dev/loop0 ${fs}/sparse100M 
md5sum /dev/loop0

#show its actual used size afterwards
echo "After:"
ls ${fs}/sparse100M -s

#release loopback and remove file
losetup -d /dev/loop0
rm ${fs}/sparse100M

que produz

Before:
0 sparse100M
2f282b84e7e608d5852449ed940bfc51  /dev/loop0
After:
0 sparse100M

Fazendo a mesma coisa no tmpfs como:

fs="/tmp"

rendimentos

Before:
0 /tmp/sparse100M
2f282b84e7e608d5852449ed940bfc51  /dev/loop0
After:
102400 /tmp/sparse100M

que basicamente significa que algo que eu esperava apenas ler os dados, fez com que o arquivo esparso "explodisse como um balão"?

Espero que isso se deva ao suporte menos perfeito para arquivos esparsos no sistema de arquivos tmpfs e, em particular, devido à falta do FIEMAP ioctl, mas não tenho certeza do que causa esse comportamento? Você pode me dizer?

    
por humanityANDpeace 09.03.2017 / 00:54

1 resposta

4

Primeiro, você não está sozinho na intrigante sobre esse tipo de problema.

Isso não se limita apenas a tmpfs , mas tem sido uma preocupação citada com NFSv4 .

If an application reads 'holes' in a sparse file, the file system converts empty blocks into "real" blocks filled with zeros, and returns them to the application.

Quando md5sum está tentando verificar um arquivo, ele explicitamente escolhe fazer isso ordem sequencial , que faz muito sentido com base no que o md5sum é tentando fazer.

Como existem fundamentalmente "buracos" no arquivo, essa leitura sequencial vai para (em algumas situações) causar uma cópia em escrever como operação para preencher o arquivo. Isso então fica em uma questão mais profunda em torno de se fallocate() , como implementado no o sistema de arquivos suporta FALLOC_FL_PUNCH_HOLE .

Felizmente, o tmpfs não apenas suporta isso, mas há um mecanismo para "cavar" os buracos de volta para fora.

Usando o utilitário CLI fallocate , podemos detectar e reimprimir com êxito esses furos.

Como por man 1 fallocate :

-d, --dig-holes
      Detect and dig holes.  This makes the file sparse in-place, without
      using extra disk space.  The minimum size of the hole depends on
      filesystem I/O  block size (usually 4096 bytes).  Also, when using
      this option, --keep-size is implied.  If no range is specified by
      --offset and --length, then the entire file is analyzed for holes.

      You can think of this option as doing a "cp --sparse" and then
      renaming the destination file to the original, without the need for
      extra disk space.

      See --punch-hole for a list of supported filesystems.

fallocate opera no nível arquivo e quando você está executando md5sum contra um dispositivo de bloco (solicitando leituras sequenciais) você está tropeçando no lacuna exata entre como o fallocate() syscall deve operar. Nós podemos ver isso em ação:

Em ação, usando seu exemplo, vemos o seguinte:

$ fs=$(mktemp -d)
$ echo ${fs}
/tmp/tmp.ONTGAS8L06
$ dd if=/dev/zero of=${fs}/sparse100M conv=sparse seek=$((100*2*1024-1)) count=1 2>/dev/null
$ echo "Before:" "$(ls ${fs}/sparse100M -s)"
Before: 0 /tmp/tmp.ONTGAS8L06/sparse100M
$ sudo losetup /dev/loop0 ${fs}/sparse100M
$ sudo md5sum /dev/loop0
2f282b84e7e608d5852449ed940bfc51  /dev/loop0
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 102400 /tmp/tmp.ONTGAS8L06/sparse100M
$ fallocate -d ${fs}/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 0 /tmp/tmp.ONTGAS8L06/sparse100M

Agora ... isso responde à sua pergunta básica. Meu lema geral é "ficar estranho", então eu cavou ainda mais ...

$ fs=$(mktemp -d)
$ echo ${fs}
/tmp/tmp.ZcAxvW32GY
$ dd if=/dev/zero of=${fs}/sparse100M conv=sparse seek=$((100*2*1024-1)) count=1 2>/dev/null
$ echo "Before:" "$(ls ${fs}/sparse100M -s)"
Before: 0 /tmp/tmp.ZcAxvW32GY/sparse100M
$ sudo losetup /dev/loop0 ${fs}/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 1036 /tmp/tmp.ZcAxvW32GY/sparse100M
$ sudo md5sum ${fs}/sparse100M
2f282b84e7e608d5852449ed940bfc51  /tmp/tmp.ZcAxvW32GY/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 1036 /tmp/tmp.ZcAxvW32GY/sparse100M
$ fallocate -d ${fs}/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 520 /tmp/tmp.ZcAxvW32GY/sparse100M
$ sudo md5sum ${fs}/sparse100M
2f282b84e7e608d5852449ed940bfc51  /tmp/tmp.ZcAxvW32GY/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 520 /tmp/tmp.ZcAxvW32GY/sparse100M
$ fallocate -d ${fs}/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 516 /tmp/tmp.ZcAxvW32GY/sparse100M
$ fallocate -d ${fs}/sparse100M
$ sudo md5sum ${fs}/sparse100M
2f282b84e7e608d5852449ed940bfc51  /tmp/tmp.ZcAxvW32GY/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 512 /tmp/tmp.ZcAxvW32GY/sparse100M
$ fallocate -d ${fs}/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 0 /tmp/tmp.ZcAxvW32GY/sparse100M
$ sudo md5sum ${fs}/sparse100M
2f282b84e7e608d5852449ed940bfc51  /tmp/tmp.ZcAxvW32GY/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 0 /tmp/tmp.ZcAxvW32GY/sparse100M

Você vê que apenas o ato de executar o losetup altera o tamanho de o arquivo esparso. Então, isso se torna uma combinação interessante de onde tmpfs , o mecanismo HOLE_PUNCH, fallocate e dispositivos de bloco se cruzam.

    
por 20.09.2018 / 20:00