Linux mini-HOWTO

       

Задержки


Прежде всего, я должен сказать, что в следствии многозадачной природы Linux, вы не можете гарантировать точную синхронизацию в пользовательской программе. Ваш процесс может выйти из расписания выполняемых задач на интервал от 10 миллисекунд до нескольких секунд (на системе с высокой загрузкой). Тем не менее, к большинству приложений, использующих порты ввода/вывода, это не относится. Чтобы свести это к минимуму, вы можете выполнять свой процесс при высоком приоритете (см. руководство nice(2)) или использовать расписание задач в реальном времени (см. ниже).

Если вам нужна более точная синхронизация в обычных пользовательских программах, существует несколько возможностей поддержки `реального времени' в пользовательском режиме. Ядра Linux версий 2.x имеют относительную поддержку реального времени; см. руководство по sched_setscheduler(2). Кроме того, существует специальное ядро, жестко поддерживающее реальное время; см. http://luz.cs.nmt.edu/~rtlinux/.


Засыпание: sleep() и usleep()

Итак, начнем с самых простых процедур синхронизации. Для задержки в несколько секунд, лучше всего использовать sleep(). Для задержек, с точностью до десятков миллисекунд может работать usleep(). Во время выполнения этих функций, процессор приостанавливает данный процесс на заданное время (``засыпает'') и выполняет другие процессы. Для детальной информации см. руководство sleep(3) и usleep(3).

Задержки менее 50 миллисекунд (в зависимости от скорости процессора, машины, и загрузки системы) не представляются возможными, т.к. для возвращения управления процессу в Linux тратится, как минимум, 10-30 миллисекунд. Указание таких задержек для usleep(3), на самом деле, приведет к большей задержке (уж, как минимум, в 10 мс).


nanosleep()

В серии 2.0.x ядр Linux существует системный вызов nanosleep() (см. руководство nanosleep(2)), который позволяет выполнить самую короткую задержку (в несколько микросекунд и выше).

Задержки nanosleep() выполняет при помощи цикла; иначе он работает так же, как и usleep().

В циклах используется udelay()(внутренняя фунция ядра), а длина цикла вычисляется в зависимости от значения BogoMips. Для более детальной информации, как это работает см. /usr/include/asm/delay.h.


Задержка через порты ввода/вывода


Другой способ обеспечить задержку в несколько микросекунд - это порты ввода/вывода. Запись или чтение любого значения из порта 0x80 (о том, как это сделать, см. выше) обеспечивает задержку почти точно равную одной микросекунде, независимо от типа и скорости вашего процессора. Чтобы обеспечить задержку в несколько микросекунд, вы можете выполнить эту операцию несколько раз. Запись в этот порт не оказывает никакого влияния на компьютер (и некоторые драйверы используют это). Таким образом, обычно выполняется задержка в {in|out}[bw]_p() (см. asm/io.h).

На самом деле, операции ввода/вывода на большинство портов в диапазоне от 0 до 0x3ff тоже дают задержку около одной микросекунды. Так что, если вы, например, производите запись в параллельный порт, для обеспечения задержки, просто добавьте дополнительные inb() из порта.

Задержка при помощи команд ассемблера

Если вы знаете тип и частоту процессора машины, на которой будет работать программа, вы можете вставить меньшие задержки, путем выполнения определенных инструкций ассемблера (но помните, что ваш процесс может быть в любое время исключен из расписания, так что задержка может быть на много больше, чем вы хотите). В нижеуказанной таблице скорость процессора определяет число необходимых тактовых циклов для задержки; например, для процессора с частотой 50МГц (напр. 486DX-50 или 486DX2-50), один такт дает 1/50000000 секунд (=200 наносекунд).


Содержание раздела