Основы программирования в Linux - Нейл Мэтью
Шрифт:
Интервал:
Закладка:
...
Упражнение 3.2. Вторая версия программы кодирования файлаВы можете добиться лучших результатов, копируя блоки большего размера. Взгляните на модифицированную программу copy_block.c, которая копирует файл блоками в 1 Кбайт и снова использует системные вызовы.
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main() {
char block[1024];
int in, out;
int nread;
in = open("file.in", O_RDONLY);
out = open("file.out", O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
while((nread = read(in, block, sizeof(block))) > 0)
write(out, block, nread);
exit(0);
}
Теперь испытайте программу, но сначала удалите старый выходной файл.
$ rm file.out
$ TIMEFORMAT="" time ./copy_block
0.00user 0.02system 0:00.04elapsed 78%CPU
...
Как это работает
Теперь программа выполняется только сотые доли секунды, поскольку ей требуется около 2000 системных вызовов. Конечно, это время очень зависит от системы, но оно показывает, что системные вызовы сопряжены с поддающимися измерению издержками, поэтому их применение стоит оптимизировать.
Другие системные вызовы для управления файлами
Существует ряд других системных вызовов, оперирующих низкоуровневыми дескрипторами файлов. Они позволяют программе контролировать использование файла, возвращая информацию о его состоянии,
lseekСистемный вызов lseek задает указатель текущей позиции чтения/записи дескриптора файла, т.е. вы можете применять его для установки в файле места, с которого будет происходить следующее считывание или на которое будет производиться следующая запись. Вы можете задать указатель на абсолютную позицию файла или позицию, относительно текущего положения указателя или конца файла.
#include <unistd.h>
#include <sys/types.h>
off_t lseek(int fildes, off_t offset, int whence);
Параметр offset применяется для указания позиции, а параметр whence определяет способ применения offset и может принимать следующие значения:
□ SEEK_SET — offset задает абсолютную позицию;
□ SEEK_CUR — offset задается относительно текущей позиции;
□ SEEK_END — offset задается относительно конца файла.
Вызов lseek возвращает величину параметра offset в байтах, измеряемую от начала файла, для которого установлен указатель, или -1 в случае неудачного завершения. Тип данных off_t, применяемый для параметра offset в операциях поиска, — зависящий от реализации тип integer (целое), определенный в файле sys/types.h.
fstat, stat и lstatСистемный вызов fstat возвращает информацию о состоянии файла, ассоциированного с открытым дескриптором файла. Эта информация записывается в структуру buf, адрес которой передается как параметр.
Далее приведена синтаксическая запись вызовов.
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
int fstat(int fildes, struct stat *buf);
int stat(const char *path, struct stat *buf);
int lstat(const char *path, struct stat *buf);
ПримечаниеУчтите, что включение файла sys/types.h не обязательное, но мы рекомендуем включать его при использовании системных вызовов, поскольку некоторые из их определений применяют для стандартных типов псевдонимы, которые могут измениться когда-нибудь.
Родственные функции stat и lstat возвращают информацию о состоянии названного файла. Они возвращают те же результаты за исключением того, что файл является символической ссылкой. Вызов lstat возвращает данные о самой ссылке, а вызов stat — о файле, на который ссылка указывает.
Элементы вызываемой структуры stat могут меняться в разных UNIX-подобных системах, но обязательно включают перечисленные в табл. 3.4 элементы.
Таблица 3.4
Элемент структуры stat Описание st_mode Права доступа к файлу и сведения о типе файла st_ino Индекс, ассоциированный с файлом st_dev Устройство, на котором размещен файл st_uid Идентификатор (user identity) владельца файла st_gid Идентификатор группы (group identity) владельца файла st_atime Время последнего обращения st_ctime Время последнего изменения прав доступа, владельца, группы или объема st_mtime Время последней модификации содержимого st_nlink Количество жестких ссылок на файлУ флагов st_mode, возвращаемых в структуре stat, также есть ряд ассоциированных макросов в заголовочном файле sys/stat.h. В эти макросы включены имена флагов для прав доступа и типов файлов и некоторые маски, помогающие проверять специфические типы и права.
Флаги прав доступа такие же, как в системном вызове open, описанном ранее. Для флагов типов файла включены следующие имена:
□ S_IFBLK — блочное устройство;
□ S_IFDIR — каталог;
□ S_IFCHR — символьное устройство;
□ S_IFIFO — FIFO (именованный канал);
□ S_IFREG — обычный файл;
□ S_IFLNK — символическая ссылка.
Для других флагов режима файла включены следующие имена:
□ S_ISUID — элемент получает setUID при выполнении;
□ S_ISGUID — элемент получает setGID при выполнении.
Для масок, интерпретирующих флаги st_mode, включены следующие имена:
□ S_IFMT — тип файла;
□ S_IRWXU — права пользователя на чтение/запись/выполнение;
□ S_IRWXG — права группы на чтение/запись/выполнение;
□ S_IRWXO — права остальных на чтение/запись/выполнение.
Существует ряд макросов, помогающих определить типы файлов. Они просто сравнивают надлежащим образом установленные флаги режима файла с подходящим флагом, типа устройства. К ним относятся следующие:
□ S_ISBLK — проверка для блочного файла;
□ S_ISCHR — проверка для символьного файла;
□ S_ISDIR — проверка для каталога;
□ S_ISFIFO — проверка для FIFO;
□ S_ISREG — проверка для обычного файла;
□ S_ISLNK — проверка для символической ссылки.
Например, для проверки того, что файл не является каталогом и у него есть права на выполнение только для владельца и больше никаких других прав, вы можете воспользоваться следующим тестом;
struct stat statbuf;
mode_t modes;
stat("filename", &statbuf);
modes = statbuf.st_mode;
if (!S_ISDIR(modes) && (modes & S_IRWXU) = S_IXUSR)
...
dup и dup2Системные вызовы dup позволяют дублировать дескриптор файла, предоставляя два или несколько разных дескрипторов, обращающихся к одному и тому же файлу. Эта возможность может применяться для чтения и записи в разные части файла. Системный вызов dup дублирует файловый дескриптор fildes и возвращает новый дескриптор. Системный вызов dup2 умело копирует один дескриптор файла в другой, задавая дескриптор, применяемый для копии.
Далее приведена синтаксическая запись для вызовов.
#include <unistd.h>
int dup(int fildes);
int dup2(int fildes, int fildes2);
Эти вызовы могут оказаться полезными в случае нескольких процессов, взаимодействующих через именованные каналы. Более глубоко мы рассмотрим системные вызовы dup в главе 13.
Стандартная библиотека ввода/вывода
Стандартная библиотека ввода/вывода (stdio) и ее заголовочный файл stdio.h предоставляют универсальный интерфейс для системных вызовов ввода/вывода нижнего уровня. Библиотека, теперь часть языка С стандарта ANSI, в отличие от системных вызовов, с которыми вы встречались ранее, включает много сложных функций для форматирования вывода и просмотра ввода. Она также обеспечивает необходимые условия буферизации для устройств.
Во многих случаях эта библиотека используется так же, как низкоуровневые дескрипторы файлов. Вы должны открыть файл для установления пути доступа. Это действие возвращает значение, применяемое как параметр в других функциях библиотеки ввода/вывода. Эквивалент низкоуровневого дескриптора файла называется потоком и реализуется как указатель на структуру FILE*.