6. Системные вызовы и взаимодействие с UNIX.
процессом.
UNIX - многозадачная система (мультипрограммная). Это означает, что одновре-
менно может быть запущено много процессов. Процессор выполняет их в режиме разделения
времени - выделяя по очереди квант времени одному процессу, затем другому,
третьему... В результате создается впечатление параллельного быть временно откачан (т.е. скопирован из памяти машины) на диск, чтобы освобо-
дить память для других процессов. Когда "спящий" процесс дождется события, он будет
"разбужен" системой, переведен в ранг "готовых к выполнению" и, если был откачан -
будет возвращен с диска в память (но, может быть, на другое место в памяти!). Эта
процедура носит название "своппинг" (swapping).
Можно запустить несколько процессов, выполняющих программу из одного и того же процесс-интерпретатор команд (своя копия), выполняющий прог-
рамму из файла /bin/csh (или /bin/sh).
Процесс представляет собой изолированный "мир", общающийся с другими "мирами" во
Вселенной при помощи:
a) Аргументов функции main:
void main(int argc, char *argv[], char *envp[]);
Если мы наберем команду
$ a.out a1 a2 a3
то функция main программы из файла a.out вызовется с
argc = 4 /* количество аргументов */
argv[0] = "a.out" argv[1] = "a1"
argv[2] = "a2" argv[3] = "a3"
argv[4] = NULL
По соглашению argv[0] содержит имя выполняемого файла из которого загружена эта
программа[*].
b) Так называемого "окружения" (или "среды") char *envp[], продублированного также
в предопределенной переменной
extern char **environ;
Окружение состоит из строк вида
"ИМЯПЕРЕМЕННОЙ=значение"
Массив этих строк завершается NULL (как и argv). Для получения значения пере-
менной с именем ИМЯ существует стандартная функция
char *getenv( char *ИМЯ );
Она выдает либо значение, либо NULL если переменной с таким именем нет.
c) Открытых файлов. По умолчанию (неявно) всегда открыты 3 канала:
ВВОД В Ы В О Д
FILE * stdin stdout stderr
соответствует fd 0 1 2
связан с клавиатурой дисплеем
____________________
[*] Именно это имя показывает команда ps -ef
#include <stdio.h>
main(ac, av) char **av; {
execl("/bin/sleep", "Take it easy", "1000", NULL);
}
Эти каналы достаются процессу "в наследство" от запускающего процесса и связаны
с дисплеем и клавиатурой, если только не были перенаправлены. Кроме того, прог-
рамма может сама явно открывать файлы (при помощи open, creat, pipe, fopen).
Всего программа может одновременно открыть до 20 файлов (считая стандартные
каналы), а в некоторых системах и больше (например, 64). В MS DOS есть еще 2
предопределенных канала вывода: stdaux - в последовательный коммуникационный
порт, stdprn - на принтер.
d) Процесс имеет уникальный номер, который он может узнать вызовом
int pid = getpid();
а также узнать номер "родителя" вызовом
int ppid = getppid();
Процессы могут по этому номеру посылать друг другу сигналы:
kill(pid /* кому */, sig /* номер сигнала */);
и реагировать на них
signal (sig /*по сигналу*/, f /*вызывать f(sig)*/);
e) Существуют и другие средства коммуникации процессов: семафоры, сообщения, общая
память, сетевые коммуникации.
f) Существуют некоторые другие параметры (контекст) процесса: например, его текущий
каталог, который достается в наследство от процесса-"родителя", и может быть
затем изменен системным вызовом
chdir(char *имя_нового_каталога);
У каждого процесса есть свой собственный текущий рабочий каталог (в отличие от
MS DOS, где текущий каталог одинаков для всех задач). К "прочим" характеристи-
кам отнесем также: управляющий терминал; группу процессов (pgrp); идентификатор
(номер) владельца процесса (uid), идентификатор группы владельца (gid), реакции
и маски, заданные на различные сигналы; и.т.п.
g) Издания других запросов (системных вызовов) к операционной системе ("богу") для
выполнения различных "внешних" операций.
h) Все остальные действия происходят внутри процесса и никак не влияют на другие
процессы и устройства ("миры"). В частности, один процесс НИКАК не может полу-
чить доступ к памяти другого процесса, если тот не позволил ему это явно (меха-
низм shared memory); адресные пространства процессов независимы и изолированы
(равно и пространство ядра изолировано от памяти процессов).
Операционная система выступает в качестве коммуникационной среды, связывающей
"миры"-процессы, "миры"-внешние устройства (включая терминал пользователя); а также в
качестве распорядителя ресурсов "Вселенной", в частности - времени (по очереди выде-
ляемого активным процессам) и пространства (в памяти компьютера и на дисках).
Мы уже неоднократно упоминали "системные вызовы". Что же это такое? С точки
зрения Си-программиста - это обычные функции. В них передают аргументы, они возвра-
щают значения. Внешне они ничем не отличаются от написанных нами или библиотечных
функций и вызываются из программ одинаковым с ними способом.
С точки же зрения реализации - есть глубокое различие. Тело функции-сисвызова
расположено не в нашей программе, а в резидентной (т.е. постоянно находящейся в
памяти компьютера) управляющей программе, называемой ядром операционной системы[*].
____________________
[*] Собственно, операционная система характеризуется набором предоставляемых ею сис-
темных вызовов, поскольку все концепции, заложенные в системе, доступны нам только
через них. Если мы имеем две реализации системы с разным внутренним устройством
ядер, но предоставляющие одинаковый интерфейс системных вызовов (их набор, смысл и
поведение), то это все-таки одна и та же система! Ядра могут не просто отличаться,
но и быть построенными на совершенно различных принципах: так обстоит дело с UNIX-ами
на однопроцессорных и многопроцессорных машинах. Но для нас ядро - это "черный
ящик", полностью определяемый его поведением, т.е. своим интерфейсом с программами,
но не внутренним устройством. Вторым параметром, характеризующим ОС, являются фор-
маты данных, используемые системой: форматы данных для сисвызовов и формат информации
в различных файлах, в том числе формат оформления выполняемых файлов (формат данных в
физической памяти машины в этот список не include-файлов с
описанием этих форматов. Имена этих файлов также можно отнести к интерфейсу системы.
Сам термин "системный вызов" как раз означает "вызов системы для выполнения дейст-
вия", т.е. вызов функции в ядре системы. Ядро работает в привелегированном режиме,
в котором имеет доступ к некоторым системным таблицам[**], регистрам и портам внешних
устройств и диспетчера памяти, к которым обычным программам доступ аппаратно запрещен
(в отличие от MS DOS функция-"корешок", тело которой напи-
сано на ассемблере и содержит команду генерации программного прерывания. Это - глав-
ное отличие от нормальных Си-функций - вызов по прерыванию. Вторым этапом является
реакция ядра на прерывание:
1. переход в привелегированный режим;
2. разбирательство, КТО обратился к ядру, и подключение u-area этого процесса к
адресному пространству ядра (context switching);
3. извлечение аргументов из памяти запросившего процесса;
4. выяснение, ЧТО же хотят от ядра (один из аргументов, невидимый нам - это номер Си-функция в ядре;
8. возврат ответа в память процесса;
9. выключение привелегированного режима;
10. возврат из прерывания.
Во время системного вызова (шаг 7) процесс может "заснуть", дожидаясь некоторого
события (например, нажатия кнопки на клавиатуре). В это время ядро передаст управле-
ние другому процессу. Когда наш процесс будет "разбужен" (событие произошло) - он
продолжит выполнение шагов системного вызова.
Большинство системных вызовов возвращают в программу в качестве своего значения
признак успеха: 0 - все сделано, (-1) - сисвызов завершился неудачей; либо некоторое
содержательное значение при успехе (вроде дескриптора файла в open(), и (-1) при неу-
даче. В случае неудачного завершения в предопределенную переменную errno заносится
номер ошибки, описывающий причину неудачи (коды ошибок предопределены, описаны в
include-файле <errno.h> и имеют вид Eчтото). Заметим, что при УДАЧЕ эта переменная
просто не изменяется и может содержать любой мусор, поэтому проверять ее имеет смысл
лишь в случае, если ошибка действительно произошла:
#include <errno.h> /* коды ошибок */
extern int errno;
extern char *sys_errlist[];
int value;
if((value = sys_call(...)) < 0 ){
printf("Error:%s(%d)\n", sys_errlist[errno],
errno );
exit(errno); /* принудительное завершение программы */
}
____________________
Поведение всех программ в системе вытекает из поведения системных вызовов, кото-
рыми они пользуются. Даже то, что UNIX является многозадачной системой, непосредст-
венно вытекает из наличия системных вызовов fork, exec, wait и спецификации их функ-
ционирования!
То же можно сказать про язык Си - мобильность программы зависит в основном от
набора используемых в ней библиотечных функций (и, в меньшей степени, от диалекта са-
мого языка, который должен удовлетворять стандарту в обоих
системах, более того, работать в них одинаково.
[**] Таким как таблица процессов, таблица открытых файлов (всех вместе и для каждого
процесса), и.т.п.
Предопределенный массив sys_errlist, хранящийся в стандартной библиотеке, содержит
строки-расшифровку смысла ошибок (по-английски). Посмотрите описание функции per-
ror().
6.1. Файлы и каталоги.
6.1.1. Используя системный вызов stat, напишите программу, определяющую тип файла:
обычный файл, каталог, устройство, FIFO-файл. Ответ:
#include <sys/types.h>
#include <sys/stat.h>
typeOf( name ) char *name;
{ int type; struct stat st;
if( stat( name, &st ) < 0 ){
printf( "%s не существует\n", name );
return 0;
}
printf("Файл имеет %d имен\n", st.st_nlink);
switch(type = (st.st_mode & S_IFMT)){
case S_IFREG:
printf( "Обычный файл размером %ld байт\n",
st.st_size ); break;
case S_IFDIR:
printf( "Каталог\n" ); break;
case S_IFCHR: /* байтоориентированное */
case S_IFBLK: /* блочноориентированное */
printf( "Устройство\n" ); break;
case S_IFIFO:
printf( "FIFO-файл\n" ); break;
default:
printf( "Другой тип\n" ); break;
} return type;
}
6.1.2. Напишите программу, печатающую: свои аргументы, переменные окружения, инфор-
мацию о всех открытых ею файлах и используемых трубах. Для этой цели используйте
системный вызов
struct stat st; int used, fd;
for(fd=0; fd < NOFILE; fd++ ){
used = fstat(fd, &st) < 0 ? 0 : 1;
...
}
Программа может использовать дескрипторы файлов с номерами 0..NOFILE-1 (обычно
0..19). Если fstat для какого-то fd вернул код ошибки (<0), это означает, что данный
дескриптор не связан с открытым файлом (т.е. не используется). NOFILE определено в
include-файле <sys/param.h>, содержащем разнообразные параметры данной системы.
6.1.3. Напишите упрощенный аналог команды ls выдавать.
Формат каталога описан в header-файле <sys/dir.h> и в "канонической" версии выг-
лядит так: каталог - это файл, состоящий из структур direct, каждая описывает одно
имя файла, входящего в каталог:
struct direct {
unsigned short d_ino; /* 2 байта: номер I-узла */
char d_name[DIRSIZ]; /* имя файла */
};
В семействе BSD формат каталога несколько иной - там записи имеют разную длину, зави-
сящую от длины имени файла, которое может иметь длину от 1 до 256 символов.
Имя файла может состоять из любых символов, кроме '\0', служащего признаком
конца имени и '/', служащего разделителем. В имени допустимы пробелы, управляющие
символы (но не рекомендуются!), любое число точек (в отличие от MS DOS, где допустима
единственная точка, отделяющая собственно имя от суффикса (расширения)), разрешены
даже непечатные (т.е. управляющие) символы! Если имя файла имеет длину 14 (DIRSIZ)
символов, то оно не оканчивается байтом '\0'. В этом случае для печати имени файла
возможны три подхода:
1. Выводить символы при помощи putchar()-а в цикле. Цикл прерывать по индексу рав-
ному DIRSIZ, либо по достижению байта '\0'.
2. Скопировать поле d_name в другое место:
char buf[ DIRSIZ + 1 ];
strncpy(buf, d.d_name, DIRSIZ);
buf[ DIRSIZ ] = '\0';
Этот способ лучший, если имя файла надо не просто напечатать, но и запомнить на
будущее, чтобы использовать в своей программе.
3. Использовать такую особенность функции printf():
#include <sys/types.h>
#include <sys/dir.h>
struct direct d;
...
printf( "%*.*s\n", DIRSIZ, DIRSIZ, d.d_name );
Если файл был стерт, то в поле d_ino затирается физи-
чески, а просто помечается как стертое: d_ino=0; Каталог при этом никак не уплотня-
ется и не укорачивается! Поэтому имена с d_ino==0 выдавать не следует - это имена
уже уничтоженных файлов.
При создании нового имени (creat, link, mknod) система просматривает каталог и
переиспользует первый от начала свободный слот (ячейку каталога) где d_ino==0, запи-
сывая новое имя в него (только в этот момент старое имя-призрак окончательно исчезнет
физически). Если пустых мест нет - каталог удлиняется.
Любой каталог всегда содержит два стандартных имени: "." - ссылка на этот же
каталог (на его собственный I-node), ".." - на вышележащий каталог. У корневого
каталога "/" оба этих имени ссылаются на него же самого (т.е. содержат d_ino==2).
Имя каталога не содержится в нем самом. Оно содержится в "родительском" каталоге
...
Каталог в UNIX - это обычный дисковый файл. Вы можете читать его из своих прог-
рамм. Однако никто (включая суперпользователя[**]) не может записывать что-либо в ката-
лог при помощи write. Изменения содержимого каталогов выполняет только ядро, отвечая
на запросы в виде системных вызовов creat, unlink, link, mkdir, rmdir, rename, mknod.
Коды доступа для каталога интерпретируются следующим образом:
w запись
S_IWRITE. Означает право создавать и уничтожать в каталоге имена файлов при
____________________
[**] Суперпользователь (superuser) имеет uid==0. Это "привелегированный" пользова-
тель, который имеет право делать ВСЕ. Ему доступны любые сисвызовы и файлы, несмотря
на коды доступа и.т.п.
- достаточно иметь доступ по записи к
каталогу, содержащему его имя!
r чтение
S_IREAD. Право читать каталог как обычный файл (право выполнять opendir доступа "выполнение" для этого каталога!
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 реализуется через эти функции.
#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).
& Ритчи).
* Ключи компиляции:
* 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 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 для продолжения. */
/* Обойти дерево с корнем в 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;
}
}
/* Обработать каталог: прочитать его и найти подкаталоги */
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; }
}
#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;
}
/* -------------------------------------------------------------- */
/* Рекурсивное удаление файлов и каталогов */
/* -------------------------------------------------------------- */
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 %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);
}
/* -------------------------------------------------------------- */
#ifndef TREEONLY
void main # 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 для проверки каждого файла.
[Назад] [Содержание] [Вперед]
| Главная |