Однако, если мы ЗАРАНЕЕ знаем имена файлов в каталоге, мы МОЖЕМ рабо-
тать с ними - если имеем право доступа "выполнение" для этого каталога!
x выполнение
S_IEXEC. Разрешает поиск в каталоге. Для открытия файла, создания/удаления
файла, перехода в другой каталог (chdir), система выполняет следующие действия
(осуществляемые функцией namei() в ядре): чтение каталога и поиск в нем указан-
ного имени файла или каталога; найденному имени соответствует номер I-узла
d_ino; по номеру узла система считывает с диска сам I-узел нужного файла и по
нему добирается до содержимого файла. Код "выполнение" - это как раз разрешение
такого просмотра каталога системой. Если каталог имеет доступ на чтение - мы
можем получить список файлов (т.е. применить команду ls); но если он при этом не
имеет кода доступа "выполнение" - мы не сможем получить доступа ни к одному из
файлов каталога (ни открыть, ни удалить, ни создать, ни сделать stat, ни chdir).
Т.е. "чтение" разрешает применение вызова read, а "выполнение" - функции ядра
namei. Фактически "выполнение" означает "доступ к файлам в данном каталоге";
еще более точно - к I-nodам файлов этого каталога.
t sticky bit
S_ISVTX - для каталога он означает, что удалить или переименовать некий файл в
данном каталоге могут только: владелец каталога, владелец данного файла, супер-
пользователь. И никто другой. Это исключает удаление файлов чужими.
Совет: для каталога полезно иметь такие коды доступа:
chmod o-w,+t каталог
В системах BSD используется, как уже было упомянуто, формат каталога с переменной
длиной записей. Чтобы иметь удобный доступ к именам в каталоге, возникли специальные
функции чтения каталога: opendir, closedir, readdir. Покажем, как простейшая команда
ls реализуется через эти функции.
А. Богатырев, 1992-95 - 192 - Си в 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;
}
/* Без сортировки по алфавиту */
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("Поврежденный каталог: нет имени \".\"\n");
if(dotdot_ino == 0) printf("Поврежденный каталог: нет имени \"..\"\n");
if(dot_ino && dot_ino == dotdot_ino) printf("Это корневой каталог диска\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;
}
Обратите внимание, что тут не требуется добавление '\0' в конец поля d_name, пос-
кольку его предоставляет нам сама функция readdir().
6.1.4. Напишите программу удаления файлов и каталогов, заданных в argv. Делайте
stat, чтобы определить тип файла (файл/каталог). Программа должна отказываться уда-
лять файлы устройств.
Для удаления пустого каталога (не содержащего иных имен, кроме "." и "..") сле-
дует использовать сисвызов
rmdir(имя_каталога);
(если каталог не пуст - errno получит значение EEXIST); а для удаления обычных файлов
(не каталогов)
unlink(имя_файла);
Программа должна запрашивать подтверждение на удаление каждого файла, выдавая его
имя, тип, размер в килобайтах и вопрос "удалить ?".
6.1.5. Напишите функцию рекурсивного обхода дерева подкаталогов и печати имен всех
файлов в нем. Ключ U42 означает файловую систему с длинными именами файлов (BSD 4.2).
А. Богатырев, 1992-95 - 193 - Си в UNIX
/*#!/bin/cc -DFIND -DU42 -DMATCHONLY treemk.c match.c -o tree -lx
* Обход поддерева каталогов (по мотивам Керниган & Ритчи).
* Ключи компиляции:
* BSD-4.2 BSD-4.3 -DU42
* XENIX с канонической файл.сист. ничего
* XENIX с библиотекой -lx -DU42
* программа поиска файлов -DFIND
* программа рекурсивного удаления -DRM_REC
* программа подсчета используемого места на диске БЕЗ_КЛЮЧА
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h> /* для MAXPATHLEN */
#if defined(M_XENIX) && defined(U42)
# include <sys/ndir.h> /* XENIX + U42 эмуляция */
#else
# include <dirent.h>
# define stat(f,s) lstat(f,s) /* не проходить по символьным ссылкам */
# define d_namlen d_reclen
#endif
/* проверка: каталог ли это */
#define isdir(st) ((st.st_mode & S_IFMT) == S_IFDIR)
struct stat st; /* для сисвызова stat() */
char buf[MAXPATHLEN+1]; /* буфер для имени файла */
#define FAILURE (-1) /* код неудачи */
#define SUCCESS 1 /* код успеха */
#define WARNING 0 /* нефатальная ошибка */
/* Сообщения об ошибках во время обхода дерева: */
#ifndef ERR_CANT_READ
# define ERR_CANT_READ(name) \
fprintf( stderr, "\tНе могу читать \"%s\"\n", name), WARNING
# define ERR_NAME_TOO_LONG() \
fprintf( stderr, "\tСлишком длинное полное имя\n" ), WARNING
#endif
/* Прототипы для предварительного объявления функций. */
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));
/* Функции-обработчики enter, leave, touch должны
* возвращать (-1) для прерывания просмотра дерева,
* либо значение >= 0 для продолжения. */
А. Богатырев, 1992-95 - 194 - Си в UNIX
/* Обойти дерево с корнем в rootdir */
int walktree (
char *rootdir, /* корень дерева */
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(rootdir, &st) < 0 || !isdir(st)){
fprintf( stderr, "\tПлохой корень дерева \"%s\"\n", rootdir );
return FAILURE; /* неудача */
}
strcpy (buf, rootdir);
return act (buf, 0, enter, leave, touch);
}
/* Оценка файла с именем 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; /* ошибка, но не фатальная */
if(isdir(st)){ /* позвать обработчик каталогов */
if(enter)
if( enter(name, level, &st) == FAILURE ) return FAILURE;
return directory (name, level+1, enter, leave, touch);
} else { /* позвать обработчик файлов */
if(touch) return touch (name, level, &st);
else return SUCCESS;
}
}
А. Богатырев, 1992-95 - 195 - Си в UNIX
/* Обработать каталог: прочитать его и найти подкаталоги */
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); /* указатель на закрывающий \0 */
if( strcmp( name, "/" )) /* если не "/" */
*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) /* стертый файл */
continue;
if (strcmp (dirbuf.d_name, "." ) == 0 ||
strcmp (dirbuf.d_name, "..") == 0) /* не интересуют */
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; }
}
А. Богатырев, 1992-95 - 196 - Си в UNIX
#ifndef U42
close (fd);
#else
closedir(fd);
#endif
*tail = '\0'; /* восстановить старое name */
if(retcode != FAILURE && leave)
if( leave(name, level) == FAILURE) retcode = FAILURE;
return retcode;
}
/* -------------------------------------------------------------- */
/* Disk Usage -- Оценка места, занимаемого файлами поддерева */
/* -------------------------------------------------------------- */
/* Пересчет байтов в килобайты */
#define KB(s) (((s)/1024L) + ((s)%1024L ? 1L:0L))
/* или #define KB(s) (((s) + 1024L - 1) / 1024L) */
long size; /* общий размер */
long nfiles; /* всего файлов */
long ndirs; /* из них каталогов */
#define WARNING_LIMIT 150L /* подозрительно большой файл */
static int du_touch (char *name, int level, struct stat *st){
long sz;
size += (sz = KB(st->st_size)); /* размер файла в Кб. */
nfiles++;
#ifndef TREEONLY
if( sz >= WARNING_LIMIT )
fprintf(stderr,"\tВнимание! \"%s\" очень большой: %ld Кб.\n",
name, sz);
#endif /*TREEONLY*/
return SUCCESS;
}
static int du_enter (char *name, int level, struct stat *st){
#ifndef TREEONLY
fprintf( stderr, "Каталог \"%s\"\n", name );
#endif
size += KB(st->st_size); /* размер каталога в Кб. */
nfiles++; ++ndirs; return SUCCESS;
}
long du (char *name){
size = nfiles = ndirs = 0L;
walktree(name, du_enter, NULL, du_touch );
return size;
}
А. Богатырев, 1992-95 - 197 - Си в UNIX
/* -------------------------------------------------------------- */
/* Рекурсивное удаление файлов и каталогов */
/* -------------------------------------------------------------- */
int deleted; /* сколько файлов и каталогов удалено */
static int recrm_dir (char *name, int level){
if( rmdir(name) >= 0){ deleted++; return SUCCESS; }
fprintf(stderr, "Не могу 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, "Не могу 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("Удалено %d файлов и каталогов в %s\n", deleted, name);
return ok_code;
}
/* -------------------------------------------------------------- */
/* Поиск файлов с подходящим именем (по шаблону имени) */
/* -------------------------------------------------------------- */
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("Найден дамп %s, поиск прекращен.\n", fullname);
return FAILURE;
}
return SUCCESS;
}
void find (char *root, char *pattern){
find_PATTERN = pattern;
walktree(root, find_check, NULL, find_check);
}
А. Богатырев, 1992-95 - 198 - Си в 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 килобайт в %ld файлах.\n", size, nfiles );
printf( "%ld каталогов.\n", ndirs );
# endif
#endif
exit(0);
}
#endif /*TREEONLY*/
6.1.6. Используя предыдущий алгоритм, напишите программу рекурсивного копирования
поддерева каталогов в другое место. Для создания новых каталогов используйте систем-
ный вызов
mkdir(имя_каталога, коды_доступа);
6.1.7. Используя тот же алгоритм, напишите программу удаления каталога, которая уда-
ляет все файлы в нем и, рекурсивно, все его подкаталоги. Таким образом, удаляется
дерево каталогов. В UNIX подобную операцию выполняет команда
rm -r имя_каталога_корня_дерева
6.1.8. Используя все тот же алгоритм обхода, напишите аналог команды find, который
будет позволять:
- находить все файлы, чьи имена удовлетворяют заданному шаблону (используйте функ-
цию match() из главы "Текстовая обработка");
- находить все выполняемые файлы: обычные файлы S_IFREG, у которых
(st.st_mode & 0111) != 0
Как уже ясно, следует пользоваться вызовом stat для проверки каждого файла.
6.2. Время в UNIX.
6.2.1. Напишите функцию, переводящую год, месяц, день, часы, минуты и секунды в
число секунд, прошедшее до указанного момента с 00 часов 00 минут 00 секунд 1 Января
1970 года. Внимание: результат должен иметь тип long (точнее time_t).
Эта функция облегчит вам сравнение двух моментов времени, заданных в общеприня-
том "человеческом" формате, поскольку сравнить два long числа гораздо проще, чем
сравнивать по очереди годы, затем, если они равны - месяцы, если месяцы равны - даты,
и.т.д.; а также облегчит измерение интервала между двумя событиями - он вычисляется
просто как разность двух чисел. В системе UNIX время обрабатывается и хранится
именно в виде числа секунд; в частности текущее астрономическое время можно узнать
системным вызовом
#include <sys/types.h>
#include <time.h>
time_t t = time(NULL); /* time(&t); */
Функция
struct tm *tm = localtime( &t );
А. Богатырев, 1992-95 - 199 - Си в UNIX
разлагает число секунд на отдельные составляющие, содержащиеся в int-полях структуры:
tm_year год (надо прибавлять 1900)
tm_yday день в году 0..365
tm_mon номер месяца 0..11 (0 - Январь)
tm_mday дата месяца 1..31
tm_wday день недели 0..6 (0 - Воскресенье)
tm_hour часы 0..23
tm_min минуты 0..59
tm_sec секунды 0..59
Номера месяца и дня недели начинаются с нуля, чтобы вы могли использовать их в
качестве индексов:
char *months[] = { "Январь", "Февраль", ..., "Декабрь" };
printf( "%s\n", months[ tm->tm_mon ] );
Пример использования этих функций есть в приложении.
Установить время в системе может суперпользователь вызовом
stime(&t);
6.2.2. Напишите функцию печати текущего времени в формате ЧЧ:ММ:СС ДД-МЕС-ГГ.
Используйте системный вызов time() и функцию localtime().
Существует стандартная функция ctime(), которая печатает время в формате:
/* Mon Mar 25 18:56:36 1991 */
#include <stdio.h>
#include <time.h>
main(){ /* команда date */
time_t t = time(NULL);
char *s = ctime(&t);
printf("%s", s);
}
Обратите внимание, что строка s уже содержит на конце символ '\n'.
6.2.3. Структура stat, заполняемая системным вызовом stat(), кроме прочих полей
содержит поля типа time_t st_ctime, st_mtime и st_atime - время последнего изменения
содержимого I-узла файла, время последнего изменения файла и время последнего доступа
к файлу.
- Поле st_ctime изменяется (устанавливается равным текущему астрономическому вре-
мени) при применении к файлу вызовов creat, chmod, chown, link, unlink, mknod,
utime|-, write (т.к. изменяется длина файла); Это поле следует рассматривать как
время модификации прав доступа к файлу;
- st_mtime - write, creat, mknod, utime; Это поле следует рассматривать как время
модификации содержимого файла (данных);
- st_atime - read, creat, mknod, utime; Это поле следует рассматривать как время
чтения содержимого файла (данных).
Модифицируйте функцию typeOf(), чтобы она печатала еще и эти даты.
____________________
|- Время модификации файла можно изменить на текущее астрономическое время и не
производя записи в файл. Для этого используется вызов
utime(имяФайла, NULL);
Он используется для взаимодействия с программой make - в команде touch. Изменить
время можно только своему файлу.
А. Богатырев, 1992-95 - 200 - Си в UNIX
6.2.4. Напишите аналог команды ls -tm, выдающей список имен файлов текущего ката-
лога, отсортированный по убыванию поля st_mtime, то есть недавно модифицированные
файлы выдаются первыми. Для каждого прочитанного из каталога имени надо сделать
stat; имена файлов и времена следует сохранить в массиве структур, а затем отсортиро-
вать его.
6.2.5. Напишите аналогичную программу, сортирующую файлы в порядке возрастания их
размера (st_size).
6.2.6. Напишите аналог команды ls -l, выдающий имена файлов каталога и их коды дос-
тупа в формате rwxrw-r--. Для получения кодов доступа используйте вызов stat
stat( имяФайла, &st);
кодыДоступа = st.st_mode & 0777;
Для изменения кодов доступа используется вызов
chmod(имя_файла, новые_коды);
Можно изменять коды доступа, соответствующие битовой маске
0777 | S_ISUID | S_ISGID | S_ISVTX
(смотри <sys/stat.h>). Тип файла (см. функцию typeOf) не может быть изменен. Изме-
нить коды доступа к файлу может только его владелец.
Печатайте еще номер I-узла файла: поле d_ino каталога либо поле st_ino структуры
stat.
6.2.7. Вот программа, которая каждые 2 секунды проверяет - не изменилось ли содержи-
мое текущего каталога:
#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("Был создан или удален какой-то файл: %s",
ctime(&last));
}
}
}
Модифицируйте ее, чтобы она сообщала какое имя (имена) было удалено или создано (для
этого надо при запуске программы прочитать и запомнить содержимое каталога, а при
обнаружении модификации - перечитать каталог и сравнить его с прежним содержимым).
6.2.8. Напишите по аналогии программу, которая выдает сообщение, если указанный вами
файл был кем-то прочитан, записан или удален. Вам следует отслеживать изменение полей
st_atime, st_mtime и значение stat() < 0 соответственно. Если файл удален - программа
завершается.
6.2.9. Современные UNIX-машины имеют встроенные таймеры (как правило несколько) с
довольно высоким разрешением. Некоторые из них могут использоваться как "будильники"
с обратным отсчетом времени: в таймер загружается некоторое значение; таймер ведет
обратный отсчет, уменьшая загруженный счетчик; как только это время истекает - посы-
лается сигнал процессу, загрузившему таймер.
А. Богатырев, 1992-95 - 201 - Си в UNIX
Вот как, к примеру, выглядит функция задержки в микросекундах (миллионных долях
секунды). Примечание: эту функцию не следует использовать вперемежку с функциями
sleep и alarm (смотри статью про них ниже, в главе про сигналы).
#include <sys/types.h>
#include <signal.h>
#include <sys/time.h>
void do_nothing() {}
/* Задержка на usec миллионных долей секунды (микросекунд) */
void usleep(unsigned int usec) {
struct itimerval new, old;
/* struct itimerval содержит поля:
struct timeval it_interval;
struct timeval it_value;
Где struct timeval содержит поля:
long tv_sec; -- число целых секунд
long tv_usec; -- число микросекунд
*/
struct sigaction new_vec, old_vec;
if (usec == 0) return;
/* Поле tv_sec содержит число целых секунд.
Поле tv_usec содержит число микросекунд.
it_value - это время, через которое В ПЕРВЫЙ раз
таймер "прозвонит",
то есть пошлет нашему процессу
сигнал SIGALRM.
Время, равное нулю, немедленно остановит таймер.
it_interval - это интервал времени, который будет загружаться
в таймер после каждого "звонка"
(но не в первый раз).
Время, равное нулю, остановит таймер
после его первого "звонка".
*/
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;
А. Богатырев, 1992-95 - 202 - Си в UNIX
/* Сохраняем прежнюю реакцию на сигнал SIGALRM в old_vec,
заносим в качестве новой реакции 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);
/* Загрузка интервального таймера значением new, начало отсчета.
* Прежнее значение спасти в old.
* Вместо &old можно также NULL - не спасать.
*/
setitimer(ITIMER_REAL, &new, &old);
/* Ждать прихода сигнала SIGALRM */
sigpause(SIGALRM);
/* Восстановить реакцию на SIGALRM */
sigaction(SIGALRM, &old_vec, (struct sigaction *) 0);
sigrelse(SIGALRM);
/* Восстановить прежние параметры таймера */
setitimer(ITIMER_REAL, &old, (struct itimerval *) 0);
}
6.2.10. Второй пример использования таймера - это таймер, отсчитывающий текущее
время суток (а также дату). Чтобы получить значение этого таймера используется вызов
функции 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);
}
Поле tv_sec содержит число секунд, прошедшее с полуночи 1 января 1970 года до данного
момента; в чем полностью соответствует системному вызову time. Однако плюс к тому
поле tv_usec содержит число миллионных долей текущей секунды (значение этого поля
всегда меньше 1000000).
6.2.11. К данному параграфу вернитесь, изучив раздел про fork() и exit(). Каждый
процесс может пребывать в двух фазах: системной (внутри тела системного вызова - его
выполняет для нас ядро операционной системы) и пользовательской (внутри кода самой
программы). Время, затраченное процессом в каждой фазе, может быть измеряно системным
вызовом times(). Кроме того, этот вызов позволяет узнать суммарное время, затраченное
порожденными процессами (порожденными при помощи fork). Системный вызов заполняет
структуру
А. Богатырев, 1992-95 - 203 - Си в UNIX
struct tms {
clock_t tms_utime;
clock_t tms_stime;
clock_t tms_cutime;
clock_t tms_cstime;
};
и возвращает значение
#include <sys/times.h>
struct tms time_buf;
clock_t real_time = times(&time_buf);
Все времена измеряются в "тиках" - некоторых долях секунды. Число тиков в секунде
можно узнать таким системным вызовом (в системе Solaris):
#include <unistd.h>
clock_t HZ = sysconf(_SC_CLK_TCK);
В старых системах, где таймер работал от сети переменного тока, это число получалось
равным 60 (60 Герц - частота сети переменного тока). В современных системах это 100.
Поля структуры содержат:
tms_utime
время, затраченное вызывающим процессом в пользовательской фазе.
tms_stime
время, затраченное вызывающим процессом в системной фазе.
tms_cutime
время, затраченное порожденными процессами в пользовательской фазе: оно равно
сумме всех tms_utime и tms_cutime порожденных процессов (рекурсивное суммирова-
ние).
tms_cstime
время, затраченное порожденными процессами в системной фазе: оно равно сумме
всех tms_stime и tms_cstime порожденных процессов (рекурсивное суммирование).
real_time
время, соответствующее астрономическому времени системы. Имеет смысл мерять
только их разность.
Вот пример программы:
#include <stdio.h>
#include <unistd.h> /* _SC_CLK_TCK */
#include <signal.h> /* SIGALRM */
#include <sys/time.h> /* не используется */
#include <sys/times.h> /* struct tms */
struct tms tms_stop, tms_start;
clock_t real_stop, real_start;
clock_t HZ; /* число ticks в секунде */
А. Богатырев, 1992-95 - 204 - Си в UNIX
/* Засечь время момента старта процесса */
void hello(void){
real_start = times(&tms_start);
}
/* Засечь время окончания процесса */
void bye(int n){
real_stop = times(&tms_stop);
#ifdef CRONO
/* Разность времен */
tms_stop.tms_utime -= tms_start.tms_utime;
tms_stop.tms_stime -= tms_start.tms_stime;
#endif
/* Распечатать времена */
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);
}
/* По сигналу SIGALRM - завершить процесс */
void onalarm(int nsig){
printf("Выход #%d ================\n", getpid());
bye(0);
}
/* Порожденный процесс */
void dochild(int n){
hello();
printf("Старт #%d ================\n", getpid());
signal(SIGALRM, onalarm);
/* Заказать сигнал SIGALRM через 1 + n*3 секунд */
alarm(1 + n*3);
for(;;){} /* зациклиться в user mode */
}
А. Богатырев, 1992-95 - 205 - Си в UNIX
#define NCHLD 4
int main(int ac, char *av[]){
int i;
/* Узнать число тиков в секунде */
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("Выход MAIN =================\n");
bye(0);
return 0;
}
и ее выдача:
Старт #3883 ================
Старт #3884 ================
Старт #3885 ================
Старт #3886 ================
Выход #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]
Выход #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]
Выход #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]
Выход #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]
Выход 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]
Обратите внимание, что 72+188+441+890=1591 (поле tms_cutime для main).
6.2.12. Еще одна программа: хронометрирование выполнения другой программы. Пример:
timer ls -l
А. Богатырев, 1992-95 - 206 - Си в UNIX
/* Хронометрирование выполнения программы */
#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)
);
}
А. Богатырев, 1992-95 - 207 - Си в 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. Свободное место на диске.
6.3.1. Системный вызов ustat() позволяет узнать количество свободного места в файло-
вой системе, содержащей заданный файл (в примере ниже - текущий каталог):
#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("На диске %*.*s\n"
"%ld свободных блоков (%ld Кб)\n"
"%d свободных I-узлов\n",
sizeof ust.f_fname, sizeof ust.f_fname,
ust.f_fname, /* название файловой системы (метка) */
ust.f_tfree, /* блоки по 512 байт */
(ust.f_tfree * 512L) / 1024,
ust.f_tinode );
}
Обратите внимание на запись длинной строки в printf: строки, перечисленные последова-
тельно, склеиваются ANSI C компилятором в одну длинную строку:
char s[] = "This is" " a line " "of words";
совпадает с
char s[] = "This is a line of words";
6.3.2. Более правильно, однако, пользоваться сисвызовом statvfs - статистика по вир-
туальной файловой системе. Рассмотрим его в следующем примере: копирование файла с
проверкой на наличие свободного места.
А. Богатырев, 1992-95 - 208 - Си в 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; /* имя программы */
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){ /* куда, откуда */
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);
}
А. Богатырев, 1992-95 - 209 - Си в UNIX
if(stat(to, &stt) >= 0){
/* Файл уже существует */
if((stt.st_mode & S_IFMT) == S_IFDIR){
/* И это каталог */
/* Выделить последнюю компоненту пути from */
if((s = strrchr(from, '/')) && s[1])
s++;
else s = from;
dirname = to;
/* Целевой файл - файл в этом каталоге */
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;
}
}
А. Богатырев, 1992-95 - 210 - Си в UNIX
not_exist:
printf("COPY %s TO %s\n", from, to);