Новые книги

Master the fundamental concepts of real-time embedded system programming and jumpstart your embedded projects with effective design and implementation practices. This book bridges the gap between higher abstract modeling concepts and the lower-level programming aspects of embedded systems development. You gain a solid understanding of real-time embedded systems with detailed practical examples and industry wisdom on key concepts, design processes, and the available tools and methods.

Delve into the details of real-time programming so you can develop a working knowledge of the common design patterns and program structures of real-time operating systems (RTOS). The objects and services that are a part of most RTOS kernels are described and real-time system design is explored in detail. You learn how to decompose an application into units and how to combine these units with other objects and services to create standard building blocks. A rich set of ready-to-use, embedded design “building blocks” is also supplied to accelerate your development efforts and increase your productivity.

Experienced developers new to embedded systems and engineering or computer science students will both appreciate the careful balance between theory, illustrations, and practical discussions. Hard-won insights and experiences shed new light on application development, common design problems, and solutions in the embedded space. Technical managers active in software design reviews of real-time embedded systems will find this a valuable reference to the design and implementation phases.

Qing Li is a senior architect at Wind River Systems, Inc., and the lead architect of the company’s embedded IPv6 products. Qing holds four patents pending in the embedded kernel and networking protocol design areas. His 12+ years in engineering include expertise as a principal engineer designing and developing protocol stacks and embedded applications for the telecommunications and networks arena. Qing was one of a four-member Silicon Valley startup that designed and developed proprietary algorithms and applications for embedded biometric devices in the security industry.

Caroline Yao has more than 15 years of high tech experience ranging from development, project and product management, product marketing, business development, and strategic alliances. She is co-inventor of a pending patent and recently served as the director of partner solutions for Wind River Systems, Inc.

About the Authors
В 1998 году вышла книга автора, посвященная инструментальным средствам системного анализа и проектирования информационных систем -BPwin и ERwin. (Маклаков С. BPwin и ERwin. CASE-средства разработки информационных систем. М: Диалог-МИФИ). Книга выдержала два издания и пользовалась популярностью среди специалистов в области информационных технологий. BPwin является средством, которое позволяет облегчить проведение обследования предприятия, построить функциональные модели и в дальнейшем с их помощью проанализировать и улучшить бизнес-процессы. Этот инструмент используют в основном системные аналитики и специалисты по внедрению информационных систем. ERwin предназначен для другого круга задач и для специалистов другого профиля - это система проектирования баз данных.

Многочисленные пожелания читателей и выход новой версии продукта фирмы Computer Associater - BPwin 4.0 побудили автора написать книгу, целиком посвященную BPwin и предназначенную для специалистов, задачей которых является создание функциональных моделей и реинжиниринг бизнес-процессов.

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

Глава 5. Win32 API

ГЛАВА 5
Любой программист, создающий приложения Windows, должен иметь представление о процессах, происходящих в операционной системе, для создания качественных и эффективных приложений. Такие знания помогут решить многие проблемы, возникающие при разработке приложений, тесно связанных с операционной системой. В рассмотренных ранее главах мы уже изучили понятие потока и многопоточности. Эти понятия имеют непосредственное отношение к процессам операционной системы.
В данной главе мы рассмотрим, что такое объект ядра и что такое процесс ядра операционной системы. Изучим объекты GDI и User. Рассмотрим, как Windows управляет памятью и как эта операционная система обрабатывает ошибки.
Объекты и процессы ядра Windows
Сразу оговоримся, что все сказанное в этой главе относится к следующим версиям Windows: Windows 95, 98, 2000 и Windows NT, т. к. только в данных версиях была введена поддержка 32-разрядных приложений.

Примечание.
Для среды Windows 3.1 Microsoft специально разработала пакет Win32s, позволяющий с некоторыми ограничениями использовать поддержку приложений Win32.

Ядро Windows (Windows kernel) - это часть операционной системы, которая обеспечивает поддержку низкоуровневых функций, необходимых для выполнения приложений. Например, всякий раз, когда приложению нужна дополнительная память, оно обращается к ядру Windows.
Между всеми вышеперечисленными системами существуют различия в поддержке 32-разрядных приложений. В табл. 1.8 перечислены некоторые отличия, существующие между тремя операционными системами.
Таблица 1.8. Различия операционных систем при поддержке Win32 API

Характеристика

Windows NT

Windows 95

Windows 3.1 с поддержкой Win32

32-битная система координат

Есть

Нет

Нет

Асинхронный файловый ввод/вывод

Есть

Нет

Нет

Асинхронная модель ввода информации

Есть

Есть

Нет

Мультимедиа API

Есть

Есть

На уровне Windows 3.1

Поддержка многопроцессорных материнских плат

Есть

Нет

Нет

Динамический обмен данными (DDE) по сети

Есть

Есть

Нет

Поддержка процессоров других фирм-производителей (не Intel)

Есть

Нет

Нет

Вытесняющая многозадачность

Есть

Есть

Нет

Безопасность (сертификат С2)

Есть

Нет

Нет

Разделяемое адресное пространство

Есть

Есть

Нет

Поддержка TAPI (Telephone API)

Есть

Есть

Нет

Потоки

Есть

Есть

Нет

Системные ресурсы для объектов User и GDI

Практически не ограничены

Расширенные

Ограниченные


Итак, Win32 API (Application Programming Interface) - это интерфейс разработки 32-разрядных приложений Windows.
Многозадачность
Многозадачность (multitasking) - свойство операционной системы выполнять одновременно несколько приложений.

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

В ранних, 16-разрядных операционных системах поддерживалась так называемая кооперативная многозадачность (cooperative multitasking). Это такой вид многозадачности, когда приложение в процессе своего выполнения само "решает" передавать управление операционной системе или продолжать занимать процессорное время. При этом другие приложения, запущенные вместе с первым, просто не выполняют никаких действий. Данный вид многозадачности приводил к "зависаниям" операционной системы вместе со всеми запущенными приложениями, если "висло" приложение, захватившее ресурсы процессора.
На смену кооперативной многозадачности пришла вытесняющая многозадачность (preemptive multitasking). Вытесняющая многозадачность появилась лишь в 32-разрядных операционных системах. Данный вид многозадачности подразумевает, что управление всеми приложениями ведет операционная система. Она выделяет каждому приложению определенный квант времени процессора в зависимости от приоритета приложения (см. главу 3).
Объекты ядра Windows
Рассмотрим объекты, с которыми работает операционная система Windows.
Сразу обратим ваше внимание на то, что объекты Win32 и объекты Delphi - абсолютно разные объекты. В Win32 объекты делятся на объекты ядра (kernel) и объекты GDI и User.
Процессы и потоки
Процесс - это выполняющееся приложение Windows. Так как Windows - многозадачная операционная система, то в ней может работать сразу несколько процессов. Каждый процесс получает свое адресное пространство (размером до 4-х гигабайт). В этом пространстве хранится код приложения, его данные, а также все подключаемые библиотеки (DLL).
Сами процессы ничего не выполняют. Каждый процесс состоит из потоков (threads), которые выполняют код процесса (подробнее о потоках и об их создании см. главу 3). Любой процесс состоит как минимум из одного потока, который называется первичным или главным потоком (primary thread). Процесс может состоять из нескольких потоков, только один из которых будет главным.
Поток - это объект операционной системы, который представляет собой часть кода, находящегося внутри некоторого процесса. .
При создании процесса операционная система создает его главный поток, который может генерировать дополнительные потоки. При этом каждому потоку процесса Windows выделяет свои кванты времени процессора, в зависимости от приоритета потока.
Для работы с процессами Win32 API имеет встроенные функции, перечисленные в табл. 1.9.
Таблица 1.9. Функции Win32 API для работы с процессами

Функция Win32 АРI

Предназначение

CreateProcess

Создает новый процесс и его главный поток. Используется вместо функции Windows 3.1 winExec. При помощи данной функции можно запускать приложения

ExitProcess

Завершение выполнения процесса и всех его потоков

GetCurrentProcess

Возвращает псевдодескриптор текущего процесса. Настоящий дескриптор текущего процесса можно получить С ПОМОЩЬЮ функции DuplicateHandle

DuplicateHandle

Функция, предназначенная для создания копии объекта ядра

GetCurrentProcess ID

Возвращает идентификатор текущего процесса. Данный идентификатор уникален для каждого процесса операционной системы

GetExitCodeProeess

Получение статуса окончания процесса

GetPriorityClass

Возвращает класс приоритета для конкретного процесса

GetStartupInfo

Возвращает содержимое структуры TStartupinfo,

которая создается во время создания процесса

OpenProcess

Возвращает дескриптор процесса по его идентификатору

SetPriorityClass

Устанавливает класс приоритета для конкретного процесса

TerminateProcess

Прекращение выполнения процесса и всех его потоков

WaitForInput Idle

Перевод процесса в режим ожидания ввода


Объекты ядра
Объекты ядра (kernel objects) - это процессы, потоки, события, семафоры, мьютексы и т. д., т. е. все то, с чем работает ядро Windows.
При создании объекта он существует в адресном пространстве процесса. При этом дескриптор объекта доступен породившему его процессу. Данный дескриптор не может быть использован другими приложениями для доступа к объекту разных процессов. Но эта проблема разрешима: с помощью функций Win32 API процесс имеет возможность получения собственного дескриптора (отличного от дескриптора процесса, породившего объект) для уже существующего объекта.
Например, первый процесс создает именованный или неименованный мьютекс и возвращает его дескриптор с помощью команды createMutex. Для того чтобы данный мьютекс мог использоваться другим процессом, необходимо воспользоваться функцией openMutex, которая возвращает дескриптор уже существующего мьютекса.
При обращении к объектам ядра Windows использует счетчик обращений к объекту. При создании или использовании объекта ядра приложениями счетчик увеличивается. При прекращении использования объекта ядра приложением счетчик уменьшается. Наконец, при обнулении счетчика объект ядра уничтожается.
Объекты GDI и User
В Windows 3.1 не было объектов ядра. Доступ ко всем объектам операционной системы осуществлялся с помощью дескрипторов. Объекты операционной системы делились на две группы: объекты, находящиеся в локальной памяти модулей GDI и User, и объекты, находящиеся в глобальной памяти.
Интерфейс графического устройства (Graphical Device Interface, GDI) - это часть Windows, которая управляет шрифтами, средствами печати и другими графическими системами Windows.
Когда приложение выводит что-либо на экран, оно использует службы, представляемые GDI.
Объекты GDI- это палитры, изображения, шрифты, и т. д., т.е. то, чем управляет GDI.
Средства пользовательского интерфейса (User) - это часть Windows, отвечающая за все окна, которые создаются приложениями.
Объекты User - это, в первую очередь, окна, меню.
В 16-разрядной версии Windows имелась непосредственная связь между объектом и его дескриптором. Таким образом, в данной версии Windows существовала таблица, содержащая указатели на все объекты операционной системы. Эта таблица была доступна любому приложению или динамически компонуемой библиотеке Windows. Она называлась таблицей локальных дескрипторов (Local Descriptor Table). В результате, любое приложение (или DLL) могло обращаться к объекту, используемому другим приложением.
В 32-разрядной операционной системе объекты хранятся в собственных адресных пространствах процессов, и для каждого процесса существует своя собственная таблица дескрипторов объектов. Таким образом, каждый процесс теперь работает с собственными дескрипторами объектов.
Любые дескрипторы объектов GDI или User управляются специальными подсистемами Win32 API. Для GDI - это GDI.EXE, для User - USER.EXE. Данные подсистемы выполняют создание, освобождение и проверку корректности работы дескрипторов объектов.
Управление памятью в Win32
В данном разделе мы рассмотрим все, чего еще не касались ранее о распределении памяти в Win32.
Организация виртуальной памяти в Windows
Как нам уже известно, Win32 - это 32-разрядная операционная система. Таким образом, любое приложение, запущенное в Win32, может захватывать адресное пространство в размере 4 Гбайта. Каждое приложение (процесс операционной системы) обладает своим индивидуальным адресным пространством, которое не пересекается с адресными пространствами других приложений.
Процесс выделения памяти в Windows состоит из двух последовательных этапов:
- резервирования участка памяти (виртуального адресного пространства), необходимого размера для размещения приложения и его данных. На этом этапе, физически, операционная система не выделяет оперативную память. То есть, фактически, вы можете дать команду операционной системе зарезервировать 300 Мбайт адресного пространства, и при этом не займете практически никаких системных ресурсов. Вы можете непосредственно указать операционной системе адреса, которые резервируете, или предоставить это дело операционной системе;

Примечание
Адресное пространство в Win32 резервируется блоками по 64 Кбайта. Поэтому, несмотря на ваши пожелания, при резервировании адресного пространства операционная система реально зарезервирует первый (базовый) участок адресного пространства объемом 64 Кбайта. Данное разбиение адресного пространства на блоки служит для ускорения работы ядра операционной системы.

- выделения реальной, физической памяти в зарезервированный участок виртуального адресного пространства. На данном этапе операционная система выделяет реальный блок памяти, который также состоит из небольших блоков.

Примечание
Минимальный блок памяти, с которым работает операционная система, называется страницей памяти. Размер страницы памяти различен в разных операционных системах. Например, в Windows NT - он равен 8 Кбайт, а в Windows 95/98 - 4 Кбайта.

Так как объем оперативной памяти компьютера обычно достаточно небольшой (для современных компьютеров 32-128 Мбайт), то операционная система вынуждена при нехватке основной оперативной памяти использовать так называемый файл подкачки (swap file) или виртуальную память. Таким образом, если в оперативной памяти компьютера имеется несколько приложений, запущенных одновременно, и им не хватает оперативной памяти, Windows просто выгружает те страницы памяти, к которым длительное время не было обращений. В случае, когда приложению потребуется выгруженная страница памяти, Windows освободит страницу, выгрузив страницу, к которой давно не было обращений, и загрузит на ее место требуемую, после чего вернет управление приложению.
Все вышеперечисленные действия абсолютно незаметны для приложения. Приложению не нужно заботиться о выгрузке и загрузке страниц, все эти действия выполняет операционная система.
Кучи и менеджеры куч
Серьезные приложения интенсивно используют механизмы выделения и освобождения памяти. В коммерческих приложениях широко применяются такие элементы, как: динамические массивы, строки, объекты и многое другое. При этом, создание и удаление подобных элементов происходит довольно часто, а сами элементы имеют относительно небольшой размер.
Такая работа по выделению памяти и ее освобождению, по отношению к элементам небольшого размера, является неэффективной. Во-первых, снижается производительность, т. к. резервирование адресного пространства и выделение страниц памяти происходит на уровне ядра операционной системы. Во-вторых, теряются большие объемы памяти. Например, если приложение требует выделить 256 байт под строку, операционная система реально выделяет одну страницу памяти, объемом 4 или 8 килобайт (в зависимости от операционной системы).
Для решения проблемы выделения памяти небольшим элементам приложения была введена организация по принципу кучи (heap). Куча - это достаточно большой непрерывный участок памяти, из которого выделяются небольшие блоки. Для того чтобы куча могла функционировать, в операционную систему был включен так называемый менеджер кучи. Менеджер кучи - это специальный механизм, который следит за выделением и освобождением блоков памяти. Для каждого вновь созданного процесса Windows по умолчанию создает кучу. Все кучи, которые создаются операционной системой, являются потокобезопасными. Следовательно, у программиста есть возможность обращаться к одной куче из разных потоков одновременно. Для работы с менеджером кучи Win32 API можно пользоваться следующими функциями (табл. 1.10).
Таблица 1.10. Функции Win32 API для работы с кучей

Функция

Назначение

HeapCreate

Резервирует непрерывный блок памяти в виртуальном адресном пространстве процесса, т. е. создает кучу

HeapAlloc

Выделяет блок неперемещаемой памяти в куче

HeapReAlloc

Служит для изменения размера блока памяти, выделенного функцией HeapAlloc

HeapFree

Освобождает блок памяти, выделенной функцией HeapAlloc

HeapDestroy

Уничтожает кучу, созданную с помощью функции

HeapCreate

VirtualAlloc

Резервирует или размещает страницы в виртуальной памяти процесса

VirtualFree

Освобождает страницы в виртуальной памяти процесса

VirtualLock

Защищает диапазон адресов от переноса в страницу памяти

VirtualUnlock

Снимает защиту, установленную функцией VirtualLock

VirtualQuery

Выдает информацию о диапазоне страниц в виртуальной памяти процесса, вызвавшего данную функцию

VirtualQusryEx

То же, что И VirtualQuery, только для определенного процесса

VirtualProtect

Изменяет права доступа к определенному диапазону памяти в адресном пространстве процесса, вызвавшего данную функцию

VirtualProtectEx

То же, что и VirtualProtect, только для определенного процесса


Кроме перечисленных в табл. 1.10, существуют еще функции LocalAlloc (GlobalAlloc) и LocalFree (GlobalFree). Вы можете использовать любое на-писание функций, т. к. в Win32 нет разделения на глобальные и локальные кучи. Данные функции применяются, в основном, при работе с буфером обмена Windows. Эти функции являются устаревшими и не рекомендуются к использованию.
Кроме стандартного менеджера кучи, который предлагает Microsoft, создателями Delphi был предложен свой менеджер кучи, который поставляется вместе с Delphi. Менеджер кучи Delphi резервирует блоки виртуальной памяти размером 1 Мбайт. Размер выделяемых Delphi блоков реальной памяти - 16 Кбайт. При работе с менеджером куч Delphi можно использовать следующие функции:
- New, Dispose - применяются для выделения и освобождения памяти. Используются для динамической работы с элементами приложения;
- GetMem, FreeMem - также применяются для выделения и освобождения памяти. Используются для динамической работы с небольшими двоичными блоками памяти (блоки, буферы и т. п.).
Кроме вышеперечисленных менеджеров куч, программисту предоставлена возможность создания собственного менеджера. .
Динамические хранилища
Часто при создании приложений возникает задача, когда необходимо накапливать поступающие данные. В качестве примера можно привести задачу записи звука, прием данных по сети и т. п. В случае, когда объем передаваемых данных заранее известен - проблемы не возникает. В этом случае можно выделить блок памяти требуемого объема и заполнять его поступающими данными. Проблема может возникнуть, когда объем поступающих данных заранее неизвестен, и мы не знаем, какой объем памяти нам может понадобиться. Тогда используются так называемые динамические (или потоковые) хранилища. В Delphi в качестве таких хранилищ могут выступать динамические массивы, объект TMemoryStream и динамическое перераспределение памяти. Принцип работы всех перечисленных хранилищ достаточно прост: под запись данных выделяется блок памяти, затем, когда этот блок полностью заполняется данными, он изменяет свой размер на больший.
Нужно помнить, что такое изменение объема блока памяти внутри кучи достаточно неэффективно, особенно когда размер блока памяти достаточно большой. Сам процесс изменения объема блока памяти занимает большое количество системных ресурсов.
Обработка ошибок в Win32
Многие из функций Win32 API возвращают в качестве результата своего выполнения значение true или false. По данному значению мы можем узнать, успешно ли выполнилась функция.
Обработка ошибок с помощью функции GetLastError
При неудачном завершении своего выполнения функция возвращает значение false. Для того чтобы получить код происшедшей ошибки, можно воспользоваться функцией Win32 API GetLastError. Данная функция не содержит параметров.

Примечание
Не во всех случаях при возвращении функцией значения false, GetLastError возвращает код происшедшей ошибки. Поэтому, в некоторых случаях, вызов функции GetLastError не приведет к нужному результату.

Обратите внимание на то, что код ошибки связан с потоком, в котором данная ошибка произошла. Ошибки, произошедшие в разных потоках, будут иметь собственные коды ошибок, определяемые функцией GetLastError. Поэтому вызывайте функцию GetLastError в том же потоке, в котором произошла ошибка.

Примечание
Коды ошибок можно посмотреть в справочной системе Win32 Programmer's Reference, поставляемой вместе с Delphi.

Рассмотрим пример использования функции GetLastError:
If not CreateProcess (CommandLine, nil, nil, nil, False,
NORMAL_PRIORITY_CLASS, nil, nil, Startuplnfo, Processlnfo) then
Raise Exception.Create ('Ошибка создания процесса: ' +
IntToStr (GetLastError));

Данный код пытается создать процесс. При возникновении ошибки будет сгенерировано исключение, которое выведет код ошибки, полученный с помощью функции GetLastError.
Обработка ошибок с помощью функции SetErrorMode
Некоторые ошибки, такие как, например, "Устройство не готово" (при записи информации на дискету, которая защищена от записи), вызывают окна сообщений Windows, для информирования пользователя о случившейся ошибке. Приложение может обрабатывать такие ошибки, с помощью использования функции SetErrorMode:
SetErrorMode (uMode);
В данную функцию можно передавать любое из перечисленных в табл. 1.11 значение параметра uMode.
Таблица 1.11. Значения параметра uMode функции SetErrorMode

Значение параметра

Выполняемое действие

SEM_FAILCRITICALERRORS

При установке данного значения операционная система не отображает окно сообщения об ошибке. Вместо этого, операционная система отправляет данную ошибку вызвавшему ее процессу

SEM_NOALIGNMENTFAULTEXCEPT

Данное значение не используется для процессоров семейства х86

SEM_NOGPFAULTERRORBOX

Данное значение применяется при отладке приложений. При установке этого значения операционная система не отображает окно сообщения об ошибке

SEM_NOOPENFILEERRORBOX

При установке данного значения, операционная система не отображает окно сообщения об ошибке открытия файла. Ошибка возвращается в вызвавший ее процесс


Экстренное завершение приложения
Для экстренного завершения работы приложения служит функция Win32 API FatalAppExit. Данная функция отображает окно сообщения о критической ошибке и, после закрытия этого окна пользователем, приложение завершает свою работу.

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


Глава 4 Содержание Глава 6