Приложение B Компьютерные часы

Так как в большинстве примеров в этом тексте рассчитываются какие-либо временные интервалы, нам необходимо более подробно описать способы поддержки времени в современных Unix системах. Все сказанное ниже имеет отношение к системам, на которых использовались примеры в этой книге. Более подробно о поддержке времени можно прочитать в разделах 3.4 и 3.5 публикации [Leffler et al. 1989].

С определенной частотой генерируются аппаратные прерывания от часов. В случае Sun SPARCs и Intel 80386s эти прерывания возникают каждые 10 миллисекунд.

Необходимо отметить, что в большинстве компьютеров используется нескомпенсированный кварцевый генератор для генерации прерываний. Как можно заметить из таблицы 7 RFC 1305 [Mills 1992], не стоит беспокоиться о том, на сколько отстает в день этот генератор. Очень немногие компьютеры могут поддерживать точное время (другими словами, прерывания не возникают точно каждые 10 миллисекунд). Приближение 0,01% дает ошибку примерно в 8,64 секунды в день. Чтобы поддерживать более точное время, (1) нужно использовать лучший генератор (кварц), (2) можно использовать внешний источник времени с повышенной точностью (например, источник времени, предоставляемый Global Positioning Satellites) или (3) получить доступ по Internet к системам с более точными часами. Последнее решается с помощью протокола Network Time Protocol, как описано в RFC 1305, но это выходит за рамки обсуждения нашей книги.

Еще один хорошо известный источник ошибок во времени в Unix системах заключается в том, что прерывания, которые появляются каждые 10 миллисекунд, всего лишь заставляют ядро увеличивать переменную, которая отслеживает время. Если ядро потеряло прерывание (например, оно было слишком занято в период между двумя соседними 10-миллисекундными прерываниями), часы отстанут на 10 миллисекунд. Подобная потеря прерываний часто приводит к отставанию часов в Unix системах.

Даже если прерывания часов происходят примерно каждые 10 миллисекунд, более новые системы, такие как SPARCs, предоставляют более высокую точность часов. При работе с NIT драйвером (описанным в приложении А), tcpdump имеет доступ к этому таймеру с повышенным разрешением. В SPARC этот таймер предоставляет микросекундное разрешение. Пользовательские процессы могут получить доступ к этому таймеру с повышенным разрешением через функцию gettimeofday(2).

Автор провел следующий эксперимент. Была запущена программа, которая вызывает функцию gettimeofday циклически 10000 раз, при этом каждый раз возвращенное значение сохранялось в массиве. В конце цикла были напечатаны 9999 отрезков времени. Для SPARC ELC величины отрезков времени показаны на рисунке В.1.

Микросекунды | Счетчик
36 | 4914
37 | 4831
38 | 167
39 | 8
другие | 79

Рисунок В.1 Распределение времени, необходимое для вызова gettimeofday 10000 раз в SPARC ELC.

Полное время, необходимое для запуска программы, составило 0,38 секунды, при практически свободной системе. Отсюда можно сделать вывод, что процессу потребовалось для вызова gettimeofday примерно 37 микросекунд. Так как ELC имеет скорость примерно 21 MIPS (миллион инструкций в секунду), 37 микросекунд соответствуют примерно 800 инструкциям. Ядро принимает системный вызов от пользовательского процесса, исполняет системный вызов, копирует назад 8 байт в качестве результата и возвращает управление пользовательскому процессу. (Скорость MIPS достаточно спорная величина, поэтому на современных системах сложно оценить время исполнения инструкций. Все что мы попробуем сделать, это получить примерное представление и увидеть, могут ли полученные значения иметь какой-то определенный смысл.)

Из этого простого эксперимента мы можем сказать, что значения, возвращенные gettimeofday, содержат микросекундную точность.

Если мы запустим подобный тест в SVR4/386, результаты будут отличны от приведенных выше. Это объясняется тем, что большинство 386 Unix систем, таких как SVR4, имеют только 10-миллисекундные прерывания часов и даже не стараются предоставить более высокое разрешение часов. На рисунке В.2 показано распределение 9999 отрезков времени для компьютера 25Мгц 80386, работающего под управлением операционной системы SVR4.

Микросекунды | Счетчик
0 | 9871
10000 | 128

Рисунок В.2 Время, необходимое для вызова gettimeofday 10000 раз в случае SVR4/386.

Эти значения менее точны, разница во времени обычно меньше чем 10 миллисекунд, что воспринимается как 0. Практически все, что мы можем сделать на подобных системах, это рассчитать время часов на свободной машине и поделить его на количество циклов. При этом мы получим верхний предел, так как сюда входит время, необходимое, чтобы 9999 раз вызвать printf и записать результаты в файл. (В случае SPARC, рисунок В.1, промежутки времени не включают в себя время, необходимое для работы функции printf, так как все 10000 значений были сначала получены, а затем был напечатан результат.) Под управлением SVR4 время часов составило 3,15 секунды, что дает примерно 315 микросекунд на один системный вызов. Время системного вызова в 8,5 раз медленнее, чем в SPARC, что выглядит похожим на правду.

BSD/386 Version 1.0 предоставляет микросекундную точность, такую же как SPARC. Она читает регистр часов 8253 и рассчитывает количество микросекунд, прошедшее с последнего тика часов. Это разрешение доступно для процессов вызывающих gettimeofday и для модулей ядра, таких как пакетный фильтр BSD.

В случае tcpdump эти цифры означают, что мы можем доверять миллисекундным значениям и значениям, равным частям миллисекунд, которые напечатаны в системах SPARC и BSD/386, однако значения, напечатанные tcpdump под управлением SVR4/386, всегда будут кратны 10 миллисекундам. Для других программ, которые печатают время возврата, таких как ping (глава 7) и traceroute (глава 8), в случае SPARC и BSD/386 систем мы можем верить миллисекундным значениям вывода, однако значения, напечатанные под управлением SVR4/386, будут всегда кратны 10. Чтобы оценить время возврата ping в локальной сети, что мы показывали в главе 7, равное примерно 3 миллисекундам, требуется запустить ping на SPARC или BSD/386.

В некоторых примерах запущенных под BSD/386 Version 0.9.4 получена 10-миллисекундная точность часов (как под SVR4). Когда мы показывали вывод команды tcpdump с этих систем, то приводили только две цифры справа от десятичной точки, в соответствии с предоставляемой точностью часов.