учебники, программирование, основы, введение в,
На www.viktoria-models.com эскорт услуги в питере.

 

Заключение

Основные понятия и объекты, рассматриваемые в курсе
Настоящий курс является продолжением курса [1]. В принципе, разделение единой темы "Программирование в стандарте POSIX" на две части носит скорее технический, чем принципиальный характер, однако у второй части есть свой стержень – мобильное программирование приложений реального времени.
Основной структурной единицей приложения реального времени является поток управления. В этом контексте процессы трактуются не столько как самостоятельные сущности, сколько как организующие оболочки для потоков. Более точно, под процессом понимается адресное пространство вместе с выполняемыми в нем потоками управления, а также системными ресурсами, которые этим потокам требуются.
Для задания характеристик потоков управления (и других рассматриваемых в настоящем курсе сущностей) в стандарте POSIX-2001 активно используются атрибутные объекты. Атрибутный объект – это шаблон, по которому можно создать произвольное число потоков с одинаковыми характеристиками, избегая их многократной спецификации.
По отношению к потокам управления вводится понятие безопасных функций, которые можно вызывать параллельно в нескольких потоках без нарушения корректности их функционирования. К числу безопасных принадлежат "чистые" функции, а также функции, обеспечивающие взаимное исключение перед доступом к разделяемым объектам. Если в стандарте явно не оговорено противное, функция считается потоково-безопасной.
Для приложений реального времени исключительно важно и активно используется понятие планирования.
Планированием, согласно стандарту POSIX-2001, называется применение политики выбора процесса или потока управления, готового к выполнению, для его перевода в число активных, а также политики изменения списков равноприоритетных потоков управления.
Под политикой планирования понимается набор правил, используемых для определения порядка выполнения процессов или потоков управления для достижения некоторой цели.
Операции с потоками управления можно подразделить на две группы:

  • создание, терминирование, выполнение других управляющих операций;
  • синхронизация.

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

  • на порядок меньшими накладными расходами на обслуживание;
  • существенно более тесным взаимодействием в общем адресном пространстве.

Чтобы быть практически полезными, средства синхронизации, специально ориентированные на потоки управления, должны быть оптимизированы с учетом обеих отмеченных особенностей.
К числу таких средств, присутствующих в стандарте POSIX-2001, принадлежат мьютексы, переменные условия, блокировки чтение-запись, спин-блокировки и барьеры.
Мьютекс – это синхронизирующий объект, использование которого позволяет множеству потоков управления упорядочить доступ к разделяемым данным.
Переменная условия в качестве синхронизирующего объекта дает потокам управления возможность многократно приостанавливать выполнение, пока некий ассоциированный предикат не станет истинным. Говорят, что поток, выполнение которого приостановлено на переменной условия, блокирован на этой переменной.
Блокировки чтение-запись (много читателей или один писатель) в каждый момент времени позволяют нескольким потокам управления одновременно иметь к данным доступ на чтение или только одному потоку – доступ на запись.
Спин-блокировки представляют собой низкоуровневое средство синхронизации. Как и мьютексы, они предназначены для упорядочения доступа множества потоков управления к разделяемым данным.
Барьеры предназначены для синхронизации множества потоков управления в определенной точке их выполнения.
Средства синхронизации могут использоваться для достижения двух существенно разных целей:

  • захват (как правило, на короткое время) разделяемого объекта для защиты критического интервала;
  • ожидание (долгое или даже потенциально неограниченное) наступления некоторого события, выполнения некоторого условия.

Мьютексы и блокировки можно отнести к первой из выделенных категорий, переменные условия и барьеры – ко второй.
Тема "мобильное программирование приложений реального времени" разбита в курсе на следующие разделы:

  • одношаговое порождение процессов;
  • сигналы реального времени;
  • часы и таймеры;
  • средства межпроцессного взаимодействия;
  • объекты в памяти;
  • удержание процессов в памяти;
  • приоритетное планирование;
  • асинхронный ввод/вывод;
  • рекомендательные интерфейсы.

Одношаговое порождение процессов основано на применении функций posix_spawn() и posix_spawnp().
Под сигналами реального времени понимается не нарушающее совместимости расширение общего механизма сигналов, повышающее детерминированность за счет постановки сигналов, асинхронно доставленных приложению, в очередь.
Основные понятия, ассоциированные с часами и таймерами, были рассмотрены в курсе [1]. В настоящем курсе анализируются необязательные элементы стандарта POSIX-2001, специфичные для реального времени.
В качестве средств межпроцессного взаимодействия в реальном времени в стандарт POSIX-2001 включены очереди сообщений, семафоры и разделяемые сегменты памяти (см. также курс [1], где детально анализируется другой класс средств межпроцессного взаимодействия с аналогичными названиями).
Чтобы время доступа к объекту было по возможности минимальным и не превышало заданной величины, этот объект делают резидентным в физической памяти. Применительно к процессам подобный подход реализуется посредством удержания в памяти страниц, принадлежащих прикладному процессу.
Достижению той же цели – минимизации времени (а также унификации) доступа – служит механизм объектов в памяти.
Приоритетное планирование (или планирование, основанное на приоритетах) – это средство усиления детерминированности, позволяющее приложениям определять порядок, в соответствии с которым потоки управления, готовые к выполнению, получают доступ к процессорным ресурсам.
Средства асинхронного ввода/вывода позволяют прикладным процессам ставить в очередь команды ввода/вывода данных и получать асинхронные уведомления о завершении выполнения этих команд.
Рекомендательные интерфейсы – это средство проинформировать операционную систему о поведении мобильного приложения, чтобы ОС могла принять меры для оптимизации его обслуживания. Например, если прогнозируется последовательный доступ к файлу, ОС может осуществлять предвыборку (опережающее чтение), чтобы совместить во времени ввод и обработку данных.
Приложения реального времени сложно не только разрабатывать, но и отлаживать, поскольку в понятие семантической корректности входит дополнительный компонент – соблюдение временных ограничений. Для решения этой проблемы в стандарте POSIX-2001 предусмотрен механизм трассировки.
Под трассировкой понимается порождение, накопление и анализ данных о событиях, имевших место при выполнении пользовательского приложения.
Применительно к приложениям реального времени трассировка помогает достичь по крайней мере трех целей:

  • оптимизировать структуру приложения на основе анализа трассировочных данных;
  • отладить приложение;
  • выявить причину аварийного завершения работы приложения.

В настоящем курсе рассматриваются также так называемые технологические интерфейсы, играющие техническую, подчиненную роль, но необходимые для использования возможностей более высокого уровня. К числу технологических интерфейсов отнесены функции и утилиты для работы с системным журналом, функции для работы с базой данных учетной информации о пользователях, функции для работы с простыми базами данных, поиск и сортировка в оперативной памяти, манипулирование пользовательскими контекстами, управление средой вещественной арифметики, функции для обхода файловых иерархий, формирование и выполнение командных строк, функции для работы с псевдотерминалами.
Таковы основные понятия и объекты, ставшие предметом рассмотрения в настоящем курсе.

Потоки управления
В рамках любого процесса существует по крайней мере один поток управления. Потоки в пределах процесса именуются идентификаторами. Для выяснения собственного идентификатора поток может воспользоваться функцией pthread_self().
Порождению потоков управления обычно предшествует создание атрибутного объекта, осуществляемое функцией pthread_attr_init(). Когда объект перестает быть нужным, его ликвидируют, вызывая pthread_attr_destroy().
Атрибутные объекты трактуются стандартом POSIX-2001 как абстрактные. Их структура скрыта от приложений, а все манипуляции выполняются посредством методов для выборки и изменения атрибутов.
В число поддерживаемых входят атрибуты стека (функции pthread_attr_getstack(), pthread_attr_setstack(), pthread_attr_getguardsize(), pthread_attr_setguardsize()), планирования (pthread_attr_getschedparam(), pthread_attr_setschedparam(), pthread_attr_getschedpolicy(), pthread_attr_setschedpolicy(), pthread_attr_getscope(), pthread_attr_setscope(), pthread_attr_getinheritsched(), pthread_attr_setinheritsched()) и обособленности (pthread_attr_getdetachstate(), pthread_attr_setdetachstate()).
Обратим внимание на средство контроля переполнения стека – защитную область, располагающуюся за верхней границей стека. При переполнении и попадании указателя стека в защитную область операционная система должна фиксировать ошибку.
Значения атрибутов планирования могут задаваться не только при создании потока управления. Стандарт POSIX-2001 предоставляет средства для их динамического изменения и опроса – функции pthread_getschedparam(), pthread_setschedparam(), pthread_setschedprio(), pthread_getconcurrency(), pthread_setconcurrency().
К числу атрибутов потока управления можно отнести обслуживающие его часы процессорного времени. Для выяснения их идентификатора достаточно обратиться к функции pthread_getcpuclockid().
Еще один атрибут потока управления – маска блокированных сигналов. Поток может опросить и/или изменить ее посредством вызова функции pthread_sigmask().
Все потоки управления одного процесса разделяют общее адресное пространство и, следовательно, имеют общие данные. Чтобы сделать некоторые данные индивидуальными для потока, нужно с помощью функции pthread_key_create() создать ключ и ассоциировать с ним индивидуальные данные, воспользовавшись функцией pthread_setspecific(). В дальнейшем эти данные можно извлекать посредством функции pthread_getspecific(). Подчеркнем, что при обращении по одному (разделяемому) ключу разные потоки будут получать доступ к разным данным.
Создать один ключ, очевидно, нужно один раз. Для решения проблемы однократного выполнения инициализирующих действий в многопотоковой среде стандарт POSIX-2001 предлагает функцию pthread_once().
За удаление ключа индивидуальных данных потоков управления отвечает функция pthread_key_delete().
Модель порождения потоков управления отличается от соответствующей модели для процессов. При создании нового потока задается функция, с вызова которой начнется его выполнение, то есть вместо пары вида fork()/exec() создающий поток должен обратиться лишь к одной функции – pthread_create().
От "родительского" вновь созданный поток управления наследует маску сигналов и среду вещественной арифметики.
К числу средств создания потоков можно отнести и функцию fork(). С ней можно ассоциировать обработчики, зарегистрировав их с помощью функции pthread_atfork(). В каждом обращении к pthread_atfork() фигурируют три обработчика. Первый выполняется в контексте потока, вызвавшего fork(), до разветвления процесса; второй – в том же контексте, но после разветвления; третий – в контексте единственного потока порожденного процесса.
поток управления можно терминировать изнутри и извне (из других потоков того же процесса). Обычное средство внутреннего терминирования – выход из стартовой функции. Тот же эффект достигается вызовом функции pthread_exit().
Заказать терминирование извне потока управления с заданным идентификатором можно, воспользовавшись функцией pthread_cancel().
На выполнение "заказа" влияют состояние восприимчивости к терминированию (разрешено/запрещено) и тип терминирования (отложенное или немедленное, асинхронное), установленные для потока, а также достижение точки терминирования. Эти атрибуты опрашиваются и изменяются с помощью функций pthread_setcancelstate(), pthread_setcanceltype() и pthread_testcancel().
Стандартом POSIX-2001 предусмотрено существование стека обработчиков завершения, ассоциированного с потоком управления. Операции над этим стеком возложены на функции pthread_cleanup_push() и pthread_cleanup_pop().
После того как выполнятся все обработчики завершения, в неспецифицированном порядке вызываются деструкторы индивидуальных данных.
Возможность дождаться завершения заданного потока управления реализуется функцией pthread_join().
Помимо заказа на терминирование, потоку управления можно направить сигнал, воспользовавшись функцией pthread_kill().
Полезная операция, связанная с обработкой завершения потока управления, – его динамическое обособление, выполняемое функцией pthread_detach().
Обратим внимание на то, что, согласно стандарту POSIX-2001, функции, обслуживающие потоки управления, никогда не завершаются с частичным результатом и не выдают код ошибки EINTR. Восстановление нормального состояния после того, как ожидание было прервано доставкой и обработкой сигнала, возлагается на операционную систему, а не на приложение.

Средства синхронизации потоков управления
Функции, обслуживающие мьютексы, можно разбить на следующие группы:

  • инициализация и разрушение мьютексов: pthread_mutex_init(), pthread_mutex_destroy();
  • захват и освобождение мьютексов: pthread_mutex_lock(), pthread_mutex_trylock(), pthread_mutex_timedlock(), pthread_mutex_unlock();
  • опрос и установка атрибутов мьютекса:
  • pthread_mutex_getprioceiling(), pthread_mutex_setprioceiling();
  • инициализация и разрушение атрибутных объектов мьютексов:
  • pthread_mutexattr_init(), pthread_mutexattr_destroy();
  • опрос и установка атрибутов мьютекса в атрибутных объектах:
  • pthread_mutexattr_gettype(), pthread_mutexattr_settype(),
  • pthread_mutexattr_getpshared(), pthread_mutexattr_setpshared(),
  • pthread_mutexattr_getprotocol(), pthread_mutexattr_setprotocol(),
  • pthread_mutexattr_getprioceiling(),
  • pthread_mutexattr_setprioceiling();

Для инициализации статически описанных мьютексов с подразумеваемыми значениями атрибутов целесообразно пользоваться макросом PTHREAD_MUTEX_INITIALIZER.
У инициализированного мьютекса имеется четыре атрибута:

  • тип (обслуживается функциями pthread_mutexattr_gettype() и pthread_mutexattr_settype());
  • верхняя грань приоритетов выполнения (функции pthread_mutex_getprioceiling(), pthread_mutex_setprioceiling(), pthread_mutexattr_getprioceiling(), pthread_mutexattr_setprioceiling());
  • протокол (pthread_mutexattr_getprotocol(), pthread_mutexattr_setprotocol());
  • признак использования несколькими процессами (pthread_mutexattr_getpshared(), pthread_mutexattr_setpshared()).

В стандарте POSIX-2001 определены четыре типа мьютексов:
PTHREAD_MUTEX_NORMAL, PTHREAD_MUTEX_ERRORCHECK,
PTHREAD_MUTEX_RECURSIVE, PTHREAD_MUTEX_DEFAULT.
Атрибут "протокол" влияет на планирование потока управления во время владения мьютексом. Согласно стандарту, возможных протоколов три: PTHREAD_PRIO_NONE, PTHREAD_PRIO_INHERIT, PTHREAD_PRIO_PROTECT.
Пусть имеется некоторый предикат (условие), зависящий от значений переменных, разделяемых несколькими потоками управления. Совместное использование мьютексов, переменных условия и обслуживающих их функций позволяет организовать экономное ожидание состояния истинности этого предиката. С разделяемыми переменными, фигурирующими в предикате, ассоциируется мьютекс, который необходимо захватить перед началом проверок. Затем поток управления входит в цикл вида
while (! предикат) {
Ожидание на переменной условия с освобождением мьютекса.
После успешного завершения ожидания поток вновь
оказывается владельцем мьютекса.
}
После нормального выхода из цикла проверяемое условие истинно; можно выполнить требуемые действия и освободить мьютекс.
Разблокирование потоков управления, ожидающих на переменной условия, должен обеспечить другой поток, изменивший значения разделяемых переменных и отправивший ждущим соответствующее уведомление.
Функции, предлагаемые стандартом POSIX-2001 для обслуживания переменных условия, можно разделить на следующие группы:

  • инициализация и разрушение переменных условия:
  • pthread_cond_init(), pthread_cond_destroy();
  • блокирование (ожидание) на переменной условия:
  • pthread_cond_wait(), pthread_cond_timedwait();
  • разблокирование (прекращение ожидания) потоков управления, блокированных на переменной условия: pthread_cond_broadcast(), pthread_cond_signal();
  • инициализация и разрушение атрибутных объектов переменных условия: pthread_condattr_init(), pthread_condattr_destroy();
  • опрос и установки атрибутов переменных условия в атрибутных объектах: признака использования несколькими процессами (обслуживается функциями pthread_condattr_getpshared(), pthread_condattr_setpshared()) и идентификатора часов реального времени, используемых для ограничения ожидания на переменной условия (функции pthread_condattr_getclock(), pthread_condattr_setclock().

Блокировки чтение-запись – интеллектуальное средство синхронизации, отличающее читателей от писателей. В большинстве случаев разделяемые данные чаще читают, чем изменяют, и это делает блокировки чтение-запись весьма употребительными.
Применительно к блокировкам чтение-запись предоставляются следующие группы функций:

  • инициализация и разрушение блокировок:
  • pthread_rwlock_init(), pthread_rwlock_destroy();
  • установка блокировки на чтение: pthread_rwlock_rdlock(), pthread_rwlock_tryrdlock(), pthread_rwlock_timedrdlock();
  • установка блокировки на запись: pthread_rwlock_wrlock(), pthread_rwlock_trywrlock(), pthread_rwlock_timedwrlock();
  • снятие блокировки чтение-запись: pthread_rwlock_unlock();
  • инициализация и разрушение атрибутных объектов блокировок:
  • pthread_rwlockattr_init(), pthread_rwlockattr_destroy();
  • опрос и установки атрибутов блокировок в атрибутных объектах:
  • pthread_rwlockattr_getpshared(), pthread_rwlockattr_setpshared().

Спин-блокировки представляют собой низкоуровневое средство синхронизации, предназначенное в первую очередь для применения в многопроцессорных конфигурациях с разделяемой памятью. По сравнению с мьютексами спин-блокировки могут иметь то преимущество, что (активное) ожидание и установка не связаны с переключением контекстов, активизацией планировщика и т.п. Если ожидание оказывается кратким, минимальными будут и накладные расходы.
Согласно стандарту POSIX-2001, спин-блокировки обслуживаются следующими группами функций:

  • инициализация и разрушение спин-блокировок:
  • pthread_spin_init(), pthread_spin_destroy();
  • установка спин-блокировки: pthread_spin_lock(), pthread_spin_trylock();
  • снятие спин-блокировки: pthread_spin_unlock().

Барьеры – своеобразное средство синхронизации, идея которого заключается в том, чтобы в определенной точке ожидания собралось заданное число потоков управления. Только после этого они смогут продолжить выполнение.
Барьеры полезны для организации коллективных распределенных вычислений в многопроцессорной конфигурации, когда каждый участник (поток управления) выполняет часть работы, а в точке сбора частичные результаты объединяются в общий итог.
Функции, ассоциированные с барьерами, подразделяются на следующие группы:

  • инициализация и разрушение барьеров: pthread_barrier_init(), pthread_barrier_destroy();
  • синхронизация на барьере: pthread_barrier_wait();
  • инициализация и разрушение атрибутных объектов барьеров:
  • pthread_barrierattr_init(), pthread_barrierattr_destroy();
  • опрос и установки атрибутов барьеров в атрибутных объектах:
  • pthread_barrierattr_getpshared(),
  • pthread_barrierattr_setpshared().

Когда к функции pthread_barrier_wait() обратилось требуемое число потоков управления, одному из них (стандарт POSIX-2001 не специфицирует, какому именно) в качестве результата возвращается именованная константа PTHREAD_BARRIER_SERIAL_THREAD, а всем другим достаются нули. После этого барьер возвращается в начальное (инициализированное) состояние, а выделенный поток может выполнить соответствующие объединительные действия.


Одношаговое порождение процессов
При стандартизации средств одношагового порождения процессов преследовались две основные цели:
  • возможность использования на аппаратных конфигурациях без устройства управления памятью и без какого-либо специфического оборудования;
  • совместимость с существующими POSIX-стандартами.

Чтобы достичь перечисленных целей, функции posix_spawn() и posix_spawnp() контролируют шесть видов наследуемых сущностей:

  • файловые дескрипторы;
  • идентификатор группы процессов;
  • идентификаторы пользователя и группы процесса;
  • параметры планирования;
  • маску сигналов;
  • способ обработки сигналов, игнорируемых родительским процессом.

Как правило, все открытые дескрипторы родительского процесса остаются таковыми и в порожденном, за исключением тех, у которых установлен флаг FD_CLOEXEC. Кроме того, принимается во внимание объект типа posix_spawn_file_actions_t, который содержит действия по закрытию, открытию и/или дублированию файловых дескрипторов. Для формирования объектов типа posix_spawn_file_actions_t служат функции posix_spawn_file_actions_addclose(), posix_spawn_file_actions_addopen() и posix_spawn_file_actions_adddup2().
За контроль других сущностей, наследуемых при одношаговом порождении процессов, отвечают атрибутные объекты, для формирования и опроса которых служат функции posix_spawnattr_init(), posix_spawnattr_destroy(), posix_spawnattr_getflags(), posix_spawnattr_setflags(), posix_spawnattr_getpgroup(), posix_spawnattr_setpgroup(), posix_spawnattr_getschedparam(), posix_spawnattr_setschedparam(), posix_spawnattr_getschedpolicy(), posix_spawnattr_setschedpolicy(), posix_spawnattr_getsigdefault(), posix_spawnattr_setsigdefault(), posix_spawnattr_getsigmask(), posix_spawnattr_setsigmask().
Реализация одношагового порождения процессов может быть непосредственной, выполненной на уровне ядра операционной системы, и библиотечной, основанной на функциях fork()/exec(). Естественно, надеяться на повышение эффективности по сравнению с двухшаговым порождением имеет смысл только в первом случае.
Сигналы реального времени
Приложениям реального времени требуются средства надежного, детерминированного, асинхронного извещения (уведомления) о событиях. Для удовлетворения этой потребности механизм сигналов был расширен, а в стандарт была введена необязательная часть, получившая название "сигналы реального времени" (Realtime Signals Extension, RTS).
Номера сигналов реального времени лежат в диапазоне от SIGRTMIN до SIGRTMAX. Всего таких сигналов должно быть не меньше, чем RTSIG_MAX.
"Жизненный цикл" сигналов реального времени состоит из четырех фаз:

  • генерация;
  • ожидание;
  • доставка;
  • обработка.

Сигналы реального времени могут генерироваться при срабатывании высокоточных таймеров, завершении операции асинхронного ввода/вывода, поступлении межпроцессного сообщения, выполнении функции sigqueue() и т.д.
На фазе генерации сигналов центральную роль играет структура типа sigevent, которая, помимо прочего, определяет способ уведомления и значение сигнала.
Стандартом POSIX-2001 предусмотрено три способа уведомления об асинхронных событиях: SIGEV_NONE (отсутствие уведомления), SIGEV_SIGNAL (генерация сигнала с возможной постановкой в очередь к процессу) и SIGEV_THREAD (вызов функции).
Значение сигнала реального времени может быть целым числом или указателем.
Сгенерировать сигнал реального времени можно, обратившись к функции sigqueue().
После того, как сигнал сгенерирован, наступает фаза ожидания. Сигналы одного типа ставятся в очередь к процессу в порядке генерации.
Дождаться доставки сигнала реального времени можно с помощью функций sigwaitinfo() и sigtimedwait().
При наличии нескольких неблокированных ждущих сигналов реального времени, их доставка процессу производится в порядке возрастания номеров. Тем самым поддерживается ранжированная по приоритетам доставка уведомлений.
Для приложений реального времени, функционирующих на аппаратных конфигурациях с ограниченными ресурсами, может оказаться полезной возможность (распространяющаяся на все сигналы) выполнять функции обработки сигналов не на основном, а на альтернативном стеке. Функция sigaltstack() позволяет установить и/или опросить характеристики альтернативного стека.
Обработка сигналов (не обязательно реального времени) нередко сочетается с нелокальными переходами. В таких случаях могут оказаться полезными функции sigsetjmp() и siglongjmp().
Часы и таймеры
Стандартом POSIX-2001 предусмотрены средства, позволяющие создавать для процессов таймеры, которые генерируют уведомления о наступлении заданного момента в виде сигналов реального времени. таймеры полезны, например, для организации периодических процессов.
Чтобы создать для процессов таймер, следует обратиться к функции timer_create().
Для удаления таймера служит функция timer_delete().
Для выполнения содержательных действий с таймерами служат функции timer_gettime(), timer_settime() и timer_getoverrun().
Отметим, что наличие функции timer_getoverrun() позволяет избавиться от неопределенно большого расхода ресурсов на постановку в очередь сигналов реального времени, которые процесс не успевает обрабатывать.
Для приложений реального времени важна возможность использования не только процессорных, но и монотонных часов. В основном из этих соображений в стандарте POSIX-2001 присутствует расширенный аналог рассмотренной в курсе [1] функции nanosleep() – clock_nanosleep().

Передача и прием сообщений в реальном времени
Над описанными в настоящем курсе очередями сообщений определены следующие группы функций:

  • открытие очереди;
  • отправка сообщения в очередь;
  • прием (синхронный или асинхронный) сообщения из очереди;
  • изменение атрибутов очереди;
  • регистрация на получение уведомления о появлении сообщения в очереди;
  • закрытие очереди;
  • удаление очереди.

Для открытия очереди служит функция mq_open(). Одну очередь могут открыть несколько посылающих и/или принимающих сообщения процессов. При открытии может производиться контроль прав доступа.
Отправка осуществляется функциями mq_send() и mq_timedsend().
Для извлечения сообщений из очереди служат функции mq_receive() и mq_timedreceive(). На порядок приема влияет имеющийся механизм приоритетов сообщений.
Для опроса и/или установки атрибутов очереди служат функции mq_getattr() и mq_setattr(). Для каждой очереди задается фиксированная верхняя граница размера сообщений, которые могут быть в эту очередь отправлены.
Посредством функции mq_notify() процесс может зарегистрироваться на получение уведомления о том, что в очередь, бывшую до этого пустой, поступило сообщение.
После того, как процесс завершил работу с очередью сообщений, соответствующий дескриптор следует закрыть, воспользовавшись функцией mq_close().
Очередь сообщений можно удалить с помощью функции mq_unlink().
Семафоры реального времени
Семафор реального времени – это эффективный механизм синхронизации процессов, который представляет собой общесистемный разделяемый ресурс, имеющий неотрицательное целочисленное значение.
Основными операциями над семафором являются захват и освобождение. Если делается попытка захвата семафора, когда его значение равно нулю, выполнение вызывающего потока управления приостанавливается и он добавляется к множеству потоков, ждущих на семафоре.
Если при освобождении семафора множество ждущих потоков было непусто, один из них удаляется из этого множества и его выполнение возобновляется; в противном случае значение семафора просто увеличивается.
Семафоры бывают именованными и безымянными. Первые именуются цепочками символов и создаются функцией sem_open() с флагом O_CREAT, вторые создаются функцией sem_init().
Именованный семафор можно закрыть, обратившись к функции sem_close(), и удалить с помощью функции sem_unlink(); для ликвидации неименованных семафоров служит функция sem_destroy().
Для захвата семафоров служат функции sem_wait(), sem_trywait() и sem_timedwait().
Освобождение семафора осуществляется функцией sem_post().
Функция sem_getvalue() позволяет опросить значение семафора, не меняя его состояния.
Необходимо отметить, что описание семафоров реального времени в стандарте POSIX-2001 внутренне противоречиво. В большинстве мест явно написано, что семафоры эти целочисленные; в то же время, из описания функции sem_wait() следует, что имеются в виду бинарные семафоры. Вероятно, ошибочно это последнее описание.
Объекты в памяти
Механизм объектов в памяти служит цели минимизации времени и унификации доступа. Стандартом POSIX-2001 предусмотрено три вида таких объектов:

  • файлы, отображенные в память;
  • объекты в разделяемой памяти;
  • объекты в типизированной памяти.

Рассматриваемый класс средств базируется на идее отображения объекта в адресное пространство процесса, после чего доступ к объекту можно осуществлять обычными операциями чтения/записи. Если один объект отображен в адресное пространство нескольких процессов, он превращается в средство межпроцессного взаимодействия.
Отображаться могут обычные файлы, а также объекты в разделяемой и типизированной памяти.
Отображение объектов в адресное пространство процессов осуществляется функцией mmap().
Стандарт POSIX-2001 предусматривает возможность динамической смены разрешенных видов доступа к отображенным страницам посредством вызова функции mprotect().
Для отмены отображений в адресное пространство процессов служит функция munmap().
При работе с объектами в памяти полезны функции truncate() и ftruncate(), позволяющие установить размер объекта равным заданной величине.
Еще одна возможность, полезная в связи с отображением объектов в адресное пространство процессов, – синхронизация (согласование состояния) оперативной и долговременной памяти. Эту возможность реализует функция msync().
Стандартизованный программный интерфейс к объектам в разделяемой памяти включает функции shm_open() для открытия (возможно, с созданием) подобного объекта и получения его дескриптора, а также shm_unlink() для удаления ранее созданного объекта.
Объекты в типизированной памяти – это конфигурируемые реализацией именованные пулы памяти, доступные одному или нескольким процессорам системы через один или несколько портов.
Каждая допустимая комбинация пула памяти и порта идентифицируется именем, определяемым при конфигурировании системы способом, зависящим от реализации. Используя это имя, объект в типизированной памяти можно открыть при помощи функции posix_typed_mem_open(), а затем отобразить в адресное пространство процесса.
После того, как объект в типизированной памяти открыт, посредством функции posix_typed_mem_get_info() можно выяснить максимальный объем памяти, доступной для резервирования.
Функция posix_mem_offset() позволяет выяснить адрес (смещение от начала), длину и дескриптор объекта (блока) в типизированной памяти, отображенного в адресное пространство процесса.
Средства удержания процессов в памяти
Для повышения детерминированности поведения приложений реального времени в стандарт POSIX введены средства удержания в физической памяти страниц из адресного пространства процессов.
Для удержания в физической памяти группы страниц из адресного пространства процесса служит функция mlock().
Удержание отменяется после вызовов fork() и exec(), а также в результате ликвидации по какой-либо причине соответствующей части адресного пространства процесса. Явная отмена удержания группы страниц реализуется функцией munlock().
Если нужно удерживать в памяти все адресное пространство процесса (что имеет место для большинства приложений реального времени), целесообразно воспользоваться функциями mlockall() и munlockall().

Приоритетное планирование
У каждого потока управления (процесса) в каждый момент времени есть определенный приоритет. Равноприоритетные потоки управления, готовые к выполнению, объединяются в списки. Эти списки упорядочены, от первого элемента (головы) к последнему (хвосту).
Цель политики планирования состоит в определении допустимых операций над множеством списков (например, перемещение потоков между списками и внутри них).
Реализация, удовлетворяющая стандарту POSIX, должна выбирать для выполнения процесс (поток управления), находящийся в голове наиболее приоритетного списка, независимо от ассоциированной политики планирования.
Политика SCHED_FIFO (планирование по очереди) предписывает упорядочивать потоки управления в пределах каждого списка по длительности ожидания.
Чтобы сделать планирование более справедливым по отношению к равноприоритетным процессам, можно воспользоваться политикой SCHED_RR (циклическое планирование). Она эквивалентна SCHED_FIFO с одним исключением: когда время, в течение которого поток управления занимал процессор, становится больше или равным результату функции sched_rr_get_interval(), он перемещается в хвост соответствующего списка, а для выполнения выбирается головной поток. Таким образом, ни один из равноприоритетных потоков не сможет монополизировать процессор.
По сравнению с SCHED_FIFO и SCHED_RR, политика SCHED_SPORADIC (спорадическое планирование) представляется гораздо более сложной. Она позволяет резервировать процессорное время для выполнения непериодических действий.
Четвертая политика планирования, которую должны поддерживать реализации, соответствующие стандарту POSIX, называется "прочей" (SCHED_OTHER). Она необходима, чтобы мобильные приложения могли заявить, что они больше не нуждаются в политике планирования реального времени.
Стандарт POSIX-2001 предусматривает следующие функции управления планированием процессов: sched_getscheduler() (опрос политики планирования процесса), sched_getparam() (опрос параметров планирования процесса), sched_setscheduler() (установка политики и параметров планирования процесса) и sched_setparam() (установка параметров планирования процесса).
Стандарт POSIX-2001 предоставляет также средства для опроса характеристик политик планирования – минимального (sched_get_priority_min()) и максимального (sched_get_priority_max()) среди допустимых приоритетов, а также величины кванта выделяемого процессорного времени для политики циклического планирования (sched_rr_get_interval()).
Функция sched_yield() позволяет вызывающему потоку управления добровольно уступить процессор.
Асинхронный ввод/вывод
Средства асинхронного ввода/вывода позволяют прикладным процессам ставить в очередь команды ввода/вывода данных, продолжать работу параллельно с операциями передачи данных и получать асинхронные уведомления о завершении выполнения этих команд.
После завершения асинхронной операции ввода/вывода приложению может быть доставлен сигнал.
Жизненный цикл операции асинхронного ввода/вывода включает два этапа:

  • постановка запроса в очередь;
  • выполнение запроса, осуществление ввода/вывода.

Основными операциями асинхронного ввода/вывода являются чтение (aio_read()) и запись (aio_write()) данных.
Функция lio_listio() позволяет за один вызов поставить в очередь список запросов на чтение и/или запись данных.
Для выяснения статуса операций асинхронного ввода/вывода служат функции aio_return() и aio_error().
Ожидающие обработки запросы на асинхронный ввод/вывод можно аннулировать, воспользовавшись функцией aio_cancel().
Согласно стандарту POSIX-2001, списком можно не только инициировать запросы на асинхронный ввод/вывод, но и ожидать их завершения. Для этого служит функция aio_suspend().
Функции sync(), fsync(), fdatasync(), aio_fsync() предназначены для согласования состояния буферизованных и хранимых в долговременной памяти данных, обеспечения целостности данных и файлов.
Рекомендательные интерфейсы
В стандарте POSIX-2001 предусмотрена оптимизация работы с файлами, которая может затрагивать следующие аспекты осуществляемого приложением ввода/вывода:

  • последовательный доступ;
  • кэширование;
  • передача данных;
  • предварительное резервирование долговременной и оперативной памяти.

Описанные возможности реализуют функции posix_fadvise(), posix_fallocate(), posix_madvise() и posix_memalign().
Ожидаемое поведение может специфицироваться как:

  • отсутствие рекомендаций;
  • последовательный доступ;
  • случайный доступ;
  • доступ в ближайшее время;
  • отсутствие доступа в ближайшее время;
  • однократный доступ.

При оптимизации обмена данными с файлами, наряду с применением рекомендательных интерфейсов, целесообразно учитывать значения ряда конфигурационных констант:
POSIX_REC_MIN_XFER_SIZE
Минимальное рекомендуемое число передаваемых байт при обмене данными с файлами. Рекомендуется также, чтобы и смещение передаваемой порции данных от начала файла было кратным POSIX_REC_MIN_XFER_SIZE.
POSIX_REC_MAX_XFER_SIZE
Максимальное рекомендуемое число передаваемых байт при обмене данными с файлами.
POSIX_REC_XFER_ALIGN
Рекомендуемое значение для выравнивания границы буфера обмена данными с файлами.

Трассировка
С логической точки зрения в трассировке участвуют три процесса:

  • трассируемый (целевой);
  • трассирующий (управляющий трассировкой);
  • анализирующий данные трассировки.

Сведения о действиях, производимых при выполнении приложения, фиксируются в виде объектов данных, называемых событиями трассировки. События записываются в потоки трассировки, которые содержат также служебные данные, необходимые для интерпретации событий.
Трассируемый процесс должен быть специальным образом оборудован: его программа должна содержать точки трассировки – действия, способные генерировать события трассировки.
События трассировки подразделяются на две категории:

  • пользовательские;
  • системные.

Пользовательские события генерируются при вызове функции posix_trace_event().
Системные события генерируются реализацией в ответ на действия ОС или приложения.
У событий трассировки есть типы и имена, между которыми устанавливается взаимно-однозначное соответствие.
Стандартом POSIX-2001 предусмотрен механизм фильтрации, позволяющий отключить генерацию событий определенных типов и, тем самым, уменьшать объем трассировочных данных.
Потоки трассировки, как правило, хранят только в оперативной памяти. Если нужно получить стабильную копию потока для последующего анализа, его следует сбросить в журнал трассировки.
И для потоков, и для журналов трассировки правила обработки ситуации заполнения могут сводиться к записи новых событий поверх самых старых или к приостановке трассировки. Кроме того, для потоков может быть предусмотрен сброс в журнал с последующей очисткой, а для для журналов – потенциально неограниченное расширение.
При создании потока трассировки его атрибуты стандартным для POSIX-2001 образом извлекаются из атрибутного объекта.
Для создания и уничтожения атрибутных объектов потоков трассировки применяются функции posix_trace_attr_init() и posix_trace_attr_destroy().
Для манипулирования атрибутами, идентифицирующими поток трассировки, служат функции posix_trace_attr_getgenversion(), posix_trace_attr_getname(), posix_trace_attr_setname(), posix_trace_attr_getcreatetime().
Функция posix_trace_attr_getclockres() позволяет опросить разрешающую способность часов, с помощью которых проставляются временные штампы.
Поведенческие атрибуты потоков и журналов обслуживаются функциями posix_trace_attr_getinherited(), posix_trace_attr_setinherited(), posix_trace_attr_getstreamfullpolicy(), posix_trace_attr_setstreamfullpolicy(), posix_trace_attr_getlogfullpolicy(), posix_trace_attr_setlogfullpolicy().
Опросить и установить хранящиеся в атрибутном объекте размеры событий, потоков и журналов можно с помощью функций posix_trace_attr_getmaxdatasize(), posix_trace_attr_setmaxdatasize(), posix_trace_attr_getmaxsystemeventsize(), posix_trace_attr_getmaxusereventsize(), posix_trace_attr_getstreamsize(), posix_trace_attr_setstreamsize(), posix_trace_attr_getlogsize(), posix_trace_attr_setlogsize().
Создание потока трассировки осуществляется функциями posix_trace_create() или posix_trace_create_withlog().
Чтобы активизировать трассировку, следует воспользоваться функцией posix_trace_start(); для последующей приостановки нужно вызвать функцию posix_trace_stop().
Функция posix_trace_shutdown() завершает трассировку и освобождает ресурсы, ассоциированные с потоком.
По ходу трассировки управляющий процесс может инициировать сброс потока в журнал, обратившись к функции posix_trace_flush().
Опрос статуса потока трассировки осуществляется с помощью функции posix_trace_get_status().
Стандартом POSIX-2001 предусмотрен опрос атрибутов потока трассировки. Для этого служит функция posix_trace_get_attr().
Поток и журнал трассировки можно очистить от хранящихся там событий, воспользовавшись функцией posix_trace_clear().
При опросе и установке фильтров событий трассировки (функции posix_trace_get_filter() и posix_trace_set_filter()) применяется апробированная схема работы с множествами, основанная на функциях posix_trace_eventset_empty(), posix_trace_eventset_fill(), posix_trace_eventset_add(), posix_trace_eventset_del(), posix_trace_eventset_ismember().
Играющие техническую роль функции posix_trace_trid_eventid_open(), posix_trace_eventid_get_name() и posix_trace_eventid_equal() обслуживают идентификаторы типов событий, рассматриваемые как абстрактные объекты.
Функции posix_trace_eventtypelist_getnext_id() и posix_trace_eventtypelist_rewind() позволяют обрабатывать совокупность идентификаторов типов событий, присутствующих в заданном потоке трассировки.
Функция posix_trace_eventid_open() ассоциирует имя события с идентификатором типа.
Анализирующий процесс обычно открывает журнал трассировки, обращаясь к функции posix_trace_open(), а обработав, закрывает его с помощью функции posix_trace_close(), быть может, в промежутке позиционируясь на начало журнала посредством функции posix_trace_rewind(). Для чтения событий служат функции posix_trace_getnext_event(), posix_trace_timedgetnext_event() и posix_trace_trygetnext_event().

Технологические интерфейсы
Для работы с системным журналом стандарт POSIX-2001 предлагает функции записи сообщений (syslog()), установки фильтра (маски журналируемых сообщений, setlogmask()) и других параметров журналирования (openlog()) и, наконец, завершения работы с системным журналом (closelog()).
У журналируемых сообщений имеются такие атрибуты, как уровень серьезности и источник.
К этой же прикладной области можно отнести служебную программу logger и функцию fmtmsg().
Стандартом POSIX-2001 предусмотрен набор функций для работы с базой данных учетной информации о пользователях. Эти функции реализуют последовательный просмотр учетных записей (getutxent()), поиск в базе (getutxid(), getutxline()), модификацию или добавление записей (pututxline()), возврат к началу (setutxent()) и завершение работы с базой (endutxent()).
Универсальный характер носят функции для работы с простыми базами данных. Подобную базу можно открыть (dbm_open()) и закрыть (dbm_close()), выбрать (dbm_fetch()), сохранить (dbm_store()) и удалить (dbm_delete()) запись по ключу, перебрать имеющиеся в базе ключи (dbm_firstkey(), dbm_nextkey()), опросить статус ошибки (dbm_error()) и очистить его (dbm_clearerr()).
Важный подкласс технологических интерфейсов образуют средства поиска и сортировки.
Бинарный поиск представлен функцией bsearch(), последовательный – функциями lsearch() (поиск с вставкой) и lfind(). Управление хэш-таблицами осуществляется посредством функций hcreate(), hdestroy() и hsearch(). Бинарные деревья поиска обслуживаются функциями tsearch() (поиск с вставкой), tfind(), tdelete() и twalk() (обход деревьев).
Для сортировки массивов целесообразно пользоваться функцией qsort(). Существует также служебная программа tsort.
Функции insque() и remque() реализуют вставку и удаление элементов очереди.
Согласно стандарту POSIX-2001, пользовательский контекст потока управления включает содержимое машинных регистров, маску сигналов и текущий стек выполнения. Стандарт предоставляет функции для опроса (getcontext()), модификации (makecontext()) и смены (setcontext() и swapcontext()) пользовательских контекстов.
Рассматриваемая в стандарте POSIX-2001 среда вещественной арифметики включает сущности двух видов: флаги состояния и управляющие режимы.
Если приложение проверяет флаги состояния, устанавливает собственные управляющие режимы или выполняется в режимах, отличных от подразумеваемого, то при компиляции необходимо воспользоваться управляющим комментарием (#pragma) FENV_ACCESS:
#pragma STDC FENV_ACCESS ON
Опросить и установить текущую среду вещественной арифметики можно с помощью функций fegetenv() и fesetenv().
Сохранение текущей среды может сочетаться с ее изменением. Функция feholdexcept() очищает флаги состояния и устанавливает "безостановочный" режим, а feupdateenv() сохраняет в своей локальной памяти информацию о текущей исключительной ситуации, устанавливает новую среду и затем пытается возбудить в ней сохраненное исключение.
Для опроса и установки флагов состояния стандартом POSIX-2001 предусмотрены функции fegetexceptflag() и fesetexceptflag().
Функции fetestexcept(), feclearexcept() и feraiseexcept() служат, соответственно, для проверки, сброса и возбуждения исключительных ситуаций.
Опросить и установить режим округления можно с помощью функций fegetround() и fesetround().
Обход файловой иерархии – типовая задача, для решения которой стандартом POSIX-2001 предлагаются две сходные функции – ftw() и nftw().
Служебная программа xargs дает возможность формировать и выполнять командные строки, объединяя зафиксированный набор начальных аргументов с аргументами, прочитанными со стандартного ввода.
В качестве последнего элемента технологических интерфейсов рассматриваются функции для работы с псевдотерминалами.
В стандарте POSIX-2001 выстроена пятиэтапная модель получения доступа к псевдотерминалу:

  • открытие главного устройства псевдотерминала, получение его файлового дескриптора (осуществляется функцией posix_openpt());
  • разблокирование подчиненного устройства псевдотерминала (функция unlockpt());
  • формирование прав доступа к подчиненному устройству псевдотерминала (grantpt());
  • получение имени подчиненного устройства псевдотерминала (ptsname());
  • открытие подчиненного устройства псевдотерминала, получение его файлового дескриптора (open()).

На этом мы завершаем описание средств программирования приложений в стандарте POSIX.

 

 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г.Яндекс.Метрика