End - Северо-Кавказский горно

advertisement
СИСТЕМЫ РЕАЛЬНОГО ВРЕМЕНИ
Методические указания
к лабораторным работам
для студентов направления подготовки 230100.68
«Информатика и вычислительная техника»
Составитель А. А. Будаева
Владикавказ 2015
МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РФ
Федеральное государственное бюджетное образовательное учреждение
высшего профессионального образования
«СЕВЕРО-КАВКАЗСКИЙ ГОРНО-МЕТАЛЛУРГИЧЕСКИЙ ИНСТИТУТ
(ГОСУДАРСТВЕННЫЙ ТЕХНОЛОГИЧЕСКИЙ УНИВЕРСИТЕТ)»
Кафедра Автоматизированной обработки информации
СИСТЕМЫ РЕАЛЬНОГО ВРЕМЕНИ
Методические указания
к лабораторным работам
для студентов направления подготовки 230100.68
«Информатика и вычислительная техника»
Составитель А. А. Будаева
Допущено
редакционно-издательским советом
Северо-Кавказского горно-металлургического института
(государственного технологического университета).
Протокол заседания РИСа № 2 от 18.02.2014 г.
Владикавказ 2015
1
УДК 004.415.2
ББК 32.973.26-0.18.2
Б90
Рецензент:
кандидат технических наук, доцент
Северо-Кавказского горно-металлургического института
(государственного технологического университета)
Даурова А. А.
С59
Системы реального времени: Методические указания к лабораторным работам для студентов направления подготовки 230100.68 –
«Информатика и вычислительная техника» / Сост.: А. А. Будаева;
Северо-Кавказский горно-металлургический институт (государственный технологический университет). – Владикавказ: Северо-Кавказский
горно-металлургический
институт
(государственный
технологический университет). Изд-во «Терек», 2015. – 68 с.
Методические указания содержат описание 6 лабораторных работ,
соответствующих рабочей программе по дисциплине «Системы реального
времени» для студентов направления подготовки «Информатика и вычислительная
техника» 230100.68.
В методических указаниях на примерах рассматриваются основные
механизмы программного обеспечения реального времени, предоставляемые
ОСРВ, на основе QNX Momentics. Приведены индивидуальные задания по каждой
лабораторной работе. Методические указания способствуют приобретению
навыков и умений работы с операционными системами реального времени,
удовлетворяющих стандарту POSIX и расширениям операционной системы
QNX Neutrino.
УДК 004.415.2
ББК 32.973.26-0.18.2
Редактор: Иванченко Н. К.
Компьютерная верстка: Куликова М. П.
 Составление. ФГБОУ ВПО «Северо-Кавказский
горно-металлургический институт
(государственный технологический университет)», 2015
 Будаева А. А., составление, 2015
Подписано в печать 30.01.2015. Формат 60х84 1/16. Бумага офсетная. Гарнитура «Таймс».
Печать на ризографе. Усл. п.л. 3,95. Уч.-изд. л. 2,59. Тираж 20 экз. Заказ №
.
Северо-Кавказский горно-металлургический институт (государственный технологический
университет). Издательство «Терек».
Отпечатано в отделе оперативной полиграфии СКГМИ (ГТУ).
362021, г. Владикавказ, ул. Николаева, 44.
2
Содержание
Лабораторная работа № 1. Таймеры в Windows ....................................
Теоретический материал ...........................................................................
Порядок выполнения работы ....................................................................
Контрольные вопросы ...............................................................................
Лабораторная работа № 2. Решение задачи «взаимоисключающий
доступ» с помощью механизма семафоров ...............................................
Теоретический материал ...........................................................................
Порядок выполнения работы ....................................................................
Контрольные вопросы ...............................................................................
Лабораторная работа № 3. Установка системы QNX6. Работа в
оболочке Photon ............................................................................................
Теоретический материал ...........................................................................
Порядок выполнения работы ....................................................................
Контрольные вопросы ...............................................................................
Лабораторная работа № 4. Команды операционной системы QNX ....
Теоретический материал ...........................................................................
Порядок выполнения работы ....................................................................
Контрольные вопросы ...............................................................................
Лабораторная работа № 5. Командные сценарии операционной
системы QNX ................................................................................................
Теоретический материал ...........................................................................
Порядок выполнения работы ....................................................................
Контрольные вопросы ...............................................................................
Лабораторная работа № 6. Разработка приложений в среде PhAB .....
Теоретический материал ...........................................................................
Порядок выполнения работы ....................................................................
Контрольные вопросы ...............................................................................
Список литературы .......................................................................................
Приложение. Описание типов данных _SYSTEMTIME, LongWord…
3
5
–
19
24
25
–
33
34
36
–
42
–
43
–
49
50
51
–
57
58
59
–
66
–
67
68
Общие методические указания к выполнению лабораторных работ
При выполнении лабораторных работ необходимо:
1. В соответствии с целью работы сформулировать задачу, которая
должна быть решена с помощью приложения.
2. Разработать алгоритм решения задачи.
3. Разработать приложение, включающее интерфейс, программные
модули вычислительных процедур, формы представления
результатов.
4. Выполнить компьютерное моделирование.
5. Провести тестирование алгоритма и приложения.
6. Сделать выводы и обобщения.
7. Составить электронный вариант отчета с результатами выполнения
приложения. Образец оформления титульного листа приведен в
приложении.
При выполнении работ рекомендуется обратиться к литературе [1–12].
4
Лабораторная работа № 1
ТАЙМЕРЫ В WINDOWS
Цель работы:
1. Изучить службы таймеров, предоставляемые операционной
системой Microsoft Windows.
2. Рассмотреть назначение основных процедур и функций этих
служб, назначение и смысл их входных параметров и возвращаемых
значений.
3. Научиться применять на практике полученные знания.
Теоретический материал
Службы таймеров для Microsoft Windows
Таймер – внутренняя функция операционной системы, которая
позволяет многократно измерять указанный интервал времени. После
истечения промежутка времени, контролируемого таймером, должно
выполняться то или иное действие, например, вызываться на выполнение
определённая программа.
Точность, с которой таймер контролирует заданный интервал
времени, называется его разрешающей способностью. Точность таймера
зависит от тактовой частоты системы.
Имеется два типа событий от таймера: единственное, которое
происходит однажды после указанного количества миллисекунд, и
периодические. Периодические вызовы по таймеру поступают
неоднократно, каждый раз, когда протекает указанное число
миллисекунд.
Операционная система Microsoft Windows поддерживает службы
обычных
и
мультимедийных
таймеров.
Различие
между
мультимедийными и обычными таймерами состоит в том, что
мультимедийные таймеры вызывают события обработки сразу, а обычные
таймеры добавляют сообщение о необходимости обработки события по
таймеру к очереди сообщений.
Мультимедийные таймеры работают с наибольшей точностью среди
других служб таймеров и позволяют приложению намечать события с
наивысшей разрешающей способностью, возможной для используемых
аппаратных средств. Мультимедийные таймеры полезны для
приложений, которым требуется выбор времени с высоким разрешением.
Приложения, не требующие высокоточного выбора времени, должны
использовать службу обычных таймеров.
5
Мультимедийные таймеры
С помощью мультимедийных таймеров приложения могут
запрашивать и получать сообщения таймеров через заданные интервалы
времени. Сервис мультимедийных таймеров позволяет намечать как
однократные, так и периодические события.
Прежде
чем
приложение
начинает
использовать
услуги
мультимедийного таймера, оно должно определить текущее системное
время. Системное время – это время, измеряемое в миллисекундах от
начала работы операционной системы Microsoft Windows. Для определения астрономического времени используется функция GetSystemTime,
а для определения системного времени – функция GetTickCount.
Функция GetSystemTime заполняет структуру _SYSTEMTIME, а
GetTickCount возвращает значение LongWord. Описание используемых
типов данных (_SYSTEMTIME, LongWord) приведено в приложении.
Для установки мультимедийного таймера используется функция
timeSetEvent. Чтобы запустить таймер, приложение должно вызвать
функцию timeSetEvent, указывая в качестве параметров задержку
времени, разрешающую способность, адрес функции вызова, данные
пользователя (в дальнейшем они будут сообщаться функции вызова при
её запуске таймером) и тип таймера (одиночный или периодический).
Функция timeSetEvent возвращает идентификатор таймера, который
может использоваться, чтобы остановить или идентифицировать события
от таймера.
Формат функции timeSetEvent:
Function timeSetEvent(uDelay: UINT; uResolution: UINT;
lpTimeProc: TFNTimeCallBack; dwUser: DWORD; fuEvent:
UINT): MMRESULT.
Здесь uDelay – задержка времени в миллисекундах; uResolution –
разрешающая способность таймера, в миллисекундах; lpTimeProc – адрес
функции, которая запускается при срабатывании таймера; dwUser –
данные пользователя, сообщаемые вызываемой функции при
срабатывании таймера (при отсутствии данных параметру присваивают
нулевое значение); fuEvent – тип таймера.
Функция timeSetEvent в случае успеха возвращает идентификатор
таймера uID, в противоположном случае – ошибка. Типы используемых
параметров (UINT, TFNTimeCallBack, MMRESULT) приведены в
приложении.
Интервал между вызовами таймера называется задержкой времени
(uDelay). Разрешающая способность тем выше, чем меньше значение
параметра uResolution. Разрешающая способность, равная нулю,
6
указывает на то, что события должны произойти с максимально
возможной точностью. Чтобы уменьшить затраты ресурсов системы,
нужно использовать как можно большее значение параметра uResolution.
Отношение между разрешающей способностью таймера и длиной
задержки времени играют важную роль в событиях от таймера.
Например, если разрешающая способность равна 5 мс, а задержка
времени равна 100 мс, то таймер запускает функцию вызова (lpTimeProc)
в интервале от 95 до 105 миллисекунд.
Определены следующие типы таймеров (fuEvent): TIME_ONESHOT
– таймер срабатывает один раз после истечения uDelay миллисекунд;
TIME_PERIODIC – таймер срабатывает периодически, после истечения
каждых uDelay миллисекунд.
После инициализации таймер будет запускать функцию, адрес
которой указан в параметре lpTimeProc функции timeSetEvent. Функцию
вызова обычно называют TimeProc.
Формат функции TimeProc:
Procedure TimeProc(uID: UINT; uMsg: UINT; dwUser: DWORD;
dw1: DWORD; dw2: DWORD) stdcall.
Здесь uID – идентификатор таймера; uMsg – зарезервировано (не
использовать); dwUser – данные пользователя, указанные в
соответствующем параметре функции timeSetEvent; dw1 и dw2 –
зарезервировано (не использовать).
Приставка stdcall ставится для функций и процедур, запуск которых
осуществляет операционная система, а также это означает, что параметры
будут считываться справа налево.
Удаляет таймер функция timeKillEvent. Формат функции
timeKillEvent:
Function timeKillEvent(uID: UINT): MMRESULT.
Здесь uID – идентификатор таймера, который следует удалить.
Приложение не должно вызывать никакие системые функции внутри
функции вызова TimeProc. К ним относятся: GetSystemTime,
GetTickCount, timeSetEvent и timeKillEvent. В этом случае необходимо
предусмотреть дополнительные функции установки и удаления таймера
вне пределов процедуры TimeProc. Например, для создания таймера
можно использовать функцию следующего вида:
Function BuildTimer(uDelay : UINT; uResolution:UINT; fuEvent :
UINT): MMRESULT;
Begin
Result := TimeSetEvent(uDelay,uResolution,@TimeProc,0,fuEvent);
End;
7
для удаления таймера – процедуру:
Procedure DestroyTimer(Var T_ID: UINT);
Begin
TimeKillEvent(T_ID);
T_ID := 0;
End;
Перед освобождением памяти (например, в случае завершения
работы приложения), содержащей функцию вызова (lpTimeProc),
необходимо выключить все активные таймеры.
Рассмотрим задачи на применение службы мультимедийных
таймеров в среде Delphi.
Задача 1. Необходимо создать периодический таймер TM1 с
интервалом срабатывания 5 секунд, точностью 5 миллисекунд, который
после запускает на выполнение функцию GetWorkResult и в зависимости
от возвращённого этой функцией значения выполняет следующие
действия:
 если функция GetWorkResult вернёт значение 0, то запускается
одиночный таймер TM2 с интервалом срабатывания 1 секунда и
точностью 1 миллисекунда, который вызывает на выполнение
процедуру Work_TM2;
 если функция GetWorkResult вернёт значение 1, то таймер TM1
выключается.
Разрабатываемая
программа
должна
содержать
описание
переменных, используемых в качестве идентификаторов таймеров;
описание процедур и функций и основную программу.
Сначала рассмотрим функцию GetWorkResult и процедуру
Work_TM2, которые вызываются при срабатывании соответствующих
таймеров.
Работу функции GetWorkResult реализуем с помощью обращения к
датчику равномерно распределённых случайных чисел 0 и 1:
Function GetWorkResult: Integer;
Begin
Result := Random(2);
End;
В результате выполнения функции GetWorkResult возвращаемое
значение (Result) будет равно 0 или 1.
Пусть процедура Work_TM2 выполняет следующие действия:
8
Procedure Work_TM2;
Var
I : Integer;
Begin
I := 30000;
While I <> 0 do I := I – 1;
End;
Сначала должен быть запущен таймер TM1. Для его установки будем
использовать функцию TimeSetEvent следующего вида:
TM1 := timeSetEvent(5000, 5, @TimeProc, 0, TIME_PERIODIC).
Опишем процедуру вызова TimeProc, адрес которой был указан в
качестве одного из аргументов функции TimeSetEvent:
Procedure TimeProc(uTimerID,uMessage:UINT;dwUser,dw1,dw2:DWORD)
stdcall;
Begin
If uTimerID = TM1 Then//сработал таймер с идентификатором TM1
Case GetWorkResult of
0:TM2 := BuildTimer(1000,1,TIME_ONESHOT);
1:DestroyTimer(TM1);
End Else Work_TM2;//сработал таймер TM2
End;
Так как внутри процедуры TimeProc не должны вызываться
системные функции timeSetEvent и timeKillEvent, для создания таймера
TM2 использована функция BuildTimer, а для удаления таймера TM1 –
функцияDestroyTimer.
В результате код программы, соответствующий поставленной задаче,
будет выглядеть следующим образом:
Var
//описание идентификаторов таймеров
TM1: UINT;
TM2: UINT;
Function GetWorkResult: Integer;
Begin
Result := Random(2);
End;
9
Procedure Work_TM2;
Var
I : Integer;
Begin
I := 30000;
While I <> 0 do I := I – 1;
End;
Procedure DestroyTimer(Var T_ID : UINT);
Begin
TimeKillEvent(T_ID);
T_ID := 0;
End;
Function BuildTimer(uDelay : UINT; uResolution:UINT; fuEvent :
UINT) : UINT;
Begin
Result := TimeSetEvent(uDelay,uResolution,@TimeProc,0,fuEvent);
End;
Procedure TimeProc(uTimerID,uMessage:UINT;dwUser,dw1,dw2:DWORD)
stdcall;
Begin
If uTimerID = TM1 Then//сработал таймер с идентификатором TM1
Case GetWorkResult of
0:TM2 := BuildTimer(1000,1,TIME_ONESHOT);
1:DestroyTimer(TM1);
End Else Work_TM2;//сработал таймер TM2
End;
//начало основной программы
Begin
Randomize;
TM1 := timeSetEvent(5000, 5, @TimeProc, 0, TIME_PERIODIC);
//здесь подразумевается создание окон приложения
End.
Задача 2. Периодический таймер TM1 с интервалом срабатывания 10
секунд и разрешающей способностью 0,01 секунды, а также с
автокорректировкой времени запуска (отсчёт следующего момента
запуска таймера сдвигается на время выполнения процедуры или
функции), срабатывает 5 раз и при срабатывании вызывает функцию
10
GetWorkResult, возвращаемое значение которой влияет на дальнейшие
события следующим образом.
Если функция GetWorkResult вернёт значение 0, то запускается
периодический таймер TM2 (интервал срабатывания 3 секунды,
максимальная разрешающая способность) на два срабатывания.
Срабатывание таймера TM2 вызывает на выполнение функцию GetTM2.
Если функция GetTM2 возвращает значение 0, то запускается одиночный
таймер TM3 с интервалом срабатывания 1 секунда и максимальной
разрешающей способностью. Срабатывание таймера TM3 вызывает на
выполнение процедуру Work_TM3. Если функция GetTM2 возвращает
значение, отличное от 0 (GetTM2 0), никаких действий производить не
следует.
Если функция GetWorkResult вернет значение 1, то запускается
одиночный таймер TM3 (интервал срабатывания 1с, максимальная
разрешающая способность), вызывающий по срабатыванию процедуру
Work_TM3.
Разрабатываемая
программа
должна
содержать
описание
переменных, используемых в качестве идентификаторов таймеров;
описание процедур и функций и основную программу.
Функция GetWorkResult аналогична одноимённой функции из
первой задачи. Работу функции GetTM2 реализуем с помощью
обращения к счётчику равномерно распределённых случайных чисел:
Function GetTM2: Integer;
Begin
Result := Random(3);
End;
В результате выполнения функции GetTM2 возвращаемое значение
(Result) будет равно 0,1 или 2.
Работа процедуры Work_TM3 аналогична работе процедуры
Work_TM2, описанной в первой задаче.
Сначала должен быть запущен таймер TM1. Для его установки будем
использовать функцию TimeSetEvent следующего вида:
TM1 := timeSetEvent(10000, 10, @TimeProc, 0, TIME_PERIODIC).
Опишем процедуру вызова TimeProc, адрес которой был указан в
качестве одного из аргументов функции TimeSetEvent:
Procedure TimeProc(uTimerID,uMessage:UINT;dwUser,dw1,dw2:DWORD)
stdcall;
Begin
11
If uTimerID = TM1 Then//сработал первый таймер
Begin
C1 := C1 – 1;
If C1 = 0 Then DestroyTimer(TM1);
Case GetWorkResult of
0:Begin
C2 := 2;
TM2 := BuildTimer(3000,0,TIME_PERIODIC);
End;
1:TM3 := BuildTimer(1000,0,TIME_ONESHOT);
End;
If C1 > 0 Then
Begin
DestroyTimer(TM1);
TM1 := BuildTimer(10000,10,TIME_PERIODIC);
End;
End Else
If uTimerID = TM2 Then//сработал второй таймер
Begin
If GetTM2 = 0 Then
TM3 := BuildTimer(1000,0,TIME_ONESHOT);
C2 := C2 – 1;
If C2 = 0 Then DestroyTimer(TM2);
End Else Work_TM3;//сработал третий таймер
End;
Так как внутри функции вызова TimeProc не должны вызываться
системные функции timeSetEvent и timeKillEvent, для создания таймеров
использована функция BuildTimer, а для удаления таймеров –
функцияDestroyTimer.
В результате код программы, соответствующий поставленной задаче,
будет выглядеть следующим образом:
Var
//описание идентификаторов таймеров
TM1: UINT;
TM2: UINT;
TM3: UINT;
//описание счётчиков количества срабатываний таймеров
C1, C2: Integer;
12
Function GetWorkResult: Integer;
Begin
Result := Random(2);
End;
Procedure Work_TM3;
Var
I : Integer;
Begin
I := 30000;
While I <> 0 do I := I – 1;
End;
Function GetTM2: Integer;
Begin
Result := Random(3);
End;
Procedure DestroyTimer(Var T_ID : UINT);
Begin
TimeKillEvent(T_ID);
T_ID := 0;
End;
Function BuildTimer(uDelay : UINT; uResolution:UINT; fuEvent :
UINT) : UINT;
Begin
Result := TimeSetEvent(uDelay,uResolution,@TimeProc,0,fuEvent);
End;
Procedure TimeProc(uTimerID,uMessage:UINT;dwUser,dw1,dw2:DWORD)
stdcall;
Begin
If uTimerID = TM1 Then//сработал первый таймер
Begin
Case GetWorkResult of
0:Begin
C2 := 2;
TM2 := BuildTimer(3000,0,TIME_PERIODIC);
End;
1:TM3 := BuildTimer(1000,0,TIME_ONESHOT);
13
End;
C1 := C1 – 1;
If C1 = 0 Then DestroyTimer(TM1) Else
Begin
DestroyTimer(TM1);
TM1 := BuildTimer(10000,10,TIME_PERIODIC);
End;
End Else
If uTimerID = TM2 Then//сработал второй таймер
Begin
If GetTM2 = 0 Then
TM3 := BuildTimer(1000,0,TIME_ONESHOT);
C2 := C2 – 1;
If C2 = 0 Then DestroyTimer(TM2);
End Else Work_TM3;//сработал третий таймер
End;
//начало основной программы
Begin
Randomize;
C1 := 5;
TM1 := timeSetEvent(10000, 10, @TimeProc, 0, TIME_PERIODIC);
//здесь подразумевается создание окон приложения
End.
Обычные таймеры
Служба обычных таймеров не такая точная, как служба
мультимедийных таймеров. Это объясняется тем, что после истечения
указанного интервала времени задержки для данного таймера служба
обычных таймеров посылает к окнам Microsoft Windows сообщение
WM_TIMER. Так как точность таймера зависит от тактовой частоты
системы и от частоты чтения приложением сообщений из очереди
сообщений, то значение времени задержки только приблизительно.
Приложение, использующее службу обычных таймеров, должно
создавать таймер с помощью функции SetTimer. Каждый таймер имеет
уникальный идентификатор. При создании таймера приложение может
использовать своё уникальное значение.
Формат функции SetTimer:
Function SetTimer(handle: HWND; nIDEvent: UINT; uElapse:
UINT; lpTimerFunc: TFNTimerProc): UINT.
14
Здесь параметр handle идентифицирует окно, которое будет связано с
таймером; параметр nIDEvent определяет идентификатор таймера,
отличный от нуля; nElapse – задержка времени в миллисекундах;
параметр lpTimerFuncне используется.
Возвращаемое функцией SetTimer значение в случае успеха – целое
число, идентифицирующее новый таймер, а если функция потерпела
неудачу, то возвращаемое значение – нулевое.
Параметр handle является идентификатором окна, принадлежащим
инициализирующему таймер приложению. Если этот параметр нулевой,
то никакое окно не будет связано с таймером, и параметр nIDEvent
игнорируется.
Всякий раз, когда значение времени задержки для таймера истекает,
система посылает сообщение WM_TIMER к окну (handle), связанному с
таймером. Если идентификатор окна не был указан в параметрах
SetTimer, то приложение, которое создавало таймер, должно само
контролировать приход сообщения WM_TIMER и обрабатывать его.
Формат сообщения WM_TIMER:
WM_TIMER
wTimerID – wParam; tmprc – lParam.
Здесь параметр wTimerID определяет идентификатор таймера;
параметр tmprc указывает на функцию вызова (не используется).
Это сообщение считывается системной функцией DispatchMessage
только тогда, когда никакие другие сообщения не находятся в очереди
сообщений.
Новый таймер, как только он создан, начинает отсчитывать
указанный интервал времени. Приложение может изменить значение
времени задержки, используя функцию SetTimer, и может удалить
таймер, используя функцию KillTimer. Чтобы использовать ресурсы
системы эффективно, приложение должно удалить таймеры, которые
больше ему не нужны.
Формат функции KillTimer:
Function KillTimer(handle: HWND; nIDEvent: UINT): Boolean,
Здесь handle – идентификатор окна, связанного с указанным
таймером; параметр nIDEvent определяет таймер, подлежащий удалению.
В случае успеха возвращаемое функцией KillTimer значение
«истина», иначе возвращаемое значение «ложь».
Значение параметра handle должно быть тем же самым, что и
значение, которое было послано в параметре handle к функции SetTimer,
создававшей таймер.
Если идентификатор окна (handle), указанный в параметрах функции
SetTimer, был не нулевым, то параметр nIDEvent функции KillTimer
15
должен быть таким же, что и посланный к SetTimer параметр nIDEvent.
Если приложение вызвало функцию SetTimer с нулевым значением
параметра handle, то параметр nIDEvent функции KillTimer должен быть
равен значению, возвращенному функцией SetTimer.
Функция KillTimer не удаляет сообщения WM_TIMER, уже
помещённые в очередь сообщений.
Рассмотрим задачу на применение службы обычных таймеров.
Условие задачи. Необходимо создать периодический таймер TM1 с
интервалом срабатывания 5 секунд, который запускает функцию
GetWorkResult и, в зависимости от возвращённого этой функцией
значения, программа выполняет следующие действия:
 если функция GetWorkResult вернёт значение 0, то запускается
одиночный таймер TM2 с интервалом срабатывания 1 секунда,
который в свою очередь вызывает на выполнение процедуру
Work_TM2;
 если функция GetWorkResult вернёт значение 1, то таймер TM1
выключается;
 если возвращаемое функцией GetWorkResult значение отличается
от 0 или 1, то никаких действий не производится.
Пусть функция GetWorkResult будет возвращать одно из пяти
значений: 0, 1, 2, 3, 4. Тогда работу функции GetWorkResult реализуем с
помощью обращения к датчику равномерно распределённых случайных
чисел:
Function GetWorkResult: Integer;
Begin
Result := Random(5);
End;
В результате выполнения функции GetWorkResult возвращаемое
значение (Result) будет равно 0, 1, 2, 3 или 4.
Пусть процедура Work_TM2 выполняет следующие действия:
Procedure Work_TM2;
Var
I : Integer;
Begin
I := 30000;
While I <> 0 do I := I – 1;
End;
16
Поскольку сначала должен быть запущен таймер TM1, для его
установки будем использовать функцию SetTimer:
SetTimer(SimpleWindow.Handle, tm1, 5000, nil);
Это действие будет выполняться в момент создания окна, то есть
внутри процедуры
Procedure TSimpleWindow.FormCreate(Sender: TObject).
При завершении работы приложения, то есть в момент уничтожения
его основного окна, следует удалить используемые таймеры, для чего
используется следующая процедура:
Procedure TSimpleWindow.FormDestroy(Sender: TObject);
Begin
KillTimer(SimpleWindow.Handle, tm1);
KillTimer(SimpleWindow.Handle, tm2);
End;
Обозначим процедуру GetTimerMes в качестве перехватчика
сообщения WM_TIMER:
Procedure GetTimerMes(var Msg: TWMTimer); message WM_TIMER/
Описание структуры TWMTimer приведено в приложении 1.
Действия внутри перехватчика сообщения будут выглядеть
следующим образом:
Procedure TSimpleWindow.GetTimerMes(var Msg: TWMTimer);
Begin
Case Msg.TimerID of
tm1: Begin//обработка срабатывания первого таймера
Case GetWorkResult of
0: SetTimer(SimpleWindow.Handle, tm2, 1000, nil);
1: KillTimer(SimpleWindow.Handle, tm1);
End;
Msg.Result := 0;//возвращаем 0, т.к. удачно обработали
End;
tm2: Begin//обработка срабатывания второго таймера
Work_TM2;
KillTimer(SimpleWindow.Handle, tm2);
Msg.Result := 0;//возвращаем 0, т. к. удачно обработали
End;
End;
Так как служба обычных таймеров должна быть привязана к окну, то
необходимо описать тип данных, к которому будет принадлежать окно.
17
В результате текст основного модуля программы, соответствующий
поставленной задаче, будет выглядеть следующим образом:
unit Simple;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs; const
//описание уникальных идентификаторов таймеров
tm1 = 500;
tm2 = 600;
//описание типа данных, к которому будет принадлежать окно
type
TSimpleWindow = class(TForm)
procedure FormCreate(Sender: TObject);//создание окна
procedure FormDestroy(Sender: TObject);//уничтожение окна
protected
procedure GetTimerMes(var Msg: TWMTimer); message WM_TIMER;
end;
var SimpleWindow: TSimpleWindow;//окно
implementation
{$R *.DFM}
Function GetWorkResult: Integer;
Begin
Result := Random(5);
End;
Procedure Work_TM2;
Var
I : Integer;
Begin
I := Random(3000000);
While I <> 0 Do Dec(I);
End;
Procedure TSimpleWindow.GetTimerMes(var Msg: TWMTimer);
Begin
Case Msg.TimerID of
tm1: Begin//обработка срабатывания первого таймера
18
Case GetWorkResult of
0: SetTimer(SimpleWindow.Handle, tm2, 1000, nil);
1: KillTimer(SimpleWindow.Handle, tm1);
End;
Msg.Result := 0;//возвращаем 0, т.к. удачно обработали
End;
tm2: Begin//обработка срабатывания второго таймера
Work_TM2;
KillTimer(SimpleWindow.Handle, tm2);
Msg.Result := 0;//возвращаем 0, т.к. удачно обработали
End;
End;
End;
procedure TSimpleWindow.FormCreate(Sender: TObject);
Begin
Randomize;
SetTimer(SimpleWindow.Handle, tm1, 5000, nil);
End;
procedure TSimpleWindow.FormDestroy(Sender: TObject);
Begin
KillTimer(SimpleWindow.Handle, tm1);
KillTimer(SimpleWindow.Handle, tm2);
End;
End.
Порядок выполнения работы
Индивидуальным
вариантом
к
лабораторной
работе
предусматривается решение двух заданий: одного – с использованием
службы мультимедийных таймеров и другого – с использованием службы
обычных таймеров. Номер варианта задания состоит из двух частей:
номера варианта в виде римской цифры (см. раздел 5) и номера строки из
таблицы, соответствующей указанному варианту, например, I-2 (указана
служба МТ) и III-5 (указана служба ОТ).
Порядок подготовки к работе:
1. Изучить службы мультимедийных и обычных таймеров для
Microsoft Windows.
2. Согласно заданному варианту, руководствуясь положениями и
примерами раздела 2, написать программу, соответствующую
поставленным задачам.
19
Отчет по лабораторной работе должен содержать:
1. Вариант индивидуального задания с описанием требований к
срабатыванию таймеров.
2. Коды программ, соответствующие решению поставленных
индивидуальных задач для служб мультимедийных и обычных таймеров,
аналогично кодам программ, приведенных в подразделах 1 и 2.
3. Временные диаграммы работы.
4. Выводы по проделанной работе.
20
Варианты индивидуального задания
Вариант I
Программное приложение должно в моменты времени t1, t2, t3 с
точностью r1, r2, r3 запустить на выполнение процедуры p1, p2, p3.
Моменты запуска и точность представлены в табл.1 в миллисекундах
(max – максимальная точность, X – точность не указана). Номера
вызываемых процедур соответствуют номерам строк табл. 4. В графе
«Служба» указано: МТ – мультимедийные таймеры, ОТ – обычные
таймеры.
Таблица № 1
№
t1
t2
t3
r1
r2
r3
p1
p2
p3
Служба
1
9000
7000
1500
50
35
max
1
5
6
МТ
2
400
6800
8900
max
35
35
2
3
4
МТ
3
6100
2300
2700
X
X
X
1
3
4
ОТ
4
2300
2500
6000
X
X
X
2
5
6
ОТ
5
2600
6500
7200
50
30
35
1
2
3
МТ
6
8400
8700
4500
X
X
X
2
3
4
ОТ
7
3200
5700
5900
X
X
X
1
3
6
ОТ
8
5700
6000
750
30
25
max
1
2
6
МТ
9
3000
900
7600
X
X
X
3
4
6
ОТ
10
7600
3300
4400
40
5
45
1
2
3
МТ
11
1300
6500
2200
X
X
X
2
3
6
ОТ
12
4700
3400
3300
45
20
max
1
2
4
МТ
13
6900
3700
2900
10
35
max
1
3
5
МТ
14
8600
2300
2500
X
X
X
3
4
6
ОТ
15
8100
3600
5900
30
max
40
1
4
5
МТ
Вариант II
Через каждые t1 миллисекунд с точностью r1 необходимо n раз
запускать процедуру p1, после выполнения которой, спустя t2
миллисекунд с точностью r2, должна запуститься процедура p2. Исходные
данные индивидуальных вариантов представлены в табл. 2. Номера
вызываемых процедур соответствуют номерам строк табл. 4.
21
Таблица № 2
№
t1
t2
r1
r2
n
p1
p2
Служба
1
9600
4800
40
50
7
1
4
МТ
2
8800
2900
30
20
4
2
3
МТ
3
7500
6900
X
X
3
2
5
ОТ
4
7800
1800
max
40
7
3
4
МТ
5
5300
2800
45
max
6
5
6
МТ
6
1400
700
X
X
5
2
4
ОТ
7
7100
3300
35
50
9
1
6
МТ
8
5800
4400
X
X
6
1
3
ОТ
9
3100
2400
X
X
10
4
5
ОТ
10
4800
1900
20
35
9
2
5
МТ
11
3600
1100
X
X
3
3
6
ОТ
12
3900
2700
max
5
7
1
3
МТ
13
8300
1700
X
X
4
5
6
ОТ
14
9100
1200
X
X
8
1
2
ОТ
15
4200
1300
25
max
6
3
5
МТ
Вариант III
Программное приложение должно в момент времени t1 с точностью r1
запустить на выполнение функцию p1. Если функция p1 вернёт значение z1, то от этого момента через интервал времени t2 с точностью r2
должна выполниться процедура p2, иначе, если функция p1 вернёт
значение z2, то спустя t3 миллисекунд с точностью r3 должна выполниться
процедура p3. Повторное выполнение функции p1 должно прекратиться,
если она вернёт значение z3 либо после n срабатываний этой функции.
Исходные данные индивидуальных вариантов представлены в табл. 3.
Номера вызываемых процедур соответствуют номерам строк табл. 4.
22
Таблица № 3
p3
z1
z2
z3
n
4
6
0
3
-
5
Служба
МТ
1
3
4
1
2
4
-
ОТ
max max 1
5
6
4
1
2
-
МТ
2
3
4
2
3
-
7
МТ
X
4
5
6
0
1
3
-
ОТ
7500 6300 4500 max 35
45
1
2
3
0
2
3
-
МТ
7
9700 9100 4300 X
X
X
3
5
6
2
4
-
7
ОТ
8
8900 7000 3800 X
X
X
1
4
5
0
1
-
5
ОТ
9
6600 4200 1800 50
25
max 2
3
6
1
3
4
-
МТ
10
9900 5900 4900 max 5
10
2
4
5
3
0
-
7
МТ
11
9000 8000 2200 X
X
X
1
5
6
0
4
2
-
ОТ
12
8300 4900 4200 40
10
5
1
2
5
2
3
-
9
МТ
13
8100 7900 7400 45
35
5
1
4
6
0
1
2
-
МТ
14
9200 3400 1000 X
X
X
1
2
5
1
2
4
-
ОТ
15
8500 3400 1200 45
5
max 4
5
6
0
3
-
5
МТ
№ t1
t2
t3
r1
r2
r3
p 1 p2
1
6200 5100 4900 25
30
max 2
2
8500 6500 4600 X
X
X
3
3200 2700 2900 5
4
5400 4100 3200 max 20
10
5
9900 6000 5500 X
X
6
Коды наполнения вызываемых процедур p1, p2, p3. (2)
Таблица № 4
№
1
1
2
Код
2
Var I, J: Cardinal;
begin
I := 1 + Random(30);J := GetTickCount;
While GetTickCount - J < I do;
result := random (5);
end;
Var I, J: Cardinal;
begin
I := 1 + Random(40);J := GetTickCount;
While GetTickCount - J < I do;
result := random (5);
end;
23
Окончание табл. 4
1
3
4
5
6
2
Var I, J: Cardinal;
begin
I := 1 + Random(50);J := GetTickCount;
While GetTickCount - J < I do;
result := random (5);
end;
Var I, J: Cardinal;
begin
I := 1 + Random(60);J := GetTickCount;
While GetTickCount - J < I do;
result := random (5);
end;
Var I, J: Cardinal;
begin
I := 1 + Random(70);J := GetTickCount;
While GetTickCount - J < I do;
result := random (5);
end;
Var I, J: Cardinal;
begin
I := 1 + Random(80);J := GetTickCount;
While GetTickCount - J < I do;
result := random (5);
end;
Контрольные вопросы
1.
2.
3.
4.
Перечислите основные службы таймеров в ОС Microsoft Windows.
Классификация и особенности таймеров в Windows.
Принципы работы с мультимедийными таймерами.
Принципы работы с обычными таймерами.
24
Лабораторная работа №2
РЕШЕНИЕ ЗАДАЧИ «ВЗАИМОИСКЛЮЧАЮЩИЙ ДОСТУП»
С ПОМОЩЬЮ МЕХАНИЗМА СЕМАФОРОВ
Цель работы:
1. Получить представление об использовании механизма семафоров
для решения проблемы взаимоисключающего доступа.
2. Ознакомиться с реализацией механизма семафоров в операционной
системе Windows.
Теоретический материал
Постановка задачи
В лабораторной работе предлагается реализовать программно
процедуру взаимоисключающего доступа к критическому объекту. Под
критическим объектом понимается некоторая переменная, принимающая
целые положительные значения. Присвоением определенных значений
указанной переменной управляют два потока, работающие в рамках
одного приложения. Требуется исключить одновременные обращения
потоков к критическому объекту, при этом необходимо, чтобы задержка
любого потока вне его критической секции не влияла на работу другого
потока. Решение должно быть симметричным для обоих потоков (оба
потока равноправны).
Семафоры в Windows
Семафоры и операции над ними, как правило, реализуются в ядре
операционной системы, где осуществляется управление сменой
состояния процессов.
Создание семафора
Для создания семафора приложение должно вызвать функцию
CreateSemaphore. Синтаксис функции CreateSemaphore:
HANDLE CreateSemaphore(lpsa: LPSECURITY_ATTRIBUTES; cSemInitial:
LONG; cSemMax: LONG; lpszSemName: LPTSTR);
Параметр lpsa задаёт дескриптор защиты, отличный от стандартного
дескриптора. Значение параметра lpsa NULL указывает на стандартные
атрибуты защиты.
Через параметры cSemInitial и cSemMax передаются, соответственно,
начальное и максимальное значения счетчика, связанного с создаваемым
семафором. Начальное значение счетчика cSemInitial должно быть
больше или равно нулю и не должно превосходить максимальное
значение счетчика, передаваемое через параметр cSemMax.
25
Параметр lpszSemName присваивает имя в виде строки. В
дальнейшем это имя используется для получения описателя семафора из
других процессов. Если семафор используется только для синхронизации
задач, созданных в рамках одного приложения, можно создать
безымянный семафор, указав в качестве параметра lpszSemName функции
CreateSemaphore значение NULL. В том случае, когда необходимо
синхронизовать задачи разных процессов, следует определить имя
семафора.
В случае удачного создания семафора функция CreateSemaphore
возвращает его идентификатор. В случае возникновения ошибки
возвращается значение NULL, при этом код ошибки можно узнать при
помощи функции GetLastError.
Так как имена семафоров доступны всем приложениям в системе,
возможно возникновение ситуации, когда приложение пытается создать
семафор с уже использованным именем. При этом новый семафор не
создается, а приложение получает идентификатор для уже
существующего семафора. Если возникла такая ситуация, функция
GetLastError, вызванная сразу после функции CreateSemaphore,
возвращает значение ERROR_ALREADY_EXISTS.
Уничтожение семафора
Для уничтожения семафора необходимо передать его идентификатор
функции CloseHandle. Заметим, что при завершении процесса все
созданные им семафоры уничтожаются автоматически.
Увеличение значения счетчика семафора
Для увеличения значения счетчика семафора приложение должно
использовать функцию ReleaseSemaphore. Синтаксис вызова функции
ReleaseSemaphore:
BOOL ReleaseSemaphore(Semaphore: HANDLE; cRelease: LONG;
lplPrevious LPLONG);
Функция ReleaseSemaphore увеличивает значение счетчика
семафора, идентификатор которого передается ей через параметр
Semaphore, на значение, указанное в параметре cRelease. С помощью
функции ReleaseSemaphore счётчик ресурсов, принадлежащий семафору,
можно увеличивать, более чем на единицу единовременно. Параметр
cRelease определяет, какими порциями должен освобождаться семафор.
Заметим, что через параметр cRelease можно передавать только
положительное значение, большее нуля. При этом, если в результате
увеличения новое значение счетчика должно будет превысить
максимальное значение, заданное при создании семафора, функция
ReleaseSemaphore возвращает признак ошибки и не изменяет значение
счетчика.
26
Предыдущее значение счетчика, которое было до использования
функции ReleaseSemaphore, записывается в переменную типа LONG.
Адрес этой переменной передается функции через параметр lplPrevious.
Если функция ReleaseSemaphore завершилась успешно, она
возвращает значение TRUE. При ошибке возвращается значение FALSE.
Код ошибки в этом случае можно определить, как обычно, при помощи
функции GetLastError.
Функция ReleaseSemaphore используется обычно для решения двух
задач.
Во-первых, с помощью этой функции задачи освобождают ресурс,
доступ к которому регулируется семафором. Они могут делать это после
использования ресурса или перед своим завершением.
Во-вторых, эта функция может быть использована на этапе инициализации мультизадачного приложения. Создавая семафор с начальным
значением счетчика, равным нулю, главная задача блокирует работу задач, выполняющих ожидание этого семафора. После завершения инициализации главная задача с помощью функции ReleaseSemaphore может
увеличить значение счетчика семафора до максимального, в результате
чего известное количество ожидающих задач будет активизировано.
Уменьшение значения счетчика семафора
В программном интерфейсе операционной системы Windows нет
функции, специально предназначенной для уменьшения значения
счетчика семафора. Этот счетчик уменьшается, когда задача вызывает
функцию ожидания WaitForSingleObject.
Синтаксис вызова функции WaitForSingleObject:
DWORD WaitForSingleObject(Semaphore: HANDLE; Milliseconds:
DWORD);
Функция WaitForSingleObject уменьшает значение счетчика
семафора, идентификатор которого передается ей через параметр
Semaphore. Параметр Milliseconds указывает интервал ожидания в
миллисекундах. Если параметру Milliseconds присвоено значение infinite,
то
интервал
ожидания
освобождения
принимается
равным
бесконечности.
Если задача вызывает несколько раз функцию ожидания для одного
и того же семафора, содержимое его счетчика каждый раз будет
уменьшаться.
Функция WaitForSingleObject используется, чтобы занять ресурс.
Определение текущего значения счетчика семафора
Единственная возможность определения текущего значения
счетчика семафора заключается в увеличении этого значения функцией
ReleaseSemaphore. Значение счетчика, которое было до увеличения,
27
будет записано в переменную, адрес которой передается функции
ReleaseSemaphore через параметр lplPrevious.
Заметим, что в операционной системе Windows не предусмотрено
средств, с помощью которых можно было бы определить текущее
значение семафора, не изменяя его. В частности, нельзя задать функции
ReleaseSemaphore нулевое значение инкремента lplPrevious, так как в
этом случае указанная функция просто вернет соответствующий код
ошибки.
Создание потоков в Windows
Потоки создаются и выполняются в родительском адресном
пространстве, то есть в адресном пространстве процесса, создавшего
потоки, но каждому потоку даётся свой стек. Потоки могут обмениваться
друг с другом информацией без ограничений.
Для создания потока используется функция CreateThread. Синтаксис
функции CreateThread:
HANDLE CreateThread(lpsa: LPSECURITY_ATTRIBUTES; cbStack:
DWORD;
lpStartAddr:
LPTHREAD_START_ROUTINE;
lpvThreadParam: LPVOID; fdwCreate: DWORD; lpIDThread: LPVOID);
Параметр lpsa – это указатель на структуру, задающую
нестандартные дескрипторы защиты. Значение параметра lpsa NULL
указывает на стандартные атрибуты защиты.
Параметр cbStack указывает размер стека для создаваемого потока.
Если указать нулевое значение, то размер стека по умолчанию будет
равен 1Мб, причём адресное пространство стека будет лишь
зарезервировано. Реально поток обычно получает одну страницу
оперативной памяти и при её переполнении происходит выделение
дополнительной памяти. Ограничение на резервирование памяти под стек
необходимо для прекращения деятельности функций с бесконечной
рекурсией.
Параметр lpStartAddr указывает адрес функции потока, с которой
должен будет начать работу создаваемый поток. Синтаксис функции
должен иметь следующий вид:
DWORD ThreadFunc(lpvThreadParam: LPVOID) {
…
return(dwResult);
}
Параметр, получаемый данной функцией ThreadFunc, есть
четвёртый параметр функции создания потока lpvThreadParam. Через
него можно передать некоторое 32-битное значение, или просто данные
или указатель на структуру, содержащую расширенную информацию.
28
Параметр fdwCreate – дополнительные флаги управления потоком.
Флаг – CREATE_SUSPENDED указывает системе создать поток и приостановить его выполнение. Возобновить выполнение потока может родительский поток вызовом функции ResumeThread(HANDLE), передав ей в
качестве параметра описатель, возвращённый функцией создания потока.
Вообще родительский поток может в любое время остановить поток
вызовом функции SuspendThread(HANDLE), и возвратить его к
выполнению приведённой выше функцией ResumeThread(HANDLE). В
структуре управления потоком есть переменная SuspendCount, в которой
запоминается
количество
вызовов
SuspendThread,
а
вызов
ResumeThread уменьшает эту переменную на единицу. Как только она
примет нулевое значение, поток возобновляет выполнение.
Последний параметр lpIDThread – адрес переменной типа двойного
слова, в которую будет записан уникальный идентификатор потока.
Описание программы «взаимоисключающий доступ»
Механизм семафоров, обеспечивающий взаимоисключающий доступ,
реализуется в среде Borland (Embarcadero) Delphi операционной системы
Windows. Проект (Semafore.dpr) состоит из двух модулей: основного
модуля Main.pasи модуля WriterThread.pas.
В основном модуле необходимо прописать процедуры создания
потоков и их начальные параметры, а также процедуры, обеспечивающие
отображение работы программы (графику) в двух режимах: «Числовые
параметры» и «Диаграмма работы». При инициализации основного
модуля создается семафор Semaphore с параметрами: начальное значение
счётчика равно 1, максимальное значение счётчика равно 1. Для создания
семафора вызывается функция Semaphore := CreateSemaphore(nil,
1,1,'NameSemaphore'). При нажатии на кнопку «Создать потоки»
создаются два потока, которые, получив соответствующие параметры,
запускаются и работают, пока не выполнят функцию Execute (основная
функция потока).
Модуль WriterThread.pas имитирует работу потоков. Работа потоков
должна быть оформлена в виде процедур Thread1.Execute и
Thread2.Execute. Процедура Execute для каждого потока содержит
следующие этапы:
1. Имитация работы потока вне критической секции в течение
заданного промежутка времени.
2. Ожидание освобождения семафора (время ожидания вычисляется и
выводится в поле «числовые параметры»).
3. Работа с критическим объектом. Время работы задается.
4. Освобождение семафора.
29
Примерный фрагмент модуля программы, содержащий процедуру
Execute, представлен на рис. 1. Результаты работы программы
отображаются в просмотровом окне в режимах "Числовые параметры"
(рис. 2) и "Диаграмма работы" (рис. 3).
procedure Thread1.Execute;
var
i,j,jj,kk: integer;
x1,x2:systemtime;
begin
i:=1;
randomize;
repeat
jj:=round(random(FSleepDuration)); - 1-й этап
sleep(jj);
getsystemtime(x1);
WaitForSingleObject(Semaphore , infinite); - 2-й этап
getsystemtime(x2);
UpdateGraphic(1,round(jj/k),30,cllime);
kk:=(x2.wMinute-x1.wMinute)*60000+(x2.wSecondx1.wSecond)*1000+(x2.wMilliseconds-x1.wMilliseconds);
if kk>0 then UpdateGraphic(1,round(kk/k),20,clblue);
if pos[3].X<pos[1].X then UpdateGraphic(3,(pos[1].X-pos[3].X),40,clYellow);
j:=round(random(FWorkSemafor));
sleep(j); - 3-й этап
x:=round(random(100))+1;
inc(i);
WriteText:='---1---'+IntToStr(Self.ThreadID)+'-----X='+inttostr(x)+'------Tраб.='+inttostr(jj)+'-------Tраб.кр.='+inttostr(j)+'-------Tож.='+inttostr(kk);
UpdateGraphic(1,round(j/k),40,clred);
UpdateGraphic(3,round(j/k),20,claqua);
ReleaseSemaphore(Semaphore, 1, nil); -4-й этап
Synchronize(UpdateListBox);
until i>10;
end;
Рис. 1
30
В программе, отображающей процедуру Execute, используются
переменные целого типа i, j, jj, kk, а также переменные системного
времени (systemtime) x1 и x2. Переменная i=10 задает для каждого потока
по 10 обращений к критической секции.
Работа потока вне критической секции имитируется обращением к
генератору равномерно распределенных случайных чисел FSleepDuration
(1-й этап). Число, выработанное этим генератором, присваивается
переменной jj, которая определяет длительность работы потока перед
обращением к критическому объекту (sleep(jj)). В режиме вывода
числовых параметров (рис. 2) значение переменной jj отображает
переменная Tраб..
Момент обращения к семафору фиксируется в переменной x1
вызовом функции системного времени: getsystemtime(x1).
Для доступа к критическому объекту (2-й этап) производится вызов
функции WaitForSingleObject(Semaphore , infinite). Время ожидания
освобождения семафора принимается равным бесконечности. Момент
срабатывания семафора, обеспечивающего доступ процесса к
критическому объекту, фиксируется в переменной x2 вызовом функции
системного времени: getsystemtime(x2). Разность моментов времени x1 и
x2 определяет интервал времени ожидания освобождения критической
секции. Эта разность присваивается переменной kk и отображается в
режиме "Числовые параметры" с помощью переменной Tож. (рис. 2).
Рис. 2
Работа потока с критическим объектом (3-й этап) имитируется
обращением к генератору равномерно распределенных случайных чисел
FWorkSemafor. Число, выработанное этим генератором, присваивается
31
переменной j, которая определяет длительность работы потока с
критической секцией. В режиме "Числовые параметры" (рис. 2) значение
переменной j отображает переменная Tраб.кр. Новое значение переменной
x, присваемое потоком критическому объекту, вырабатывается
обращением к генератору равномерно распределенных случайных чисел в
диапазоне от 1 до 100.
Освобождение семафора (4-й этап) осуществляется вызовом функции
ReleaseSemaphore(Semaphore, 1, nil).
Фрагмент числовых характеристик, отображаемых в окне режима
«Числовые параметры», представлен в табл. 1.
Таблица 1
№
потока
1
2
Идентификаторы
потоков
в операционной
системе
916
864
X
Tраб.
Tраб.кр.
Tож.
31
8
511
338
460
85
20
120
Здесь 916, 864 – текущие идентификаторы потоков в операционной
системе, X – текущее значение защищённой переменной, Tраб. – время
работы потока вне критической секции, Tраб.кр. – время работы потока в
критической секции, Tож. – время ожидания освобождения семафора.
Рис. 3. Фрагмент временной диаграммы
Варианты индивидуального задания
Предполагается, что каждый из двух взаимодействующих с
критическим объектом потоков должен обратиться к нему
последовательно три раза, при этом длительности работы потоков вне
критической секции (Tраб) и в критической секции (Tраб.кр.) задаются
вариантом индивидуального задания. Вариантом индивидуального
задания (табл. 2) также задается значение числа X , записываемое
процессом при его работе с критической секцией.
32
Таблица 2
№
варианта
0
1
2
3
4
5
6
7
8
9
Траб
700
100
200
50
100
200
135
325
550
300
200
100
250
220
50
100
50
40
225
125
100
90
25
300
700
200
300
500
125
100
1 процесс
Траб.кр.
100
150
50
30
75
100
100
200
300
100
50
75
120
125
25
50
100
20
25
40
60
50
10
20
200
100
150
100
50
25
Х
10
20
30
15
20
45
13
26
39
30
40
60
60
25
15
75
80
95
40
20
10
12
32
43
56
7
6
8
14
56
Траб
750
125
80
15
25
450
200
400
500
350
100
150
270
200
40
125
75
50
200
20
50
100
10
300
800
100
250
550
40
75
2 процесс
Траб.кр.
300
250
400
100
200
300
175
275
250
75
75
50
100
55
10
75
25
20
100
20
50
30
10
50
300
50
125
150
20
60
Х
25
50
75
33
44
55
23
33
43
56
76
36
12
15
16
45
55
75
80
90
10
14
24
34
55
66
77
57
37
87
Порядок выполнения работы
Нарисовать временные диаграммы работы потоков согласно
индивидуальному варианту (см. рис. 3) и на их основании определить
времена ожидания освобождения семафора. Подготовить листинги
33
программ procedureThread1.Execute и procedure Thread2.Execute,
соответствующие индивидуальному варианту работы потоков. Изменения
состоят в следующем:
1. Убирается цикл работы (переменная i и связанные с ней
операторы).
2. При работе потока вне критической секции (1-й этап, рис. 1)
переменной jj присваивается значение Траб, соответствующее
длительности работы потока вне критической секции.
3. При работе потока с критической секцией (3-й этап, рис.1)
переменной j присваивается значение Траб.кр., соответствующее
длительности работы потока в критической секции в первом цикле,
переменной xприсваивается заданное значение защищенной переменной.
4. Программа Execute должна повторять действия 2 и 3 три раза и
включать соответствующие процедуры отображения хода работы.
Вызвать файл Writer Thread.pas, содержащий исходный модуль,
задающий работу потоков, и внести изменения в процедуры
Thread1.Execute и Thread2.Execute. Откомпилировать измененный
исходный модуль WriterThread.pas. Запустить на выполнение основной
модуль Main.pas.
Результаты решения просмотреть в режиме "Числовые параметры" и
сопоставить с заданными условиями работы. Сделать выводы.
Отчет по лабораторной работе должен включать:
1) Вариант индивидуального задания.
2) Временные диаграммы работы потоков, построенные при
домашней подготовке.
3)
Листинги
фрагментов
программ
Thread1.Execute
и
Thread2.Execute, содержащие изменения согласно индивидуальному
варианту задания.
4) Результаты решения, представленные в режиме «Числовые
параметры».
5) Выводы по работе.
Контрольные вопросы
Определите термины «ресурс» и «разделяемый ресурс».
В чем состоит проблема взаимного исключения?
Какие участки программ называется критическими секциями?
Перечислите требования, которым должны удовлетворять
механизмы взаимоисключения.
5. Что понимается под состоянием взаимоблокировки?
1.
2.
3.
4.
34
6. Что представляет собой семафор?
7. Определите P-операцию над семафором.
8. Определите V-операцию над семафором.
9. Что означает неделимость P- и V-операций?
10. Как защитить критическую секцию семафором?
11. Какие действия выполняются операционной системой, если в
коде задачи встречаются операции Р и V?
12. Как создается семафор в операционной системе Windows?
13. Как в операционной системе Windows производится
увеличение и уменьшение значения счетчика семафора?
14. В чем разница между процессами и потоками?
15. Как решается задача взаимоисключения с помощью механизма
семафора?
35
Лабораторная работа 3
УСТАНОВКА СИСТЕМЫ QNX6. РАБОТА В ОБОЛОЧКЕ PHOTON
Цель работы – изучение процессов установки ОС QNX и среды
разработки Momentics, а также изучение графической оболочки Photon
microGUI.
Теоретический материал
Установка QNX
Операционная система QNX является системой жесткого реального
времени и ориентирована прежде всего на рынок встраиваемых систем.
Для коммерческого использования доступны три семейства:
 QNX2 – предназначена для применения с процессорами Intel 286 и
мало распространена в России, т. к. до 1991г. она была запрещена к
вывозу из США;
 QNX4 – может применяться на процессорах не ниже Intel 386,
последняя версия – 4.25, вышла в 2003 г.;
 QNX6 (или QNX Neutrino) может использоваться не только на x86совместимых ЭВМ, но и на PowerPC, MIPS32, ARM, Xscale и др. Она
полностью поддерживает стандарт POSIX, последняя версия QNX
Neutrino – 6.4.
Комплект разработчика, в который входит QNX Neutrino и средства
разработки приложений, имеет название QNX Momentics. В настоящее
время выпущено несколько дистрибутивов QNX Momentics:
 Non-Commercial Edition (NC) – бесплатный ознакомительный
комплект разработчика для некоммерческого использования;
 Standart Edition (SE) – пакет разработчика для коммерческого
применения, позволяет формировать целевые системы для различных
платформ;
 Professional Edition (PE) – расширенный пакет разработчика для
коммерческого применения, дополненный интегрированной средой
разработки Eclipse и расширенной базой данных примеров исходных
программ и рядом дополнительных компонентов.
Для использования QNX Momentics PE требуются:
 процессор не ниже Pentium III 700 Мгц (желательно Pentium IV не
ниже 2 Ггц);
 ОЗУ – не менее 256 Мбайт (желательно 512 Мбайт);
 свободное дисковое пространство – не меньше 2 Гбайт.
36
QNX6 может быть установлена двумя способами – в отдельный
раздел жесткого диска или в среду виртуальной машины (например,
VMWare). При установке в собственный раздел жесткого диска перед
началом установки необходимо выполнить дефрагментацию жесткого
диска и с помощью любого дискового редактора (например Acronis
DiskEditor или PartitionMagic) уменьшить размер одного из
существующих разделов диска.
Порядок инсталляции системы в отдельный раздел диска:
 провести загрузку компьютера с дистрибутивного диска QNX;
 выбрать вариант инсталляции QNX в отдельный раздел диска
(F3 – Install QNX to a new disk partition);
 принять условия лицензии после появления на экране информации о
лицензировании (F1 - accept);
 выбрать место размещения раздела QNX на диске (F1 – раздел может
находиться в любом месте диска, F2 – раздел QNX должен
находиться в пределах 8,4 Гбайт);
 выбрать объем раздела QNX (F1 – 100 % свободного места на диске;
F2 – 50 %, F3 – 25 %, F4 – 12,5 %);
 выбрать вариант загрузчика операционной системы (F1 – загрузчик
QNX в случае ее установки за пределами 8,4 Гбайт, F2 - загрузчик
QNX в случае ее установки в пределах 8,4 Гбайт, F3 – оставить
прежний загрузчик).
Установка в среду виртуальной машины VMWare Server проводится
следующим образом:
– создать виртуальную машину QNX6.3, выбрав в предлагаемом
списке операционных систем пункт «Other» и задав размер минимального
дискового пространства не менее 2 Гб;
– установить QNX6.3 c установочного диска или из предварительно
созданного iso-образа этого диска, рекомендуемая последовательность
ответов – F3, F1, F1, F1, F1, F3, F2, на все вопросы отвечаем Y.
После завершения установки и перезагрузки необходимо:
 настроить параметры видеорежима (разрешение монитора и
глубину цвета) и частоту обновления экрана;
 установить возможность ввода символов кириллицы для приложений графической оболочки Photon (Localization/Keyboard);
 вручную внести изменения в файл
/etc/system/trap/.KEYBOARD.myname так, чтобы он содержал
следующие строки
en_US_101.kbd
ru_RU_102.kbd
37
здесь myname – имя Вашего компьютера. После этого в системе будет
зарегистрирован только один пользователь с именем root, не имеющий
пароля.
 настроить параметры сетевого подключения (например, при
работе в компьютерном классе необходимо на закладке Devices
указать любой адрес из сети 192.168.200.0..24, а в качестве
шлюза и Name-сервера на закладке Network указать адрес
192.168.200.254) и провести его проверку с помощью команды
ping.
 настроить виртуальную машину для работы с USB –
устройствами.
Виртуальная машина позволяет гостевой ОС работать с
реальными USB устройствами. Для этого необходимо дополнить
файл конфигурации виртуальной машины, расположенный в
указанном при инсталляции QNX каталоге
(C:\Virtual
Machines\%ИмяВиртМашины%\%ИмяВиртМашины%.vmx)
следующими строками:
usb.present = “TRUE”
usb. autoConnect.device0 = “”
uchi.newcore = FALSE
Обратите внимание: некоторые из этих строк уже могут быть в Вашем
файле конфигурации и при их повторении будет возникать ошибка.
Подключение USB-устройства проводится в меню виртуальной
машины выбором пункта VM/Removable Devices, при этом указанное
устройство отключается от ОС Windows. Проверить подключение
устройства можно с помощью команды mount.
Работать в QNX можно двумя способами: в командной строке и в
графической оболочке Photon. При работе в оболочке командная строка
все равно доступна с помощью программы псевдотерминала pterm.
Обратите внимание: в командном режиме работа с символами
кириллицы невозможна, для решения этой проблемы необходимо
использовать пакет SWD Cyrillic Pack.
Работа в командном режиме
Интерфейс командной строки является стандартным UNIXинтерфейсом и обеспечивается командным интерпретатором Korn Shell.
При загрузке системы командный интерпретатор Korn Shell
выполняет команды, содержащиеся в файле /etc/profile, затем команды,
содержащиеся в файле $HOME /.profile (разумеется, если эти файлы
существуют и доступны для чтения). Для того, чтобы при входе в систему
автоматически устанавливались необходимые переменные, следует
38
откорректировать файл .profile, находящийся в вашем домашнем
каталоге. Для этого можно воспользоваться редактором vi или его
клонами (vim, elvis). Редактор запускается следующим образом: vi
имя_файла. Если файл с именем имя_файла не существует, то он будет
создан. Редактор vi может находиться в одном из двух режимов: в режиме
ввода или в командном режиме. После запуска редактор находится в
командном режиме. Переход из командного режима в режим ввода
осуществляется нажатием клавиши <I> (input) или <А> (add). После
нажатия клавиши <I> текст будет вводиться с текущей позиции курсора, а
после нажатия клавиши <А> текст будет вводиться со следующей
позиции после курсора. Возврат в командный режим осуществляется
нажатием клавиши <Esc>. Сводка по основным командам редактора
приведена в таблице 8
Таблица 8
Команда
Назначение
Позиционирование курсора
H
Перейти в начало файла
L или G
Перейти в конец файла
{n}G
Перейти на строку с номером n
Работа с буфером
yy
Скопировать текущую строку в буфер
{n}yy
Скопировать n строк, начиная с текущей, в буфер
dd
Удалить текущую строку в буфер
{n}dd
Удалить n строк, начиная с текущей, в буфер
p
Вставить содержимое буфера после текущей строки
P
Вставить содержимое буфера перед текущей строкой
Редактирование текста
o
Вставка пустой строки после текущей строки
O
Вставка пустой строки перед текущей строкой
i
Вставка произвольного текста перед курсором
a
Вставка произвольного текста после курсора
cw
Замена слова
dw
Удаление слова
{n}dw
Удаление n слов
u
Отмена действия последней команды
Команды режима командной строки
:w
Сохранить изменения
:w имя_файла
Сохранить изменения в указанном файле
:q
Выход
:q!
Выход без сохранения
:s/строка_1/строка_2 Поиск и замена строки «строка_1» на «строка_2»
:help
Вызов справки
39
Работа в графической оболочке Photon microGUI
Графический интерфейс Photon содержит окна, поля редактирования,
флажки, кнопки и напоминает интерфейс системы Windows. В правой
части главного окна находится панель быстрого запуска приложений
Shelf. Оболочка дает возможность удобного вызова следующих
программ:
 файловый менеджер (pfm);
 текстовый редактор (ped);
 псевдотерминал для работы в режиме командной строки
(pterm);
 программу просмотра документации (helpviewer);
 программу конфигурирования;
 интегрированную среду программирования.
При запуске Photon выполняет скрипт, находящийся в файле
$HOME/.ph/phapps, в котором можно перечислить приложения для
автоматического запуска. Этот файл, конечно, должен иметь атрибут
«исполняемый» для владельца.
Интерфейс
файлового
процессора
является
классическим
двухоконным. Для настройки параметров используется меню
Edit/Preferences
(F10). При этом можно задать необходимость
вывода
скрытых
файлов
(Hide ‘dot’ Files), подтверждение на
выполнение операций копирования, перемещения или удаления
файлов и т. д.
Для настройки реакции на нажатие ENTER имеется меню Edit/
Associations (F11) , где в появившемся окне можно задать обработчики
для открытия (Open), просмотра(View) или редактирования (Edit) файлов,
имена которых соответствуют определенным шаблонам. Атрибуты
любого файла можно просмотреть через пункт Inspect контекстного меню
или F9.
Текстовый редактор ped имеет стандартный набор функций:
редактирование текста, работа с буфером, поиск и замена строк и т. д. В
режиме настройки можно установить кодовую страницу, параметры
шрифтов, режим переноса строк.
Основы администрирования
Информация о рабочих группах и пользователях системы QNX
содержится в нескольких файлах (см. таблицу 9):
40
Таблица 9
Имя файла
/etc/group
/etc/passwd
/etc/shadow
Назначение
Информация о
рабочих группах
Информация о
пользователях
Информация о
паролях
Структура записи файла
groupname: reserved: group: member
username: haspw: userid: group:
comment: homedir: shell
username: password: lastch: minch:
maxch: warn: inact: expire: reserved
Здесь обозначено:
groupname – имя группы;
reserved – зарезервировано для дальнейшего использования;
group – имя группы;
member – список имен пользователей, принадлежащих к данной
группе;
username – имя пользователя;
haspw – признак наличия пароля (если поле не пустое, то в файле
/etc/shadow хранится пароль);
userid – идентификатор пользователя;
comment – любая символьная строка, не содержащая символ «:»;
homedir – имя домашнего каталога пользователя;
shell – имя командного интерпретатора, вызываемого при входе
пользователя;
password – зашифрованный пароль пользователя;
lastch – время последней модификации;
minch – минимальное количество дней для модификации;
maxch - максимальное количество дней для модификации;
warn – количество дней для предупреждения;
inact – максимальное количество дней между входами в систему;
expire – дата истечения доступа.
Все поля записей являются позиционными, т. е. если какое-либо поле,
кроме последнего, не указывается, то символ – разделитель (двоеточие)
должен обязательно присутствовать.
Добавление новой группы проводится путем редактирования файла
/etc/group с помощью редактора vi. Добавление нового пользователя
проводится командой passwd, которая предложит администратору ввести
регистрационные данные для этого пользователя. Удаление пользователя
из системы проводится путем простого редактирования файлов
/etc/passwd и /etc/shadow.
При внесении изменений в файлы администрирования старые версии
этих файлов сохраняются в файлах /etc/opasswd и /etc/oshadow.
41
Порядок выполнения работы
1. Дефрагментировать жесткий диск компьютера.
2. Установить QNX6 в созданный раздел размером не менее 2 Гбайт или в
виртуальную машину VMWare.
3. Запустить QNX Installer и установить с дистрибутивного диска
программное обеспечение разработчика QNX Momentics из репозитария
fs/cd0/rep621.
4. С помощью программы конфигурирования настроить графический
режим монитора (Graphics) , установить параметры сетевого соединения
(Network) и включить возможность работы с кириллицей (Language).
5. С помощью текстового редактора vi создать текстовый файл и
выполнить с ним все действия согласно табл. 8.
6. Войти в графическую оболочку Photon и выполнить следующие
действия:
6.1 Установить для файлового процессора следующие режимы:
– включить режим показа скрытых файлов;
– для файлов типа .doc, .txt обеспечить при нажатии ENTER
загрузку редактора ped.
6.2. Выполнить с помощью файлового процессора типовые операции
над каталогами и файлами (создание, копирование, перемещение,
удаление).
7. Создать с помощью редактора Ped текстовый файл и выполнить с ним
все типовые операции редактирования текста (поиск, замена, работа с
буфером и т.д.). Изучить настройки редактора.
8. Создать группу пользователей mygroup путем редактирования файла
/etc/group и добавить в нее нового пользователя newuser, присвоив ему
любой пароль.
9. Войти в систему под именем newuser и определить имя текущего
каталога.
10. Удалите пользователя newuser из системы и попробуйте войти под его
именем.
Контрольные вопросы
1. Назовите основные этапы установки QNX.
2. Какие версии QNX Momentics существуют и чем они отличаются друг
от друга.
3. Основные команды редактора vi.
4. Основные компоненты графической оболочки Photon.
5. Назначение и состав файлов администрирования системы.
42
Лабораторная работа 4
КОМАНДЫ ОПЕРАЦИОННОЙ СИСТЕМЫ QNX
Цель работы – изучение командного режима ОС QNX
Теоретический материал
Общие сведения
Все команды QNX исполняются командным интерпретатором и
делятся на две группы – внутренние и внешние. Внутренние команды
выполняются непосредственно интерпретатором, а внешние команды
реализуются программами, поставляемыми вместе с операционной
системой в виде отдельных исполняемых файлов. Определить тип
команды можно с помощью команды type.
Система QNX содержит несколько командных интерпретаторов
(shell):
 Korn Shell (ksh) – стандартный интерпретатор;
 Embedded Shell (esh) – встраиваемый интерпретатор;
 Micro embedded Shell (uesh) – малый встраиваемый
интерпретатор;
 Fat embedded Shell (fesh) – расширенный встраиваемый
интерпретатор.
Встраиваемые интерпретаторы предназначены для использования в
системах реального времени с ограниченными ресурсами.
Набор команд QNX аналогичен набору команд ОС LINUX,
использующей интерпретаторы sh, bash и csh; команды могут содержать
аргументы и ключи. Синтаксис команды удобно представить в
обобщенной форме:
<команда>::= имя команды [ключи] [аргументы]
Имя команды указывает действие, которое будет выполняться,
аргументы – это имена объектов, над которыми будет выполняться
действие, а ключи уточняют действие команды. В качестве аргументов
команды используются имена файлов и каталогов, в качестве ключей –
одиночные символы, перед которыми обычно записывается символ «-»
(дефис). Обязательным элементом в командной строке является только
имя команды, аргументы и ключи могут отсутствовать. Ключи и
аргументы команды разделяются пробелами.
43
Файловая система
Файловая система QNX, так же как и в LINUX, характеризует полную
совокупность всех файлов, каталогов и устройств. Отдельные части
файловой системы могут находиться на различных физических
устройствах, например, на нескольких жестких и гибких дисках (или в
различных частях одного диска). Соответствующие фрагменты
(поддеревья файловой системы) монтируются (присоединяются) в
единую файловую систему командой mount, после чего пользователь
может обращаться к любым доступным файлам, при этом в имени никак
не отражается устройство, на котором файл находится или создается.
Имена каталогов и файлов в QNX являются регистрозависимыми, т. е.
имена newfile и NEWFILE обозначают разные файлы. Имена всех файлов
могут быть абсолютными или относительными. Абсолютное имя
начинается от корневого каталога файловой системы root (обозначается
символом «/»), а относительное имя – от текущего каталога. Имя файла
или каталога не может превышать 255 символов, а полное имя (с учетом
пути доступа) – не более 1023 символов. Если имя файла начинается с
символа «.» (точка), такой файл называется скрытым и его
характеристики не будут выводиться при просмотре содержимого
каталогов.
Каждый объект файловой системы (файл или каталог) имеет
определенные права доступа для каждой категории пользователей.
Информация о правах доступа выводится командой ls –l. При этом самый
первый символ строки, соответствующей каждому файлу, обозначает тип
файла (дефис – это обычный файл, d – каталог, l – символическая ссылка,
b – блок – ориентированный файл ). Далее в строке идут три группы,
состоящие из трех символов каждая: первая группа определяет права
владельца, вторая – права группы пользователей, третья – права
остальных пользователей. В каждой из этих групп первый символ
определяет право доступа для чтения, второй – для записи, третий – для
выполнения файла. Если в какой-либо позиции строки выводится символ
«-» (дефис), то соответствующее право доступа отключено.
Управление правами доступа проводится с помощью команды chmod.
В качестве аргументов команды указываются права доступа (может
использоваться цифровое или символьное указание прав доступа) и имя
файла или каталога. При цифровом указании каждая группа символов
представляется 8-ричным кодом, при символьном – используется
следующая система обозначений:

категории пользователей – владелец объекта (u), группа
пользователей (g), остальные пользователи (o), все пользователи (a);
44
 права доступа – чтение (r) , запись (w), выполнение (x).
Например, для добавления права чтения и записи в файл file1 членам
группы пользователей и права чтения для всех остальных пользователей
можно использовать команды chmod g+rw,o+r file_1 или chmod 764
file_1.
Управление процессами
Процесс – это выполняющаяся программа. Для каждого процесса
можно выделить образ и метаданные. Образ процесса – это совокупность
кода (команды программы) и данных (операнды команд). Метаданные –
информация о процессе, которая хранится в структурах данных ОС.
Командный интерпретатор выполняет команды последовательно одна
за другой. Если необходимо запустить какую-либо команду до того, как
завершится предыдущая команда, то надо использовать фоновый режим
исполнения процесса. Для этого в конце команды указывается символ
“&” , например для поиска файлов можно использовать команду find / –
name mydoc* -print &
Просмотр списка выполняющихся процессов проводится командой
ps, удаление заданного процесса – командой kill. Подробная информация
по всем процессам и потокам, существующим в системе, может быть
получена с помощью команды pidin или интерактивной программы psin
(аргументы процессов; переменные окружения, приоритеты, выделенные
адреса памяти и т.д.).
В командной строке можно записывать несколько команд, используя
следующие разделители:
; – команды исполняются последовательно;
| – команды исполняются последовательно, данные с выхода
предыдущей команды передаются на вход следующей команды;
&& – команды исполняются последовательно до тех пор, пока одна
из команд не сможет выполниться;
|| – команды исполняются последовательно до тех пор, пока одна из
команд выполнится.
Стандартные переменные командного процессора
При работе в командной строке важную роль играют глобальные
переменные, которые хранятся в специальной области памяти,
называемой областью окружения. Список некоторых из этих переменных
приведен в таблице 10.
45
Таблица 10
Имя переменной
LOGNAME
HOME
PATH
LD_LIBRARY_PATH
PWD
PS1
SHELL
?
$
Назначение
Имя пользователя
Имя домашнего каталога пользователя
Перечень каталогов для поиска исполняемых файлов
Перечень каталогов для поиска динамических
библиотек
Имя текущего каталога
Вид первичного приглашения
Путь к командному процессору
Код завершения последней выполненной команды
Идентификатор текущего процесса
Для работы с переменными окружения предназначены следующие
команды из таблицы 11:
Таблица 11
Команда Назначение
Set
вывод содержимого области окружения
export
экспорт переменной в дочерние
процессы командного процессора
echo
вывод значения переменных
Пример
set
export
TOWN=Новосибирск
echo $TOWN
Обратите внимание: при обращении к переменной окружения
необходимо перед именем поставить символ «$».
Команды QNX
Информация по наиболее часто используемым командам QNX
приведена в таблице 12.
Таблица 12
Команда
1
Touch
Cp
Mv
cat
Назначение
Пример
Действие
2
3
4
1. Команды для работы с файлами
Создание пустого
touch myfile
Создает файл myfile в
файла
текущем каталоге
Копирование файла
cp file1 file2
Копирует файл file1 в фал
file2 в текущем каталоге
Переименование или mv file_1 file_2 Переименование файла
перемещение файла
file_1 в файл file_2 в
текущем каталоге
Вывод содержимого cat file1
Выводит содержимое
файла на экран или
cat file1 file2 >
файла file1.
конкатенация файлов file3
Объединяет содержимое
файлов file1 и file2 в файл
file3
46
Продолжение табл. 12
1
rm
ln
more
wc
sort
find
md
cd
rmdir
rm -r
ls
pwd
cp -r
2
Удаление файла
3
4
rm file1
Удаляет файл file1 из
текущего каталога
Создание ссылки на
ln folder1/file1
Создает ссылку на файл
файл
intro
folder1/file1 в текущем
каталоге с именем intro.
Постраничный вывод more myfile
Выводит постранично
файла
файл myfile
Вывод числа
wc –c file1
Выводит число символов
символов или строк в wc –l file2
в файле file1.
файле
Выводит число строк в
файле file1
Сортировка записей
sort –n –k 4
Сортировка записей
файла
myfile
файла myfile по
четвертому полю
Поиск файлов
find inf*
Выводит имена всех
файлов текущего
каталога, имена которых
начинаются на “inf”
2. Команды для работы с каталогами
Создание каталога
md folder1
Создает каталог folder1 в
текущем каталоге
Переход в заданный
cd /root/alex
Делает текущим каталог
каталог
cd ..
/root/alex.
cd
Делает текущим
родительский каталог.
Делает текущим
домашний каталог.
Удаление каталога
rmdir
Удаляет каталог
/tmp/myfolder
/tmp/myfolder
Удаление непустого
rm –r mydir
Удаляет каталог mydir
каталога
вместе со всеми его
файлами
Вывод содержимого ls –l
Выводит текущий
каталога
каталог на экран в
полном формате
Вывод имени
Pwd
Выводит имя текущего
текущего каталога
каталога на экран
Копирование
cp –r mydir /tmp Копирует каталог mydir в
каталога
каталог /tmp
47
Продолжение табл. 12
3. Команды для работы с файловой системой
chmod
Управление правами chmod 755 file1 Для владельца файла file1
доступа
включает все права, для
группы и для всех других
пользователей – права на
чтение и выполнение,
mount,
Подключение
mount
Монтирование раздела
umount
(отключение) к
/dev/hd0t77
/dev/hd0t77 в каталог
файловой системе
/fs/qnx4
/fs/qnx4
нового устройства
<, >, >>, | Переназначение
pwd > namedir
Выводит имя текущего
ввода и вывода,
каталога в файл namedir
организация
конвейера команд
du
Вывод размера
du
Выводит общий размер
каталога или файла
текущего каталога и всех
подкаталогов
df
Вывод информации
df
Выводит информацию по
по использованию
всем смонтированным
дискового
разделам файловой
пространства
системы
смонтированных
разделов
4. Управление сеансом работы пользователя
passwd
Изменение пароля
Passwd
Изменяет пароль
пользователя
пользователя в
интерактивном режиме
ps
Вывод информации о ps
Выводит информации о
работающих
всех работающих в
процессах
данный момент
процессах
kill
Уничтожение
Kill 1245
Уничтожает процесс с
процесса
заданным
идентификатором
who
Вывод списка
Who
Выводит список
активных
активных пользователей
пользователей
id
Вывод
id
Выводит идентификатор
идентификатора
пользователя
пользователя
48
Окончание табл. 12
1
use
pidin
uname
2
Вывод справки по
заданной команде
3
use ls
Вывод подробной
pidin
информации о
работающих
процессах и потоках
Вывод информации о uname -sr
версии операционной
системы
4
Выводит справку по
команде ls
Выводит подробную
информацию о
работающих процессах и
потоках
Выводит номер версии
установленной
операционной системы
В командах при указании имен файлов можно использовать
метасимволы:
* – заменяет любое количество символов;
? – заменяет любой одиночный символ;
[список символов] – заменяет одиночный символ из указанного
списка или диапазона символов;
Порядок выполнения работы
1. Определить имя текущего пользователя и текущего каталога.
2. Подключить к заданному узлу файловой системы гибкий диск
mount –t dos /dev/fd0 <путь монтирования>.
3. Используя команды работы с файлами и каталогами, сделать
следующее:
 создать на дискете каталог cop;
 скопировать в него несколько файлов с жесткого диска;
 вывести содержимое каталога cop на экран, изучить структуры
записей каталога о файлах и подкаталогах и занести их в отчет;
 используя команду переназначения, записать каталог cop в файл
dirdat корневого каталога дискеты;
 отсортировать содержимое файла dirdat по алфавиту, по размеру и
по дате последнего изменения файлов;
 с помощью конвейера вывести имена подкаталогов Вашего
домашнего каталога на экран;
 удалить два файла из каталога cop;
 удалить подкаталог cop;.
 удалить файл dirdat
49
4. Дайте возможность пользователям просматривать каталоги командой
dir вместо ls.
5. Посмотреть информацию по активным процессам и потокам
командами pidin и psin, сравнить эти команды.
6. Создать новую группу пользователей myguest путем редактирования
файла /etc/group.
7. Создать нового пользователя в группе myguest, присвоить ему логин и
пароль. Проверить вход в систему под именем нового пользователя.
8. Просмотреть значения глобальных переменных, занести их в отчет.
Записать новую переменную town, присвоить ей значение
«Новосибирск».
9. Изменить файл /etc/profile так, чтобы при каждой загрузке ОС дискета
была смонтирована.
10. Размонтировать дискету.
11. Определить тип следующих команд: cd, find, ls, grep.
12. Очистить экран монитора (clear).
13. Определить номер версии операционной системы.
Контрольные вопросы
Спецификация файла, использование метасимволов "*", "?".
Понятие внешних и внутренних команд.
Синтаксис команд, основные соглашения об именах устройств.
Имя каталога, имя пути. Синтаксис и назначение.
Синтаксис и назначение команд работы с каталогами и файлами.
Поясните по протоколу.
6. Синтаксис и назначение команд переназначения ввода/вывода и
фильтрации. Поясните по протоколу работы.
1.
2.
3.
4.
5.
50
Лабораторная работа 5
КОМАНДНЫЕ СЦЕНАРИИ ОПЕРАЦИОННОЙ СИСТЕМЫ QNX
Цель работы – изучение языка командных сценариев.
Теоретический материал
Общие сведения
Командный сценарий (shell script, командный файл) – это
исполняемый текстовый файл, состоящий из команд интерпретатора.
Сценарий часто называют shell–программой и используют для
администрирования и управления ОС. Командный язык интерпретатора
QNX, в отличие от интерпретатора Windows, является языком высокого
уровня, поддерживающим ввод-вывод переменных, ветвления (if-thenelse, case), циклы (for, while, until), работу с функциями, пошаговый
режим отладки и т. д.
Запуск сценария может проводиться несколькими способами:
– запустить интерпретатор, указав ему в качестве аргумента имя
файла сценария, например: sh myscript.sh;
– запустить интерпретатор, связав стандартный поток ввода с
именем файла сценария, например sh < myscript.sh;
– выполнить сценарий в текущем экземпляре shell командой
«точка», например: . myscript.sh;
– установить для сценария файловый атрибут «исполняемый» и
запустить как обычную программу: chmod 755 myscript.sh
myscript.sh.
При написании сценария необходимо соблюдать следующие
требования:
 в первой строке обычно указывается имя интерпретатора,
который будет исполнять сценарий, например
#!bin/ksh
 в конце сценария обязательно вводится символ перевода
строки (нажатие ENTER).
Переменные и параметры сценария
Имена переменных, используемых в сценариях, представляют собой
последовательность букв, цифр и символов подчеркивания. Имя должно
начинаться с буквы или символа подчеркивания. Переменные задаются
сразу со значением (возможно с пустым) и всегда имеют строковый тип.
51
При объявлении переменной ее имя и значение должны быть записаны
без пробелов относительно символа равенства. Например, создадим две
переменные, присвоив переменной VAR_1 значение “245”, а переменной
VAR_2 пустое значение:
VAR_1=245
VAR_2=
Если для переменной задается значение, содержащее пробелы, то
нужно заключать его в кавычки:
VAR_3=”program files”
В качестве значения переменной можно присвоить результат
выполнения команды QNX, указав имя команды в обратных кавычках
(обычно этот символ находится на клавише для ввода символов «~» и
«ё»):
VAR_2=`pwd`
Значения переменным можно задать в диалоговом режиме, например
команда read A B C присваивает значения переменным A, B и C.
Вывод значений переменных экран проводится командой echo с
указанием символа “$” перед именем переменной:
echo $VAR_3.
При необходимости выполнения конкатенации значения переменной
и произвольной символьной строки используются фигурные скобки:
echo ${VAR_3}binary.
Командный
интерпретатор
имеет
возможность
обработки
переменных как целых чисел, выполняя операции сложения (+),
вычитания (-), умножения (*), целочисленного деления (/) и получения
остатка от деления (%):
A=`expr $x + $y`; B=`expr $x - $y`
C=`expr $x / $y`; D=`expr $x % $y`; E=`expr $x “*” $y`
Обратите внимание: символ умножения должен быть взят в кавычки,
т. к. это служебный символ интерпретатора, а имена переменных и знаки
операций всегда разделяются пробелами.
Все переменные доступны только в том процессе shell , в котором они
были объявлены. Для того, чтобы к переменным можно было обращаться
из дочерних процессов, их следует экспортировать. Возможны два
способа экспорта:
 при создании переменной (например, export VAR_4=myfolder);
 после создания переменной (например, export VAR_4).
Команда
export
без
аргументов
выводит
список
всех
экспортированных переменных интерпретатора.
Интерпретатор поддерживает определенный набор внутренних
переменных, список которых приведен в таблице 13.
52
Сценарий может принимать аргументы, используя механизм
формальных и фактических параметров. Для описания формальных
параметров в программе используются специальные переменные, имена
которых состоят из одной цифры в диапазоне от 0 до 9. При этом
переменная 0 содержит имя сценария, переменная 1 – значение первого
фактического параметра, переменная 2 – второго фактического параметра
и т. д. Общее число параметров содержится в переменной #.
Таблица 13
Переменная
?
$
!
#
*
@
-
Значение
Код завершения последней команды
(0 – нормальное завершение)
PID текущего процесса shell
PID последнего фонового процесса
Число параметров, переданных в shell
Список параметров shell в виде одной строки
Список параметров shell в виде набора слов
Флаги, передаваемые в shell
Если число фактических параметров превышает число формальных
параметров, то в сценарии можно использовать команду shift , которая
сдвигает список формальных параметров относительно списка
фактических параметров на заданное количество позиций.
Операторы языка Korn Shell
1. Оператор TEST
Оператор test проверяет выполнение заданного условия и возвращает
логическое значение «истина» (0) или «ложь» (1).
Общий формат оператора:
test условие или [ условие ]
Обратите внимание: вторая форма записи оператора предусматривает
наличие обязательных пробелов между квадратными скобками и
выражением условия. Если строка условия в команде test пустая, то
возвращается значение 1.
Возможны условия нескольких видов:
1) проверка файлов или переменных
формат команды: test ключ имя_файла | имя_переменной
ключ команды задает режим проверки и может принимать
следующие значения
-f – указанный файл является обычным файлом;
-d – указанный файл является обычным каталогом;
-с – указанный файл является символьным устройством;
-r – указанный файл доступен для чтения;
53
-w – указанный файл доступен для записи;
-s – указанный файл не является пустым;
-n – указанная переменная имеет значение (не пустая);
-z – указанная переменная не имеет значения.
Примеры команды:
test –f /usr/user1/file_1 возвращает 0, если файл /usr/user1/file_1
является обычным файлом;
test –w /usr/user1/file_1 возвращает 0, если файл /usr/user1/file_1
доступен для записи;
test –n $ALFA возвращает 0, если переменная ALFA имеет значение.
2) сравнение строк
формат команды: test строка1 отношение строка2
операции отношения строк: = (равно) или != (не равно) .
примеры команд:
test $A = $B возвращает 0, если значение переменной A равно
значению переменной B
test $A != $B возвращает 0, если значение переменной A не равно
значению переменной B
Обратите внимание: операция отношения обязательно с двух сторон
окружается пробелами.
3) сравнение целых чисел.
формат команды: test число1 отношение число2
операции отношения чисел:
-eq – равно; -ne – не равно;
-gt – больше; -ge – больше или равно;
-lt – меньше; -le – меньше или равно.
2. Оператор IF
Условный оператор IF имеет следующий формат:
if условие then
список_операторов
[elif условие then
список_операторов ]
[else список_операторов]
fi
Здесь условие – любой оператор, возвращающий значение true или
false (обычно используется оператор test);
список_операторов – операторы, разделенные точкой с запятой (;).
Пример оператора:
54
if [ -f /tmp/myfile ] then
cksum /tmp/myfile
else
ls /tmp > /tmp/myfile; cksum /tmp/myfile
fi
Оператор CASE
Оператор множественного выбора CASE имеет следующий формат:
Case строка in
шаблон_1) список_операторов;;
шаблон_2) список_операторов;;
шаблон_n) список_операторов;;
esac
Пример оператора:
echo –n ‘please enter symbol from A to C: ‘
read ENTRY
case $ENTRY in
A|a) echo ‘Apple’;;
B|b) echo ‘Baby’;;
C|c) echo ‘Coke’;;
*) echo ‘Please type A, B or C ! ’;;
esac
Оператор FOR
Оператор предопределенного цикла FOR имеет следующий формат:
for имя [in список] do
список_операторов
done
Если список значений для переменной не задан, то в качестве списка
используется значение внутренней переменной @, содержащей список
аргументов сценария (см. табл. 11). Пример оператора:
list=”word 1 word2 word3 word4”
for VAL in $LIST do
echo $VAL
done
Операторы WHILE и UNTIL
Операторы WHILE и UNTIL предназначены для создания циклов с
неопределенным
числом
исполнения
списка
операторов.
Список_операторов оператора WHILE исполняется только тогда, когда
условие истинно, а для оператора UNTIL – когда условие ложно. Выход
55
из циклов проводится путем
принудительно по команде break.
Форматы операторов:
while условие do
список_операторов
done
контроля
значения
условия
или
until условие do
список_операторов
done
Оператор TRAP
Командный сценарий может перехватывать сигналы прерываний,
генерируемых при возникновении различных ненормальных ситуаций
(нажатие клавиши DEL, срабатывание команды KILL и т. д.), и
обрабатывать их собственными средствами (за исключением сигнала 9
команды KILL). Для этих целей предназначен оператор TRAP.
Формат оператора:
trap “список_команд” список_сигналов
Здесь список_команд – задает команды, которые будут выполняться
при возникновении любого сигнала из списка_сигналов. Команды с
списке_команд разделяются точкой с запятой (;), а сигналы в
списке_сигналов разделяются пробелами.
Пример оператора:
trap “rm /tmp/*; echo Game is over; exit” 16
Здесь при получении сигнала 16 будут удалены все файлы из каталога
/tmp, на экран выводится сообщение и завершается работа
интерпретатора.
Использование функций в сценариях
Функции Korn Shell описываются следующим образом:
имя_функции ()
(
список_операторов
)
Функции поддерживают механизм формальных и фактических
параметров. Формальные параметры являются локальными и имеют
имена от 0 до 9. Если функция должна вернуть код завершения, то
используется оператор return.
56
Порядок выполнения работы
1. Разработать сценарий для создания файла данных с информацией
согласно таблице 14. Количество записей в файле – не менее 10, в скобках
указано смещение поля относительно начала записи. Форматирование
полей записи проводить путем циклического добавления необходимого
числа пробелов, например:
len_fam=` expr length $fam;
while [ $len_fam –lt 20 ]
do
#записываем пробел без перевода строки
echo –n “ “ >>$file_dat;
len_fam=`expr $len_fam + 1;
done
Здесь len_fam – переменная, в которой хранится текущая
длина введенного пользователем значения поля.
Таблица 14
№
Предметная
варианта
область
1
Студенческая
группа
2
Расписание
поездов
3
Туристическая
фирма
4
5
6
7
8
9
10
Название поля
Фамилия (0), год рождения (20), место рождения
(25), средний балл (40).
Номер поезда (0), пункт назначения (5),
время отправления (30), время в пути (40).
Номер тура (0), страна (5), город (20),
продолжительность тура (40),
дата отправления (45).
Расписание
Номер рейса (0), пункт назначения (10),
самолетов
время отправления (30), время в пути (40)
Склад
Код товара (0), группа товара (5), наименование
товара (20), дата поступления (40), цена (45).
Отдел кадров
Фамилия (0), год рождения (20), место рождения
(25), должность (45), адрес, телефон (55)
Успеваемость
Группа (0), фамилия (10), номер семестра (30),
студентов
название дисциплины (32), оценка (50).
Договорной отдел Шифр договора (0), заказчик (10), источник
финансирования (30), сумма договора (50),
дата начала (60), дата окончания (70)
Бухгалтерия
Табельный номер (0), фамилия (7), оклад (30),
надбавка (40), наличие налогового вычета (45),
ГИБДД
Госномер автомобиля(0), модель (10),
год выпуска (25), тип двигателя (30),
адрес регистрации (40), дата регистрации (65)
57
2. Разработать сценарий для поиска в файле данных заданной
пользователем строки символов.
3. Разработать сценарий для сортировки файла данных по заданному
пользователем полю записи.
4. Разработать сценарий, формирующий меню для опроса пользователя
(пункты меню – ДОБАВЛЕНИЕ ДАННЫХ, ПОИСК ДАННЫХ,
СОРТИРОВКА ДАННЫХ, ВЫХОД) и выполнить это меню с помощью
соответствующих сценариев, разработанных в п.3.1 – 3.3 задания.
Контрольные вопросы
1. Командный сценарий: определение, назначение.
2. Способы запуска командного сценария.
3. Способы передачи данных в командный сценарий.
4. Переменные в командном сценарии: типы, область видимости,
допустимые операции.
5. Организация циклов в сценарии.
6. Организация ветвлений в сценарии.
7. Поясните алгоритм форматирования записей при выполнении п. 3.1
лабораторной работы.
58
Лабораторная работа 6
РАЗРАБОТКА ПРИЛОЖЕНИЙ В СРЕДЕ PHAB
Цель работы – изучение технологии разработки приложений в среде
Photon Application Builder.
Теоретический материал
Общие сведения
Дистрибутив QNX комплектуется собственным инструментом для
разработки приложений на C/C++ - Photon Application Builder (PhAB).
Установка PhAB с дистрибутивного диска проводится в следующем
порядке:
 запустить инсталлятор, встроенный в среду Photon (Installer);
 установить содержимое репозиториев QNX Installer for Neutrino
и QNX Updater for Neutrino (выбрать режим Select All и нажать кнопку
Install).
Основные особенности среды PhAB:
 отсутствуют встроенный редактор исходных текстов и
символьный отладчик;
 не генерируется "проект" в том смысле, как это делают,
например, MS Visual CPP, Borland Delphi или Borland Builder.
Поэтому PhAB не является интегрированной средой разработки в
привычном смысле и его можно рассматривать как построитель
графического интерфейса приложения (Graphic User Interface, GUI).
PhAB избавляет разработчика от рутинной работы по отслеживанию
размеров, взаимных положений, цветов и других характеристик GUI
компонентов приложения, и позволяет привязать обрабатывающий
код к GUI событиям (нажатия кнопок, перемещения окон, ввод с
клавиатуры).
Программный код, определяющий реакцию на события, разработчик
прописывает традиционными методами на C/C++.
Перечень основных понятий PhAB и соответствующих им понятий
Borland IDE приведен в таблице 15.
59
Таблица 15
PhAB
виджет
(widget)
callback
ресурс
(resource)
свойство
ресурса
событие
Определение
GUI – компонент системы Photon
Borland
форма, компонент
функция, обеспечивающая обработку
события
свойство виджета, которое может читаться
или устанавливаться из программного кода
обработчик
событий, метод
свойство
компонента
(property)
значение свойства
текущее значение ресурса
внешнее воздействие на виджет (нажатие
кнопки, перемещение мыши и т. д.)
событие (event)
Окно PhAB – содержит меню, панели инструментов, набор виджетов
и контрольную панель (аналог инспектора объектов в Borland IDE).
Контрольная панель имеет несколько закладок:
Resources – для установки свойств ресурсов (активация с
помощью F12);
Callbacks – для связи виджета с функциями, определяющими
реакцию на различные события (активация с помощью Shift + F12);
Search Panel – для поиска заданной символьной строки (активация с
помощью Ctrl + R).
В отдельное окно с помощью клавиши F11 можно выводить
содержимое буфера обмена Clipboard.
PhAB имеет большое количество виджетов (кнопки, списки, меню и
т. д.). При добавлении виджета в проект ему присваивается стандартное
имя, совпадающее с именем класса, к которому принадлежит этот
виджет. Если для виджета определяется реакция на событие через
callback-функцию, то ему необходимо присвоить уникальное имя.
Виджет, у которого не определена реакция на события, называется
пассивным.
Результатом работы генератора PhAB является набор файлов,
включающий Makefile, файлы заголовков, определений и т.д.; обработка
файлов проводится с помощью утилиты make.
Методика построения приложений PhAB
1. Запуск PhAB
Может проводиться двумя способами:
 кнопкой Launch (Launch → Development → Builder);
 с помощью командной строки: /usr/photon/appbuilder/ab
60
2. Создание модулей
Разработка приложения пользовательского интерфейса начинается с
создания модулей. Каждый модуль группирует взаимосвязанную
информацию
и
позволяет
пользователю
особым
образом
взаимодействовать с этой информацией. PhAB предоставляет несколько
типов модулей:
 window – обычно используется для формирования главного
окна приложения;
 dialog – позволяет приложению обмениваться информацией с
пользователем;
 menu – предоставляет пользователю команды;
 icon – определяет иконку приложения;
 picture – может быть использовано, например, для
формирования графических изображений.
Создание главного модуля приложения выполняется через меню
File → New, при этом будет представлен на выбор список из 13 типовых
видов окна главного модуля приложения. После выбора типа модуля
приложения ему по умолчанию будет присвоено имя base, и на экране
появится диалог с его свойствами. Основными свойствами модуля
являются:
 имя главной инициализирующей функции (Initialization
Function) , которая запускается до создания окна приложения и получает
те же параметры argv и argc, что и функция main в консольных
приложениях;
 имя функции инициализации главного окна (Setup Function),
которая может вызываться до или после отображения окна на экране;
 возможность использования синтаксиса С++ в файлах
приложения (флажок Scan Source Files For Prototypes, для С++ он должен
быть отключен);
 возможность управления состоянием, размером и положением
главного окна приложения.
3. Добавление виджетов
Добавление виджета проводится путем выбора его из палитры
виджетов и размещения его в окне модуля. Определение свойств виджета
проводится с помощью редактора ресурсов через панель управления.
4. Прикрепление ответных реакций
Для определения реакции на различные события виджету
назначаются ответные реакции (CallBack), реализуемын в виде callback61
функций. Имя функции указывается на закладке CallBacks, код пишется
на C/C++. Присоединение функций к виджету проводится на этапе
компоновки приложения.
В общем случае синтаксис имени callback-функции выглядит
следующим образом: Class::Function@File.Type, где любой элемент
записи, кроме Function, может отсутствовать. Здесь
 Function – имя функции, при генерации проекта PhAB добавляет
в программный файл «заглушку» с указанным именем и набором
параметров, требуемым для каждого класса виджета;
 File – имя файла исходного кода программы, в который будет
помещена функция;
 Type – расширение имени файла («c» или «cpp»), по умолчанию
– «c»;
 Class – используется только в С++ и определяет класс, членом
которого является функция.
При удалении или переопределении ответной реакции PhAB не
удаляет callback-функцию, это необходимо делать вручную.
Доступ к свойствам виджета из программного кода проводится с
помощью функций PtSetResource и PtGetResource путем указания имени
виджета и имени ресурса. Для виджета можно использовать
предопределенные имена, присваиваемые PhAB: ABW_xxx и ABN_xxx.
Здесь ABW_xxx – указатель на размещенный на экране виджет с именем
xxx, а ABN_xxx – целочисленный идентификатор этого виджета в системе
окон приложения. Например, для установки значения надписи в окне он
может выполняться следующим образом:
PtSetResource(ABW_xxx, Pt_ARG_TEXT_STRING, “результат равен
”, 0);
5. Генерация кода
Фактическое создание проекта в PhAB происходит при
первоначальном выборе из меню: File → Save As… При этом PhAB
предлагает нам определить имя нового проекта, т.е. его местоположение в
файловой системе. После сохранения проекта создается каталог с именем,
присвоенным проекту.
Для генерации кода приложения необходимо открыть диалог
"Build+Run" (рис. 6.2), указать целевую платформу (Target Platform),
установить необходимые параметры проекта и нажать кнопку Generate. В
общем случае QNX позволяет создавать приложения для большого числа
процессорных платформ, однако QNX Momentics NC генерирует
приложения только для платформы Intel x86.
62
PhAB создаёт на верхнем уровне проекта 2 каталога:
./wgt – содержащий параметрические описания всех модулей
приложения; для каждого модуля будет создан один файл описаний, имя
которого совпадает с именем виджета модуля, а тип – wgtX, где X – это
односимвольный определитель типа модуля: *.wgtw – window, *.wgtd –
dialog, *.wgtm – menus (многоуровневые текстовые меню), *.wgtp –
picture (база данных виджетов, в том числе – изображений), *.wgti –
“иконки”, используемые приложением. Файлы этого каталога проекта
генерируются PhAB при визуальной компоновке внешнего вида
приложения (модифицируются при всякой визуальной коррекции GUI) и,
несмотря на то, что они имеют текстовый формат, никогда не следует
редактировать их вручную.
./src – каталог исходных описаний проекта, содержащий
программную часть проекта (служебные файлы описаний, необходимые
для сборки приложения, и файлы, разработанные программистом). Для
каждой целевой платформы создаётся подкаталог с именем платформы
(для x86 это- gcc_ntox86), где содержатся все специфические для этой
платформы файлы: Makefile сборки с конкретными параметрами
компилятора, объектные файлы, итоговые файлы исполнимого формата.
Здесь находится (генерируется PhAB), как минимум, один программный
файл: abmain.cc – файл, содержащий головную программу запуска
приложения (или abmain.c для проекта на языке С).
Файл abvars.h содержит ABN_* идентификаторы всех виджетов
приложения, файл abdefine.h определяет их ABW_* имена, а abevents.h
описывает все реакции на события.
Обратите внимание: все файлы, имена которых начинаются с «ab»,
являются собственными файлами PhAB, и не должны подвергаться
модификации. Попытка модификации этих файлов может привести к
потере работоспособности приложения или к некорректному его
поведению.
Диалог "Build+Run" содержит файловый менеджер, позволяющий
редактировать исходный код и выполнять операции с файлами без
выхода из PhAB, а также дает возможность компилировать проект
(Make), запустить откомпилированное приложение (Run) или
использовать отладчик (Debug).
PhAB всегда создаёт главный файл приложения с именем abmain.c,
куда помещает его стартовый код.
После завершения очередного этапа по развитию проекта для
уменьшения размера исполняемого файла приложения рекомендуется
перейти в каталог gcc_ntox86 и выполнить команду strip имя_проекта,
которая удаляет из исполняемого файла всю отладочную информацию.
63
Пример разработки приложения PhAB
Разработаем приложение для расчета контрольной суммы введенной
символьной строки.
1. Создаем модуль главного окна (имя модуля – base, тип окна –
Plain).
2. Добавим две кнопки (выбрав Button на закладке Widgets и
“перетащив” их в окно виджета base), поля для ввода символьной строки
и вывода результата и надписи, для того чтобы указать пользователю
назначение этих полей. На закладке Resources – изменим атрибуты
ресурсов с именами ButtonText (ресурс Pt_ARG_TEXT_STRING) и Font
(ресурс Pt_ARG_TEXT_FONT).
3. Добавляем программную реакцию (callback) на нажатие кнопки
(закладка Callbacks, событие Activation), определив функцию-обработчик,
которую мы предписываем поместить в файл abmain.c
4. Выполняем генерацию кода проекта, задав ему имя Project1.
5. Редактируем файл abmain.c (добавляем в него функцию расчета
контрольной суммы, разработанную в лабораторной работе № 2).
Рассмотрим листинг, полученный в результате всех предыдущих
манипуляций, дополненный кодом, необходимым для выполнения
подсчёта контрольной суммы CRC16 для введённой символьной строки.
/* Standard headers */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* Local headers */
#include "ablibs.h"
#include "abimport.h"
#include "proto.h"
#include "abwidgets.h"
#include "abdefine.h"
#include "abevents.h"
#include "ablinks.h"
#include "abvars.h"
int main ( int argc, char *argv[ ] ) {
_Ap_.Ap_winpos.x = _Ap_.Ap_winpos.y = AB_LOCKED;
_Ap_.Ap_winsize.y = _Ap_.Ap_winsize.x = AB_LOCKED;
/* AppBuilder Initialization */
ApInitialize( argc, argv, &AbContext );
PgSetDrawBufferSize( 0xffff );
64
/* Display main window */
ApLinkWindow( NULL, &AbApplLinks[0], NULL );
/* Loop until user quits application */
PtMainLoop( );
PtExit( 0 );
return 0;
};
static const ApClassTab_t ClassTable[] = {
{ "PtWindow", &PtWindow },
{ "PtButton", &PtButton },
{ NULL, NULL }
};
ApContext_t AbContext = { ClassTable, 1, AbWidgets };
int CRC16( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo )
{
PtArg_t arg;
unsigned short res;
unsigned char i2=0;
char str[50], *str2;
int i=0;
PtSetArg(&arg, Pt_ARG_TEXT_STRING, 0, 0);
PtGetResources(ABW_PtTxt, 1, &arg);
res=0xFFFF;
str2=(char*)malloc(strlen((char*)arg.value));
str2[strlen((char*)arg.value)]='\0';
strcpy(str2, (char*)arg.value);
for(i=0; i<strlen(str2); i++)
{
res=(res&0xFF00)+(int)(str2[i])^(res%0x100);
for(i2=0; i2<8; i2++)
{
if(res&0x0001!=0) res=(res>>1)^0xA001;
else res=(res>>1);
}
}
PtSetResource(ABW_PtRes, Pt_ARG_TEXT_STRING, itoa(res, str, 10), 0);
return( Pt_CONTINUE );
};
EXT( PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo )
65
{
PtExit (EXIT_SUCCESS);
return (Pt_CONTINUE);
}
Легко видеть, что кроме текста и шаблонов, созданных PhAB на этапе
визуального проектирования, нам потребовалось добавить только код
функции для расчета контрольной суммы и для закрытия приложения
(этот текст выделен жирным шрифтом).
Порядок выполнения работы
Запустить PhAB.
Создать окно приложения.
Дополнить его нужными виджетами.
В соответствии с вариантом задания написать необходимые callbackфункции.
5. Отладить приложение.
Таблица 16
1.
2.
3.
4.
№ варианта
1
2
3
4
5
Задание
Определить базовые адреса последовательных портов
и вывести их на экран
Вычислить простую контрольную сумму текста, введённого
в форму
Вычислить контрольную сумму LRC текста, введённого
в форму
Вычислить контрольную сумму CRC32 текста, введённого
в форму
Вычислить корни квадратного уравнения, коэффициенты
которого введёны в форму
Контрольные вопросы
Каковы основные этапы построения приложений Photon в PhAB?
Какие параметры устанавливаются в панели Resources?
Какие параметры устанавливаются в панели Callbacks?
Как происходит генерация проекта. Что является её результатом?
Какие параметры Вы можете установить при генерации проекта?
В каких случаях и почему можно не присваивать виджету
уникальное имя?
7. Каким образом в приложении осуществляется связь виджетов с
Callback-функциями?
8. Какие свойств имеет главный модуль приложения?
1.
2.
3.
4.
5.
6.
66
Литература
1. Агуров П. Последовательные интерфейсы ПК. Практика
программирования. М.: Издательство: BHV. 2004.
2. Зыль С. Операционная система реального времени QNX: От
теории к практике. СПб.: БХВ. Петербург, 2004. 192 с.
3. Зыль С. QNX Momentics: основы применения. СПб.: БХВ.
Петербург, 2005. 256 с.
4. Несвижинский В. Программирование аппаратных средств в
Windows. М.: Издательство: BHV. 2004.
5. Цилюрик О. И. QNX: создание приложений в PhAB.
www.qnx.org.ru/article19.html
6. Даниленко В. Различные варианты физической компоновки
программ, использование динамических и статических библиотек
при создании приложений на С++ в RTOS QNX 6.2
http://qnxclub.net/files/articles/libraries/libraries.html.
7. Руководство программиста Photon. Справочная документация.
67
Приложение
Описание типов данных _SYSTEMTIME, LongWord
Тип
LongWord
Cardinal
Word
UINT
DWORD
Integer
LongInt
String
TColor
MMRESULT
TFNTimeCallBack
HWND
TFNTimerProc
Boolean
_SYSTEMTIME
TWMTimer
TFarProc
Принимаемое значение
Формат
0…4294967295
32 бита, без знака
0…4294967295
32 бита, без знака
0…65535
16 бит, без знака
0…4294967295
32 бита, без знака
0…4294967295
32 бита, без знака
–2147483648…2147483647
32 бита, со знаком
–2147483648…2147483647
32 бита, со знаком
строка
clRed, clGreen, clBlue, …
0…4294967295
32 бита, без знака
Type TFNTimeCallBack = procedure(uTimerID,
uMessage
: UINT;
dwUser,
dw1,
dw2
: DWORD) stdcall;
0…4294967295
32 бита, без знака
Pointer
указатель
True, False
булевский тип
_SYSTEMTIME = record
wYear: Word;
wMonth: Word;
wDayOfWeek: Word;
wDay: Word;
wHour: Word;
wMinute: Word;
wSecond: Word;
wMilliseconds: Word;
end;
TWMTimer = packed record
Msg: Cardinal;
TimerID: Longint;
TimerProc: TFarProc;
Result: Longint;
end;
Pointer
указатель
68
Download