Logo Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware
VPS/VDS серверы. 30 локаций на выбор

Серверы VPS/VDS с большим диском

Хорошие условия для реселлеров

4VPS.SU - VPS в 17-ти странах

2Gbit/s безлимит

Современное железо!

Бесплатный конструктор сайтов и Landing Page

Хостинг с DDoS защитой от 2.5$ + Бесплатный SSL и Домен

SSD VPS в Нидерландах под различные задачи от 2.6$

✅ Дешевый VPS-хостинг на AMD EPYC: 1vCore, 3GB DDR4, 15GB NVMe всего за €3,50!

🔥 Anti-DDoS защита 12 Тбит/с!

Linux I/O port programming 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)), который позволяет выполнить самую короткую задержку (в несколько микросекунд и выше).

Задержки <= 2 мс, если (и только если) ваш процесс находится в относительном режиме реального времени (soft real time scheduling), 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 наносекунд).

Инструкция        такты i386         такты i486
nop                   3                   1
xchg %ax,%ax          3                   3
or %ax,%ax            2                   1
mov %ax,%ax           2                   1
add %ax,0             2                   1

(Прошу прощения, но я не знаю о Пентиуме; скорее всего, они близки к i486. На i386 я не смог найти инструкцию, выполняемую за один такт. В общем случае, лучше всего использовать одно-тактовые инструкции, иначе конвейерная обработка в современных процессорах может сократить задержку.)

Инструкции nop и xchg не должны давать посторонние эффекты. Остальные команды могут изменять регистр флагов процессора. Впрочем, это уже не имеет значение, т.к. gcc должен заметить это. Самый лучший выбор это nop.

Чтобы использовать это метод, выполните в вашей программе функцию asm("инструкция"). Синтаксис инструкций указан в таблице выше; если вы хотите указать несколько инструкций в одном asm(), разделите их точкой с запятой. Например, asm("nop ; nop ; nop ; nop") выполняет четыре инструкции nop, делая задержку в четыре такта на i486 или Pentium (или 12 тактов на i386).

Gcc непосредственно вставляет код из asm() в программу (inline), так что здесь нет никаких других задержек, связанных с вызовом функций.

Задержки менее одного такта невозможны на архитектуре Intel x86.


rdtsc на процессорах Пентиум

На процессорах Пентиум вы можете узнать количество тактов, прошедших со времени последней перезагрузки, при помощи следующей функции:

   extern __inline__ unsigned long long int rdtsc()
   {
     unsigned long long int x;
     __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
     return x;
   }

Вы можете отслеживать это значение для задержки на любое количество тактов, на сколько хотите.

Точность

Для задержек с точностью до одной секунды, лучше всего использовать time(). Для более точных задержек, gettimeofday() имеет точность около одной микросекунды (см. выше, насчет многозадачности). На пентиумах же функция rdtsc (см. выше) дает точность до одного такта.

Если после истечения какого-то количества времени вы хотите получить в программу сигнал, используйте setitimer() или alarm(). Для подробной информации об этих функциях см. соответствующее руководство.


Пред.НачалоСлед.
Доступ к прерываниям (IRQ) и DMA Другие языки программирования

VPS в России, Европе и США

Бесплатная поддержка и администрирование

Оплата российскими и международными картами

🔥 VPS до 5.7 ГГц под любые задачи с AntiDDoS в 7 локациях

💸 Гифткод CITFORUM (250р на баланс) и попробуйте уже сейчас!

🛒 Скидка 15% на первый платеж (в течение 24ч)

Скидка до 20% на услуги дата-центра. Аренда серверной стойки. Colocation от 1U!

Миграция в облако #SotelCloud. Виртуальный сервер в облаке. Выбрать конфигурацию на сайте!

Виртуальная АТС для вашего бизнеса. Приветственные бонусы для новых клиентов!

Виртуальные VPS серверы в РФ и ЕС

Dedicated серверы в РФ и ЕС

По промокоду CITFORUM скидка 30% на заказ VPS\VDS

Новости мира IT:

Архив новостей

IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
тел. +7 495 7861149
Пресс-релизы — pr@citforum.ru
Обратная связь
Информация для авторов
Rambler's Top100 TopList This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2019 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...