Пример 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
Си в UNIX
Назад | Содержание | Вперед