Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
методичка ПО ОСРВ заочники.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
8.72 Mб
Скачать

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.