printf( "\nEND-------------------------------------\n" ); } disconnect(); /* отключиться от сервера */ exit(0); } /* P_server.c ---------------------------------------------------------*/ /* * Процесс-сервер, принимающий запросы и выполняющий их. */ #include <stdio.h> #include <signal.h> #include <fcntl.h> #include "P_packet.h" int datapipe, ctrlpipe, datafile, got_sig; char *dataname = "/etc/passwd"; /* waiting for signal */ #define WAITSIG while( !got_sig ) void handler(nsig){ signal( SIGUSR1, handler ); /* reset trap */ got_sig++; } /* завершение работы сервера: уничтожить каналы связи */ void die(nsig){ unlink( CNAME ); unlink( DNAME ); exit(0); /* Если эти файлы были открыты клиентами, * то клиенты не умрут, хотя имена файлов и будут удалены! */ } main(){ struct packet pk; struct packet sendpk; /* сделать стандартный вывод небуферизованным каналом */ setbuf( stdout, NULL ); /* make unbuffered */ /* создать каналы связи */ mknod( DNAME, S_IFIFO | 0666, 0 ); /* create FIFO */ mknod( CNAME, S_IFIFO | 0666, 0 ); /* create FIFO */ /* по этим сигналам будет вызываться функция die() */ signal( SIGINT, die ); signal( SIGQUIT, die ); signal( SIGTERM, die ); /* Открыть управляющий канал связи. O_NDELAY означает, * что файл открывается для "чтения без ожидания", * т.е. если канал пуст (нет заявок), то системный вызов * read() не будет "спать", дожидаясь появления информации, * а просто вернет 0 (прочитано 0 байт). * Этот флаг применим также к чтению с терминала. */ ctrlpipe = open( CNAME, O_RDONLY | O_NDELAY ); if( ctrlpipe < 0 ){ printf( "Can't open %s\n", CNAME ); die(0); } datafile = open( dataname, O_RDONLY ); if( datafile < 0 ){ printf( "Can't open %s\n", dataname ); die(0); } /* заранее формируем пакет для ответов */ sendpk.pk_code = SENDPID; sendpk.pk_pid = getpid(); /* server's pid */ sendpk.pk_blk = (-1); printf( "Server pid=%d\n", getpid()); handler(0); for(;;){ int n; static long i = 0L; /* active spin loop */ printf( "%20ld\r", i++ ); /* опрашивать канал насчет поступления запросов */ while((n = read( ctrlpipe, &pk, sizeof(pk))) > 0 ){ putchar( '\n' ); if( n != sizeof pk ){ printf( "Wrong packet size\n" ); continue; } /* обработать прочитанный запрос */ process( &pk, &sendpk ); } } die(0); } process( pkp, spkp ) struct packet *pkp, *spkp; { char pbuf[ PBUFSIZE ]; /* Запись в FIFO-файл будет произведена только если * он уже открыт для чтения */ datapipe = open( DNAME, O_WRONLY | O_NDELAY ); printf( "REQUEST TYPE_%d from pid=%d blk=%d\n", pkp->pk_code, pkp->pk_pid, pkp->pk_blk ); switch( pkp -> pk_code ){ case CONNECT: /* ответить своим идентификатором процесса */ write( datapipe, spkp, sizeof( struct packet )); break; case RQ_READ: /* ответить блоком информации из файла */ /* read block # pk_blk */ lseek( datafile, pkp -> pk_blk * (long)PBUFSIZE, 0 ); read( datafile, pbuf, PBUFSIZE ); write( datapipe, pbuf, PBUFSIZE ); break; case DISCONNECT: /* подтвердить отключение */ printf( "Client pid=%d finished\n", pkp -> pk_pid ); write ( datapipe, spkp, sizeof( struct packet )); break; case BYE: /* завершиться */ printf( "Server terminated.\n" ); kill( pkp-> pk_pid, SIGKILL ); die(0); default: printf( "Unknown packet type %d\n", pkp -> pk_code ); break; } close( datapipe ); /* "подтолкнуть" отправителя сигналом */ got_sig = 0; kill( pkp -> pk_pid , SIGUSR1 ); printf( "Waiting for reply... " ); /* ждать сигнала-подтверждения от клиента */ WAITSIG; printf( "server continued\n" ); } /* Пример 26 */ /* Общение процессов при помощи общей памяти и семафоров. * Вызов: shms & * shmc a & shmc b & shmc c & */ /* --------------------------- файл shm.h ----------------------- */ #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/sem.h> #include <signal.h> #include <errno.h> extern errno; /* Системный код ошибки */ struct connect { /* Структура почтового ящика */ int pid; int msgnum; int max; char message[128]; /* текст сообщения */ }; #define NSEMS 3 /* число семафоров */ /* Имена семафоров */ #define EMPTY 0 /* 1 - ящик пуст; 0 - содержит письмо */ #define NOTEMPTY 1 /* негатив для EMPTY */ #define ACCESS 2 /* 1 - ящик доступен (закрыт); * 0 - ящик уже открыт кем-то еще */ /* Значения семафоров */ #define YES 1 #define NO 0 /* Операции */ #define OPEN 1 #define CLOSE (-1) #define TEST_NO 0 #ifdef COMMENT Алгоритм одновременного изменения семафоров: semop Дано: аргумент: число семафоров : nsems аргумент: величины изменения : sem_op[i] в ядре: текущие значения семафоров группы sem_id: sem[i] Алгоритм: again: Сохранить значения всех семафоров (для отмены изменений); for(i=0; i<nsems; i++) /* OPEN */ if( sem_op[i] > 0 ){ sem[i] += sem_op[i]; разбудитьЖдущихСобытие( "sem[i]++" ); /* CLOSE */ }else if( sem_op[i] < 0 ){ if((newsm = sem[i] + sem_op[i]) >= 0 ){ sem[i] = newsm; if( sem[i] == 0 ) разбудитьЖдущихСобытие( "sem[i]==0" ); }else{ восстановитьВсеСемафоры; ждатьСобытие( "sem[i]++" ); goto again; } /* TEST0 */ }else{ /* sem_op[i] == 0 */ if( sem[i] != 0 ){ восстановитьВсеСемафоры; ждатьСобытие( "sem[i]==0" ); goto again; } } Алгоритм синхронизации в нашей схеме КЛИЕНТ-СЕРВЕР: |----------------------------------------------------------------| |семафоры: EMPTY ACCESS | |----------------------------------------------------------------| |начальное значение: YES YES | |----------------------------------------------------------------| СЕРВЕР |================================================================| |loop: | |----------------------------------------------------------------| |ждать: NO YES | |сделать: NO(test0) NO(close) | |----------------------------------------------------------------| | прочесть почту; | |----------------------------------------------------------------| |из: NO NO | |сделать: YES(open) YES(open) | |----------------------------------------------------------------| | goto loop; | |================================================================| КЛИЕНТ |================================================================| |loop: | |----------------------------------------------------------------| |ждать: YES YES | |сделать: YES(test!=0) NO(close) | |----------------------------------------------------------------| | записать почту; | |----------------------------------------------------------------| |из: YES NO | |сделать: NO(close) YES(open) | |----------------------------------------------------------------| | goto loop; | |================================================================| К сожалению, операции test!=0 не существует - приходится вводить дополнительный семафор NOTEMPTY, негативный для EMPTY: |----------------------------------------------------------------| |семафоры: EMPTY NOTEMPTY ACCESS | |----------------------------------------------------------------| |начальное значение: YES NO YES | |----------------------------------------------------------------| СЕРВЕР |================================================================| |loop: | |----------------------------------------------------------------| |ждать: NO - YES | |сделать: NO(test0) - NO(close) | |----------------------------------------------------------------| | прочесть почту; | |----------------------------------------------------------------| |из: NO YES NO | |сделать: YES(open) NO(close) YES(open) | |----------------------------------------------------------------| | goto loop; | |================================================================| КЛИЕНТ |================================================================| |loop: | |----------------------------------------------------------------| |ждать: - NO YES | |сделать: - NO(test0) NO(close) | |----------------------------------------------------------------| | записать почту; | |----------------------------------------------------------------| |из: YES NO NO | |сделать: NO(close) YES(open) YES(open) | |----------------------------------------------------------------| | goto loop; | |================================================================| #endif /*COMMENT*/ /* Общая часть сервера и клиента ------------------------------- */ key_t key = 1917; /* Уникальный ключ для доступа */ int shm_id; /* Дескриптор для доступа к общей памяти */ int sem_id; /* Дескриптор для доступа к семафорам */ char name[40]; /* имя программы */ char far *addr; struct connect far *caddr; struct sembuf ops[NSEMS]; /* EMPTY NOTEMPTY ACCESS */ short values[NSEMS] = { YES, NO, YES }; void semtell(msg, name) char *msg, *name; { int i; semctl(sem_id, NSEMS, GETALL, values); printf( "%s %-10s: значения семафоров:", name, msg); for(i=0; i < NSEMS; i++) printf( " %d", values[i]); putchar('\n'); } void inisem(){ register i; for(i=0; i < NSEMS; i++ ) ops[i].sem_flg = 0; } /* --------------------------- файл shms.c ----------------------- */ /* Shared memory server */ #include "shm.h" int npack; /* номер сообщения */ void cleanup(sig){ /* Уничтожить сегмент общей памяти (это нужно делать явно) */ shmctl( shm_id, IPC_RMID, NULL ); /* Уничтожить семафоры */ semctl( sem_id, NSEMS, IPC_RMID, NULL ); if( npack ) printf( "\t** Всего было %d сообщений **\n", npack+1); exit(0); } void main(){ register i; int pid = getpid(); FILE *fout; sprintf( name, "Server-%03d", pid ); for( i = 1; i <= SIGTERM; i++ ) signal( i, cleanup ); /* Создать разделяемый сегмент */ if((shm_id = shmget( key, sizeof(struct connect), 0644 | IPC_CREAT )) < 0 ){ perror( "shmget" ) ; exit(1); } /* Подключить общий сегмент к произвольному адресу */ if((addr = (char far *) shmat( shm_id, NULL, 0 )) == NULL ){ perror( "shmat" ); cleanup(); } caddr = (struct connect far *) addr; /* Создать группу из NSEMS семафоров */ if((sem_id = semget( key, NSEMS, 0644 |IPC_CREAT |IPC_EXCL)) < 0){ if(errno == EEXIST){ printf( "Сервер уже запущен\n");exit(2); } else{ perror( "semget" ); cleanup(); } } /* Загрузить начальные значения семафоров */ semctl( sem_id, NSEMS, SETALL, values ); setbuf(stdout, NULL); inisem(); printf( "Server is up now. Читай файл MESSAGES.\n"); fout = fopen( "MESSAGES", "w"); for(;;npack++){ printf( "%s: ждет почты\n", name ); semtell("Вход", name); ops[0].sem_num = EMPTY; ops[0].sem_op = TEST_NO; ops[1].sem_num = ACCESS; ops[1].sem_op = CLOSE; semop( sem_id, ops, 2 /* сразу два семафора */); printf( "%s: GOT-%02d/%02d от %d \"%s\"\n", name, caddr->msgnum, caddr->max, caddr->pid, caddr->message); fprintf( fout, "#%03d %02d/%02d от %d \"%s\"\n", npack, caddr->msgnum, caddr->max, caddr->pid, caddr->message); if( ! strcmp(caddr->message, "-exit" )){ printf( "%s: завершает работу.\n", name ); cleanup(); } semtell("Выход", name); ops[0].sem_num = EMPTY ; ops[0].sem_op = OPEN; ops[1].sem_num = NOTEMPTY; ops[1].sem_op = CLOSE; ops[2].sem_num = ACCESS ; ops[2].sem_op = OPEN; semop( sem_id, ops, 3 /* сразу три семафора */); } /*NOTREACHED*/ } /* --------------------------- файл shmc.c ----------------------- */ /* Shared memory client */ #include "shm.h" void ignsigs(sig){ register i; for( i = 1; i <= SIGTERM; i++ ) signal( i, ignsigs ); printf( "Клиент игнорирует сигналы,\n\ чтобы не оставлять закрытых семафоров в случае своей смерти.\n" ); } void main(argc, argv) char **argv; { int pid = getpid(); int i, ntimes = 60; if( argc < 2 ){ fprintf( stderr, "Вызов: %s сообщение [числоПовторов]\n", argv[0] ); fprintf( stderr, "сообщение \"-exit\" завершает сервер\n"); fprintf( stderr, "сообщение \"-info\" выдает значения семафоров\n"); exit(1); } if( argc > 2 ) ntimes = atoi(argv[2]); sprintf( name, "Client-%03d", pid); ignsigs(); srand( pid ); /* Получить доступ к разделяемому сегменту */ if((shm_id = shmget( key, sizeof(struct connect), 0644)) < 0 ){ perror( "shmget" ); exit(2); } /* Подключить общий сегмент к произвольному адресу */ if((addr = (char far *) shmat( shm_id, NULL, 0 )) == NULL ){ perror( "shmat" ); exit(3); } caddr = (struct connect far *) addr; /* Получить доступ к семафорам */ if((sem_id = semget( key, NSEMS, 0644)) < 0 ){ perror( "semget" ); exit(4); } setbuf(stdout, NULL); inisem(); if( !strcmp(argv[1], "-info")){ semtell("Информация", name); exit(0); } for( i=0; i < ntimes; i++ ){ printf( "%s: ждет пустого ящика\n", name); semtell("Вход", name); ops[0].sem_num = NOTEMPTY; ops[0].sem_op = TEST_NO; ops[1].sem_num = ACCESS ; ops[1].sem_op = CLOSE; if( semop( sem_id, ops, 2 /* сразу два семафора */) < 0) goto err; caddr->pid = pid; caddr->msgnum = i; caddr->max = ntimes; strncpy( caddr->message, argv[1], sizeof(caddr->message) - 1); printf( "%s: PUT-%02d \"%s\"\n", name, i, argv[1]); semtell("Выход", name); ops[0].sem_num = EMPTY ; ops[0].sem_op = CLOSE; ops[1].sem_num = NOTEMPTY; ops[1].sem_op = OPEN; ops[2].sem_num = ACCESS ; ops[2].sem_op = OPEN; if( semop( sem_id, ops, 3 /* сразу три семафора */) < 0) goto err; if( rand()%2 ) sleep(2); /* пауза */ } shmdt( addr ); /* Отключиться от общего сегмента */ exit(0); err: perror("semop"); exit(5); } /* Пример 27 */ /* Коммуникация процессов при помощи псевдо-терминала. * Данная программа позволяет сохранять полный протокол работы * экранной программы в файл. * Не экранные программы данная версия НЕ трассирует, * поскольку сама работает в "прозрачном" режиме. * * Вариацией данной программы может служить использование * системного вызова select() вместо запуска нескольких процессов. * * Программа также иллюстрирует "дерево" из 5 процессов. * Данная версия написана для UNIX System V. * TRACE__ * \ \ master slave * |экран<======\(Reader)=======!~!<====(целевая ) * / <==\ | ! !====>(программа) * \ | !P! | * | | !T! | * . . . . | | !Y! (Slave)-->Управляет * клавиатура=|===|=>(Writer)=>!_! | \ семафором * | | | | \ * | #####starter################## \ * |...................................| * ftty */ #include <stdio.h> #include <sys/types.h> #include <sys/signal.h> #include <termio.h> #include <sys/stat.h> #include <fcntl.h> extern int exit (); extern char *ttyname (); extern FILE * fopen (); extern errno; #define SEMAPHORE "/tmp/+++" /* семафорный файл */ #define TRACE "./TRACE" /* файл с протоколом */ /* псевдотерминал связи */ /* master - это часть, которая ведет себя как ФАЙЛ и умеет * реагировать на некоторые специальные ioctl()-и */ #define PTY "/dev/ptyp0" /* master */ /* slave - это часть, которая ведет себя как драйвер терминалов */ #define TTYP "/dev/ttyp0" /* slave */ int ptyfd; FILE * ftrace = NULL; /* при прерывании завершить работу процесса "писателя" */ onintr () { closeVisual (); fprintf (stderr, "\rwriter finished\r\n"); exit (0); } /* завершение работы процесса-"читателя" */ bye () { if (ftrace) fclose (ftrace); fprintf (stderr, "\rreader finished\r\n"); exit (0); } int visual = 0; struct termio old, new; /* настроить режимы работы терминала на "прозрачный" режим */ initVisual () { ioctl (0, TCGETA, &old); new = old; new.c_iflag &= ~ICRNL; new.c_lflag &= ~(ECHO | ICANON); new.c_oflag &= ~(TAB3 | ONLCR); new.c_cc[VMIN] = 1; new.c_cc[VTIME] = 0; /* new.c_cc[VINTR] = ctrl('C'); */ new.c_cc[VQUIT] = 0; new.c_cc[VERASE] = 0; new.c_cc[VKILL] = 0; } /* включить прозрачный режим */ openVisual () { if (visual) return; visual = 1; ioctl (0, TCSETAW, &new); } /* выключить прозрачный режим */ closeVisual () { if (!visual) return; visual = 0; ioctl (0, TCSETAW, &old); } struct stat st; main (argc, argv) char **argv; { int r, /* pid процесса-"читателя" */ w; /* pid процесса-"писателя" */ if (argc == 1) { fprintf (stderr, "pty CMD ...\n"); exit (1); } initVisual (); if((ptyfd = open ( PTY , O_RDWR)) < 0){ fprintf(stderr, "Cannot open pty\n"); exit(2); } /* запустить процесс чтения с псевдодисплея */ r = startReader (); /* запустить процесс чтения с клавиатуры */ w = startWriter (); sleep (2); /* запустить протоколируемый процесс */ startSlave (argv + 1, r, w); /* дождаться окончания всех потомков */ while (wait (NULL) > 0); exit (0); } /* запуск протоколируемого процесса */ startSlave (argv, r, w) char **argv; { FILE * ftty; int pid; int tfd; char *tty = ttyname (1); /* полное имя нашего терминала */ if (!(pid = fork ())) { /* PTY SLAVE process */ ftty = fopen (tty, "w"); /* Для выдачи сообщений */ setpgrp (); /* образовать новую группу процессов ; * лишиться управляющего терминала */ /* закрыть стандартные ввод, вывод, вывод ошибок */ close (0); close (1); close (2); /* первый открытый терминал станет управляющим для процесса, * не имеющего управляющего терминала. * Открываем псевдотерминал (slave) в качестве стандартных * ввода, вывода и вывода ошибок */ open ( TTYP, O_RDWR); open ( TTYP, O_RDWR); tfd = open ( TTYP, O_RDWR); if (tfd < 0) { fprintf (ftty, "\rSlave: can't read/write pty\r\n"); kill(r, SIGKILL); kill(w, SIGKILL); exit (1); } /* запускаем целевую программу */ if (!(pid = fork ())) { fprintf (ftty, "\rCreating %s\r\n", SEMAPHORE); fflush (ftty); /* создаем семафорный файл */ close (creat (SEMAPHORE, 0644)); fprintf (ftty, "\rStart %s\r\n", argv[0]); fclose(ftty); /* заменить ответвившийся процесс программой, * указанной в аргументах */ execvp (argv[0], argv); exit (errno); } /* дожидаться окончания целевой программы */ while (wait (NULL) != pid); /* уничтожить семафор, что является признаком завершения * для процессов чтения и записи */ unlink (SEMAPHORE); fprintf (ftty, "\rDied.\r\n"); fflush (ftty); /* убить процессы чтения и записи */ /* terminate reader & writer */ kill (r, SIGINT); kill (w, SIGINT); exit (0); } return pid; } /* Пара master-процессов чтения и записи */ /* запуск процесса чтения с псевдотерминала (из master-части) */ startReader () { char c[512]; int pid; int n; if (!(pid = fork ())) { /* читать данные с ptyp на экран и в файл трассировки */ signal (SIGINT, bye); /* ожидать появления семафора */ while (stat (SEMAPHORE, &st) < 0); fprintf (stderr, "\rReader: Hello\r\n"); ftrace = fopen (TRACE, "w"); /* работать, пока существует семафорный файл */ while (stat (SEMAPHORE, &st) >= 0) { /* прочесть очередные данные */ n = read (ptyfd, c, 512); if( n > 0 ) { /* записать их на настоящий терминал */ fwrite( c, sizeof(char), n, stdout ); /* и в файл протокола */ fwrite( c, sizeof(char), n, ftrace ); fflush (stdout); } } bye (); } return pid; } /* запуск процесса чтения данных с клавиатуры и записи * их на "псевдоклавиатуру". Эти данные протоколировать не надо, * так как их эхо-отобразит сам псевдотерминал */ startWriter () { char c; int pid; if (!(pid = fork ())) { /* читать клавиатуру моего терминала и выдавать это в ptyp */ openVisual (); /* наш терминал - в прозрачный режим */ signal (SIGINT, onintr); while (stat (SEMAPHORE, &st) < 0); fprintf (stderr, "\rWriter: Hello\r\n"); /* работать, пока существует семафорный файл */ while (stat (SEMAPHORE, &st) >= 0) { read (0, &c, 1); /* читать букву с клавиатуры */ write (ptyfd, &c, 1); /* записать ее на master-pty */ } onintr (); /* завершиться */ } return pid; } /* Пример 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); } /* Пример 29 */ /* * Программа восстановления блоков удаленного файла. * Работает на канонической файловой системе UNIX (ДЕМОС). * Просматривает список свободных блоков диска. * * Эта программа позволяет восстановить блоки ТОЛЬКО ЧТО удаленного файла. * Как только вы удалили нужный файл, немедленно прекратите любую * работу на машине и даже отмонтируйте диск с удаленным файлом. * Затем, находясь на ДРУГОМ диске, вызовите эту программу. */ #include <stdio.h> #include <sys/types.h> #include <sys/param.h> /* BSIZE */ #include <sys/filsys.h> /* struct filsys */ #include <sys/fblk.h> /* struct fblk */ #include <fcntl.h> #include <ctype.h> /* #define BSIZE 1024 размер блока файловой системы */ int fd; /* raw disk */ int fdout; /* дескриптор для спасенных блоков на ДРУГОМ диске */ char blk[ BSIZE ], /* буфер для прочитанного блока */ sublk[ BSIZE ]; /* буфер для суперблока */ /* структура суперблока */ struct filsys *super = (struct filsys *) sublk; /* счетчик */ long n = 0L; main( ac, av ) char *av[]; { daddr_t bno; /* номер блока из списка свободных */ extern daddr_t alloc(); if( ac < 2 ){ fprintf( stderr, "Usage: %s disk\n", av[0] ); exit(1); } if((fd = open( av[1], O_RDONLY )) < 0 ){ fprintf( stderr, "Can't read %s\n", av[1] ); exit(2); } sync(); /* syncronize */ printf( "Вы должны находиться на ДРУГОМ диске, нежели %s,\n", av[1] ); printf( "чтобы блоки файлов, в которые будут записаны спасаемые\n"); printf( "блоки, выделялись на другом устройстве и не портили\n" ); printf( "список свободных блоков на %s\n\n", av[1] ); fflush( stdout ); sleep(2); /* прочесть суперблок */ lseek( fd, (long) BSIZE, 0 ); read( fd, sublk, BSIZE ); fprintf( stderr, "%ld free blocks at %s (%6.6s)\n" , super->s_tfree, av[1], super->s_fpack ); /* Просмотр свободных блоков. Список свободных блоков * имеет организацию LIFO (стек), поэтому блоки * в списке могут идти не в том порядке, * в котором они шли в файле. Учтите, что в файле * кроме блоков, содержащих текст файла, * бывают также косвенные адресные блоки ! */ while((bno = alloc()) >= 0L ){ save( bno ); } printf( "total %ld\n", n ); exit(0); } /* Извлечь очередной блок из списка свободных блоков */ daddr_t alloc(){ daddr_t bno; if( super -> s_nfree <= 0 ) /* число адресов своб. блоков, * хранимых в суперблоке */ goto nospace; /* читаем номер блока из списка свободных */ bno = super -> s_free[ --super -> s_nfree ]; if( bno == (daddr_t) 0 ) goto nospace; if( super -> s_nfree <= 0 ){ /* Продолжение списка - не в суперблоке, * а в специальном дополнительном блоке файловой системы. */ printf( "Indirect block %ld\n", bno ); lseek( fd, (long) BSIZE * bno , 0 ); read ( fd, blk, BSIZE ); super -> s_nfree = ((struct fblk *)blk) -> df_nfree ; memcpy( (char *) (super -> s_free), (char *) (((struct fblk *) blk) -> df_free ), sizeof( super->s_free)); } if( super -> s_nfree <= 0 || super -> s_nfree > NICFREE ){ fprintf( stderr, "Bad free count %d\n", super->s_nfree ); goto nospace; } if( super -> s_tfree ) /* кол-во свободных блоков */ super -> s_tfree --; return bno; nospace: super -> s_nfree = 0; super -> s_tfree = 0; return (-1L); /* конец списка */ } /* пересылка участка памяти длиной n байт */ memcpy( to, from, n ) register char *to, *from; register n; { while( n > 0 ){ *to++ = *from++; n--; } } save( bno ) daddr_t bno; { register i; char answer[ 20 ]; printf( "block %ld-------------------\n", bno ); lseek( fd, bno * BSIZE , 0 ); read ( fd, blk, BSIZE ); for( i=0; i < BSIZE; i++ ) putchar(isprint(blk[i]) || isspace(blk[i]) ? blk[i] : '.' ); printf( "\n\7===> save block %ld ? ", bno ); fflush( stdout ); gets( answer ); if( *answer == 'y' || *answer == 'Y' ){ sprintf( answer, "#%012ld", n ); fdout = creat( answer, 0644 ); if( fdout < 0 ){ fprintf( stderr, "Can't create %s\n", answer ); exit(3); } write( fdout, blk, BSIZE ); close( fdout ); } n++; } /* Пример 30 */ /* /bin/cc -M2 -Ml -DMATCHONLY -LARGE dosfs.c match.c -o dosfs * Копирование файлов с дискеты, записанной в MS DOS, в UNIX. * Предполагается, что ваша UNIX-машина имеет соответствующий драйвер * для чтения дискет, сформатированных на IBM PC. * match.c - файл, содержащий текст функции match(). */ #include <stdio.h> #include <fcntl.h> #include <ctype.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> extern char *malloc(); /* выделитель памяти */ extern char *strrchr(); /* поиск последнего вхождения буквы */ extern long lseek(); void readBoot(), readFAT(), readRootDir(), main(), line(), getFile(), doDirectory(), mkname(), enterDir(), countFree(), traceclu(); int fd; /* дескриптор файла - дисковода */ FILE *mapfp; /* файл трассировки */ int trace = 0; /* трассировка пока выключена */ int ask = 1; /* спрашивать ли подтверждение на перезапись файлов */ int dironly = 0; /* 1: только показывать имена, файлы не скидывать */ typedef unsigned char uchar; /*typedef unsigned short ushort; Есть в sys/types.h */ /* Формат сектора загрузки */ struct boot { char jmp[3]; /* команда jmp */ char label[8]; /* название системы */ char bfs[2]; /* размер boot-сектора */ uchar sectorsPerCluster; /* число секторов в кластере */ char fatoff[2]; /* смещение до начала FAT */ uchar copies; /* число копий FAT */ char dirsize[2]; /* число записей в корневом каталоге */ char sectors[2]; /* размер дискеты в секторах */ uchar desc; /* описатель типа дискеты */ char FATsize[2]; /* размер FAT в секторах */ char sectorsPerTrack[2]; /* число секторов на трек */ char sides[2]; /* число сторон (1, 2) */ char hidden[2]; /* число спрятанных секторов */ } *boot; #define SECTOR 512 /* Размер сектора в байтах */ int CLU; /* Размер кластера в байтах */ int SPC; /* Размер кластера в секторах */ int SECT; /* Число секторов на дискете */ long capacity; /* емкость дискеты в байтах */ ushort MAXCLU; /* максимальный номер кластера + 1 */ int NDIR; /* Число слотов в корневом каталоге */ int DIRSIZE; /* Длина корневого каталога в байтах */ int ENTRperCLUSTER; /* Количество слотов в одном кластере каталога */ int SPF; /* Размер FAT в секторах */ int FATSIZE; /* Размер FAT в байтах */ int FATSTART; /* Смещение до FAT в байтах */ int NFAT; /* Количество копий FAT */ uchar DESC; /* Описатель типа дискеты */ int DATACLU; /* Начало области данных (номер физич. кластера) */ int bit16 = 0; /* 1 если FAT использует 16-битные поля, а не 12 */ /* Преобразование char[] в integer */ #define INT(s) ( * (short *)s) #define LONG(s) ( * (long *)s) /* Формат одной записи каталога. */ struct dir{ char name[8]; /* имя файла */ char ext[3]; /* расширение (суффикс) */ uchar attrib; /* атрибуты файла */ char unused[10]; char creat_time[2]; /* время создания */ char creat_date[2]; /* дата создания */ char firstCluster[2]; /* начальный кластер */ char size[4]; /* размер в байтах */ }; #define isdir(attr) (attr & 0x10) /* Является ли каталогом ? */ #define islabel(attr) (attr & 0x08) /* Метка тома ? */ #define eq(s1, s2) (!strcmp(s1, s2)) /* сравнение строк на == */ struct dir *droot; /* Содержимое корневого каталога */ char *FAT1; /* File Allocation Table, копия 1 */ char *FAT2; /* копия 2 */ char cwd[256] = ""; /* Текущий каталог в DOS. "" - корневой */ char *root = "/tmp"; /* Каталог в UNIX, куда копируются файлы */ char *pattern = NULL; /* шаблон базового имени */ char *dirpattern; /* каталог (не шаблон) */ char newname[256]; /* буфер дла генерации имен */ char cluster[4098]; /* буфер для чтения кластера */ /* Чтение n байт по адресу s */ Read(fd, s, n) char *s; { int nn = read(fd, s, n); if(nn != n ){ fprintf(stderr, "Ошибка чтения: %d вместо %d\n", nn, n); perror( "read" ); exit(1); } return nn; } /* Позиционирование головок */ long Lseek(fd, off, how) long off; { long offf; if((offf = lseek(fd, off, how)) < 0){ fprintf(stderr, "Ошибка lseek(%ld,%d)\n", off, how); } return offf; } /* Отведение памяти и ее зачистка */ char *Malloc(n) unsigned n;{ char *ptr = malloc(n); register unsigned i; if( !ptr){ fprintf(stderr, "Не могу malloc(%u)\n", n ); exit(2); } for(i=0; i < n ; i++ ) ptr[i] = 0; /* Можно было бы использовать ptr = calloc(1,n); эта функция * как раз отводит и очищает память */ return ptr; } /* Нарисовать горизонтальную черту */ void line(c) char c;{ register i; for(i=0; i < 78; i++) putchar(c); putchar('\n'); } /* Обработка псевдо-имен устройств. Используются имена для XENIX */ char *drive(name) char *name; { if( eq(name, "360")) return "/dev/fd048ds9"; if( eq(name, "720")) return "/dev/fd096ds9"; if( eq(name, "1.2")) return "/dev/fd096ds15"; return name; } /* Создать каталог */ char command[512]; /* буфер дла формирования команд */ mkdir(name, mode) char *name; { int retcode; struct stat st; if( stat(name, &st) >= 0 && (st.st_mode & S_IFMT) == S_IFDIR ) return 0; /* уже есть */ sprintf(command, "mkdir \"%s\"", name ); retcode = system(command); /* выполнить команду, записанную в command */ chmod(name, mode & 0777); /* установить коды доступа */ return retcode; /* 0 - успешно */ } /* Открыть файл, создавая (если надо) недостаюшие каталоги */ FILE *fmdopen(name, mode) char *name, *mode; { extern errno; char *s; FILE *fp; if( fp = fopen(name, mode)) return fp; /* OK */ /* иначе файл не смог создаться */ /* if( errno != ENOENT ) return NULL; /* из-за недостатка прав */ /* Пробуем создать все каталоги по пути к файлу */ if((s = strrchr(name, '/' )) == NULL ) return NULL; *s = '\0'; md(name); *s = '/'; return fopen(name, mode); } /* Рекурсивный mkdir */ md(path) char *path; {