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);