Мобильное программирование приложений реального времени в стандарте POSIX

       

Функции для работы с событиями


От рассмотрения средств, доступных трассирующему процессу, мы переходим к функциям, используемым для оборудования трассируемых пользовательских приложений. Таких функций две: posix_trace_event() и posix_trace_eventid_open() (см. листинг 8.19).

#include <sys/types.h> #include <trace.h>

void posix_trace_event ( trace_event_id_t event_id, const void *restrict data_ptr, size_t data_len);

int posix_trace_eventid_open ( const char *restrict event_name, trace_event_id_t *restrict event_id);

Листинг 8.19. Описание функций для оборудования трассируемых пользовательских приложений. (html, txt)

Вызов функции posix_trace_event() создает точку трассировки, при попадании в которую генерируется пользовательское событие с идентификатором типа event_id и ассоциированными данными, специфицированными аргументами data_ptr (адрес буфера) и data_len (размер буфера). Если трассировка идет и события заданного типа не задерживаются фильтром, соответствующая информация будет записана в потоки, созданные ранее для трассировки вызывающего процесса. В противном случае вызов posix_trace_event() игнорируется.

Функция posix_trace_eventid_open(), ассоциирующая имя события с идентификатором типа, по сути аналогична posix_trace_trid_eventid_open(), только поток задается неявно (как поток, куда идет трассировка вызывающего процесса). Очевидно, подобная функция необходима для формирования аргумента event_id перед вызовами posix_trace_event().

Что касается анализирующего процесса, то обычно он в первую очередь открывает ранее записанный журнал трассировки, обращаясь к функции posix_trace_open(), а, обработав, закрывает его с помощью функции posix_trace_close(), быть может, в промежутке позиционируясь на начало журнала посредством функции posix_trace_rewind() (см. листинг 8.20).

#include <trace.h> int posix_trace_open ( int fildes, trace_id_t *trid); int posix_trace_close (trace_id_t trid); int posix_trace_rewind (trace_id_t trid);

Листинг 8.20. Описание функций для работы с журналами трассировки. (html, txt)


Функция posix_trace_open(), отправляясь от файлового дескриптора журнала трассировки fildes, открытого на чтение, создает поток трассировки и записывает его идентификатор по указателю trid. С помощью этого идентификатора анализирующий процесс может опрашивать атрибуты и статус потока и, главное, читать из него события, вызывая функцию posix_trace_getnext_event() (см. листинг 8.21).

#include <sys/types.h> #include *lt;trace.h>

int posix_trace_getnext_event ( trace_id_t trid, struct posix_trace_event_info *restrict event, void *restrict data_ptr, size_t num_bytes, size_t *restrict data_len, int *restrict unavailable);

int posix_trace_timedgetnext_event ( trace_id_t trid, struct posix_trace_event_info *restrict event, void *restrict data_ptr, size_t num_bytes, size_t *restrict data_len, int *restrict unavailable, const struct timespec *restrict abstime);



int posix_trace_trygetnext_event ( trace_id_t trid, struct posix_trace_event_info *restrict event, void *restrict data_ptr, size_t num_bytes, size_t *restrict data_len, int *restrict unavailable);

Листинг 8.21. Описание функций чтения событий трассировки. (html, txt)

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

Информация о событии помещается в структуру типа posix_trace_event_info, на которую указывает аргумент event. Данные, ассоциированные с событием, записываются в буфер с адресом data_ptr и длиной num_bytes; по указателю data_len размещается число записанных в буфер байт данных. Наконец, в случае успешного чтения по указателю unavailable помещается нулевое значение.

Если аргумент trid идентифицирует активный поток трассировки, вызов posix_trace_getnext_event() блокируется при отсутствии непрочитанных событий.

Функции posix_trace_timedgetnext_event() и posix_trace_trygetnext_event() применимы только к активным потокам трассировки.


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

В качестве иллюстрации применения описанных выше средств рассмотрим трассировку обеда философов (см. листинг 8.22). Мы не будем приводить весь исходный текст, а перечислим лишь изменения в трассируемой части программы и полностью приведем новую функцию main(), в которой сосредоточены управление трассировкой и анализ сгенерированных событий.

Листинг 8.22. Фрагмент исходного текста программы обеда философов с трассировкой. (html, txt)

Отметим, что в качестве данных, ассоциированных с пользовательскими событиями, выступают целые числа – номера философов (по одному на событие, см. вызовы posix_trace_event()).

Отметим также стиль формирования фильтра системных событий. Сначала в фильтрующее множество включаются все системные события (вызов posix_trace_eventset_fill(), а затем из него удаляются типы событий, представляющих интерес (вызовы posix_trace_eventset_del()).

Если интересоваться системными событиями, то исходный текст управляющей и анализирующей частей неизбежно получается зависящим от целевой платформы. Приведенный вариант программы ориентирован на операционную систему реального времени oc2000. В этой связи обратим внимание на идентификаторы типов системных событий (traceSigGeneration, traceSigDelivery, traceSigCatchFunc) и на структуру ассоциированных с событиями данных (evdat [0], evdat [2], evdat [5] и т.п.), вообще говоря, свою для каждого типа.

Фрагмент возможных результатов трассировки обеда двух философов показан на листинге 8.23.

Листинг 8.23. Фрагмент возможных результатов трассировки обеда двух философов на операционной платформе oc2000. (html, txt)

Трассировка – по-настоящему полезный инструмент в ситуациях, когда многопотоковая программа по непонятным причинам ведет себя не так, как хотелось бы. Рассмотрим пример, предложенный автору Н.В.


Шмыревым (см. листинг 8.24). Пусть имеется критический интервал, вход в который охраняется мьютексом. Один поток управления пытается захватывать этот мьютекс с ожиданием, другой – без ожидания, с паузами между попытками (должен же мьютекс когда-нибудь освободиться?). Выясняется, что второму потоку не удается войти в критический интервал. Почему?

Чтобы получить ответ на этот вопрос, уместно воспользоваться трассировкой системных событий, связанных с мьютексами.

Листинг 8.24. Программа, демонстрирующая трассировку системных событий, связанных с мьютексами, на операционной платформе oc2000. (html, txt)

Результаты трассировки сводятся к многократному повторению фрагмента, показанного на листинге 8.25. Второй поток управления не может попасть в критический интервал, потому что мьютекс оказывается захваченным первым потоком. Разумеется, величины задержек в приведенном примере подобраны так, чтобы длительность пауз между попытками входа в критический интервал второго потока управления была кратна периоду выполнения первого потока (что вполне может иметь место и в реальном приложении реального времени). Абстрактная надежда на то, что "все проходит" и мьютекс должен когда-нибудь освободиться, надо только набраться терпения и подождать, может и не сбыться. На подобные проблемы указывал еще Э. Дейкстра в своих первых работах по синхронизации параллельных процессов (см. [2] в дополнительной литературе).

Листинг 8.25. Фрагмент возможных результатов трассировки системных событий, связанных с мьютексами, на операционной платформе oc2000. (html, txt)

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


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