Пример 28

/* Оценка фрагментированности тома файловой системы
 * (неупорядоченности блоков в файлах).
 * Иллюстрация работы с файловой системой UNIX напрямую,
 * в обход ядра системы. Для этого вы должны иметь права
 * суперпользователя !!! Данная программа относится к классу
 * "системных" (администраторских) программ.
 * Эта программа предполагает каноническую файловую систему V7
 * ("старую"), а не ту, которая используется начиная с BSD/4.2 и
 * в которой все устроено несколько сложнее и эффективнее.
 * Поэтому вы должны будете модифицировать эту программу для
 * использования в современных UNIX-системах.
 * По мотивам книги М.Дансмура и Г.Дейвиса.
 */

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/ino.h>            /* struct dinode: disk inode */
#include <sys/stat.h>           /* struct stat */
#include <sys/dir.h>            /* struct direct */

char blkflag;   /* печатать ли номера блоков файла */

/* Отведение памяти в куче с выдачей ошибки, если нет памяти */
char *MyAlloc( n ){
        extern char *malloc();
        char *ptr;

        ptr = malloc( n );
        if( ptr == NULL ){
                fprintf( stderr, "Cannot allocate %d bytes\n", n );
                exit(77);
        }
        return ptr;
}
char DEV[] = "/dev" ;   /* каталог, где лежат все файлы устройств */

/* Определить имя устройства по его st_dev номеру.
 * Поиск - по каталогу /dev
 */
char *whichdev( dev ) dev_t dev;
{
        struct stat s;
        struct direct d;
        long i;
        int fd;         /* дескриптор чтения каталога */
        long dsize;     /* число слотов каталога */
        char *devname;

        if( stat( DEV, &s ) < 0 ){
                fprintf( stderr, "Cannot stat %s\n", DEV );
                exit(1);
        }

        if((fd = open( DEV, O_RDONLY )) < 0 ){
                fprintf( stderr, "Cannot read %s\n", DEV );
                exit(2);
        }
        dsize = s.st_size / sizeof( struct direct );

        /* читать каталог */
        for( i = 0 ; i < dsize ; i++ ){
                char leaf[ DIRSIZ + 1 ];

                if( read( fd, &d, sizeof d ) != sizeof d ){
                        fprintf( stderr, "Cannot read %s\n", DEV );
                        exit(14);
                }

                if( ! d.d_ino ) continue;  /* пустой слот */

                strncpy( leaf, d.d_name, DIRSIZ );
                leaf[ DIRSIZ ] = '\0';

                devname = MyAlloc( strlen( DEV ) + 1 + strlen( leaf ) + 1 );
                                /*        /dev     /      xxxx         \0  */
                sprintf( devname, "%s/%s", DEV, leaf );
                if( stat( devname, &s ) < 0 ){
                        fprintf( stderr, "Cannot stat %s\n", devname );
                        exit(3);
                }
                if( (s.st_mode & S_IFMT ) == S_IFBLK && s.st_rdev == dev ){
                        close(fd);
                        return devname;
                } else  free( devname );
        }
        close( fd );
        return NULL;
}

/* Файловая система UNIX: константы подстроены под ДЕМОС 2.2 */

/* размер блока файловой системы */
#define BLOCK 1024 /* либо станд. константа BSIZE из <sys/param.h> */

/* число адресов блоков в косвенном блоке */
#define NAPB        (BLOCK/sizeof(daddr_t))
#define LNAPB        ((long) NAPB )

/* число I-узлов в блоке I-файла */
#ifndef INOPB
# define INOPB (BLOCK/sizeof(struct dinode))
#endif

/* I-узлы - "паспорта" файлов. I-узлы расположены в начале диска,
   в области, называемой I-файл. В I-узле файла содержатся:
   размер файла, коды доступа, владелец файла, и.т.п.
   В частности - адреса блоков файла хранятся в массиве di_addr:
   0  :
   ...  сначала   DIR0 адресов первых блоков
   IX1: 1 адрес   косвенного блока, содержащего адреса еще NAPB блоков
   IX2: 1 адрес   косв. блока, содержащего адреса NAPB косв. блоков
   IX3: 1 адрес   косв. блока, содержащего адреса NAPB косв. блоков,
                               содержащих адреса еще NAPB косв. блоков
   Сисвызов stat() выдает как раз часть информации из I-узла.
   Поле d_ino в каталоге хранит номер I-узла файла.
*/

/* число адресных полей по 3 байта в I-узле */
#define NADDR 7

/* число прямо адресуемых блоков */
#define DIR0 ((long)(NADDR-3))

/* число прямых и первых косвенных блоков */
#define DIR1 (DIR0 + LNAPB)

/* число прямых, первых и вторых косвенных блоков */
#define DIR2 (DIR0 + LNAPB + LNAPB*LNAPB)

/* число прямых, вторых и третьих косвенных блоков */
#define DIR3 (DIR0 + LNAPB + LNAPB*LNAPB + LNAPB*LNAPB*LNAPB)

/* индекс адреса первичного блока косвенности */
#define IX1 (NADDR-3)

/* индекс адреса вторичного блока косвенности */
#define IX2 (NADDR-2)

/* индекс адреса третичного блока косвенности */
#define IX3 (NADDR-1)

/* Выдать физический номер блока диска,
 * соответствующий логическому блоку файла
 */
daddr_t bmap( fd, ip, lb )
        int fd;                 /* raw диск */
        daddr_t lb;             /* логический блок */
        struct dinode *ip;      /* дисковый I-узел */
{
        long di_map[ NADDR ];
        long dd_map[ NAPB ];

        /* перевести 3х байтовые адреса в daddr_t */
        l3tol( di_map, ip->di_addr, NADDR );

        if( lb < DIR0 )
                return di_map[ lb ];
        if( lb < DIR1 ){
                lb -= DIR0;

                lseek( fd, di_map[ IX1 ] * BLOCK, 0 );
                read( fd, dd_map, BLOCK );

                return dd_map[ lb % LNAPB ];
        }
        if( lb < DIR2 ){
                lb -= DIR1;

                lseek( fd, di_map[ IX2 ] * BLOCK, 0 );
                read(  fd, dd_map, BLOCK );

                lseek( fd, dd_map[ lb / LNAPB ] * BLOCK, 0 );
                read(  fd, dd_map, BLOCK );

                return dd_map[ lb % LNAPB ];
        }
        if( lb < DIR2 ){
                lb -= DIR2;

                lseek( fd, di_map[ IX3 ] * BLOCK, 0 );
                read(  fd, dd_map, BLOCK );

                lseek( fd, dd_map[ lb / (LNAPB*LNAPB) ] * BLOCK, 0 );
                read(  fd, dd_map, BLOCK );

                lseek( fd, dd_map[ lb % (LNAPB*LNAPB) ] * BLOCK, 0 );
                read(  fd, dd_map, BLOCK );

                return dd_map[ lb % LNAPB ];
        }
        fprintf( stderr, "Strange block %ld\n", lb );
        exit(4);
}

/* Рассчитать фрагментацию файла,
   то есть среднее расстояние между блоками файла.
   Норма равна фактору интерливинга для данного устройства.

                          N
                      SUM          | p(j) - p(j-1) |
                          j = 2
               F =  ---------------------------------                           N

   p(j) - номер физ.блока диска, соответствующего
          логич. блоку j
   Замечания:
   1) I-узлы нумеруются с 1 (а не с 0), 0 - признак пустого
      места в каталоге (d_ino == 0).
   2) I-файл начинается со 2-ого блока диска (0-boot, 1-superblock)
   3) если файл пуст - он не содержит блоков, N = 0, F = 0
   4) если блок не отведен ("дырка"), то его адрес равен 0L
*/

double xabs( l ) daddr_t l;
{
        return ( l < (daddr_t) 0 ? -l : l );
}

double getfrag( dev, ino )
        char *dev;      /* имя диска */
        ino_t ino;      /* I-узел файла */
{
        struct dinode db;
        int fd;         /* дескриптор диска */
        daddr_t i;      /* лог. блок */
        daddr_t op;     /* физ.блок */
        daddr_t ip;
        daddr_t nb;     /* длина файла (блоков) */
        long ni = 0L;   /* число интервалов между блоками */
        double ifrag = 0.0;

        if((fd = open( dev, O_RDONLY )) < 0 ){
                fprintf( stderr, "Cannot read %s\n", dev );
                perror( "open" );
                exit(5);
        }

        /* прочитать I-узел с номером ino.
         * Файл I-узлов размещен на диске начиная со 2 блока
         * по INOPB узлов в блоке.
         */
        lseek( fd, (( 2 + ((ino-1)/INOPB)) * (long)BLOCK )  +
                   ( sizeof(struct dinode) * ((ino-1) % INOPB)),   0 );
        if( read( fd, &db, sizeof db ) != sizeof db ){
                fprintf( stderr, "Cannot read %s\n", dev );
                perror( "read" );
                exit(6);
        }

        /* вычислить размер файла в блоках */
        nb = ((long) db.di_size + BLOCK - 1) / BLOCK;
        printf( "%4ld blk%s\t" , nb, nb > 1 ? "s" : " " );

        /* игнорировать пустой файл */
        if( nb == 0L ){
                close(fd);
                return 0.0;
        }

        /* вычислить фрагментацию */
        op = bmap( fd, &db, 0L );       /* 0-block */
        if( blkflag ) printf( "%ld ", op );

        for( i = 1 ; i < nb ; i++ ){
                ip = bmap( fd, &db, i );
                if( blkflag ) printf( "%ld ", ip );
                /* адреса, равные 0, следует игнорировать ("дырки") */
                if( ip && op ){
                        ni++;
                        ifrag += xabs( ip - op );
                }
                if( ip ) op = ip;
        }
        close ( fd );
        if( blkflag ) putchar( '\n' );
        return ni ? (ifrag/ni) : 0.0 ;
}

double process( name ) char *name;
{
        struct stat ss;
        char *dn;
        double f;

        /* определяем имя устройства, на котором расположен
         * файл name */
        if( stat( name, &ss ) < 0 ){
                fprintf( stderr, "Cannot stat %s\n", name );
                exit(8);
        }
 /* printf( "major %d     minor %d", major(ss.st_dev), minor(ss.st_dev)); */
        if((dn = whichdev( ss.st_dev )) == NULL){
                fprintf( stderr, "Cannot determine device\n" );
                exit(9);
        }

        printf( "%-14s on %-12s %12.3f\n",
                name, dn, f = getfrag(dn, ss.st_ino ));
        free( dn );
        return f;
}

usage( name ) char *name; {
        fprintf( stderr, "Usage: %s [-b] file ...\n" , name );
        exit(7);
}

main(ac, av) char *av[];
{
        double fr = 0.0;
        int n = 0;

        if( ac < 2 )
                usage( av[0] );

        if( !strcmp( av[1], "-b" )){
                blkflag = 1;
                av++;
                ac--;
        }
        while( av[1] ){
                fr += process( av[1] );
                n++;
                av++;
        }
        if( n > 1 )
                printf( "\nAverage %12.3f\n", fr / n );
        exit(0);
}

© Copyright А. Богатырев, 1992-95 www.abyss-group.narod.ru
Си в UNIX

Назад | Содержание | Вперед

Hosted by uCoz