- •2.1. Введение
- •2.2. Вопросы, относящиеся к команде who
- •2.2.1. Программы состоят из команд
- •2.3. Вопрос 1: Что делает команда who?
- •2.3.1. Обращение к справочнику
- •2.4. Вопрос 2: Как работает команда who?
- •2.4.1. Мы теперь знаем, как работает who
- •2.5. Вопрос 3: Могу ли я написать who?
- •2.5.1. Вопрос: Как я буду читать структуры из файла?
- •2.5.2. Ответ: Использование open, read и close
- •2.5.3. Написание программы who 1.С
- •2.5.4. Отображение записей о вхождениях в систему
- •2.5.5. Написание версии who2.С
- •2.6. Проект два: Разработка программы ср (чтение и запись)
- •2.6.1. Вопрос 1: Что делает команда ср?
- •2.6.2. Вопрос 2: Как команда ср создает файл и как пишет в него? Создание/транкатенация файла
- •2.6.3. Вопрос 3: Могу ли я написать программу ср?
- •2.6.4. Программирование в Unix кажется достаточно простым
- •2.7. Увеличение эффективности файловых операций ввода/ вывода: Буферирование
- •2.7.1. Какой размер буфера следует считать лучшим?
- •2.7.2 Почему на системные вызовы требуется тратить время?
- •2.7.3. Означает ли, что наша программа who2.С неэффективна?
- •2.7.4. Добавление буферирования к программе who2.С
- •2.8. Буферизация и ядро
- •2.8.1. Если буферизация столь хороша, то почему ее не использует ядро?
- •2.9. Чтение файла и запись в файл
- •2.9.1. Выход из системы: Что происходит?
- •2.9.2. Выход из системы: Как это происходит
- •2.9.3. Смещение текущего указателя: Iseek
- •2.9.4. Кодирование выхода из системы через терминал
- •2.10. Что делать с ошибками системных вызовов?
- •3.1. Введение
- •3.2. Вопрос 1: Что делает команда is?
- •3.2.1. Команда Is выводит список имен файлов и оповещает об атрибутах файлов
- •3.2.3. Наиболее употребимые опции
- •3.2.4. Первый ответ: Итоговые замечания
- •3.3. Краткий обзор дерева файловой системы
- •3.4. Вопрос 2: Как работает команда Is?
- •3.4.1. Что же такое каталог, в конце концов?
- •3.4.2. Работают ли системные вызовы open, read и close в отношении каталогов?
- •3.4.3. Хорошо, хорошо. Но как же мне прочитать каталог?
- •3.5. Вопрос 3: Могу ли я написать Is?
- •3.5.1. Что еще нужно делать?
- •3.6. Проект 2: Написание версии Is -I
- •3.6.1. Вопрос 1: Что делает Is-I?
- •3.6.2. Вопрос 2: Какработает Is -I?
- •3.6.3. Ответ: Системный вызов stat получает информацию о файле
- •3.6.4. Какую еще информацию можно получить с помощью системного вызова stat?
- •3.6.5. Чего мы достигли?
- •3.6.6. Преобразование числового значения поля mode в символьное значение
- •3.6.7. Преобразования числового представления идентификаторов собственника/группы в строковое представление
- •3.6.8. Объединение всего вместе: Is2.C
- •3.7. Три специальных разряда
- •3.7.1. Разряд Set-User-id
- •3.7.2Разряд Set-Group-id
- •3.7.3 Разряд Sticky Bit
- •3.7.4. Специальные разряды nls-l
- •3.8. Итоги для команды is
- •3.9. Установка и модификация свойств файла
- •3.9.1. Тип файла
- •3.9.2. Разряды прав доступа и специальные разряды
- •3.9.3. Число ссылок на файл
- •3.9.4. Собственник и группа для файла
- •3.9.5. Размер файла
- •3.9.6. Время последней модификации и доступа
- •3.9.7. Имя файла
2.7.3. Означает ли, что наша программа who2.С неэффективна?
Да! Выполнение для каждой записи utmp одного системного вызова выглядит также неэффективно, как если бы мы покупали пиццу слоями или покупали бы яйца поштучно. Если вы собрались приготовить на завтрак яичницу из трех яиц, то вы должны будете поехать в магазин, купить одно яйцо, возвратиться обратно, поджарить яйцо, затем съесть его. После [ того как вы разделались с первым яйцом, должны будете поехать в магазин, купить другое яйцо, приехать обратно, поджарить яйцо и съесть его. Наконец, вы должны будете поехать и купить третье яйцо и понять, почему же яйца упаковывают в эти удобные коробки.
Хорошая идея состоит в том, чтобы читать сразу несколько (связку) записей. Тогда (как в случае приобретения яиц в упаковке) связка записей помещается в локальную память. Упаковка с яйцами является по смыслу буфером. Далее показан псевдокод для метода getegg, где будет использована буферизация при покупке яиц.
getegg(){
if (eggs_ left_in_carton == 0){
вновь упаковать коробку с яйцами в магазине
if (eggs_at_store == 0)
return EndOfEggs
eggs left in carton = 12
}
eggsJeftjn_carton--;
return one egg;
}
При каждом обращении к getegg выбирается одно яйцо, но не из магазина. Когда упаковка с яйцами опустеет, то по алгоритму функции следует ехать в магазин. Но какое отношение все это имеет к программированию в Unix?
Ознакомьтесь с содержанием заголовочного файла /usr/include/stdio.h для getc. В некоторые версиях Unix функция getc, реализованная как макрос, использует ту же логику, что и функция getegg.
2.7.4. Добавление буферирования к программе who2.С
Мы создадим версию программы who2.c, которая будет работать более эффективно за счет введения буферирования, что должно уменьшить число используемых системных вызовов. Идея, которая была представлена на примере функции getegg, может быть представлена в программном виде. На рисунке 2.6 показано, как будет работать программа с буферизацией.
Буферирование файла с utmplib
Функция main обращается в utmplib.c для получения следующей utmp структуры
Функция из utmplib.c читают эти структуры 16 раз с диска в массив
Ядро будет вызвано, только когда отработают все 16 чтений
Рисунок 2.6
Поток управления при работе системных вызовов
Мы создаем массив, который может содержать 16 utmp структур. Этот массив именуется как буфер в нижней части схематического изображения процесса. Массив содержит последовательность структур в пространстве процесса, что аналогично случаю с коробкой для яиц, в которой находились яйца у вас дома. Напишем функцию с именем utmpnext, которая будет извлекать записи из буфера.
Модифицируем функцию main так, чтобы получать структуры из нашего буфера в пользо- I вательском пространстве. Это будет сводиться к вызову нашей собственной функции I utmp_next в пользовательском пространстве. После того как будут обработаны все струк- I туры из буфера, функция utmp. next обратится к системному вызову read, чтобы потребовать I от ядра считать очередные 16 записей. Эта новая модель уменьшает число системных вы- I зовов read в 16 раз.
Такой буфер для размещения в нем 16 структур и функции для загрузки в буфер данных с диска и для извлечения из него структур для функции main помещены в файл utmplib.c.
Код utmplib.c
Файл utmplib.c, содержимое которого здесь приведено, реализует алгоритм буферирования записей:
/* utmplib.c - функции для чтения в буфер из файла utmp
*
функции:
utmpopen(filename) - открытие файла *
возвращает -1 при ошибке
* utmp_next() - возвращает указатель на следующую структуру
* возвращает NULL при достижении конца файла eof
* utmp_close() -закрытиефайла
* при одной операции чтения происходит чтение NRECS записей и затем они
* извлекаются из буфера
*/
#include <stdio.h>
#include <fcntl.h> «include <sys/types.h>
#include <utmp.h> «define NRECS 16
#define NULLUT ((struct utmp *)NULL)
#define UTSIZE (sizeof(struct utmp))
static char utmpbuf [NRECS * UTSIZE]; /* место хранения */
static int numrecs; /* количество хранимых элементов */
static int currec; /* переход */
static int fd_utmp = -1; /* чтение из */
utmp open(char *filename)
{
fd_utmp = open(filename, 0_RDONLY); /* открытие */
currec = numjecs - 0; /* пока нет записей */
return fd_utmp; /* сообщение */
}
struct utmp *utmp_next()
{
struct utmp *recp;
if(fd_utmp==-1) /* ошибка?*/
return NULLUT;
if (cur_rec==num_recs && utmp_reload()==0) /* еще? */
return NULLUT;
/*получить адрес следующей записи */
recp = (struct utmp *) &utmpbuf[curjec * UTSIZE];
cur_rec++;
return recp;
}
int utmpjeload
/*
Читать следующую последовательность записей в буфер
/*
{
int amt_read;
I* чтение записей в буфер */
Amtj_rad = read(fd_utmp, utmpbuf, NRECS * UTSIZE);
/* сколько было получено? */
Num_recs = amtjead/UTSIZE;
/* сброс указателя */
Cur_rec = 0; . return num_recs;
}
utmp close()
{
if (fd_utmp != ■ 1) /* не закрывать, если не было */
closeffd utmp); /* открыто */
}
utmplib.c содержит буфер, переменные и функции для управления потоком данных, который проходит через буфер. Значения переменных num_recs и cur_rec определяют, сколько структур находится в буфере и сколько из них было использовано.
Каждый раз при выборке записи функция utmpjext определяет с помощью проверки переменной curjec - не достиг ли этот счетчик значения, равного числу записей в буфере. Если те осталось неиспользованных записей, то функция utmpjiext производит перезагрузку буфера с диска. Прежде чем передать запись на использование, функция инкрементирует счетчик cur_rec.
utmplib.c поддерживает ясный интерфейс в отношении вызываемых функций, скрывая знутренние детали расположения в памяти и формат utmp записей. Функция utmp_next проcто возвращает указатели на структуры.
Далее представлена модифицированная версия функции main:
/* who3.c - who с буферируемым чтением
- подавление пустых записей
* - форматирование времени
* - буферирование ввода (используя utmplib)
#include <stdio.h>
#include <sys/types.h> «include <utmp.h>
#include <fcntl.h>
#include <time.h>
#define SHOWHOST
void show_info(struct utmp *);
void showtime(time_t);
int main()
{
struct utmp *utbufp, /* указатель на следующую запись *
*utmpnextO; /* возвращаемый указатель на следующую запись */
if (utmp_open(UTMP_FILE) == -1){
perror(UTMP_FILE); exit(1);
}
while ((utbufp = utmp_next()) != ((struct utmp *) NULL))
showjnfo(utbufp);
utmp_close();
return 0;
}
/*
*showinfo()
…
В данной версии вместо системных вызовов open, read, close будут вызываться эквивалентные функции в модуле буферизации. Функции для отображения находятся в show_nfo.
