Odnako, esli my ZARANEE znaem imena fajlov v kataloge, my MOZHEM rabo- tat' s nimi - esli imeem pravo dostupa "vypolnenie" dlya etogo kataloga! x vypolnenie S_IEXEC. Razreshaet poisk v kataloge. Dlya otkrytiya fajla, sozdaniya/udaleniya fajla, perehoda v drugoj katalog (chdir), sistema vypolnyaet sleduyushchie dejstviya (osushchestvlyaemye funkciej namei() v yadre): chtenie kataloga i poisk v nem ukazan- nogo imeni fajla ili kataloga; najdennomu imeni sootvetstvuet nomer I-uzla d_ino; po nomeru uzla sistema schityvaet s diska sam I-uzel nuzhnogo fajla i po nemu dobiraetsya do soderzhimogo fajla. Kod "vypolnenie" - eto kak raz razreshenie takogo prosmotra kataloga sistemoj. Esli katalog imeet dostup na chtenie - my mozhem poluchit' spisok fajlov (t.e. primenit' komandu ls); no esli on pri etom ne imeet koda dostupa "vypolnenie" - my ne smozhem poluchit' dostupa ni k odnomu iz fajlov kataloga (ni otkryt', ni udalit', ni sozdat', ni sdelat' stat, ni chdir). T.e. "chtenie" razreshaet primenenie vyzova read, a "vypolnenie" - funkcii yadra namei. Fakticheski "vypolnenie" oznachaet "dostup k fajlam v dannom kataloge"; eshche bolee tochno - k I-nodam fajlov etogo kataloga. t sticky bit S_ISVTX - dlya kataloga on oznachaet, chto udalit' ili pereimenovat' nekij fajl v dannom kataloge mogut tol'ko: vladelec kataloga, vladelec dannogo fajla, super- pol'zovatel'. I nikto drugoj. |to isklyuchaet udalenie fajlov chuzhimi. Sovet: dlya kataloga polezno imet' takie kody dostupa: chmod o-w,+t katalog V sistemah BSD ispol'zuetsya, kak uzhe bylo upomyanuto, format kataloga s peremennoj dlinoj zapisej. CHtoby imet' udobnyj dostup k imenam v kataloge, voznikli special'nye funkcii chteniya kataloga: opendir, closedir, readdir. Pokazhem, kak prostejshaya komanda ls realizuetsya cherez eti funkcii. A. Bogatyrev, 1992-95 - 192 - Si v UNIX #include <stdio.h> #include <sys/types.h> #include <dirent.h> int listdir(char *dirname){ register struct dirent *dirbuf; DIR *fddir; ino_t dot_ino = 0, dotdot_ino = 0; if((fddir = opendir (dirname)) == NULL){ fprintf(stderr, "Can't read %s\n", dirname); return 1; } /* Bez sortirovki po alfavitu */ while ((dirbuf = readdir (fddir)) != NULL ) { if (dirbuf->d_ino == 0) continue; if (strcmp (dirbuf->d_name, "." ) == 0){ dot_ino = dirbuf->d_ino; continue; } else if(strcmp (dirbuf->d_name, "..") == 0){ dotdot_ino = dirbuf->d_ino; continue; } else printf("%s\n", dirbuf->d_name); } closedir (fddir); if(dot_ino == 0) printf("Povrezhdennyj katalog: net imeni \".\"\n"); if(dotdot_ino == 0) printf("Povrezhdennyj katalog: net imeni \"..\"\n"); if(dot_ino && dot_ino == dotdot_ino) printf("|to kornevoj katalog diska\n"); return 0; } int main(int ac, char *av[]){ int i; if(ac > 1) for(i=1; i < ac; i++) listdir(av[i]); else listdir("."); return 0; } Obratite vnimanie, chto tut ne trebuetsya dobavlenie '\0' v konec polya d_name, pos- kol'ku ego predostavlyaet nam sama funkciya readdir(). 6.1.4. Napishite programmu udaleniya fajlov i katalogov, zadannyh v argv. Delajte stat, chtoby opredelit' tip fajla (fajl/katalog). Programma dolzhna otkazyvat'sya uda- lyat' fajly ustrojstv. Dlya udaleniya pustogo kataloga (ne soderzhashchego inyh imen, krome "." i "..") sle- duet ispol'zovat' sisvyzov rmdir(imya_kataloga); (esli katalog ne pust - errno poluchit znachenie EEXIST); a dlya udaleniya obychnyh fajlov (ne katalogov) unlink(imya_fajla); Programma dolzhna zaprashivat' podtverzhdenie na udalenie kazhdogo fajla, vydavaya ego imya, tip, razmer v kilobajtah i vopros "udalit' ?". 6.1.5. Napishite funkciyu rekursivnogo obhoda dereva podkatalogov i pechati imen vseh fajlov v nem. Klyuch U42 oznachaet fajlovuyu sistemu s dlinnymi imenami fajlov (BSD 4.2). A. Bogatyrev, 1992-95 - 193 - Si v UNIX /*#!/bin/cc -DFIND -DU42 -DMATCHONLY treemk.c match.c -o tree -lx * Obhod poddereva katalogov (po motivam Kernigan & Ritchi). * Klyuchi kompilyacii: * BSD-4.2 BSD-4.3 -DU42 * XENIX s kanonicheskoj fajl.sist. nichego * XENIX s bibliotekoj -lx -DU42 * programma poiska fajlov -DFIND * programma rekursivnogo udaleniya -DRM_REC * programma podscheta ispol'zuemogo mesta na diske BEZ_KLYUCHA */ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/param.h> /* dlya MAXPATHLEN */ #if defined(M_XENIX) && defined(U42) # include <sys/ndir.h> /* XENIX + U42 emulyaciya */ #else # include <dirent.h> # define stat(f,s) lstat(f,s) /* ne prohodit' po simvol'nym ssylkam */ # define d_namlen d_reclen #endif /* proverka: katalog li eto */ #define isdir(st) ((st.st_mode & S_IFMT) == S_IFDIR) struct stat st; /* dlya sisvyzova stat() */ char buf[MAXPATHLEN+1]; /* bufer dlya imeni fajla */ #define FAILURE (-1) /* kod neudachi */ #define SUCCESS 1 /* kod uspeha */ #define WARNING 0 /* nefatal'naya oshibka */ /* Soobshcheniya ob oshibkah vo vremya obhoda dereva: */ #ifndef ERR_CANT_READ # define ERR_CANT_READ(name) \ fprintf( stderr, "\tNe mogu chitat' \"%s\"\n", name), WARNING # define ERR_NAME_TOO_LONG() \ fprintf( stderr, "\tSlishkom dlinnoe polnoe imya\n" ), WARNING #endif /* Prototipy dlya predvaritel'nogo ob®yavleniya funkcij. */ extern char *strrchr(char *, char); int directory (char *name, int level, int (*enter)(char *full, int level, struct stat *st), int (*leave)(char *full, int level), int (*touch)(char *full, int level, struct stat *st)); /* Funkcii-obrabotchiki enter, leave, touch dolzhny * vozvrashchat' (-1) dlya preryvaniya prosmotra dereva, * libo znachenie >= 0 dlya prodolzheniya. */ A. Bogatyrev, 1992-95 - 194 - Si v UNIX /* Obojti derevo s kornem v rootdir */ int walktree ( char *rootdir, /* koren' dereva */ int (*enter)(char *full, int level, struct stat *st), int (*leave)(char *full, int level), int (*touch)(char *full, int level, struct stat *st) ){ /* proverka korrektnosti kornya */ if( stat(rootdir, &st) < 0 || !isdir(st)){ fprintf( stderr, "\tPlohoj koren' dereva \"%s\"\n", rootdir ); return FAILURE; /* neudacha */ } strcpy (buf, rootdir); return act (buf, 0, enter, leave, touch); } /* Ocenka fajla s imenem name. */ int act (char *name, int level, int (*enter)(char *full, int level, struct stat *st), int (*leave)(char *full, int level), int (*touch)(char *full, int level, struct stat *st)) { if (stat (name, &st) < 0) return WARNING; /* oshibka, no ne fatal'naya */ if(isdir(st)){ /* pozvat' obrabotchik katalogov */ if(enter) if( enter(name, level, &st) == FAILURE ) return FAILURE; return directory (name, level+1, enter, leave, touch); } else { /* pozvat' obrabotchik fajlov */ if(touch) return touch (name, level, &st); else return SUCCESS; } } A. Bogatyrev, 1992-95 - 195 - Si v UNIX /* Obrabotat' katalog: prochitat' ego i najti podkatalogi */ int directory (char *name, int level, int (*enter)(char *full, int level, struct stat *st), int (*leave)(char *full, int level), int (*touch)(char *full, int level, struct stat *st)) { #ifndef U42 struct direct dirbuf; int fd; #else register struct dirent *dirbuf; DIR *fd; extern DIR *opendir(); #endif char *nbp, *tail, *nep; int i, retcode = SUCCESS; #ifndef U42 if ((fd = open (name, 0)) < 0) { #else if ((fd = opendir (name)) == NULL) { #endif return ERR_CANT_READ(name); } tail = nbp = name + strlen (name); /* ukazatel' na zakryvayushchij \0 */ if( strcmp( name, "/" )) /* esli ne "/" */ *nbp++ = '/'; *nbp = '\0'; #ifndef U42 if (nbp + DIRSIZ + 2 >= name + MAXPATHLEN) { *tail = '\0'; return ERR_NAME_TOO_LONG(); } #endif #ifndef U42 while (read(fd, (char *) &dirbuf, sizeof(dirbuf)) == sizeof(dirbuf)){ if (dirbuf.d_ino == 0) /* stertyj fajl */ continue; if (strcmp (dirbuf.d_name, "." ) == 0 || strcmp (dirbuf.d_name, "..") == 0) /* ne interesuyut */ continue; for (i = 0, nep = nbp; i < DIRSIZ; i++) *nep++ = dirbuf.d_name[i]; # else /*U42*/ while ((dirbuf = readdir (fd)) != NULL ) { if (dirbuf->d_ino == 0) continue; if (strcmp (dirbuf->d_name, "." ) == 0 || strcmp (dirbuf->d_name, "..") == 0) continue; for (i = 0, nep = nbp; i < dirbuf->d_namlen ; i++) *nep++ = dirbuf->d_name[i]; #endif /*U42*/ *nep = '\0'; if( act(name, level, enter, leave, touch) == FAILURE) { retcode = FAILURE; break; } } A. Bogatyrev, 1992-95 - 196 - Si v UNIX #ifndef U42 close (fd); #else closedir(fd); #endif *tail = '\0'; /* vosstanovit' staroe name */ if(retcode != FAILURE && leave) if( leave(name, level) == FAILURE) retcode = FAILURE; return retcode; } /* -------------------------------------------------------------- */ /* Disk Usage -- Ocenka mesta, zanimaemogo fajlami poddereva */ /* -------------------------------------------------------------- */ /* Pereschet bajtov v kilobajty */ #define KB(s) (((s)/1024L) + ((s)%1024L ? 1L:0L)) /* ili #define KB(s) (((s) + 1024L - 1) / 1024L) */ long size; /* obshchij razmer */ long nfiles; /* vsego fajlov */ long ndirs; /* iz nih katalogov */ #define WARNING_LIMIT 150L /* podozritel'no bol'shoj fajl */ static int du_touch (char *name, int level, struct stat *st){ long sz; size += (sz = KB(st->st_size)); /* razmer fajla v Kb. */ nfiles++; #ifndef TREEONLY if( sz >= WARNING_LIMIT ) fprintf(stderr,"\tVnimanie! \"%s\" ochen' bol'shoj: %ld Kb.\n", name, sz); #endif /*TREEONLY*/ return SUCCESS; } static int du_enter (char *name, int level, struct stat *st){ #ifndef TREEONLY fprintf( stderr, "Katalog \"%s\"\n", name ); #endif size += KB(st->st_size); /* razmer kataloga v Kb. */ nfiles++; ++ndirs; return SUCCESS; } long du (char *name){ size = nfiles = ndirs = 0L; walktree(name, du_enter, NULL, du_touch ); return size; } A. Bogatyrev, 1992-95 - 197 - Si v UNIX /* -------------------------------------------------------------- */ /* Rekursivnoe udalenie fajlov i katalogov */ /* -------------------------------------------------------------- */ int deleted; /* skol'ko fajlov i katalogov udaleno */ static int recrm_dir (char *name, int level){ if( rmdir(name) >= 0){ deleted++; return SUCCESS; } fprintf(stderr, "Ne mogu rmdir '%s'\n", name); return WARNING; } static int recrm_file(char *name, int level, struct stat *st){ if( unlink(name) >= 0){ deleted++; return SUCCESS; } fprintf(stderr, "Ne mogu rm '%s'\n", name); return WARNING; } int recrmdir(char *name){ int ok_code; deleted = 0; ok_code = walktree(name, NULL, recrm_dir, recrm_file); printf("Udaleno %d fajlov i katalogov v %s\n", deleted, name); return ok_code; } /* -------------------------------------------------------------- */ /* Poisk fajlov s podhodyashchim imenem (po shablonu imeni) */ /* -------------------------------------------------------------- */ char *find_PATTERN; static int find_check(char *fullname, int level, struct stat *st){ char *basename = strrchr(fullname, '/'); if(basename) basename++; else basename = fullname; if( match(basename, find_PATTERN)) printf("Level#%02d %s\n", level, fullname); if( !strcmp( basename, "core")){ printf("Najden damp %s, poisk prekrashchen.\n", fullname); return FAILURE; } return SUCCESS; } void find (char *root, char *pattern){ find_PATTERN = pattern; walktree(root, find_check, NULL, find_check); } A. Bogatyrev, 1992-95 - 198 - Si v UNIX /* -------------------------------------------------------------- */ #ifndef TREEONLY void main(int argc, char *argv[]){ #ifdef FIND if(argc != 3){ fprintf(stderr, "Arg count\n"); exit(1); } find(argv[1], argv[2]); #else # ifdef RM_REC for(argv++; *argv; argv++) recrmdir(*argv); # else du( argc == 1 ? "." : argv[1] ); printf( "%ld kilobajt v %ld fajlah.\n", size, nfiles ); printf( "%ld katalogov.\n", ndirs ); # endif #endif exit(0); } #endif /*TREEONLY*/ 6.1.6. Ispol'zuya predydushchij algoritm, napishite programmu rekursivnogo kopirovaniya poddereva katalogov v drugoe mesto. Dlya sozdaniya novyh katalogov ispol'zujte sistem- nyj vyzov mkdir(imya_kataloga, kody_dostupa); 6.1.7. Ispol'zuya tot zhe algoritm, napishite programmu udaleniya kataloga, kotoraya uda- lyaet vse fajly v nem i, rekursivno, vse ego podkatalogi. Takim obrazom, udalyaetsya derevo katalogov. V UNIX podobnuyu operaciyu vypolnyaet komanda rm -r imya_kataloga_kornya_dereva 6.1.8. Ispol'zuya vse tot zhe algoritm obhoda, napishite analog komandy find, kotoryj budet pozvolyat': - nahodit' vse fajly, ch'i imena udovletvoryayut zadannomu shablonu (ispol'zujte funk- ciyu match() iz glavy "Tekstovaya obrabotka"); - nahodit' vse vypolnyaemye fajly: obychnye fajly S_IFREG, u kotoryh (st.st_mode & 0111) != 0 Kak uzhe yasno, sleduet pol'zovat'sya vyzovom stat dlya proverki kazhdogo fajla. 6.2. Vremya v UNIX. 6.2.1. Napishite funkciyu, perevodyashchuyu god, mesyac, den', chasy, minuty i sekundy v chislo sekund, proshedshee do ukazannogo momenta s 00 chasov 00 minut 00 sekund 1 YAnvarya 1970 goda. Vnimanie: rezul'tat dolzhen imet' tip long (tochnee time_t). |ta funkciya oblegchit vam sravnenie dvuh momentov vremeni, zadannyh v obshcheprinya- tom "chelovecheskom" formate, poskol'ku sravnit' dva long chisla gorazdo proshche, chem sravnivat' po ocheredi gody, zatem, esli oni ravny - mesyacy, esli mesyacy ravny - daty, i.t.d.; a takzhe oblegchit izmerenie intervala mezhdu dvumya sobytiyami - on vychislyaetsya prosto kak raznost' dvuh chisel. V sisteme UNIX vremya obrabatyvaetsya i hranitsya imenno v vide chisla sekund; v chastnosti tekushchee astronomicheskoe vremya mozhno uznat' sistemnym vyzovom #include <sys/types.h> #include <time.h> time_t t = time(NULL); /* time(&t); */ Funkciya struct tm *tm = localtime( &t ); A. Bogatyrev, 1992-95 - 199 - Si v UNIX razlagaet chislo sekund na otdel'nye sostavlyayushchie, soderzhashchiesya v int-polyah struktury: tm_year god (nado pribavlyat' 1900) tm_yday den' v godu 0..365 tm_mon nomer mesyaca 0..11 (0 - YAnvar') tm_mday data mesyaca 1..31 tm_wday den' nedeli 0..6 (0 - Voskresen'e) tm_hour chasy 0..23 tm_min minuty 0..59 tm_sec sekundy 0..59 Nomera mesyaca i dnya nedeli nachinayutsya s nulya, chtoby vy mogli ispol'zovat' ih v kachestve indeksov: char *months[] = { "YAnvar'", "Fevral'", ..., "Dekabr'" }; printf( "%s\n", months[ tm->tm_mon ] ); Primer ispol'zovaniya etih funkcij est' v prilozhenii. Ustanovit' vremya v sisteme mozhet superpol'zovatel' vyzovom stime(&t); 6.2.2. Napishite funkciyu pechati tekushchego vremeni v formate CHCH:MM:SS DD-MES-GG. Ispol'zujte sistemnyj vyzov time() i funkciyu localtime(). Sushchestvuet standartnaya funkciya ctime(), kotoraya pechataet vremya v formate: /* Mon Mar 25 18:56:36 1991 */ #include <stdio.h> #include <time.h> main(){ /* komanda date */ time_t t = time(NULL); char *s = ctime(&t); printf("%s", s); } Obratite vnimanie, chto stroka s uzhe soderzhit na konce simvol '\n'. 6.2.3. Struktura stat, zapolnyaemaya sistemnym vyzovom stat(), krome prochih polej soderzhit polya tipa time_t st_ctime, st_mtime i st_atime - vremya poslednego izmeneniya soderzhimogo I-uzla fajla, vremya poslednego izmeneniya fajla i vremya poslednego dostupa k fajlu. - Pole st_ctime izmenyaetsya (ustanavlivaetsya ravnym tekushchemu astronomicheskomu vre- meni) pri primenenii k fajlu vyzovov creat, chmod, chown, link, unlink, mknod, utime|-, write (t.k. izmenyaetsya dlina fajla); |to pole sleduet rassmatrivat' kak vremya modifikacii prav dostupa k fajlu; - st_mtime - write, creat, mknod, utime; |to pole sleduet rassmatrivat' kak vremya modifikacii soderzhimogo fajla (dannyh); - st_atime - read, creat, mknod, utime; |to pole sleduet rassmatrivat' kak vremya chteniya soderzhimogo fajla (dannyh). Modificirujte funkciyu typeOf(), chtoby ona pechatala eshche i eti daty. ____________________ |- Vremya modifikacii fajla mozhno izmenit' na tekushchee astronomicheskoe vremya i ne proizvodya zapisi v fajl. Dlya etogo ispol'zuetsya vyzov utime(imyaFajla, NULL); On ispol'zuetsya dlya vzaimodejstviya s programmoj make - v komande touch. Izmenit' vremya mozhno tol'ko svoemu fajlu. A. Bogatyrev, 1992-95 - 200 - Si v UNIX 6.2.4. Napishite analog komandy ls -tm, vydayushchej spisok imen fajlov tekushchego kata- loga, otsortirovannyj po ubyvaniyu polya st_mtime, to est' nedavno modificirovannye fajly vydayutsya pervymi. Dlya kazhdogo prochitannogo iz kataloga imeni nado sdelat' stat; imena fajlov i vremena sleduet sohranit' v massive struktur, a zatem otsortiro- vat' ego. 6.2.5. Napishite analogichnuyu programmu, sortiruyushchuyu fajly v poryadke vozrastaniya ih razmera (st_size). 6.2.6. Napishite analog komandy ls -l, vydayushchij imena fajlov kataloga i ih kody dos- tupa v formate rwxrw-r--. Dlya polucheniya kodov dostupa ispol'zujte vyzov stat stat( imyaFajla, &st); kodyDostupa = st.st_mode & 0777; Dlya izmeneniya kodov dostupa ispol'zuetsya vyzov chmod(imya_fajla, novye_kody); Mozhno izmenyat' kody dostupa, sootvetstvuyushchie bitovoj maske 0777 | S_ISUID | S_ISGID | S_ISVTX (smotri <sys/stat.h>). Tip fajla (sm. funkciyu typeOf) ne mozhet byt' izmenen. Izme- nit' kody dostupa k fajlu mozhet tol'ko ego vladelec. Pechatajte eshche nomer I-uzla fajla: pole d_ino kataloga libo pole st_ino struktury stat. 6.2.7. Vot programma, kotoraya kazhdye 2 sekundy proveryaet - ne izmenilos' li soderzhi- moe tekushchego kataloga: #include <sys/types.h> #include <sys/stat.h> extern char *ctime(); main(){ time_t last; struct stat st; for( stat(".", &st), last=st.st_mtime; ; sleep(2)){ stat(".", &st); if(last != st.st_mtime){ last = st.st_mtime; printf("Byl sozdan ili udalen kakoj-to fajl: %s", ctime(&last)); } } } Modificirujte ee, chtoby ona soobshchala kakoe imya (imena) bylo udaleno ili sozdano (dlya etogo nado pri zapuske programmy prochitat' i zapomnit' soderzhimoe kataloga, a pri obnaruzhenii modifikacii - perechitat' katalog i sravnit' ego s prezhnim soderzhimym). 6.2.8. Napishite po analogii programmu, kotoraya vydaet soobshchenie, esli ukazannyj vami fajl byl kem-to prochitan, zapisan ili udalen. Vam sleduet otslezhivat' izmenenie polej st_atime, st_mtime i znachenie stat() < 0 sootvetstvenno. Esli fajl udalen - programma zavershaetsya. 6.2.9. Sovremennye UNIX-mashiny imeyut vstroennye tajmery (kak pravilo neskol'ko) s dovol'no vysokim razresheniem. Nekotorye iz nih mogut ispol'zovat'sya kak "budil'niki" s obratnym otschetom vremeni: v tajmer zagruzhaetsya nekotoroe znachenie; tajmer vedet obratnyj otschet, umen'shaya zagruzhennyj schetchik; kak tol'ko eto vremya istekaet - posy- laetsya signal processu, zagruzivshemu tajmer. A. Bogatyrev, 1992-95 - 201 - Si v UNIX Vot kak, k primeru, vyglyadit funkciya zaderzhki v mikrosekundah (millionnyh dolyah sekundy). Primechanie: etu funkciyu ne sleduet ispol'zovat' vperemezhku s funkciyami sleep i alarm (smotri stat'yu pro nih nizhe, v glave pro signaly). #include <sys/types.h> #include <signal.h> #include <sys/time.h> void do_nothing() {} /* Zaderzhka na usec millionnyh dolej sekundy (mikrosekund) */ void usleep(unsigned int usec) { struct itimerval new, old; /* struct itimerval soderzhit polya: struct timeval it_interval; struct timeval it_value; Gde struct timeval soderzhit polya: long tv_sec; -- chislo celyh sekund long tv_usec; -- chislo mikrosekund */ struct sigaction new_vec, old_vec; if (usec == 0) return; /* Pole tv_sec soderzhit chislo celyh sekund. Pole tv_usec soderzhit chislo mikrosekund. it_value - eto vremya, cherez kotoroe V PERVYJ raz tajmer "prozvonit", to est' poshlet nashemu processu signal SIGALRM. Vremya, ravnoe nulyu, nemedlenno ostanovit tajmer. it_interval - eto interval vremeni, kotoryj budet zagruzhat'sya v tajmer posle kazhdogo "zvonka" (no ne v pervyj raz). Vremya, ravnoe nulyu, ostanovit tajmer posle ego pervogo "zvonka". */ new.it_interval.tv_sec = 0; new.it_interval.tv_usec = 0; new.it_value.tv_sec = usec / 1000000; new.it_value.tv_usec = usec % 1000000; A. Bogatyrev, 1992-95 - 202 - Si v UNIX /* Sohranyaem prezhnyuyu reakciyu na signal SIGALRM v old_vec, zanosim v kachestve novoj reakcii do_nothing() */ new_vec.sa_handler = do_nothing; sigemptyset(&new_vec.sa_mask); new_vec.sa_flags = 0; sighold(SIGALRM); sigaction(SIGALRM, &new_vec, &old_vec); /* Zagruzka interval'nogo tajmera znacheniem new, nachalo otscheta. * Prezhnee znachenie spasti v old. * Vmesto &old mozhno takzhe NULL - ne spasat'. */ setitimer(ITIMER_REAL, &new, &old); /* ZHdat' prihoda signala SIGALRM */ sigpause(SIGALRM); /* Vosstanovit' reakciyu na SIGALRM */ sigaction(SIGALRM, &old_vec, (struct sigaction *) 0); sigrelse(SIGALRM); /* Vosstanovit' prezhnie parametry tajmera */ setitimer(ITIMER_REAL, &old, (struct itimerval *) 0); } 6.2.10. Vtoroj primer ispol'zovaniya tajmera - eto tajmer, otschityvayushchij tekushchee vremya sutok (a takzhe datu). CHtoby poluchit' znachenie etogo tajmera ispol'zuetsya vyzov funkcii gettimeofday #include <time.h> void main(){ struct timeval timenow; gettimeofday(&timenow, NULL); printf("%u sec, %u msec\n", timenow.tv_sec, timenow.tv_usec ); printf("%s", ctime(&timenow.tv_sec)); exit(0); } Pole tv_sec soderzhit chislo sekund, proshedshee s polunochi 1 yanvarya 1970 goda do dannogo momenta; v chem polnost'yu sootvetstvuet sistemnomu vyzovu time. Odnako plyus k tomu pole tv_usec soderzhit chislo millionnyh dolej tekushchej sekundy (znachenie etogo polya vsegda men'she 1000000). 6.2.11. K dannomu paragrafu vernites', izuchiv razdel pro fork() i exit(). Kazhdyj process mozhet prebyvat' v dvuh fazah: sistemnoj (vnutri tela sistemnogo vyzova - ego vypolnyaet dlya nas yadro operacionnoj sistemy) i pol'zovatel'skoj (vnutri koda samoj programmy). Vremya, zatrachennoe processom v kazhdoj faze, mozhet byt' izmeryano sistemnym vyzovom times(). Krome togo, etot vyzov pozvolyaet uznat' summarnoe vremya, zatrachennoe porozhdennymi processami (porozhdennymi pri pomoshchi fork). Sistemnyj vyzov zapolnyaet strukturu A. Bogatyrev, 1992-95 - 203 - Si v UNIX struct tms { clock_t tms_utime; clock_t tms_stime; clock_t tms_cutime; clock_t tms_cstime; }; i vozvrashchaet znachenie #include <sys/times.h> struct tms time_buf; clock_t real_time = times(&time_buf); Vse vremena izmeryayutsya v "tikah" - nekotoryh dolyah sekundy. CHislo tikov v sekunde mozhno uznat' takim sistemnym vyzovom (v sisteme Solaris): #include <unistd.h> clock_t HZ = sysconf(_SC_CLK_TCK); V staryh sistemah, gde tajmer rabotal ot seti peremennogo toka, eto chislo poluchalos' ravnym 60 (60 Gerc - chastota seti peremennogo toka). V sovremennyh sistemah eto 100. Polya struktury soderzhat: tms_utime vremya, zatrachennoe vyzyvayushchim processom v pol'zovatel'skoj faze. tms_stime vremya, zatrachennoe vyzyvayushchim processom v sistemnoj faze. tms_cutime vremya, zatrachennoe porozhdennymi processami v pol'zovatel'skoj faze: ono ravno summe vseh tms_utime i tms_cutime porozhdennyh processov (rekursivnoe summirova- nie). tms_cstime vremya, zatrachennoe porozhdennymi processami v sistemnoj faze: ono ravno summe vseh tms_stime i tms_cstime porozhdennyh processov (rekursivnoe summirovanie). real_time vremya, sootvetstvuyushchee astronomicheskomu vremeni sistemy. Imeet smysl meryat' tol'ko ih raznost'. Vot primer programmy: #include <stdio.h> #include <unistd.h> /* _SC_CLK_TCK */ #include <signal.h> /* SIGALRM */ #include <sys/time.h> /* ne ispol'zuetsya */ #include <sys/times.h> /* struct tms */ struct tms tms_stop, tms_start; clock_t real_stop, real_start; clock_t HZ; /* chislo ticks v sekunde */ A. Bogatyrev, 1992-95 - 204 - Si v UNIX /* Zasech' vremya momenta starta processa */ void hello(void){ real_start = times(&tms_start); } /* Zasech' vremya okonchaniya processa */ void bye(int n){ real_stop = times(&tms_stop); #ifdef CRONO /* Raznost' vremen */ tms_stop.tms_utime -= tms_start.tms_utime; tms_stop.tms_stime -= tms_start.tms_stime; #endif /* Raspechatat' vremena */ printf("User time = %g seconds [%lu ticks]\n", tms_stop.tms_utime / (double)HZ, tms_stop.tms_utime); printf("System time = %g seconds [%lu ticks]\n", tms_stop.tms_stime / (double)HZ, tms_stop.tms_stime); printf("Children user time = %g seconds [%lu ticks]\n", tms_stop.tms_cutime / (double)HZ, tms_stop.tms_cutime); printf("Children system time = %g seconds [%lu ticks]\n", tms_stop.tms_cstime / (double)HZ, tms_stop.tms_cstime); printf("Real time = %g seconds [%lu ticks]\n", (real_stop - real_start) / (double)HZ, real_stop - real_start); exit(n); } /* Po signalu SIGALRM - zavershit' process */ void onalarm(int nsig){ printf("Vyhod #%d ================\n", getpid()); bye(0); } /* Porozhdennyj process */ void dochild(int n){ hello(); printf("Start #%d ================\n", getpid()); signal(SIGALRM, onalarm); /* Zakazat' signal SIGALRM cherez 1 + n*3 sekund */ alarm(1 + n*3); for(;;){} /* zaciklit'sya v user mode */ } A. Bogatyrev, 1992-95 - 205 - Si v UNIX #define NCHLD 4 int main(int ac, char *av[]){ int i; /* Uznat' chislo tikov v sekunde */ HZ = sysconf(_SC_CLK_TCK); setbuf(stdout, NULL); hello(); for(i=0; i < NCHLD; i++) if(fork() == 0) dochild(i); while(wait(NULL) > 0); printf("Vyhod MAIN =================\n"); bye(0); return 0; } i ee vydacha: Start #3883 ================ Start #3884 ================ Start #3885 ================ Start #3886 ================ Vyhod #3883 ================ User time = 0.72 seconds [72 ticks] System time = 0.01 seconds [1 ticks] Children user time = 0 seconds [0 ticks] Children system time = 0 seconds [0 ticks] Real time = 1.01 seconds [101 ticks] Vyhod #3884 ================ User time = 1.88 seconds [188 ticks] System time = 0.01 seconds [1 ticks] Children user time = 0 seconds [0 ticks] Children system time = 0 seconds [0 ticks] Real time = 4.09 seconds [409 ticks] Vyhod #3885 ================ User time = 4.41 seconds [441 ticks] System time = 0.01 seconds [1 ticks] Children user time = 0 seconds [0 ticks] Children system time = 0 seconds [0 ticks] Real time = 7.01 seconds [701 ticks] Vyhod #3886 ================ User time = 8.9 seconds [890 ticks] System time = 0 seconds [0 ticks] Children user time = 0 seconds [0 ticks] Children system time = 0 seconds [0 ticks] Real time = 10.01 seconds [1001 ticks] Vyhod MAIN ================= User time = 0.01 seconds [1 ticks] System time = 0.04 seconds [4 ticks] Children user time = 15.91 seconds [1591 ticks] Children system time = 0.03 seconds [3 ticks] Real time = 10.41 seconds [1041 ticks] Obratite vnimanie, chto 72+188+441+890=1591 (pole tms_cutime dlya main). 6.2.12. Eshche odna programma: hronometrirovanie vypolneniya drugoj programmy. Primer: timer ls -l A. Bogatyrev, 1992-95 - 206 - Si v UNIX /* Hronometrirovanie vypolneniya programmy */ #include <stdio.h> #include <unistd.h> #include <sys/times.h> extern errno; typedef struct _timeStamp { clock_t real_time; clock_t cpu_time; clock_t child_time; clock_t child_sys, child_user; } TimeStamp; TimeStamp TIME(){ struct tms tms; TimeStamp st; st.real_time = times(&tms); st.cpu_time = tms.tms_utime + tms.tms_stime + tms.tms_cutime + tms.tms_cstime; st.child_time = tms.tms_cutime + tms.tms_cstime; st.child_sys = tms.tms_cstime; st.child_user = tms.tms_cutime; return st; } void PRTIME(TimeStamp start, TimeStamp stop){ clock_t HZ = sysconf(_SC_CLK_TCK); clock_t real_time = stop.real_time - start.real_time; clock_t cpu_time = stop.cpu_time - start.cpu_time; clock_t child_time = stop.child_time - start.child_time; printf("%g real, %g cpu, %g child (%g user, %g sys), %ld%%\n", real_time / (double)HZ, cpu_time / (double)HZ, child_time / (double)HZ, stop.child_user / (double)HZ, stop.child_sys / (double)HZ, (child_time * 100L) / (real_time ? real_time : 1) ); } A. Bogatyrev, 1992-95 - 207 - Si v UNIX TimeStamp start, stop; int main(int ac, char *av[]){ char *prog = *av++; if(*av == NULL){ fprintf(stderr, "Usage: %s command [args...]\n", prog); return(1); } start = TIME(); if(fork() == 0){ execvp(av[0], av); perror(av[0]); exit(errno); } while(wait(NULL) > 0); stop = TIME(); PRTIME(start, stop); return(0); } 6.3. Svobodnoe mesto na diske. 6.3.1. Sistemnyj vyzov ustat() pozvolyaet uznat' kolichestvo svobodnogo mesta v fajlo- voj sisteme, soderzhashchej zadannyj fajl (v primere nizhe - tekushchij katalog): #include <sys/types.h> #include <sys/stat.h> #include <ustat.h> struct stat st; struct ustat ust; void main(int ac, char *av[]){ char *file = (ac==1 ? "." : av[1]); if( stat(file, &st) < 0) exit(1); ustat(st.st_dev, &ust); printf("Na diske %*.*s\n" "%ld svobodnyh blokov (%ld Kb)\n" "%d svobodnyh I-uzlov\n", sizeof ust.f_fname, sizeof ust.f_fname, ust.f_fname, /* nazvanie fajlovoj sistemy (metka) */ ust.f_tfree, /* bloki po 512 bajt */ (ust.f_tfree * 512L) / 1024, ust.f_tinode ); } Obratite vnimanie na zapis' dlinnoj stroki v printf: stroki, perechislennye posledova- tel'no, skleivayutsya ANSI C kompilyatorom v odnu dlinnuyu stroku: char s[] = "This is" " a line " "of words"; sovpadaet s char s[] = "This is a line of words"; 6.3.2. Bolee pravil'no, odnako, pol'zovat'sya sisvyzovom statvfs - statistika po vir- tual'noj fajlovoj sisteme. Rassmotrim ego v sleduyushchem primere: kopirovanie fajla s proverkoj na nalichie svobodnogo mesta. A. Bogatyrev, 1992-95 - 208 - Si v UNIX #include <stdio.h> #include <string.h> #include <stdlib.h> #include <stdarg.h> #include <fcntl.h> /* O_RDONLY */ #include <sys/types.h> #include <sys/stat.h> #include <sys/statvfs.h> #include <sys/param.h> /* MAXPATHLEN */ char *progname; /* imya programmy */ void error(char *fmt, ...){ va_list args; va_start(args, fmt); fprintf(stderr, "%s: ", progname); vfprintf(stderr, fmt, args); fputc('\n', stderr); va_end(args); } int copyFile(char *to, char *from){ /* kuda, otkuda */ char newname[MAXPATHLEN+1]; char answer[20]; struct stat stf, stt; int fdin, fdout; int n, code = 0; char iobuf[64 * 1024]; char *dirname = NULL, *s; if((fdin = open(from, O_RDONLY)) < 0){ error("Cannot read %s", from); return (-1); } fstat(fdin, &stf); if((stf.st_mode & S_IFMT) == S_IFDIR){ close(fdin); error("%s is a directory", from); return (-2); } A. Bogatyrev, 1992-95 - 209 - Si v UNIX if(stat(to, &stt) >= 0){ /* Fajl uzhe sushchestvuet */ if((stt.st_mode & S_IFMT) == S_IFDIR){ /* I eto katalog */ /* Vydelit' poslednyuyu komponentu puti from */ if((s = strrchr(from, '/')) && s[1]) s++; else s = from; dirname = to; /* Celevoj fajl - fajl v etom kataloge */ sprintf(newname, "%s/%s", to, s); to = newname; if(stat(to, &stt) < 0) goto not_exist; } if(stt.st_dev == stf.st_dev && stt.st_ino == stf.st_ino){ error("%s: cannot copy file to itself", from); return (-3); } switch(stt.st_mode & S_IFMT){ case S_IFBLK: case S_IFCHR: case S_IFIFO: break; default: printf("%s already exists, overwrite ? ", to); fflush(stdout); *answer = '\0'; gets(answer); if(*answer != 'y'){ /* NO */ close(fdin); return (-4); } break; } } A. Bogatyrev, 1992-95 - 210 - Si v UNIX not_exist: printf("COPY %s TO %s\n", from, to);