Страница:
прерывании вызывать функцию theend, затем он вычисляет диапазон адресов
программы, в пределах которых будет производиться измерение продолжительнос-
ти (начиная с адреса функции main и кончая адресом функции theend), и, нако-
нец, запускает функцию profil, сообщая ядру о том, что он собира-
ется начать измерение. В результате выполнения программы в течение 10 секунд
на несильно загруженной машине AT&T 3B20 были получены данные, представлен-
ные на Рисунке 8.13. Адрес функции f превышает адрес начала профилирования
на 204 байта; поскольку текст функции f имеет размер 12 байт, а размер цело-
го числа в машине AT&T 3B20 равен 4 байтам, адреса функции f отображаются на
элементы массива buf с номерами 51, 52 и 53. По такому же принципу адреса
функции g отображаются на элементы buf c номерами 54, 55 и 56. Элементы buf
с номерами 46, 48 и 49 предназначены для адресов, принадлежащих циклу функ-
ции main. В обычном случае диапазон адресов, в пределах которого выполняется
измерение параметров, определяется в результате обращения к таблице иденти-
фикаторов для данной программы, где указываются адреса программных секций.
Пользователи сторонятся функции profil из-за того, что она кажется им слиш-
ком сложной; вместо нее они используют при компиляции программ на языке Си
параметр, сообщающий компилятору о необходимости сгенерировать код, следящий
за ходом выполнения процессов.
248
+------------------------------------------------------------+
| #include |
| int buffer[4096]; |
| main() |
| { |
| int offset,endof,scale,eff,gee,text; |
| extern theend(),f(),g(); |
| signal(SIGINT,theend); |
| endof = (int) theend; |
| offset = (int) main; |
| /* вычисляется количество слов в тексте программы */ |
| text = (endof - offset + sizeof(int) - 1)/sizeof(int); |
| scale = Oxffff; |
| printf |
| ("смещение до начала %d до конца %d длина текста %d\n",|
| offset,endof,text); |
| eff = (int) f; |
| gee = (int) g; |
| printf("f %d g %d fdiff %d gdiff %d\n",eff,gee, |
| eff-offset,gee-offset); |
| profil(buffer,sizeof(int)*text,offset,scale); |
| for (;;) |
| { |
| f(); |
| g(); |
| } |
| } |
| f() |
| { |
| } |
| g() |
| { |
| } |
| theend() |
| { |
| int i; |
| for (i = 0; i < 4096; i++) |
| if (buffer[i]) |
| printf("buf[%d] = %d\n",i,buffer[i]); |
| exit(); |
| } |
+------------------------------------------------------------+
Рисунок 8.12. Программа, использующая системную функцию profil
+------------------------------------------------------+
| смещение до начала 212 до конца 440 длина текста 57 |
| f 416 g 428 fdiff 204 gdiff 216 |
| buf[46] = 50 |
| buf[48] = 8585216 |
| buf[49] = 151 |
| buf[51] = 12189799 |
| buf[53] = 65 |
| buf[54] = 10682455 |
| buf[56] = 67 |
+------------------------------------------------------+
Рисунок 8.13. Пример результатов выполнения программы, ис-
пользующей системную функцию profil
249
В момент поступления прерывания по таймеру система может выполняться в
режиме ядра или задачи, а также находиться в состоянии простоя (бездейст-
вия). Состояние простоя означает, что все процессы приостановлены в ожидании
наступления события. Для каждого состояния процессора ядро имеет внутренние
счетчики, устанавливаемые при каждом прерывании по таймеру. Позже пользова-
тельские процессы могут проанализировать накопленную ядром статистическую
информацию.
В пространстве каждого процесса имеются два поля для записи продолжи-
тельности времени, проведенного процессом в режиме ядра и задачи. В ходе об-
работки прерываний по таймеру ядро корректирует значение поля, соответствую-
щего текущему режиму выполнения процесса. Процессы-родители собирают статис-
тику о своих потомках при выполнении функции wait, беря за основу информа-
цию, поступающую от завершающих свое выполнение потомков.
В пространстве каждого процесса имеется также одно поле для ведения уче-
та использования памяти. В ходе обработки прерывания по таймеру ядро вычис-
ляет общий объем памяти, занимаемый текущим процессом, исходя из размера
частных областей процесса и его долевого участия в использовании разделяемых
областей памяти. Если, например, процесс использует области данных и стека
размером 25 и 40 Кбайт, соответственно, и разделяет с четырьмя другими про-
цессами одну область команд размером 50 Кбайт, ядро назначает процессу 75
Кбайт памяти (50К/5 + 25К + 40К). В системе с замещением страниц ядро вычис-
ляет объем используемой памяти путем подсчета числа используемых в каждой
области страниц. Таким образом, если прерываемый процесс имеет две частные
области и еще одну область разделяет с другим процессом, ядро назначает ему
столько страниц памяти, сколько содержится в этих частных областях, плюс по-
ловину страниц, принадлежащих разделяемой области. Вся указанная информация
отражается в учетной записи при завершении процесса и может быть использова-
на для расчетов с заказчиками машинного времени.
Ядро увеличивает показание системных часов при каждом прерывании по тай-
меру, измеряя время в таймерных тиках от момента загрузки системы. Это зна-
чение возвращается процессу через системную функцию time и дает возможность
определять общее время выполнения процесса. Время первоначального запуска
процесса сохраняется ядром в адресном пространстве процесса при исполнении
системной функции fork, в момент завершения процесса это значение вычитается
из текущего времени, результат вычитания и составляет реальное время выпол-
нения процесса. В другой переменной таймера, устанавливаемой с помощью сис-
темной функции stime и корректируемой раз в секунду, хранится календарное
время.
В настоящей главе был описан основной алгоритм диспетчеризации процессов
в системе UNIX. С каждым процессом в системе связывается приоритет планиро-
вания, значение которого появляется в момент перехода процесса в состояние
приостанова и периодически корректируется программой обработки прерываний по
таймеру. Приоритет, присваиваемый процессу в момент перехода в состояние
приостанова, имеет значение, зависящее от того, какой из алгоритмов ядра ис-
полнялся процессом в этот момент. Значение приоритета, присваиваемое процес-
су во время выполнения программой обработки прерываний по таймеру (или в тот
момент, когда процесс возвращается из режима ядра в режим задачи), зависит
от того, сколько времени процесс занимал ЦП: процесс получает низкий приори-
тет, если он обращался к ЦП, и высокий - в противном случае. Системная функ-
ция nice дает процессу возможность влиять на собственный приоритет путем до-
бавления параметра, участвующего в пересчете приоритета.
250
В главе были также рассмотрены системные функции, связанные с временем
выполнения системы и протекающих в ней процессов: с установкой и получением
системного времени, получением времени выполнения процессов и установкой
сигналов "будильника". Кроме того, описаны функции программы обработки пре-
рываний по таймеру, которая следит за временем в системе, управляет таблицей
ответных сигналов, собирает статистику, а также подготавливает запуск плани-
ровщика процессов, программы подкачки и "сборщика" страниц. Программа под-
качки и "сборщик" страниц являются объектами рассмотрения в следующей главе.
1. При переводе процессов в состояние приостанова ядро назначает процессу,
ожидающему снятия блокировки с индекса, более высокий приоритет по
сравнению с процессом, ожидающим освобождения буфера. Точно так же,
процессы, ожидающие ввода с терминала, получают более высокий приоритет
по сравнению с процессами, ожидающими возможности производить вывод на
терминал. Объясните причины такого поведения ядра.
*2. В алгоритме обработки прерываний по таймеру предусмотрен пересчет прио-
ритетов и перезапуск процессов на выполнение с интервалом в 1 секунду.
Придумайте алгоритм, в котором интервал перезапуска динамически меняет-
ся в зависимости от степени загрузки системы. Перевесит ли выигрыш уси-
лия по усложнению алгоритма ?
3. В шестой редакции системы UNIX для расчета продолжительности ИЦП теку-
щим процессом используется следующая формула:
decay(ИЦП) = max (пороговый приоритет, ИЦП-10);
а в седьмой редакции:
decay(ИЦП) = .8 * ИЦП;
Приоритет процесса в обеих редакциях вычисляется по формуле:
приоритет = ИЦП/16 + (базовый уровень приоритета);
Повторите пример на Рисунке 8.4, используя приведенные формулы.
4. Проделайте еще раз пример на Рисунке 8.4 с семью процессами вместо
трех, а затем измените частоту прерываний по таймеру с 60 на 100 преры-
ваний в секунду. Прокомментируйте результат.
5. Разработайте схему, в которой система накладывает ограничение на про-
должительность выполнения процесса, при превышении которого процесс за-
вершается. Каким образом пользователь должен отличать такой процесс от
процессов, для которых не должны существовать подобные ограничения ?
Каким образом должна работать схема, если единственным условием являет-
ся ее запуск из shell'а ?
6. Когда процесс выполняет системную функцию wait и обнаруживает прекра-
тившего существование потомка, ядро приплюсовывает к его ИЦП значение
поля ИЦП потомка. Чем объясняется такое "наказание" процесса-родителя ?
7. Команда nice запускает последующую команду с передачей ей указанного
значения, например:
nice 6 nroff -mm big_memo > output
Напишите на языке Си программу, реализующую команду nice.
8. Проследите на примере Рисунка 8.4, каким образом будет осуществляться
диспетчеризация процессов в том случае, если значение, передаваемое
функцией nice для процесса A, равно 5 или -5.
9. Проведите эксперимент с системной функцией renice x y, где x - код
идентификации процесса (активного), а y - новое значение nice для ука-
занного процесса.
10. Вернемся к примеру, приведенному на Рисунке 8.6. Предположим, что груп-
пе, в которую входит процесс A, выделяется 33% процессорного времени, а
группе, в которую входит процесс B, - 66% процессорного времени. В ка-
кой последовательности будут исполняться процессы ? Обобщите алгоритм
вычисления приоритетов таким образом, чтобы значение группового ИЦП ус-
реднялось.
251
11. Выполните команду date. Команда без аргументов выводит текущую дату:
указав аргумент, например:
date mmddhhmmyy
(супер)пользователь может установить текущую дату в системе
(соответственно, месяц, число, часы, минуты и год). Так,
date 0911205084
устанавливает в качестве текущего времени 11 сентября 1984 года 8:50
пополудни.
12. В программах можно использовать функцию пользовательского
уровня sleep:
sleep(seconds);
с помощью которой выполнение программы приостанавливается на указанное
число секунд. Разработайте ее алгоритм, в котором используйте системные
функции alarm и pause. Что произойдет, если процесс вызовет функцию
alarm раньше функции sleep ? Рассмотрите две возможности: 1) действие
ранее вызванной функции alarm истекает в то время, когда процесс нахо-
дится в состоянии приостанова, 2) действие ранее вызванной функции
alarm истекает после завершения функции sleep.
*13. Обратимся еще раз к последней проблеме. Ядро может выполнить переключе-
ние контекста во время исполнения функции sleep между вызовами alarm и
pause. Тогда есть опасность, что процесс получит сигнал alarm до того,
как вызовет функцию pause. Что произойдет в этом случае ? Как вовремя
распознать эту ситуацию ?
252
программы, в пределах которых будет производиться измерение продолжительнос-
ти (начиная с адреса функции main и кончая адресом функции theend), и, нако-
нец, запускает функцию profil, сообщая ядру о том, что он собира-
ется начать измерение. В результате выполнения программы в течение 10 секунд
на несильно загруженной машине AT&T 3B20 были получены данные, представлен-
ные на Рисунке 8.13. Адрес функции f превышает адрес начала профилирования
на 204 байта; поскольку текст функции f имеет размер 12 байт, а размер цело-
го числа в машине AT&T 3B20 равен 4 байтам, адреса функции f отображаются на
элементы массива buf с номерами 51, 52 и 53. По такому же принципу адреса
функции g отображаются на элементы buf c номерами 54, 55 и 56. Элементы buf
с номерами 46, 48 и 49 предназначены для адресов, принадлежащих циклу функ-
ции main. В обычном случае диапазон адресов, в пределах которого выполняется
измерение параметров, определяется в результате обращения к таблице иденти-
фикаторов для данной программы, где указываются адреса программных секций.
Пользователи сторонятся функции profil из-за того, что она кажется им слиш-
ком сложной; вместо нее они используют при компиляции программ на языке Си
параметр, сообщающий компилятору о необходимости сгенерировать код, следящий
за ходом выполнения процессов.
248
+------------------------------------------------------------+
| #include
| int buffer[4096]; |
| main() |
| { |
| int offset,endof,scale,eff,gee,text; |
| extern theend(),f(),g(); |
| signal(SIGINT,theend); |
| endof = (int) theend; |
| offset = (int) main; |
| /* вычисляется количество слов в тексте программы */ |
| text = (endof - offset + sizeof(int) - 1)/sizeof(int); |
| scale = Oxffff; |
| printf |
| ("смещение до начала %d до конца %d длина текста %d\n",|
| offset,endof,text); |
| eff = (int) f; |
| gee = (int) g; |
| printf("f %d g %d fdiff %d gdiff %d\n",eff,gee, |
| eff-offset,gee-offset); |
| profil(buffer,sizeof(int)*text,offset,scale); |
| for (;;) |
| { |
| f(); |
| g(); |
| } |
| } |
| f() |
| { |
| } |
| g() |
| { |
| } |
| theend() |
| { |
| int i; |
| for (i = 0; i < 4096; i++) |
| if (buffer[i]) |
| printf("buf[%d] = %d\n",i,buffer[i]); |
| exit(); |
| } |
+------------------------------------------------------------+
Рисунок 8.12. Программа, использующая системную функцию profil
+------------------------------------------------------+
| смещение до начала 212 до конца 440 длина текста 57 |
| f 416 g 428 fdiff 204 gdiff 216 |
| buf[46] = 50 |
| buf[48] = 8585216 |
| buf[49] = 151 |
| buf[51] = 12189799 |
| buf[53] = 65 |
| buf[54] = 10682455 |
| buf[56] = 67 |
+------------------------------------------------------+
Рисунок 8.13. Пример результатов выполнения программы, ис-
пользующей системную функцию profil
249
В момент поступления прерывания по таймеру система может выполняться в
режиме ядра или задачи, а также находиться в состоянии простоя (бездейст-
вия). Состояние простоя означает, что все процессы приостановлены в ожидании
наступления события. Для каждого состояния процессора ядро имеет внутренние
счетчики, устанавливаемые при каждом прерывании по таймеру. Позже пользова-
тельские процессы могут проанализировать накопленную ядром статистическую
информацию.
В пространстве каждого процесса имеются два поля для записи продолжи-
тельности времени, проведенного процессом в режиме ядра и задачи. В ходе об-
работки прерываний по таймеру ядро корректирует значение поля, соответствую-
щего текущему режиму выполнения процесса. Процессы-родители собирают статис-
тику о своих потомках при выполнении функции wait, беря за основу информа-
цию, поступающую от завершающих свое выполнение потомков.
В пространстве каждого процесса имеется также одно поле для ведения уче-
та использования памяти. В ходе обработки прерывания по таймеру ядро вычис-
ляет общий объем памяти, занимаемый текущим процессом, исходя из размера
частных областей процесса и его долевого участия в использовании разделяемых
областей памяти. Если, например, процесс использует области данных и стека
размером 25 и 40 Кбайт, соответственно, и разделяет с четырьмя другими про-
цессами одну область команд размером 50 Кбайт, ядро назначает процессу 75
Кбайт памяти (50К/5 + 25К + 40К). В системе с замещением страниц ядро вычис-
ляет объем используемой памяти путем подсчета числа используемых в каждой
области страниц. Таким образом, если прерываемый процесс имеет две частные
области и еще одну область разделяет с другим процессом, ядро назначает ему
столько страниц памяти, сколько содержится в этих частных областях, плюс по-
ловину страниц, принадлежащих разделяемой области. Вся указанная информация
отражается в учетной записи при завершении процесса и может быть использова-
на для расчетов с заказчиками машинного времени.
Ядро увеличивает показание системных часов при каждом прерывании по тай-
меру, измеряя время в таймерных тиках от момента загрузки системы. Это зна-
чение возвращается процессу через системную функцию time и дает возможность
определять общее время выполнения процесса. Время первоначального запуска
процесса сохраняется ядром в адресном пространстве процесса при исполнении
системной функции fork, в момент завершения процесса это значение вычитается
из текущего времени, результат вычитания и составляет реальное время выпол-
нения процесса. В другой переменной таймера, устанавливаемой с помощью сис-
темной функции stime и корректируемой раз в секунду, хранится календарное
время.
В настоящей главе был описан основной алгоритм диспетчеризации процессов
в системе UNIX. С каждым процессом в системе связывается приоритет планиро-
вания, значение которого появляется в момент перехода процесса в состояние
приостанова и периодически корректируется программой обработки прерываний по
таймеру. Приоритет, присваиваемый процессу в момент перехода в состояние
приостанова, имеет значение, зависящее от того, какой из алгоритмов ядра ис-
полнялся процессом в этот момент. Значение приоритета, присваиваемое процес-
су во время выполнения программой обработки прерываний по таймеру (или в тот
момент, когда процесс возвращается из режима ядра в режим задачи), зависит
от того, сколько времени процесс занимал ЦП: процесс получает низкий приори-
тет, если он обращался к ЦП, и высокий - в противном случае. Системная функ-
ция nice дает процессу возможность влиять на собственный приоритет путем до-
бавления параметра, участвующего в пересчете приоритета.
250
В главе были также рассмотрены системные функции, связанные с временем
выполнения системы и протекающих в ней процессов: с установкой и получением
системного времени, получением времени выполнения процессов и установкой
сигналов "будильника". Кроме того, описаны функции программы обработки пре-
рываний по таймеру, которая следит за временем в системе, управляет таблицей
ответных сигналов, собирает статистику, а также подготавливает запуск плани-
ровщика процессов, программы подкачки и "сборщика" страниц. Программа под-
качки и "сборщик" страниц являются объектами рассмотрения в следующей главе.
1. При переводе процессов в состояние приостанова ядро назначает процессу,
ожидающему снятия блокировки с индекса, более высокий приоритет по
сравнению с процессом, ожидающим освобождения буфера. Точно так же,
процессы, ожидающие ввода с терминала, получают более высокий приоритет
по сравнению с процессами, ожидающими возможности производить вывод на
терминал. Объясните причины такого поведения ядра.
*2. В алгоритме обработки прерываний по таймеру предусмотрен пересчет прио-
ритетов и перезапуск процессов на выполнение с интервалом в 1 секунду.
Придумайте алгоритм, в котором интервал перезапуска динамически меняет-
ся в зависимости от степени загрузки системы. Перевесит ли выигрыш уси-
лия по усложнению алгоритма ?
3. В шестой редакции системы UNIX для расчета продолжительности ИЦП теку-
щим процессом используется следующая формула:
decay(ИЦП) = max (пороговый приоритет, ИЦП-10);
а в седьмой редакции:
decay(ИЦП) = .8 * ИЦП;
Приоритет процесса в обеих редакциях вычисляется по формуле:
приоритет = ИЦП/16 + (базовый уровень приоритета);
Повторите пример на Рисунке 8.4, используя приведенные формулы.
4. Проделайте еще раз пример на Рисунке 8.4 с семью процессами вместо
трех, а затем измените частоту прерываний по таймеру с 60 на 100 преры-
ваний в секунду. Прокомментируйте результат.
5. Разработайте схему, в которой система накладывает ограничение на про-
должительность выполнения процесса, при превышении которого процесс за-
вершается. Каким образом пользователь должен отличать такой процесс от
процессов, для которых не должны существовать подобные ограничения ?
Каким образом должна работать схема, если единственным условием являет-
ся ее запуск из shell'а ?
6. Когда процесс выполняет системную функцию wait и обнаруживает прекра-
тившего существование потомка, ядро приплюсовывает к его ИЦП значение
поля ИЦП потомка. Чем объясняется такое "наказание" процесса-родителя ?
7. Команда nice запускает последующую команду с передачей ей указанного
значения, например:
nice 6 nroff -mm big_memo > output
Напишите на языке Си программу, реализующую команду nice.
8. Проследите на примере Рисунка 8.4, каким образом будет осуществляться
диспетчеризация процессов в том случае, если значение, передаваемое
функцией nice для процесса A, равно 5 или -5.
9. Проведите эксперимент с системной функцией renice x y, где x - код
идентификации процесса (активного), а y - новое значение nice для ука-
занного процесса.
10. Вернемся к примеру, приведенному на Рисунке 8.6. Предположим, что груп-
пе, в которую входит процесс A, выделяется 33% процессорного времени, а
группе, в которую входит процесс B, - 66% процессорного времени. В ка-
кой последовательности будут исполняться процессы ? Обобщите алгоритм
вычисления приоритетов таким образом, чтобы значение группового ИЦП ус-
реднялось.
251
11. Выполните команду date. Команда без аргументов выводит текущую дату:
указав аргумент, например:
date mmddhhmmyy
(супер)пользователь может установить текущую дату в системе
(соответственно, месяц, число, часы, минуты и год). Так,
date 0911205084
устанавливает в качестве текущего времени 11 сентября 1984 года 8:50
пополудни.
12. В программах можно использовать функцию пользовательского
уровня sleep:
sleep(seconds);
с помощью которой выполнение программы приостанавливается на указанное
число секунд. Разработайте ее алгоритм, в котором используйте системные
функции alarm и pause. Что произойдет, если процесс вызовет функцию
alarm раньше функции sleep ? Рассмотрите две возможности: 1) действие
ранее вызванной функции alarm истекает в то время, когда процесс нахо-
дится в состоянии приостанова, 2) действие ранее вызванной функции
alarm истекает после завершения функции sleep.
*13. Обратимся еще раз к последней проблеме. Ядро может выполнить переключе-
ние контекста во время исполнения функции sleep между вызовами alarm и
pause. Тогда есть опасность, что процесс получит сигнал alarm до того,
как вызовет функцию pause. Что произойдет в этом случае ? Как вовремя
распознать эту ситуацию ?
252