Мобильное программирование приложений реального времени в стандарте POSIX
Покупка
Тематика:
Программирование и алгоритмизация
Издательство:
ИНТУИТ
Автор:
Галатенко В. А.
Год издания: 2016
Кол-во страниц: 360
Дополнительно
Основная тема курса - мобильное программирование приложений реального времени, в том числе многопотоковых. Основой обеспечения мобильности является новая редакция стандарта на интерфейс операционной системы POSIX. Рассматриваются приемы и методы программирования приложений на основе данного стандарта. Приводятся многочисленные примеры программ. <p>Настоящий курс является продолжением курса Программирование в стандарте POSIX. В принципе, разделение единой темы "Программирование в стандарте POSIX” на две части носит скорее технический, чем принципиальный характер, однако у второй части есть свой стержень - мобильное программирование приложений реального времени. </p> <p> Обеспечение мобильности программного обеспечения - задача исключительной важности и сложности. Для приложений реального времени она важна и сложна вдвойне. Важность проистекает из многочисленности
подобных систем и ответственности решаемых ими задач. Сложность является следствием разнообразия и частой смены аппаратных платформ, а также того обстоятельства, что в понятие семантической корректности входит дополнительный компонент - соблюдение временных ограничений. </p> <p>По сравнению с предыдущими редакциями, стандарт POSIX-2001 существенно расширен средствами программирования систем реального времени. Их изучение представляется весьма актуальным, способным оказать существенную помощь разработчикам приложений. </p> <p>Основной структурной единицей приложения реального времени является поток управления. Потоки стали первой темой настоящего курса. Средства их синхронизации - тема номер два. Среди рассматриваемых механизмов - мьютексы, условные переменные, блокировки чтение-запись, спин-блокировки и барьеры. К средствам межпотокового взаимодействия можно
отнести сигналы реального времени, очереди сообщений, семафоры. </p> <p>Объекты в памяти - это и инструмент повышения эффективности приложений, и средство передачи данных между их компонентами. Стандарт POSIX-2001 позволяет добиться мобильности даже для традиционно немобильных подсистем работы с типизированной памятью. </p> <p>Разумеется, в число рассматриваемых в курсе тем вошло приоритетное планирование. Здесь особый интерес представляет политика спорадического планирования, необходимая для работы в реальном, насыщенном событиями окружении.</p> <p>Приложения реального времени сложно не только разрабатывать, но и отлаживать. Для решения этой проблемы в стандарте POSIX-2001 предусмотрен механизм трассировки, который целесообразно применять и на этапе эксплуатации приложений.</p>
Тематика:
ББК:
УДК:
ОКСО:
- ВО - Бакалавриат
- 09.03.01: Информатика и вычислительная техника
- 09.03.02: Информационные системы и технологии
- 09.03.03: Прикладная информатика
- 09.03.04: Программная инженерия
- 11.03.01: Радиотехника
- 11.03.02: Инфокоммуникационные технологии и системы связи
- 11.03.03: Конструирование и технология электронных средств
- 11.03.04: Электроника и наноэлектроника
- ВО - Магистратура
- 09.04.01: Информатика и вычислительная техника
- 09.04.02: Информационные системы и технологии
- 09.04.03: Прикладная информатика
- 09.04.04: Программная инженерия
- 11.04.01: Радиотехника
- 11.04.02: Инфокоммуникационные технологии и системы связи
- 11.04.03: Конструирование и технология электронных средств
- 11.04.04: Электроника и наноэлектроника
ГРНТИ:
Скопировать запись
Фрагмент текстового слоя документа размещен для индексирующих роботов
Мобильное программирование приложений реального времени в стандарте POSIX 2-е издание, исправленное Галатенко В.А. Национальный Открытый Университет “ИНТУИТ” 2016 2
Мобильное программирование приложений реального времени в стандарте POSIX/ В.А. Галатенко М.: Национальный Открытый Университет “ИНТУИТ”, 2016 Основная тема курса - мобильное программирование приложений реального времени, в том числе многопотоковых. Основой обеспечения мобильности является новая редакция стандарта на интерфейс операционной системы POSIX. Рассматриваются приемы и методы программирования приложений на основе данного стандарта. Приводятся многочисленные примеры программ. <p>Настоящий курс является продолжением курса Программирование в стандарте POSIX. В принципе, разделение единой темы “Программирование в стандарте POSIX” на две части носит скорее технический, чем принципиальный характер, однако у второй части есть свой стержень мобильное программирование приложений реального времени. </p> <p> Обеспечение мобильности программного обеспечения - задача исключительной важности и сложности. Для приложений реального времени она важна и сложна вдвойне. Важность проистекает из многочисленности подобных систем и ответственности решаемых ими задач. Сложность является следствием разнообразия и частой смены аппаратных платформ, а также того обстоятельства, что в понятие семантической корректности входит дополнительный компонент - соблюдение временных ограничений. </p> <p>По сравнению с предыдущими редакциями, стандарт POSIX-2001 существенно расширен средствами программирования систем реального времени. Их изучение представляется весьма актуальным, способным оказать существенную помощь разработчикам приложений. </p> <p>Основной структурной единицей приложения реального времени является поток управления. Потоки стали первой темой настоящего курса. Средства их синхронизации - тема номер два. Среди рассматриваемых механизмов - мьютексы, условные переменные, блокировки чтение-запись, спин-блокировки и барьеры. К средствам межпотокового взаимодействия можно отнести сигналы реального времени, очереди сообщений, семафоры. </p> <p>Объекты в памяти - это и инструмент повышения эффективности приложений, и средство передачи данных между их компонентами. Стандарт POSIX-2001 позволяет добиться мобильности даже для традиционно немобильных подсистем работы с типизированной памятью. </p> <p>Разумеется, в число рассматриваемых в курсе тем вошло приоритетное планирование. Здесь особый интерес представляет политика спорадического планирования, необходимая для работы в реальном, насыщенном событиями окружении.</p> <p>Приложения реального времени сложно не только разрабатывать, но и отлаживать. Для решения этой проблемы в стандарте POSIX-2001 предусмотрен механизм трассировки, который целесообразно применять и на этапе эксплуатации приложений.</p> (c) ООО “ИНТУИТ.РУ”, 2004-2016 (c) Галатенко В.А., 2004-2016 3
Потоки управления Рассматриваются основные идеи, понятия и объекты, ассоциированные с потоками управления, атрибуты потоков, средства их опроса и изменения, работа с индивидуальными данными потоков, средства создания и терминирования потоков управления. Основные идеи, понятия и объекты Напомним, уточним и дополним определения, которые были даны в курсе [1] применительно к потокам управления. Процесс – это адресное пространство вместе с выполняемыми в нем потоками управления, а также системными ресурсами, которые этим потокам требуются . После того, как процесс создан с помощью функции fork(), он считается активным. Сразу после создания в его рамках существует ровно один поток управления – копия того, что вызвал fork(). До завершения процесса в его рамках существуют по крайней мере один поток управления и адресное пространство. Большинство атрибутов процесса разделяются существующими в его рамках потоками управления. К числу индивидуальных атрибутов относятся идентификатор, приоритет и политика планирования, значение переменной errno, ассоциированные с потоком управления пары ключ/значение (служащие для организации индивидуальных данных потока и доступа к ним), а также системные ресурсы, требующиеся для поддержки потока управления. Идентификатор потока управления уникален в пределах процесса, но не системы в целом. Идентификаторы потоков управления представлены значениями типа pthread_t, который трактуется в стандарте POSIX-2001 как абстрактный. В частности, для него определен метод сравнения значений на равенство. Всем потокам управления одного процесса доступны все объекты, адреса которых могут быть определены потоком. В число таких объектов входят статические переменные, области динамической памяти, полученные от функции malloc(), прямоадресуемая память, полученная от системно-зависимых функций, автоматические переменные и т.д. По отношению к потокам управления вводится понятие безопасных функций, которые можно вызывать параллельно в нескольких потоках без нарушения корректности их функционирования. К числу безопасных принадлежат “чистые” функции, а также функции, обеспечивающие взаимное исключение перед доступом к разделяемым объектам. Если в стандарте явно не оговорено противное, функция считается потоково-безопасной . 4
Выполняющимся ( активным ) называется поток управления, обрабатываемый в данный момент процессором. В многопроцессорных конфигурациях может одновременно выполняться несколько потоков . Поток управления считается готовым к выполнению, если он способен стать активным, но не может этого сделать из-за отсутствия свободного процессора. Поток управления называется вытесненным, если его выполнение приостановлено изза того, что другой поток с более высоким приоритетом стал готов к выполнению . Поток управления считается блокированным, если для продолжения его выполнения должно стать истинным некоторое условие, отличное от доступности процессора. Списком потоков управления называется упорядоченный набор равноприоритетных потоков, готовых к выполнению. Порядок потоков в списке определяется политикой планирования. Множество наборов включает все потоки в системе, готовые к выполнению . Планированием, согласно стандарту POSIX-2001, называется применение политики выбора процесса или потока управления, готового к выполнению, для его перевода в число активных, а также политики изменения списков потоков управления . Под политикой планирования понимается набор правил, используемых для определения порядка выполнения процессов или потоков управления для достижения некоторой цели. Политика планирования воздействует на порядок процессов ( потоков управления ) по крайней мере в следующих ситуациях: когда активный процесс ( поток управления ) блокируется или вытесняется; когда блокированный процесс (поток управления) становится готовым к выполнению. Область планирования размещения – это набор процессоров, по отношению к которым в некоторый момент времени может планироваться поток управления . Областью планирования конкуренции называется свойство потока управления, определяющее набор потоков, с которыми он конкурирует за ресурсы, например, за процессор. В стандарте POSIX-2001 предусмотрены две подобные области – PTHREAD_SCOPE_SYSTEM ( конкуренция в масштабе системы) и PTHREAD_SCOPE_PROCESS ( конкуренция в масштабе процесса ). Пожалуй, общей проблемой всех приложений является контроль переполнения стека. Для ее решения стандартом POSIX-2001 предусмотрено существование так называемой защитной области, расположенной за верхней границей стека. При переполнении и попадания указателя стека в защитную область операционная система должна фиксировать ошибку – например, доставлять потоку управления сигнал SIGSEGV . 5
С каждым потоком управления ассоциирован атрибутный объект – собрание атрибутов потока с конфигурируемыми значениями, таких как адрес и размер стека, параметры планирования и т.п. В стандарте POSIX-2001 атрибутные объекты представлены как значения абстрактного типа pthread_attr_t, внутренняя структура значений которого скрыта от приложений. Смысл введения атрибутных объектов – сгруппировать немобильные параметры потоков, чтобы облегчить адаптацию приложений к новым целевым платформам. Использование идеологии абстрактных объектов позволяет безболезненно добавлять новые атрибуты, не теряя обратной совместимости. Обратим внимание на следующее обстоятельство, важное для реализации многопотоковых приложений. Иногда потоки управления называют легковесными процессами, поскольку они требуют существенно меньшей, чем обычные процессы, аппаратно-программной поддержки и, кроме того, их функционирование сопряжено с меньшими накладными расходами. С этой точки зрения потоками можно пользоваться более свободно, чем процессами. С другой стороны, потоки одного процесса никак не защищены друг от друга, они разделяют одно адресное пространство, поэтому, в отличие от полноценных процессов, ошибки в программе одного из них могут сказаться на других, породить ситуации, которые трудно воспроизвести, что делает поиск и исправление ошибок крайне сложными. В этом смысле легковесной можно назвать аналогию с процессами, основанную только на возможности параллельной работы. Она обманчива, поскольку из вида упускается очень важный аспект разделения доменов выполнения. Вообще говоря, многопотоковые приложения существенно менее надежны, чем многопроцессные, поэтому потоками управления следует пользоваться осторожно, систематически. Операции с потоками управления можно подразделить на две группы: создание, терминирование, выполнение других управляющих операций; синхронизация. В таком порядке они и будут рассматриваться далее. Отметим, что стандарт POSIX2001 относит их к необязательной части, именуемой, как нетрудно догадаться, ” Потоки управления ” (“Threads”, THR). Модель, принятая в стандарте применительно к созданию потоков управления, отличается от соответствующей модели для процессов. При создании нового потока задается функция, с вызова которой начнется его выполнение, то есть вместо пары вида fork() / exec() создающий поток должен обратиться лишь к одной функции – pthread_create() . Впрочем, как мы увидим далее, и для процессов в стандарте POSIX2001 произошел отход от классических канонов – введены средства (функции семейства posix_spawn() ) для порождения процессов “в один ход”. Потоки управления бывают обособленными (отсоединенными) и присоединяемыми ; только последние доступны другим потокам для ожидания завершения и, быть может, утилизации освободившихся ресурсов. Ресурсы, освободившиеся после завершения обособленных потоков управления, утилизирует операционная система. 6
Поток управления можно терминировать изнутри и извне (из других потоков того же процесса ). Поток может управлять состоянием восприимчивости к терминированию ( разрешить / запретить собственное терминирование извне ), а также специфицировать тип терминирования ( отложенное или немедленное, асинхронное ). Отложенное терминирование происходит только по достижении потоком управления точек терминирования – мест в оговоренных в стандарте POSIX функциях, где поток должен отреагировать на ждущие запросы на терминирование (если оно разрешено ), перед тем как его выполнение будет приостановлено на неопределенное время с сохранением состояния восприимчивости к терминированию. Согласно стандарту POSIX-2001, точки терминирования имеются в таких функциях, как accept(), connect(), msgrcv(), msgsnd(), pause(), read(), sleep(), wait(), write() и сходных с ними по поведению. Допускается существование подобных точек и в других, также оговоренных в стандарте POSIX функциях – printf(), scanf(), semop() и т.п. Опрос и изменение атрибутов потоков управления Следуя классическому принципу “познай самого себя”, описание функций, обслуживающих потоки управления, мы начнем с функции pthread_self(), возвращающей в качестве результата идентификатор вызвавшего ее потока (см. листинг 1.1). #include <pthread.h> pthread_t pthread_self (void); Листинг 1.1. Описание функции pthread_self(). Выше мы отмечали, что тип pthread_t трактуется стандартом POSIX-2001 как абстрактный. На уровне языка C он может быть представлен, например, структурой. Для работы со значениями типа pthread_t предусмотрены два метода: присваивание и сравнение на равенство, реализуемое функцией pthread_equal() (см. листинг 1.2). #include <pthread.h> int pthread_equal (pthread_t t1, pthread_t t2); Листинг 1.2. Описание функции pthread_equal(). Если значения аргументов t1 и t2 равны, результат функции pthread_equal() отличен от нуля. Атрибуты потоков управления, используемые при создании последних, сгруппированы в упоминавшиеся выше атрибутные объекты. Для инициализации и разрушения атрибутных объектов служат функции pthread_attr_init() и pthread_attr_destroy() (см. листинг 1.3). #include <pthread.h> 7
int pthread_attr_init ( pthread_attr_t *attr); int pthread_attr_destroy ( pthread_attr_t *attr); Листинг 1.3. Описание функций pthread_attr_init() и pthread_attr_destroy(). Функция pthread_attr_init() инициализирует атрибутный объект, заданный указателем attr, подразумеваемыми значениями для всех индивидуальных атрибутов потоков управления, предусмотренных реализацией. Функция pthread_attr_destroy() разрушает заданный атрибутный объект. Впрочем, “разрушает”, возможно, слишком сильный термин. Быть может, реализация просто присваивает значениям атрибутов недопустимые значения. Во всяком случае, разрушенный атрибутный объект в дальнейшем может быть вновь инициализирован. Структура атрибутных объектов скрыта от приложений, но сам набор стандартизованных атрибутов выглядит вполне естественно. Их описание мы начнем с атрибутов стека – начального адреса и размера – и методов для их опроса и установки (см. листинг 1.4). #include <pthread.h> int pthread_attr_getstack ( const pthread_attr_t *restrict attr, void **restrict stackaddr, size_t *restrict stacksize); int pthread_attr_setstack ( pthread_attr_t *attr, void *stackaddr, size_t stacksize); Листинг 1.4. Описание функций pthread_attr_getstack() и pthread_attr_setstack(). Размер стека должен составлять не менее PTHREAD_STACK_MIN, начальный адрес – должным образом выровнен. Память, отведенная под стек, должна быть доступна на чтение и запись. Функция pthread_attr_getstack() помещает атрибуты стека по указателям stackaddr и stacksize. Это – проявление единообразной для семейства функций pthread*(), обслуживающих потоки управления, дисциплины возврата результатов. Содержательные данные помещаются в выходные аргументы. При нормальном завершении результат функции равен нулю; в противном случае выдается код ошибки. Подобная дисциплинированность является похвальной, но вынужденной. Ее причина – в разделении данных между потоками. Нельзя просто вернуть указатель на статический буфер – другой поток может в это время так или иначе работать с ним. 8
Поэтому поток должен зарезервировать индивидуальные области памяти для размещения выходных значений (обратившись, например, к malloc() ) и передать функции указатели на них. Для опроса и изменения размера защитной области, служащей цели обнаружения переполнения стека, предназначены функции pthread_attr_getguardsize() и pthread_attr_setguardsize() (см. листинг 1.5). #include <pthread.h> int pthread_attr_getguardsize ( const pthread_attr_t *restrict attr, size_t *restrict guardsize); int pthread_attr_setguardsize ( pthread_attr_t *attr, size_t guardsize); Листинг 1.5. Описание функций pthread_attr_getguardsize() и pthread_attr_setguardsize(). Если значение аргумента guardsize функции pthread_attr_setguardsize() равно нулю, при создании потоков управления с атрибутным объектом *attr защитная область отводиться не будет. Положительные величины guardsize также становятся новыми значениями одноименного атрибута, однако являются лишь указанием операционной системе; реальный размер защитной области может быть больше заданного. Приложение, соответствующее стандарту POSIX, должно использовать значения guardsize, кратные конфигурационной константе PAGESIZE, которая одновременно является подразумеваемым значением данного атрибута. Если приложение посредством функции pthread_attr_setstack() взяло на себя управление стеками потоков, атрибут guardsize игнорируется, операционная система не отводит защитную область, а контроль за переполнением стека возлагается на приложение. Отметим, что, в зависимости от ситуации, приложениям есть смысл как отказываться от защитных областей (например, если потоков управления много, памяти на защитные области уходит также много, и авторы приложения уверены, что переполнения стека быть не может), так и делать их размер больше подразумеваемого (например, если используются большие массивы и указатель стека рискует оказаться за верхней границей защитной области ). Стандартом POSIX-2001 предусмотрена группа атрибутов, обслуживающих планирование потоков управления. Соответствующие описания размещены в заголовочном файле <sched.h>. Центральную роль среди них играет структура типа sched_param, которая должна содержать по крайней мере поле int sched_priority; /* Приоритет планирования при выполнении потока */ 9
Реализация может поддерживать политику планирования SCHED_SPORADIC ( спорадическое планирование ), предусматривающую резервирование определенного количества вычислительной мощности для обработки с заданным приоритетом неких единичных, непериодических (спорадических) событий. В этом случае должны быть определены конфигурационные константы _POSIX_SPORADIC_SERVER и/или _POSIX_THREAD_SPORADIC_SERVER, а в структуре sched_param должны присутствовать следующие дополнительные поля. int sched_ss_low_priority; /* Нижняя граница приоритета */ /* планирования сервера */ /* спорадических событий */ struct timespec sched_ss_repl_period; /* Период пополнения бюджета */ /* спорадического сервера */ struct timespec sched_ss_init_budget; /* Начальный бюджет */ /* спорадического сервера */ int sched_ss_max_repl; /* Максимальное число */ /* ждущих операций */ /* пополнений бюджета */ /* спорадического сервера */ Для опроса и установки атрибутов планирования в атрибутных объектах служат функции pthread_attr_getschedparam() и pthread_attr_setschedparam() (см. листинг 1.6). #include <pthread.h> int pthread_attr_getschedparam ( const pthread_attr_t *restrict attr, struct sched_param *restrict param); int pthread_attr_setschedparam ( pthread_attr_t *restrict attr, const struct sched_param *restrict param); Листинг 1.6. Описание функций pthread_attr_getschedparam() и pthread_attr_setschedparam(). Атрибут “политика планирования”, способный принимать значения SCHED_FIFO ( планирование по очереди ), SCHED_RR ( циклическое планирование ), SCHED_OTHER ( “прочее” планирование ) и, возможно, SCHED_SPORADIC ( спорадическое планирование ), можно опросить и установить посредством функций pthread_attr_getschedpolicy() и pthread_attr_setschedpolicy() (см. листинг 1.7). #include <pthread.h> 10