Salta al contenuto
Note per Studenti Esercizio - Frammentazione interna di un file con stat e statvfs (laboratorio 10)

Esercizio - Frammentazione interna di un file con stat e statvfs (laboratorio 10)

In questa pagina 4

Testo (laboratorio 10 del corso di Sistemi Operativi, Ing. Informatica UniPD).

  1. Task 1. Verificare che anche un file di 1 byte occupa almeno 4096 byte (cioè 8×5128\times512 byte): scrivere un carattere in abcde.txt e controllare con stat. Poi scaricare nella propria home il file ENIAC_Penn1.jpg (con wget), controllare il numero di blocchi allocati, calcolare i byte "sprecati" per frammentazione interna e consegnare il risultato.
  2. Task 2. Modificare fileinfo.c (che stampa le informazioni di un file con lstat() e statvfs()) per creare fraginfo.c, un programma che calcola la frammentazione interna di un file dato. Verificare che il risultato sia coerente con quello del Task 1.

Teoria: File system - interfaccia, directory e implementazioneUn file è uno spazio di indirizzi logici contigui con attributi (nome, identificatore, tipo, posizione, dimensione, protezione, date) e operazioni (create, read, write, seek, delete, truncate, open, close); il sistema tiene una tabella dei file aperti di sistema e una per processo; l'accesso è sequenziale o diretto; le directory si organizzano a livello singolo, a due livelli, ad albero, a grafo aciclico (hard link e link simbolici) o a grafo generale; i permessi UNIX rwx per proprietario, gruppo e altri (chmod 754); il file system è a strati (file system logico, organizzazione dei file, file system di base, controllo dell'I/O), con strutture su disco (boot block, superblock, directory, FCB o inode) e in memoria; i blocchi si allocano in modo contiguo (extent), concatenato (FAT) o indicizzato (inode con puntatori diretti e indiretti, ext4 con extent); lo spazio libero con bitmap, lista, raggruppamento o conteggio; mount, VFS, file system distribuiti (NFS) e semantiche di consistenza.File system - interfaccia, directory e implementazione →.


Il comando stat

stat nomefile stampa (tra le altre cose) la dimensione in byte, il numero di blocchi allocati e la dimensione del blocco di I/O:

  File: abcde.txt
  Size: 1948        Blocks: 8          IO Block: 4096   regular file

Per ragioni storiche i blocchi contati da stat (campo st_blocks) sono sempre unità da 512 byte, mentre il blocco fisico di I/O del file system (campo IO Block, st_blksize, e f_bsize di statvfs) è di 4096 byte. Una unità fisica corrisponde quindi a 4096/512=84096/512=8 blocchi contati.

dimensione del file blocchi fisici da 4096 Blocks (da 512) frammentazione interna
1 byte 1 8 4096−1=40954096-1=4095 byte
1948 byte 1 8 4096−1948=21484096-1948=2148 byte
4096 byte 1 8 0
4097 byte 2 16 8192−4097=40958192-4097=4095 byte
17 112 byte 5 40 5⋅4096−17112=33685\cdot4096-17112=3368 byte
1 981 975 byte 484 3872 484⋅4096−1981975=489484\cdot4096-1981975=489 byte

Task 1

File da 1 byte. Occupa comunque un blocco fisico da 4096 byte: Blocks: 8 (8 blocchi da 512), 4095 byte sprecati. Un file vuoto ha invece Blocks: 0.

File ENIAC_Penn1.jpg. La dimensione è 1 981 975 byte e il blocco fisico 4096 byte:

⌈1 981 9754096⌉=⌈483,89⌉=484 blocchi fisici (=3872 blocchi da 512).\left\lceil\frac{1\,981\,975}{4096}\right\rceil=\lceil483{,}89\rceil=484\ \text{blocchi fisici}\ (=3872\ \text{blocchi da 512}).

La memoria allocata è 484⋅4096=1 982 464484\cdot4096=1\,982\,464 byte e la frammentazione interna

1 982 464−1 981 975=489 byte.1\,982\,464-1\,981\,975=\mathbf{489\ byte}.

Il risultato coincide con la soluzione del corso (nell'ultimo blocco sono usati 1 981 975−483⋅4096=36071\,981\,975-483\cdot4096=3607 byte su 4096, ne avanzano 4096−3607=4894096-3607=489).

Task 2: fraginfo.c

Si parte da fileinfo.c, che chiama lstat(argv[1], &sfile) (come stat, ma non segue i link simbolici) e statvfs(argv[1], &sdisk):

  • sdisk.f_bsize è la dimensione del blocco del file system;
  • sfile.st_size è la dimensione in byte del file.

Il numero di blocchi occupati è ⌈dimensione/blocco⌉\lceil\text{dimensione}/\text{blocco}\rceil, e la frammentazione interna è lo spazio allocato meno quello usato:

frammentazione=⌈dimB⌉B−dim.\text{frammentazione}=\left\lceil\frac{\text{dim}}{B}\right\rceil B-\text{dim}.

c
/* fraginfo.c - frammentazione interna dell'ultimo blocco di un file.
 * Compilare:  gcc -Wall -Wextra -o fraginfo fraginfo.c
 * Usare:      ./fraginfo abcde.txt
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/stat.h>
#include <sys/statvfs.h>

int main(int argc, char *argv[]) {
    struct stat sf;
    struct statvfs sv;
    if (argc != 2) { fprintf(stderr, "uso: %s file\n", argv[0]); return EXIT_FAILURE; }
    if (lstat(argv[1], &sf) < 0) { perror("lstat"); return EXIT_FAILURE; }
    if (statvfs(argv[1], &sv) < 0) { perror("statvfs"); return EXIT_FAILURE; }

    intmax_t blocco = (intmax_t)sv.f_bsize; /* blocco del file system */
    intmax_t dim = (intmax_t)sf.st_size;    /* byte del file */
    intmax_t blocchi = (dim + blocco - 1) / blocco; /* blocchi occupati, per eccesso */
    intmax_t sprecati = blocchi * blocco - dim;     /* 0 se la dimensione e' multipla */

    printf("blocco del file system:  %jd byte\n", blocco);
    printf("dimensione del file:     %jd byte\n", dim);
    printf("blocchi necessari:       %jd (stat dice %jd da 512 byte)\n", blocchi, (intmax_t)sf.st_blocks);
    printf("frammentazione interna:  %jd byte\n", sprecati);
    return EXIT_SUCCESS;
}

Compilato con zig cc per Linux, senza avvisi; non eseguito. Per ENIAC_Penn1.jpg l'uscita attesa è: blocco 4096, dimensione 1981975, blocchi necessari 484, frammentazione 489.

Differenza dalla soluzione del corso. La soluzione distribuita calcola

c
int wasted = sdisk.f_bsize - sfile.st_size % sdisk.f_bsize;

cioè B−(dim mod B)B-(\text{dim}\bmod B). Per quasi tutti i file dà il risultato giusto (per esempio 4096 − 3607 = 489 per il file del laboratorio) ma è sbagliata quando la dimensione è un multiplo del blocco: per un file di 4096 byte il resto è 0 e wasted vale 4096, mentre la frammentazione è 0; lo stesso per un file vuoto (0 blocchi, nessuno spreco). Il calcolo ⌈dim/B⌉B−dim\lceil\text{dim}/B\rceil B-\text{dim} è corretto in tutti i casi. In più, il programma del corso usa int per un valore che arriva a intmax_t e non controlla gli argomenti.

Altre osservazioni: st_blocks può essere maggiore del valore calcolato se il file system ha allocato blocchi aggiuntivi (per esempio blocchi di indirezione per file grandi, o preallocazione); per file piccoli o medi su ext4 il valore coincide con quello calcolato qui. Il numero st_blocks è la verifica incrociata richiesta dal testo: 484⋅8=3872484\cdot8=3872.


Errori tipici

  • Contare i blocchi di stat come da 4096 (sono da 512).
  • Usare la divisione per difetto: servono i blocchi per eccesso (dim / blocco perde l'ultimo blocco parziale).
  • Dimenticare il caso di un file lungo esattamente un multiplo del blocco (frammentazione 0, non BB).
  • Confondere la dimensione del file (st_size) con lo spazio allocato (st_blocks⋅512\cdot512).
  • Scambiare lstat e stat su un link simbolico: lstat descrive il link, stat il file puntato.

Versione ripasso

stat. Blocks è in unità da 512 byte, IO Block (e f_bsize) è 4096: un blocco fisico =8=8 blocchi contati. 1 byte →\to Blocks: 8, 4095 byte sprecati; 1948 byte →\to 8 blocchi; 17 112 byte →\to 40 blocchi, frammentazione 5⋅4096−17112=33685\cdot4096-17112=3368.

Task 1. ENIAC_Penn1.jpg: 1 981 975 byte →⌈1 981 975/4096⌉=484\to\lceil1\,981\,975/4096\rceil=484 blocchi (3872 da 512)(3872\ \text{da 512}); frammentazione 484⋅4096−1 981 975=489484\cdot4096-1\,981\,975=\mathbf{489} byte.

Task 2. lstat(argv[1], &sf), statvfs(argv[1], &sv): blocchi=(dim+B−1)/B\text{blocchi}=(\text{dim}+B-1)/B, frammentazione=blocchi⋅B−dim\text{frammentazione}=\text{blocchi}\cdot B-\text{dim} con B=B= f_bsize. La formula del corso B−dim mod BB-\text{dim}\bmod B dà BB (invece di 0) se dim\text{dim} è multiplo di BB.

Passaggi. lstat e statvfs; B=B= f_bsize (4096); blocchi=⌈dim/B⌉=(dim+B−1)/B\text{blocchi}=\lceil\text{dim}/B\rceil=(\text{dim}+B-1)/B; frammentazione=blocchi⋅B−dim\text{frammentazione}=\text{blocchi}\cdot B-\text{dim}; controllo con st_blocks (blocchi⋅8\text{blocchi}\cdot8). Esempi: 1 byte →\to 8 blocchi da 512, 4095 byte sprecati; 4096 byte →\to 0 (la formula del corso dà 4096); 4097 byte →\to 2 blocchi, 4095; 17 112 byte →\to 5 blocchi (Blocks: 40), 3368; 1 981 975 byte →\to 484 blocchi (38723872 da 512), 489.

Errori tipici: blocchi di stat da 4096; divisione per difetto; multiplo del blocco →\to 0; st_size contro st_blocks; lstat contro stat.

Teoria: File system - interfaccia, directory e implementazioneUn file è uno spazio di indirizzi logici contigui con attributi (nome, identificatore, tipo, posizione, dimensione, protezione, date) e operazioni (create, read, write, seek, delete, truncate, open, close); il sistema tiene una tabella dei file aperti di sistema e una per processo; l'accesso è sequenziale o diretto; le directory si organizzano a livello singolo, a due livelli, ad albero, a grafo aciclico (hard link e link simbolici) o a grafo generale; i permessi UNIX rwx per proprietario, gruppo e altri (chmod 754); il file system è a strati (file system logico, organizzazione dei file, file system di base, controllo dell'I/O), con strutture su disco (boot block, superblock, directory, FCB o inode) e in memoria; i blocchi si allocano in modo contiguo (extent), concatenato (FAT) o indicizzato (inode con puntatori diretti e indiretti, ext4 con extent); lo spazio libero con bitmap, lista, raggruppamento o conteggio; mount, VFS, file system distribuiti (NFS) e semantiche di consistenza.File system - interfaccia, directory e implementazione →.

Teoria collegata