The design of the unix operating system by Maurice J

Вид материалаРеферат
8.2 Системные операции, связанные со временем
Подобный материал:
1   ...   29   30   31   32   33   34   35   36   ...   55

прерываний по таймеру решает, что процесс использовал выделенный

ему квант времени, и поскольку множество процессов при этом меня-

ют свои приоритеты, ядро выполняет переключение контекста.


8.1.4 Управление приоритетами


Процессы могут управлять своими приоритетами с помощью сис-

темной функции nice:

nice(value);

где value - значение, в процессе пересчета прибавляемое к приори-

тету процесса:

приоритет = (ИЦП/константа) + (базовый приоритет) + (значение

nice)

Системная функция nice увеличивает или уменьшает значение поля

nice в таблице процессов на величину параметра функции, при этом

только суперпользователю дозволено указывать значения, увеличива-

ющие приоритет процесса. Кроме того, только суперпользователь мо-

жет указывать значения, лежащие ниже определенного порога. Поль-

зователи, вызывающие системную функцию nice для того, чтобы

понизить приоритет во время выполнения интенсивных вычислительных

работ, "удобны, приятны" (nice) для остальных пользователей сис-


----------┐ ----------┐ ----------┐

60 │ │ │ │ │ │

+---------+ +---------+ +---------+

│ │ │ │ │ │ B │

│ +---------+ +---------+ +---------+

│ │ │ │ │ │ │

│ +---------+ +---------+ +---------+

Более │ │ │ B │ │ A │

высокий +---------+ +---------+ +---------+

приори- │ │ │ │ │ │

тет +---------+ +---------+ +---------+

│ │ │ │ A │ │ │

│ +---------+ +---------+ +---------+

│ │ │ │ │ │ │

│ +---------+ +---------+ +---------+

│ │ A │ │ │ │ │

L---------- L---------- L----------

(а) (б) (в)


----------┐ ----------┐ ----------┐

60 │ B │ │ A B │ │ B │(процесс

+---------+ +---------+ +---------+ A за-

│ A │ │ │ │ │ пуска-

+---------+ +---------+ +---------+ ется

│ │ │ │ │ │ первым)

+---------+ +---------+ +---------+

│ │ │ │ │ │

+---------+ +---------+ +---------+

│ │ │ │ │ │

+---------+ +---------+ +---------+

│ │ │ │ │ │

+---------+ +---------+ +---------+

│ │ │ │ │ │

+---------+ +---------+ +---------+

│ │ │ │ │ │

L---------- L---------- L----------

(г) (д) (е)


Рисунок 8.5. Планирование на основе кольцевого списка и прио-

ритеты процессов


темы, отсюда название функции. Процессы наследуют значение nice у

своего родителя при выполнении системной функции fork. Функция

nice действует только для выполняющихся процессов; процесс не мо-

жет сбросить значение nice у другого процесса. С практической

точки зрения это означает, что если администратору системы пона-

добилось понизить приоритеты различных процессов, требующих для

своего выполнения слишком много времени, у него не будет другого

способа сделать это быстро, кроме как вызвать функцию удаления

(kill) для всех них сразу.


8.1.5 Планирование на основе справедливого раздела


Вышеописанный алгоритм планирования не видит никакой разницы

между пользователями различных классов (категорий). Другими сло-

вами, невозможно выделить определенной совокупности процессов,

например, половину сеанса работы с ЦП. Тем не менее, такая воз-

можность имеет важное значение для организации работы в условиях

вычислительного центра, где группа пользователей может пожелать

купить только половину машинного времени на гарантированной осно-

ве и с гарантированным уровнем реакции. Здесь мы рассмотрим схе-

му, именуемую "Планированием на основе справедливого раздела"

(Fair Share Scheduler) и реализованную на вычислительном центре

Indian Hill фирмы AT&T Bell Laboratories [Henry 84].

Принцип "планирования на основе справедливого раздела" состо-

ит в делении совокупности пользователей на группы, являющиеся

объектами ограничений, накладываемых обычным планировщиком на об-

работку процессов из каждой группы. При этом система выделяет

время ЦП пропорционально числу групп, вне зависимости от того,

сколько процессов выполняется в группе. Пусть, например, в систе-

ме имеются четыре планируемые группы, каждая из которых загружает

ЦП на 25% и содержит, соответственно, 1, 2, 3 и 4 процесса, реа-

лизующих счетные задачи, которые никогда по своей воле не уступят

ЦП. При условии, что в системе больше нет никаких других процес-

сов, каждый процесс при использовании традиционного алгоритма

планирования получил бы 10% времени ЦП (поскольку всего процессов

10 и между ними не делается никаких различий). При использовании

алгоритма планирования на основе справедливого раздела процесс из

первой группы получит в два раза больше времени ЦП по сравнению с

каждым процессом из второй группы, в 3 раза больше по сравнению с

каждым процессом из третьей группы и в 4 раза больше по сравнению

с каждым процессом из четвертой. В этом примере всем процессам в

группе выделяется равное время, поскольку продолжительность цик-

ла, реализуемого каждым процессом, заранее не установлена.

Реализация этой схемы довольно проста, что и делает ее прив-

лекательной. В формуле расчета приоритета процесса появляется еще

один термин - "приоритет группы справедливого раздела". В прост-

ранстве процесса также появляется новое поле, описывающее продол-

жительность ИЦП на основе справедливого раздела, общую для всех

процессов из группы. Программа обработки прерываний по таймеру

увеличивает значение этого поля для текущего процесса и ежесе-

кундно пересчитывает значения соответствующих полей для всех про-

цессов в системе. Новая компонента формулы вычисления приоритета

процесса представляет собой нормализованное значение ИЦП для каж-

дой группы. Чем больше процессорного времени выделяется процессам

группы, тем выше значение этого показателя и ниже приоритет.

В качестве примера рассмотрим две группы процессов (Рисунок

8.6), в одной из которых один процесс (A), в другой - два (B и

C). Предположим, что ядро первым запустило на выполнение процесс

A, в течение секунды увеличивая соответствующие этому процессу

значения полей, описывающих индивидуальное и групповое ИЦП. В ре-

зультате пересчета приоритетов по истечении секунды процессы B и

C будут иметь наивысшие приоритеты. Допустим, что ядро выбирает

на выполнение процесс B. В течение следующей секунды значение по-

ля ИЦП для процесса B поднимается до 60, точно такое же значение

принимает поле группового ИЦП для процессов B и C. Таким образом,

по истечении второй секунды процесс C получит приоритет, равный

75 (сравните с Рисунком 8.4), и ядро запустит на выполнение про-

цесс A с приоритетом 74. Дальнейшие действия можно проследить на

рисунке: ядро по очереди запускает процессы A, B, A, C, A, B и т.

д.


Время Процесс A Процесс B Процесс C

│ Прио- Ин- Груп- Прио- Ин- Груп- Прио- Ин- Груп-

│ ритет диви- по- ритет диви- по- ритет диви- по-

│ дуал. вое дуал. вое дуал. вое

│ ИЦП ИЦП ИЦП ИЦП ИЦП ИЦП

0 --+--

│ 60 0 0 60 0 0 60 0 0

│ 1 1

│ 2 2

│ · ·

│ · ·

│ · ·

1 --+-- 60 60

│ 90 30 30 60 0 0 60 0 0

│ 1 1 1

│ 2 2 2

│ · · ·

│ · · ·

│ · · ·

2 --+-- 60 60 60

│ 74 15 15 90 30 30 75 0 30

│ 16 16

│ 17 17

│ · ·

│ · ·

│ · ·

3 --+-- 75 75

│ 96 37 37 74 15 15 67 0 15

│ 16 1 16

│ 17 2 17

│ · · ·

│ · · ·

│ · · ·

4 --+-- 75 60 75

│ 78 18 18 81 7 37 93 30 37

│ 19 19

│ 20 20

│ · ·

│ · ·

│ · ·

5 --+-- 78 78

│ 98 39 39 70 3 18 76 15 18








Рисунок 8.6. Пример планирования на основе справедливого раз-

дела, в котором используются две группы с тремя

процессами


8.1.6 Работа в режиме реального времени


Режим реального времени подразумевает возможность обеспечения

достаточной скорости реакции на внешние прерывания и выполнения

отдельных процессов в темпе, соизмеримом с частотой возникновения

вызывающих прерывания событий. Примером системы, работающей в ре-

жиме реального времени, может служить система управления жизнео-


беспечением пациентов больниц, мгновенно реагирующая на изменение

состояния пациента. Процессы, подобные текстовым редакторам, не

считаются процессами реального времени: в них быстрая реакция на

действия пользователя является желательной, но не необходимой

(ничего страшного не произойдет, если пользователь, выполняющий

редактирование текста, подождет ответа несколько лишних секунд,

хотя у пользователя на этот счет могут быть и свои соображения).

Вышеописанные алгоритмы планирования выполнения процессов пред-

назначены специально для использования в системах разделения вре-

мени и не годятся для условий работы в режиме реального времени,

поскольку не гарантируют запуск ядром каждого процесса в течение

фиксированного интервала времени, позволяющего говорить о взаимо-

действии вычислительной системы с процессами в темпе, соизмеримом

со скоростью протекания этих процессов. Другой помехой в поддерж-

ке работы в режиме реального времени является невыгружаемость яд-

ра; ядро не может планировать выполнение процесса реального вре-

мени в режиме задачи, если оно уже исполняет другой процесс в

режиме ядра, без внесения в работу существенных изменений. В нас-

тоящее время системным программистам приходится переводить про-

цессы реального времени в режим ядра, чтобы обеспечить достаточ-

ную скорость реакции. Правильное решение этой проблемы - дать

таким процессам возможность динамического протекания (другими

словами, они не должны быть встроены в ядро) с предоставлением

соответствующего механизма, с помощью которого они могли бы сооб-

щать ядру о своих нуждах, вытекающих из особенностей работы в ре-

жиме реального времени. На сегодняшний день в стандартной системе

UNIX такая возможность отсутствует.


8.2 СИСТЕМНЫЕ ОПЕРАЦИИ, СВЯЗАННЫЕ СО ВРЕМЕНЕМ


Существует несколько системных функций, имеющих отношение к

времени протекания процесса: stime, time, times и alarm. Первые

две имеют дело с глобальным системным временем, последние две - с

временем выполнения отдельных процессов.

Функция stime дает суперпользователю возможность заносить в

глобальную переменную, определенную на уровне ядра, значение те-

кущего времени:

stime(pvalue);

где pvalue - целое число двойной длины, показывающее время в се-

кундах, прошедшее с полуночи (00:00:00) накануне 1 января 1970

года (по Гринвичу). Программа обработки прерываний по таймеру

каждую секунду увеличивает значение глобальной переменной. Выби-

рается время из этой переменной с помощью функции time:

time(tloc);

где tloc - указатель на переменную, принадлежащую процессу, в ко-

торую заносится возвращаемое функцией значение. Функция возвраща-

ет это значение и из самой себя, например, команде date, которая

вызывает эту функцию, чтобы определить текущее время.

Функция times возвращает суммарное время выполнения процесса

и всех его потомков, прекративших существование, в режимах ядра и

задачи. Синтаксис вызова функции:

times(tbuffer)

struct tms *tbuffer;

где tms - имя структуры, в которую помещаются возвращаемые значе-

ния и которая описывается следующим образом:


-------------------------------------------------------------┐

│ #include

│ #include

│ extern long times(); │

│ │

│ main() │

│ { │

│ int i; │

│ /* tms - имя структуры данных, состоящей из 4 элемен- │

│ тов */ │

│ struct tms pb1,pb2; │

│ long pt1,pt2; │

│ │

│ pt1 = times(&pb1); │

│ for (i = 0; i < 10; i++) │

│ if (fork() == 0) │

│ child(i); │

│ │

│ for (i = 0; i < 10; i++) │

│ wait((int*) 0); │

│ pt2 = times(&pb2); │

│ printf("процесс-родитель: реальное время %u │

│ в режиме задачи %u в режиме ядра %u │

│ потомки: в режиме задачи %u в режиме ядра %u\n",│

│ pt2 - pt1,pb2.tms_utime - pb1.tms_utime, │

│ pb2.tms_stime - pb1.tms_stime, │

│ pb2.tms_cutime - pb1.tms_cutime, │

│ pb2.tms_cstime - pb1.tms_cstime); │

│ } │

│ │

│ child(n); │

│ int n; │

│ { │

│ int i; │

│ struct tms cb1,cb2; │

│ long t1,t2; │


│ │

│ t1 = times(&cb1); │

│ for (i = 0; i < 10000; i++) │

│ ; │

│ t2 = times(&cb2); │

│ printf("потомок %d: реальное время %u в режиме задачи %u│

│ в режиме ядра %u\n",n,t2 - t1, │

│ cb2.tms_utime - cb1.tms_utime, │

│ cb2.tms_stime - cb1.tms_stime); │

│ exit(); │

│ } │

L-------------------------------------------------------------


Рисунок 8.7. Пример программы, использующей функцию times


struct tms {

/* time_t - имя структуры данных, в которой хранится время

*/

time_t tms_utime; /* время выполнения процесса в режиме

задачи */

time_t tms_stime; /* время выполнения процесса в режиме

ядра */


time_t tms_cutime; /* время выполнения потомков в режиме

задачи */

time_t tms_cstime; /* время выполнения потомков в режиме

ядра */

};

Функция times возвращает время, прошедшее "с некоторого произ-

вольного момента в прошлом", как правило, с момента загрузки сис-

темы.

На Рисунке 8.7 приведена программа, в которой процесс-роди-

тель создает 10 потомков, каждый из которых 10000 раз выполняет

пустой цикл. Процесс-родитель обращается к функции times перед

созданием потомков и после их завершения, в свою очередь потомки

вызывают эту функцию перед началом цикла и после его завершения.

Кто-то по наивности может подумать, что время выполнения потомков

процесса в режимах задачи и ядра равно сумме соответствующих сла-

гаемых каждого потомка, а реальное время процесса-родителя явля-

ется суммой реального времени его потомков. Однако, время

выполнения потомков не включает в себя время, затраченное на ис-

полнение системных функций fork и exit, кроме того оно может быть

искажено за счет обработки прерываний и переключений контекста.

С помощью системной функции alarm пользовательские процессы

могут инициировать посылку сигналов тревоги ("будильника") через

кратные промежутки времени. Например, программа на Рисунке 8.8

каждую минуту проверяет время доступа к файлу и, если к файлу бы-

ло произведено обращение, выводит соответствующее сообщение. Для

этого в цикле, с помощью функции stat, устанавливается момент

последнего обращения к файлу и, если оно имело место в течение

последней минуты, выводится сообщение. Затем процесс с помощью

функции signal делает распоряжение принимать сигналы тревоги, с

помощью функции alarm задает интервал между сигналами в 60 секунд

и с помощью функции pause приостанавливает свое выполнение до мо-

мента получения сигнала. Через 60 секунд сигнал поступает, ядро

подготавливает стек задачи к вызову функции обработки сигнала

wakeup, функция возвращает управление на оператор, следующий за

вызовом функции pause, и процесс исполняет цикл вновь.

Все перечисленные функции работы с временем протекания про-

цесса объединяет то, что они опираются на показания системных ча-

сов (таймера). Обрабатывая прерывания по таймеру, ядро обращается

к различным таймерным счетчикам и инициирует соответствующее

действие.


8.3 ТАЙМЕР


В функции программы обработки прерываний по таймеру входит:

* перезапуск часов,

* вызов на исполнение функций ядра, использующих встроенные ча-

сы,

* поддержка возможности профилирования выполнения процессов в

режимах ядра и задачи;

* сбор статистики о системе и протекающих в ней процессах,

* слежение за временем,

* посылка процессам сигналов "будильника" по запросу,

* периодическое возобновление процесса подкачки (см. следующую

главу),

* управление диспетчеризацией процессов.

Некоторые из функций реализуются при каждом прерывании по

таймеру, другие - по прошествии нескольких таймерных тиков. Прог-

рамма обработки прерываний по таймеру запускается с высоким прио-

ритетом обращения к процессору, не допуская во время работы воз-

никновения других внешних событий (таких как прерывания от

периферийных устройств). Поэтому программа обработки прерываний

по таймеру работает очень быстро, за максимально-короткое время


-------------------------------------------------------------┐

│ #include

│ #include

│ #include

│ │

│ main(argc,argv) │

│ int argc; │

│ char *argv[]; │

│ { │

│ extern unsigned alarm(); │

│ extern wakeup(); │

│ struct stat statbuf; │

│ time_t axtime; │

│ │

│ if (argc != 2) │

│ { │

│ printf("только 1 аргумент\n"); │

│ exit(); │

│ } │

│ │

│ axtime = (time_t) 0; │

│ for (;;) │

│ { │

│ /* получение значения времени доступа к файлу */ │

│ if (stat(argv[1],&statbuf) == -1) │

│ { │

│ printf("файла с именем %s нет\n",argv[1]); │

│ exit(); │

│ } │

│ if (axtime != statbuf.st_atime) │

│ { │

│ printf("к файлу %s было обращение\n",argv[1]); │

│ axtime = statbuf.st_atime; │

│ } │

│ signal(SIGALRM,wakeup); /* подготовка к приему │

│ сигнала */ │

│ alarm(60); │

│ pause(); /* приостанов до получения сигнала */│

│ } │

│ } │

│ │

│ wakeup() │

│ { │

│ } │

L-------------------------------------------------------------


Рисунок 8.8. Программа, использующая системную функцию alarm


-------------------------------------------------------------┐

│ алгоритм clock │

│ входная информация: отсутствует │

│ выходная информация: отсутствует │

│ { │

│ перезапустить часы; /* чтобы они снова посылали преры-│

│ вания */ │

│ если (таблица ответных сигналов не пуста) │

│ { │

│ установить время для ответных сигналов; │

│ запустить функцию callout, если время истекло; │

│ } │

│ если (профилируется выполнение в режиме ядра) │

│ запомнить значение счетчика команд в момент прерыва-│

│ ния; │

│ если (профилируется выполнение в режиме задачи) │

│ запомнить значение счетчика команд в момент прерыва-│

│ ния; │

│ собрать статистику о самой системе; │

│ собрать статистику о протекающих в системе процессах; │

│ выверить значение продолжительности ИЦП процессом; │

│ если (прошла 1 секунда или более и исполняется отрезок,│

│ не являющийся критическим) │

│ { │

│ для (всех процессов в системе) │

│ { │

│ установить "будильник", если он активен; │

│ выверить значение продолжительности ИЦП; │

│ если (процесс будет исполняться в режиме задачи)│