Ir ao conteúdo
  • Cadastre-se

C Código para obter deslocamento de arquivo de swap no Linux


Marcos FRM
Ir à solução Resolvido por arfneto,

Posts recomendados

  • Membro VIP

Estava lendo a wiki do Arch para configurar hibernação com arquivo de swap

 

https://wiki.archlinux.org/title/Power_management/Suspend_and_hibernate#Hibernation_into_swap_file

 

e este comando me embrulhou o estômago:

 

filefrag -v /swapfile | awk '$1=="0:" {print substr($4, 1, length($4)-2)}'

 

🤢

 

Para lidar com o tédio, escrevi isto que faz o mesmo, sem requerer bruxaria 🧙‍♂️ na saída:

 

#define _FILE_OFFSET_BITS 64
// basename
#define _GNU_SOURCE

#include <fcntl.h>
#include <linux/fiemap.h>
#include <linux/fs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>

// https://www.kernel.org/doc/Documentation/filesystems/fiemap.txt
// https://www.kernel.org/doc/Documentation/power/swsusp-and-swap-files.txt

int main(int argc, char **argv)
{
    int fd, r = EXIT_FAILURE;
    struct stat sb;
    struct fiemap *map;

    if (argc != 2)
    {
        fprintf(stderr, "Uso: %s arquivo\n", basename(argv[0]));
        exit(EXIT_FAILURE);
    }

    fd = open(argv[1], O_RDONLY);
    if (fd < 0)
    {
        perror("open");
        exit(EXIT_FAILURE);
    }

    if (fstat(fd, &sb) != 0)
    {
        perror("fstat");
        goto fim;
    }

    if (!S_ISREG(sb.st_mode))
    {
        fprintf(stderr, "Não é arquivo regular.\n");
        goto fim;
    }

    map = calloc(1, sizeof(struct fiemap) + sizeof(struct fiemap_extent));
    if (map == NULL)
    {
        perror("calloc");
        goto fim;
    }

    map->fm_length = sb.st_size;
    map->fm_flags = FIEMAP_FLAG_SYNC;
    map->fm_extent_count = 1; // só interessa o primeiro extent

    if (ioctl(fd, FS_IOC_FIEMAP, map) != 0)
    {
        perror("ioctl FIEMAP");
    }
    else
    {
        printf("%llu\n", map->fm_extents[0].fe_physical / getpagesize());
        r = EXIT_SUCCESS;
    }

    free(map);
fim:
    close(fd);

    return r;
}

 

Funciona com drivers de sistemas de arquivos que implementem FIEMAP -- menos o Btrfs.

 

Leve o exit code em consideração, caso use num shell script:

 

DESLOCAMENTO=$(programa /arquivo) || exit 1

 

😅 🥰

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

  • Solução
2 horas atrás, Marcos FRM disse:

Para lidar com o tédio, escrevi isto que faz o mesmo, sem requerer bruxaria 

 

 Para combater o tédio vale tudo. Mas essa linha não se trata de "bruxaria" nem é nada de especial.

 

Um pouco de história

 

Todo script no Unix, hoje Linux ou Android ou MacOS 🙂  pode ser um programa C.

 

  • C foi criada para ajudar a escrever o Unix
  • awk estava no Unix desde o início

Mais ainda: o K de AWK é o mesmo do primeiro e clássico livro de C, The C Programming Language, o hoje professor em Princeton entre outras coisas, Brian Kernighan. Aho e Weinberger e também Dennis Ritchie e Ken Thompson trabalhavam todos nos laboratórios Bell.

 

O comando

 

filefrag -v /swapfile | awk '$1=="0:" {print substr($4, 1, length($4)-2)}'

 

Na documentação citada
 

image.png.44bb0a3c0f9f5846a49a677a55ac1a6a.png

 

 

 

 

 

 

 

 

 

 

 

Então está claro que o comando apenas resulta em 38912.

 

pipes são  essenciais em Unix. '|' é o símbolo de pipe em Unix e pipes foram criadas na mesma época em que C, na primeira vez em que se teve memória e performance suficiente para rodar um sistema e compilar algo em um computador, incluído aí o próprio sistema. O primeiro computador assim foi o PDP-11/45 e aí só aí provavelmente o Unix virou algo significativo.

 

De volta ao programa: a saída do filefrag é passada para um programa AWK de uma única linha e ele retorna o deslocamento do arquivo de swap.

 

O programa de uma linha, em um arquivo 'pgm'
 

	$1=="0:" {print substr($4, 1, length($4)-2)}

 

Um arquivo de teste chamado 'coisa'
 

Filesystem type is: ef53
File size of /swapfile is 4294967296 (1048576 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length:   expected: flags:
   0:        0..       0:      38912..     38912:      1:            
   1:        1..   22527:      38913..     61439:  22527:             unwritten
   2:    22528..   53247:     899072..    929791:  30720:      61440: unwritten
   0:        0..       0:      ClubeDoHardware..     38912:      1:            

 

O que você fez em seu programa C é equivalente a pegar o valor do offset do primeiro segmento do arquivo, aquele número que está na quarta coluna da linha que começa claro por : "0:" , na saída do filefrag come vê acima: 38912. O arquivo acima claro em uma linha a mais e vou mostrar porque a seguir.

 

O que o programa de uma linha faz? 

 

em todas as linhas que tem "0:" mostra na tela o quarto campo, exceto as duas últimas letras. E é só isso

 

Exemplo

 

[arfneto@fedora ~]$ cat coisa

Filesystem type is: ef53
File size of /swapfile is 4294967296 (1048576 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length:   expected: flags:
   0:        0..       0:      38912..     38912:      1:      
   1:        1..   22527:      38913..     61439:  22527:             unwritten
   2:    22528..   53247:     899072..    929791:  30720:      61440: unwritten
   0:        0..       0:      ClubedoHardware..     38912:      1:            

[arfneto@fedora ~]$ cat pgm

	$1=="0:" {print substr($4, 1, length($4)-2)}

[arfneto@fedora ~]$ awk -f pgm coisa

38912
ClubedoHardware


Como vê a saída seria mesmo 38912. Só não foi porque eu coloquei mais uma linha lá no arquivo de entrada para mostrar o efeito.

 

Do manual de AWK
 

Citação

Os programas nesse livro deixam claro que um programa AWK é muito menor e mais rápido de escrever que um similar escrito em C. Assim, há frequentemente vantagem em prototipar um algoritmo ou design em AWK para consegui-lo rodando rapidamente e expor outros problemas mais cedo. Com frequência a performance do programa interpretado é adequada e o protótipo AWK se torna o produto

 

Sobre o programa C

  • não há razão pra alocar map dinamicamente se o tamanho já é conhecido desde o início
  • 5 'goto fim' para economizar uma linha de programa talvez só deixe o programa mais confuso de ler
  •  calloc para map é redundante porque a estrutura vai ser toda reeescrita pela chamada a ioctl
  • uma variável iniciada com EXIT_FAILURE para eventualmente trocar o valor para EXIT_SUCCESS pode ser exagero. Porque não retornar as constantes simplesmente? Foram criadas para isso.
  • sair com exit() em main é pouco relevante.

 

Se pode reescrever em C todos os scripts que rodam no boot por exemplo. Tédio à parte pode não haver razão para tanto.

 

 

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

  • Membro VIP

Opa.

 

struct fiemap tem um array flexível dentro; com

 

--- antes.c     2023-05-18 10:05:04.374112514 -0300
+++ depois.c    2023-05-18 10:05:37.924002824 -0300
@@ -19,7 +19,7 @@
 {
     int fd, r = EXIT_FAILURE;
     struct stat sb;
-    struct fiemap *map;
+    struct fiemap map;

     if (argc != 2)
     {
@@ -46,16 +46,9 @@
         goto fim;
     }

-    map = calloc(1, sizeof(struct fiemap) + sizeof(struct fiemap_extent));
-    if (map == NULL)
-    {
-        perror("calloc");
-        goto fim;
-    }
-
-    map->fm_length = sb.st_size;
-    map->fm_flags = FIEMAP_FLAG_SYNC;
-    map->fm_extent_count = 1; // só interessa o primeiro extent
+    map.fm_length = sb.st_size;
+    map.fm_flags = FIEMAP_FLAG_SYNC;
+    map.fm_extent_count = 1; // só interessa o primeiro extent

     if (ioctl(fd, FS_IOC_FIEMAP, map) != 0)
     {
@@ -63,11 +56,10 @@
     }
     else
     {
-        printf("%llu\n", map->fm_extents[0].fe_physical / getpagesize());
+        printf("%llu\n", map.fm_extents[0].fe_physical / getpagesize());
         r = EXIT_SUCCESS;
     }

-    free(map);
 fim:
     close(fd);

 

não funciona.

 

Com alloca() daria, né? Mas, acabou o tédio para continuar. kkk

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

@Marcos FRM Só porque eu fiquei curioso, direto da documentação

 

Request Basics
--------------

A fiemap request is encoded within struct fiemap:

struct fiemap {
	__u64	fm_start;	 /* logical offset (inclusive) at
				  * which to start mapping (in) */
	__u64	fm_length;	 /* logical length of mapping which
				  * userspace cares about (in) */
	__u32	fm_flags;	 /* FIEMAP_FLAG_* flags for request (in/out) */
	__u32	fm_mapped_extents; /* number of extents that were
				    * mapped (out) */
	__u32	fm_extent_count; /* size of fm_extents array (in) */
	__u32	fm_reserved;
	struct fiemap_extent fm_extents[0]; /* array of mapped extents (out) */
};

 

Seu programa só vai funcionar se o arquivo tiver um único extent (não estiver fragmentado).

Mas o programa de uma linha em AWK vai lá e pega o tamanho do primeiro "extent" e pronto. 😄 

 

Como resolver? Basta ler mais uns parágrafos e logo abaixo tem a resposta:

 

Currently, there are four flags which can be set in fm_flags:

* FIEMAP_FLAG_NUM_EXTENTS
If this flag is set, extent information will not be returned via the
fm_extents array and the value of fm_extent_count will be
ignored. Instead, the total number of extents covering the range will
be returned via fm_mapped_extents. This is useful for programs which
only want to count the number of extents in a file, but don't care
about the actual extent layout.

 

Mude
 

	map->fm_flags = FIEMAP_FLAG_SYNC;

Para

	map->fm_flags = FIEMAP_FLAG_SYNC | FIEMAP_FLAG_NUM_EXTENTS;

 

e ioctl() vai devolver o total de segmentos do arquivo. Depois corrija o tamanho da área alocada e chame ioctl de novo sem essa flag claro.

 

Para economizar pode alocar para um máximo, como 10 ou 20 segmentos, e chamar ioctl. Assim não precisa chamar ioctl duas vezes nem realloc, na maioria dos casos. Note que tem uma constante com o maximo de segmentos possível, Não sei o nome.

 

Ou, melhor ainda, pode deixar isso estático para um certo número de segmentos e alocar só se der erro depois de usar a flag para saber o tamanho exato de que precisa.

 

 

 

 

 

Pode se inspirar lendo a fonte de filefrag

 

Link para o comentário
Compartilhar em outros sites

  • Membro VIP
1 hora atrás, arfneto disse:

Seu programa só vai funcionar se o arquivo tiver um único extent (não estiver fragmentado).

 

O arquivo pode ter vários extents, mas precisam ser contíguos. Isso é mandatório, pois, do contrário, o kernel nem aceita (swapon() falha com EINVAL). Portanto, basta map->fm_extents[0].fe_physical (do primeiro extent) e pronto, pois, para a hibernação funcionar, o kernel apenas precisa saber onde o arquivo começa fisicamente no volume em questão. O resto obtém do cabeçalho posto pelo mkswap nos primeiros 4 KiB do arquivo. Nele, tem, entre outras informações, o número de páginas que compõem o arquivo.

Link para o comentário
Compartilhar em outros sites

@Marcos FRM Acho que não entendeu o que eu expliquei. O erro está em seu programa. Só funcionaria se o arquivo tivesse um único extent. 

 

Te mostrei a documentação. Apenas chame ioctl com aquela flag e terá o número de extents. Aí calcule o tamanho necessário e prossiga chamando ioctl com o tamanho suficiente. Ou, como eu te expliquei, use um mínimo razoável, o tamanho é pequeno, ou use o máximo porque tem uma constante que diz isso, e use um buffer estático.

 

 

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

Em 17/05/2023 às 14:53, Marcos FRM disse:
 map = calloc(1, sizeof(struct fiemap) + sizeof(struct fiemap_extent));

 

Como te disse, o erro está nessa linha.

 

Deve chamar ioctl antes como está escrito no documento que também te mostrei, usando FIEMAP_FLAG_NUM_EXTENTS

 

A partir daí corrija a conta que fez acima e que está errada (a menos que o arquivo tenha um só fragmento, que foi o que eu te expliquei tambem). E então chame ioctl de novo

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

  • Membro VIP

Eu te afirmo que o programa funciona porque eu compilei e usei para obter com sucesso (hibernação testada depois) o deslocamento de meia dúzia de instalações, com variados tamanhos de arquivos (4GB a 16GB), em sistemas com EXT4 e XFS. Então, enquanto você não me demonstrar que não funciona, fica assim.

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

  • Membro VIP
Em 18/05/2023 às 14:00, Marcos FRM disse:

O arquivo pode ter vários extents, mas precisam ser contíguos.

 

Errado. Corrigindo a informação... 🙏 😅

 

Este arquivo de 96 GB, dentro de XFS, criado com fallocate(1), tem extents descontínuos e mesmo assim é aceito pelo kernel:

 

# filefrag -v /swap
Filesystem type is: 58465342
File size of /swap is 103079215104 (25165824 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length:   expected: flags:
   0:        0..       0:     133684..    133684:      1:
   1:        1.. 2097150:     133685..   2230834: 2097150:             unwritten
   2:  2097151.. 4194301:    2230835..   4327985: 2097151:             unwritten
   3:  4194302.. 6291452:    4327986..   6425136: 2097151:             unwritten
   4:  6291453.. 8388603:    8489853..  10587003: 2097151:    6425137: unwritten
   5:  8388604..10485754:   10587004..  12684154: 2097151:             unwritten
   6: 10485755..12582905:   12684155..  14781305: 2097151:             unwritten
   7: 12582906..14680056:   16850563..  18947713: 2097151:   14781306: unwritten
   8: 14680057..16777207:   36355047..  38452197: 2097151:   18947714: unwritten
   9: 16777208..18874358:   28036702..  30133852: 2097151:   38452198: unwritten
  10: 18874359..20971509:   30133853..  32231003: 2097151:             unwritten
  11: 20971510..23068660:   32231004..  34328154: 2097151:             unwritten
  12: 23068661..25165811:   19682561..  21779711: 2097151:   34328155: unwritten
  13: 25165812..25165823:   21779712..  21779723:     12:             last,unwritten,eof
/swap: 6 extents found

# dmesg | grep -w /swap
[   10.404033] systemd[1]: Activating swap /swap...
[   10.763899] Adding 100663292k swap on /swap.  Priority:-2 extents:6 across:153274052k FS

 

Meu programinha tabajara está ok:

 

# ./a.out /swap
133684

 

Como o kernel escreve diretamente no dispositivo, sem ajuda do sistema de arquivos, no momento em que o arquivo é ativado (swapon(2)), o driver do sistema de arquivos[*] reúne os intervalos dos extents e diz ao gerenciador de memória que pode escrever entre os deslocamentos físicos A e B, C e D, etc. Descontinuidade é aceita pois o formato swap é um sistema de arquivos rudimentar também, baseado numa lista encadeada (árvore desde o 5.3) de estruturas swap_extent. Através desse esquema o kernel consegue mapear o arquivo de swap mesmo fragmentado.

 

Este commit, que substituiu a lista encadeada, é útil para entender o mecanismo:

 

https://github.com/torvalds/linux/commit/4efaceb1c5f8136d5fec3f26549d294b8e898bd7

 

---
[*] EXT4 e XFS (outros?) usam um código compartilhado para a tarefa:

 

https://github.com/torvalds/linux/blob/v6.3/fs/iomap/swapfile.c

 

(ver https://utcc.utoronto.ca/~cks/space/blog/linux/HowSwapFindsBlocks)

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

  • Membro VIP

Mais uma correção: o "formato" swap de que falei antes é mantido apenas na memória, enquanto o arquivo de swap está ativo (entre swapon(2) e swapoff(2)). A árvore de swap_extent não é gravada no disco.

 

Hibernação usa outro código:

 

https://github.com/torvalds/linux/blob/v6.3/kernel/power/swap.c

 

Ao hibernar, o arquivo de swap é desativado; o kernel então faz uma imagem da RAM e grava no arquivo de swap, substituindo o cabeçalho para na próxima inicialização saber que tem que acordar a partir dele. Essa imagem é um sistema de arquivos rudimentar escrito no disco, que consegue "pular" as diversas descontinuidades que eventualmente compreendam o arquivo. Depois de acordado, o kernel restaura o cabeçalho e reativa o arquivo de swap.

 

Fascinante ficar garimpando no código, muitas vezes entendendo muito pouco do que tem lá... kkk. 😅

Link para o comentário
Compartilhar em outros sites

Em 17/05/2023 às 14:53, Marcos FRM disse:

Funciona com drivers de sistemas de arquivos que implementem FIEMAP -- menos o Btrfs.

 

Em versões recentes de kernel funciona também com btrfs sim. Veja nessa maquina
 

[root@fedora /]# uname -a
Linux fedora 6.2.15-300.fc38.x86_64 #1 SMP PREEMPT_DYNAMIC Thu May 11 17:37:39 UTC 2023 x86_64 GNU/Linux
[root@fedora /]# btrfs filesystem mkswapfile --size 1G /my_swap
create swapfile /my_swap size 1.00GiB (1073741824)
[root@fedora /]# swapon /my_swap
[root@fedora /]# cat /proc/swaps
Filename				Type		Size		Used		Priority
/dev/zram0                              partition	2000892		985344		100
/my_swap                                file		1048572		0		-2
[root@fedora /]# 

 

E tem dois arquivos de swap ativos, um arquivo comum em uma partição btrfs e outro em um ramdisk

 

Em 18/05/2023 às 14:28, Marcos FRM disse:

Não. Compile aí e teste num arquivo com centenas de extents.

 

Sim, tem razão, seu programa roda certinho.

 

Porque seu programa está certo?

 

Porque apesar de fiemap ser um registro de tamanho variável existe uma convenção nesse caso que aceita que se defina o número máximo de extents que o programa precisa ler, e assim essa linha 
 

    map->fm_extent_count = 1; // só interessa o primeiro extent

 

resolve o problema.

 

Em 19/05/2023 às 21:25, Marcos FRM disse:

Meu programinha tabajara está ok:

 

 

Sim, mas tem os problemas de que falei no tópico #2

 

Em 17/05/2023 às 19:14, arfneto disse:

Sobre o programa C

  • não há razão pra alocar map dinamicamente se o tamanho já é conhecido desde o início
  • 5 'goto fim' para economizar uma linha de programa talvez só deixe o programa mais confuso de ler
  •  calloc para map é redundante porque a estrutura vai ser toda reeescrita pela chamada a ioctl
  • uma variável iniciada com EXIT_FAILURE para eventualmente trocar o valor para EXIT_SUCCESS pode ser exagero. Porque não retornar as constantes simplesmente? Foram criadas para isso.
  • sair com exit() em main é pouco relevante.

 

Os tópicos #10 e #11 e mais outras coisas aqui estão um tanto fora do tópico "Código para obter deslocamento de arquivo de swap no Linux". São informações relevantes mas em outros contextos

 

Na verdade o programa apresentado retorna o tamanho do primeiro extent de um arquivo cujo nome é fornecido na linha de comando e nada tem a ver com swap. Basta ser um arquivo regular.

 

Como esse é um forum frequentado por iniciantes achei que podia ser interessante sair um pouco do tópico e mostrar o que seria o programa AWK de uma linha e que te levou a escrever esse programa C.

 

E pode ser interessante ler algo sobre como tratar registros de tamanho variável e o que são. Então vou mostrar outros exemplos.

 

Uma estrutura de tamanho variável em C

 

struct fiemap {
	__u64 fm_start; /* logical offset (inclusive) at
                         * which to start mapping (in) */
	__u64 fm_length; /* logical length of mapping which
                          * userspace wants (in) */
	__u32 fm_flags;		/* FIEMAP_FLAG_* flags for request (in/out) */
	__u32 fm_mapped_extents;/* number of extents that were mapped (out) */
	__u32 fm_extent_count;  /* size of fm_extents array (in) */
	__u32 fm_reserved;
	struct fiemap_extent fm_extents[]; /* array of mapped extents (out) */
};

 

Essa estrutura tem 32 bytes, a soma dos tamanhos exceto pelo vetor na última linha:

 

	struct fiemap_extent fm_extents[]; /* array of mapped extents (out) */

 

Isso aí é o que deixa o tamanho variável. Podia ser escrito como
 

	struct fiemap_extent fm_extents[0]; /* array of mapped extents (out) */

 

Porque o programa que roda certo está "errado"?

 

Ele está certo porque NESSE CASO tem uma convenção para limitar o tamanho do array do final, esse fm_extents[0]:

preenchendo o campo fm_extent_count antes de chamar a função que preenche o vetor com os dados. Mas nunca vai ler a estrutura toda a menos que o total seja mesmo de 1. E assim pode ser melhor considerar uma técnica mais definitiva e que permita ler a estrutura toda.

 

Como não se sabe o tamanho é preciso descobrir isso antes e depois alocar um registro do tamanho correto, porque ioctl() pode retornar mais do que cabe na área alocada. 

 

Como garantir o tamanho para ler a estrutura toda?

 

Isso é muito usado no Unix que virou Linux, Android, MacOS e outros. E também claro no Windows. Em geral há uma convenção para saber o tamanho da parte variável.

 

Nesse caso aqui é passar fm_extent_count com valor 0. E aí a função ioctl() devolve nesse campo o total de extents e se pode fazer a conta ANTES de alocar a memória.
 

// ok: então chama ioctl e retorna o tamanho esperado
    Mapa info;
    info.fie.fm_length = sb.st_size;
    info.fie.fm_flags = 0; 
    info.fie.fm_extent_count = 0; // vai retornar o tamanho
    if (ioctl(fd, FS_IOC_FIEMAP, &info.fie) != 0)
    {
        perror("ioctl FIEMAP");
        close(fd);
        return -4;
    }

    printf("\t\"%s\" tem %u extents\n", basename(argv[1]), info.fie.fm_mapped_extents);
    close(fd);
    return 0;
// ...

 

Rodaria assim

 

[arfneto@fedora fiemap]$ dd if=/dev/zero of=clube bs=1M count=12
12+0 records in
12+0 records out
12582912 bytes (13 MB, 12 MiB) copied, 3.83195 s, 3.3 MB/s
[arfneto@fedora fiemap]$ filefrag ./clube
./clube: 96 extents found
[arfneto@fedora fiemap]$ ./v4 clube
        "clube" tem 96 extents
[arfneto@fedora fiemap]$ 

 

filefrag mostra que o arquivo tem 96 extents e é isso que o programa mostra no printf.

 

Um outro programa C para o lugar do programa AWK de uma linha

 

Eu só disse dos problemas no tópico #2 mas não mostrei uma opção. Então

 

Um exemplo em C

 

__u64 extent(const char* swap)
{
typedef struct
{
    struct fiemap fie;
    struct fiemap_extent reserva;
} Mapa;

    int fd = open(swap, O_RDONLY);

// chama fstat() para definir o tamanho
    struct stat sb;
    if (fstat(fd, &sb) != 0)
    {
        perror("fstat");
        close(fd);
        exit(EXIT_FAILURE);
    }

// ok: então chama ioctl e retorna o tamanho esperado
    Mapa info = {0};
    info.fie.fm_length = sb.st_size;
    info.fie.fm_flags = FIEMAP_FLAG_SYNC; // pode ficar sem isso
    info.fie.fm_extent_count = 1; // so interessa o primeiro extent
    if (ioctl(fd, FS_IOC_FIEMAP, &info.fie) != 0)
    {
        perror("ioctl FIEMAP");
        close(fd);
        exit(EXIT_FAILURE);
    }
    return info.fie.fm_extents[0].fe_physical / (__u64) getpagesize();
}

 

A diferença maior em relação ao programa original é essa struct:

 

   typedef struct
   {
     struct fiemap fie;
     struct fiemap_extent reserva;
   } Mapa;

 

Não há razão para usar alocação dinâmica para ler algo de tamanho conhecido. Essa struct Mapa garante o tamanho necessário e é mais legível. Claro que é muitas vezes mais rápido, mas não faz diferença aqui.

 

E alguns testes não são importantes: ioctl() vai falhar se o arquivo passado não for um arquivo regular então é melhor deixar o erro acontecer lá e no fluxo normal economizar uns testes.

 

    info.fie.fm_flags = FIEMAP_FLAG_SYNC; // pode ficar sem isso

 

fm_flags podia ser zero. Afinal para um arquivo de swap não vai fazer diferença um sync e essa flag só atrasa as coisas.

 

On-topic: trata-se de um arquivo de swap

 

Os arquivos de swap então em /proc/swaps. Esse arquivo é uma aberração no Unix porque tem um titulo, mas assim é.
 

[arfneto@fedora fiemap]$ cat /proc/swaps
Filename                                Type            Size            Used            Priority
/dev/zram0                              partition       2000892         955648          100
/my_swap                                file            1048572         0               -2
[arfneto@fedora fiemap]$ 

 

Assim em relação ao programa C original

  • não é preciso digitar argumentos na linha de comando porque a lista correta está sempre disponível
  • pode ter mais de um arquivo de swap
  • o arquivo pode estar em uma partição tipo 82 num disco comum, ou num ramdisk e não em um arquivo

EXEMPLO

 

Esse programa chama a função extent() acima e retorna o tamanho do primeiro extent, como o programa original e como o programa AWK de uma linha original

 

int main(void)
{
    const char* swap_list = "/proc/swaps";
    FILE* fd = fopen(swap_list, "r");
    if (fd == NULL)
    {
        perror("open");
        return -1;
    }

    const char* formato = "%s %s";
    char buffer[512];
    char* p = fgets(buffer, sizeof(buffer), fd); //  pula header
    int res = 0;
    char     tipo[20] = {0};
    char     nome[200] = {0};    
    while( NULL != (p = fgets(buffer, sizeof(buffer), fd)))
    {
        res = sscanf(buffer,formato,nome,tipo);
        if ( res != 2 ) break; // deu m.
        if ( 0 != strcmp(tipo,"file" )) continue;
        __u64 size = extent(nome);
        printf("%llu\n" , size);
        break;
    }
    fclose(fd);
    return 0;
}

 

O programa completo

 

#define _FILE_OFFSET_BITS 64
#define _GNU_SOURCE

#include <fcntl.h>
#include <linux/fiemap.h>
#include <linux/fs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>

void uso(const char*);
__u64 extent(const char*);

int main(void)
{
    const char* swap_list = "/proc/swaps";
    FILE* fd = fopen(swap_list, "r");
    if (fd == NULL)
    {
        perror("open");
        return -1;
    }

    const char* formato = "%s %s";
    char buffer[512];
    char* p = fgets(buffer, sizeof(buffer), fd); //  pula header
    int res = 0;
    char     tipo[20] = {0};
    char     nome[200] = {0};    
    while( NULL != (p = fgets(buffer, sizeof(buffer), fd)))
    {
        res = sscanf(buffer,formato,nome,tipo);
        if ( res != 2 ) break; // deu m.
        if ( 0 != strcmp(tipo,"file" )) continue;
        __u64 size = extent(nome);
        printf("%llu\n" , size);
        break;
    }
    fclose(fd);
    return 0;
}


__u64 extent(const char* swap)
{
typedef struct
{
    struct fiemap fie;
    struct fiemap_extent reserva;
} Mapa;

    int fd = open(swap, O_RDONLY);

// chama fstat() para definir o tamanho
    struct stat sb;
    if (fstat(fd, &sb) != 0)
    {
        perror("fstat");
        close(fd);
        exit(EXIT_FAILURE);
    }

// ok: então chama ioctl e retorna o tamanho esperado
    Mapa info = {0};
    info.fie.fm_length = sb.st_size;
    info.fie.fm_flags = FIEMAP_FLAG_SYNC; // pode ficar sem isso
    info.fie.fm_extent_count = 1; // so interessa o primeiro extent
    if (ioctl(fd, FS_IOC_FIEMAP, &info.fie) != 0)
    {
        perror("ioctl FIEMAP");
        close(fd);
        exit(EXIT_FAILURE);
    }
    return info.fie.fm_extents[0].fe_physical / (__u64) getpagesize();
}

 

Um exemplo da saída

 

[toninho@fedora fiemap]$ uname -a
Linux fedora 6.2.15-300.fc38.x86_64 #1 SMP PREEMPT_DYNAMIC Thu May 11 17:37:39 UTC 2023 x86_64 GNU/Linux
[arfneto@fedora fiemap]$ gcc --version | head -1
gcc (GCC) 13.1.1 20230511 (Red Hat 13.1.1-2)
[arfneto@fedora fiemap]$ filefrag -v /my_swap
Filesystem type is: 9123683e
File size of /my_swap is 1073741824 (262144 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length:   expected: flags:
   0:        0..       0:    2366720..   2366720:      1:            
   1:        1..  262143:    2366721..   2628863: 262143:             last,unwritten,eof
/my_swap: 1 extent found
[arfneto@fedora fiemap]$ ./v3
2366720
[arfneto@fedora fiemap]$ 

 

 

Link para o comentário
Compartilhar em outros sites

  • Membro VIP
15 horas atrás, arfneto disse:

Em versões recentes de kernel funciona também com btrfs sim. Veja nessa maquina

 

Não funciona. O valor retornado pela ioctl no Btrfs é fajuto:

 

https://bugzilla.kernel.org/show_bug.cgi?id=202803#c3

 

15 horas atrás, arfneto disse:

Sim, tem razão, seu programa roda certinho.

 

🙏

 

15 horas atrás, arfneto disse:

Porque o programa que roda certo está "errado"?

 

you_failedpassed.jpg.6ffafa47dc6e875219c2b5532d195ade.jpg

Link para o comentário
Compartilhar em outros sites

@Marcos FRM Algo assim! Muito bom. 

 

seu programa trata apenas de substituir um programa em AWK de uma linha, e lê um número no meio de uma estrutura de tamanho variável. Nada tem a ver com swap ou sistemas de arquivos, mas a discussão e as referências podem ser relevantes. Não testei o ioctl com minha máquina e hibernação. Não tenho tempo e não uso. Talvez outra hora. Só disse que para /proc/swaps e para  ioctl funcionou. E o programa que te mostrei é algo mais legível e sólido. E descrevi a maneira oficial de ler a estrutura toda ou ao menos saber quanto alocar para ler a parte de que precisa.

 

[off-topic]  

 

image.png.c732d74d293af44741edf4e2c78ffb82.png

 

Essa é uma discussão de 4 anos atrás, mas David diz em 2023 que tem um comando para conseguir

Link para o comentário
Compartilhar em outros sites

@Marcos FRM Eu só tenho uma máquina que dá pra testar, e é uma instalação recente

Entendeu que pode usar /proc/swaps e aquela estrutura que te mostrei e deixar o programa mais simples? Leu o programa que te mostrei?

 

Não que eu ache que o programa em AWK não seja melhor opção 😉  

Link para o comentário
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisa ser um usuário para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...

 

GRÁTIS: ebook Redes Wi-Fi – 2ª Edição

EBOOK GRÁTIS!

CLIQUE AQUI E BAIXE AGORA MESMO!