×
Namespaces

Variants
Actions

Fundamentals of Symbian C++/Threads, Processes, and IPC/ru

From Nokia Developer Wiki
Jump to: navigation, search
Article Metadata

Статья
Перевод:
hamishwillee
Последнее редактирование: hamishwillee (09 Dec 2011)

Единицей защиты памяти в Symbian ОС является процесс, в то время как единицей выполнения - поток. Процесс может состоять из одного или нескольких потоков. Symbian OS осуществляет планирование потоков, а не процессов, являющихся лишь эффективными контейнерами для защиты памяти.

Contents

Процессы

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

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

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

RProcess — это класс управления процессами. Также как и для описанного ниже класса RThread, функция RProcess::Create() используется для запуска нового именованного процесса (такого как, например, EXE), а функция RProcess::Open() - для открытия хендла процесса, определяющегося по имени или идентификатору (TProcessId).

Другие функции классов RThread и RProcess также схожи. Например, функция Resume() определяет первый поток в процессе как готовый к выполнению. Но в отличие от RThread класс RProcess не имеет функции RProcess::Suspend(), потому что Symbian ОС выполняет планирование только для потоков.

В SDK эмуляция процессов основана на WIN32 потоках. К примеру, запуск EXE приводит к созданию нового потока, а не процесса для его исполнения. Также стоит заметить, что защита памяти процессов не реализована на эмуляторе (процессы могут 'видеть' память друг друга).

Потоки

Во многих случаях на платформе Symbian предпочтительно использование активных объектов (active objects, описанных в статье Fundamentals of Symbian C++/Active Objects) вместо потоков, потому что они оптимизированы для реализации событийно-ориентированной многозадачности. Кроме того, активные объекты довольно хорошо подвергаются масштабированию для обработки множества событий, но выполнение обработчика события не должно занимать длительное время, и только одно событие может быть обработано в каждый момент времени. Однако, когда вы разрабатываете приложение реального времени, или портируете код, написанный для других платформ, часто возникает необходимость использования многопоточности.

RThread — класс, используемый для управления потоками в Symbian C++. Он представляет возможность для управления потоком, сам поток — объект ядра.

RThread определяет несколько функций для создания потока, каждая из которых в качестве параметра принимает дескриптор с именем потока, указатель на функцию, с которой поток начинает выполнение, указатель на данные (если они есть), которые должны быть переданы этой функции, и размер стека потока (по умолчанию - 8 КБ). Несколько перегруженных функций RThread::Create() позволяют задавать различные параметры кучи, такие как её минимальный и максимальный размер, и определять, должен ли поток использовать общую с другим потоком кучу или создать собственную.

Функция RThread::Open() открывает хендл для потока, найдя его по уникальному идентификатору (возвращается функцией RThread::Id()) или имени.

Поток создается в приостановленном состоянии, поэтому для того чтобы запустить его выполнение необходимо вызвать RThread::Resume().

В Symbian C++ реализуется вытесняющее планирование потоков, и среди готовых к выполнению потоков всегда запускается тот поток, который имеет наибольший приоритет. Если два или более потоков имеют одинаковый приоритет, то их запуск осуществляется циклически на основе квантования времени. Запущенный поток может быть удален из очереди готовых к выполнению потоков в планировщике при помощи вызова функции RThread::Suspend(). Запустить заново поток можно с помощью вызова функции Resume(). Для того чтобы полностью прекратить выполнение потока необходимо вызвать метод Kill() (определяющий обычный выход) или Terminate() (определяющий выход в результате ошибки). Если главный поток процесса по каким-либо причинам завершается, процесс и все другие потоки завершаются вместе с ним. По этой причине, а также для безопасности платформы, Symbian C++ предоставляет несколько примитивов из библиотеки user для синхронизации потоков:

  • Семафор может быть использован либо для отправки сигнала от одного потока к другому, либо для защиты совместно используемых ресурсов, к которым в одно и тоже время имеют доступ множество потоков. В Symbian C++ создание и доступ к семафору осуществляется при помощи класса RSemaphore. Глобальные семафоры могут быть созданы, открыты и использованы любым процессом в системе, в то время как к локальному семафору получают доступ все потоки единственного процесса (который и создает семафор). Семафоры могут быть использованы для разграничения параллельного доступа к совместно используемым ресурсам, предоставляя его либо одному потоку, либо указанному числу потоков в определенный момент времени.
  • Мьютексы используются для защиты совместно используемых ресурсов таким образом, что доступ к ним получает только один поток в определенный момент времени. В Symbian C++ класс RMutex используется для создания и доступа к глобальным и локальным мьютексам.
  • Критическая секция — это участок кода, в который невозможен одновременный вход нескольких потоков. Например, код, работающий с глобальными статическими данными (могут возникнуть проблемы, если несколько потоков будут изменять данные одновременно). Symbian C++ предоставляет класс RCriticalSection, позволяющий только одному потоку в процессе выполнять код критической секции. Другие потоки, которые пытаются получить доступ к этому участку кода, будут блокированы до тех пор, пока первый поток не выполнит секцию до конца. Объекты класса RCriticalSection всегда локальны по отношению к процессу. Нельзя использовать критическую секцию для потоков, принадлежащих различным процессам — вместо этого должны применяться мьютексы и семафоры.

Более подробную информацию о работе с потоками в Symbian C++ вы можете найти в бSymbian Developer Library. Пример, демонстрирующий показывает возможности RThread и других системных классов Symbian C++, можно найти в SDK.

Межпроцессное взаимодействие (IPC — Inter-Process Communication)

Клиент-серверная архитектура

Статья Fundamentals of Symbian C++/Client-Server Framework описывает общепринятую форму межпроцессного взаимодействия (IPC) в Symbian ОС — архитектуру клиент-сервера.

Клиенты подключаются к серверу (в этом контексте термин 'сервер' используется в значении специальной сущности Symbian C++, а не вэб-сервера или что-то похожего на него) и устанавливает сессию для дальнейшего взаимодействия, которое состоит из запросов клиента и ответов сервера при посредничестве ядра системы. Основанное на сессиях взаимодействие, гарантирует, что все клиенты будут извещены в случае ошибки или завершения работы сервера и, наоборот, если произойдет ошибка или отключение\завершение работы клиента, все ресурсы сервера будут освобождены.

Этот тип взаимодействия идеален, когда многим клиентам требуется надежный параллельный доступ к сервису или совместно используемым ресурсам. Однако, существуют ограничения:

  • Клиенты обязаны знать, какой сервер предоставляет сервис, в котором они нуждаются.
  • Должна поддерживаться постоянная сессия между клиентом и сервером.
  • На самом деле это не является подходящим решением для широковещательных систем (когда сервер инициирует 'широковещательное' сообщение множеству клиентов).

Существуют дополнительные IPC механизмы: механизм издатель-подписчик (Publish & Subscribe), очереди сообщений и совместно используемый буфер ввода/вывода. Механизм издатель-подписчик и очереди сообщений обсуждаются более детально ниже. Совместно используемый буфер ввода/вывода не обсуждается, потому что это средство предназначено преимущественно для создателей драйверов устройств (позволяет драйверу и его клиенту получить доступ к одной и той же памяти без копирования, даже во время обработки прерывания).

Издатель и подписчик

Механизм издатель-подписчик (часто сокращается до P&S - от английского Publish & Subscribe) был создан для обеспечения асинхронного группового оповещения о событии, позволяющий потокам взаимодействовать без установки соединения. P&S может быть использован, когда компонент должен получить или отправить своевременную и периодическую информацию от или к неизвестного количества и типа заинтересованных сторон, в то время как сам остается не соединенный с ними. Типичным примеров такой ситуации является оповещение об изменении состояния устройства, например, отключение/включение режима flight-mode (режим работы телефона без GSM модуля), Bluetooth, WiFi и так далее.

P&S может быть использован на стороне ядра платформы Symbian.

P&S обладает средством для определения и публикации изменений глобальных переменных известных как 'свойства'. Об изменениях свойств может быть асинхронно сообщено (опубликовано, издано) более чем одной заинтересованной стороне (подписчику). Издатели и подписчики могут динамически присоединяться и отсоединяться без какой-либо процедуры установки или завершения соединения.

Свойства уникальны и идентифицируются с помощью 64-битного целого числа. Это единственная информация, которая должна быть распространена между издателями и подписчиками (обычно это делается через общий заголовочный файл). Нет необходимости создавать интерфейсные классы или функции для свойств. Подписчикам нет потребности знать, какой компонент публикует свойство. Они только должны знать P&S API, и идентифицировать свойства, которые интересуют их.

В P&S API имеется класс RProperty. Свойство, на изменение которого он позволяет подписаться, идентифицируется с помощью двух частей:

  • Категория (определяется стандартным UID), к которой принадлежит свойство.
  • Ключ, который уникально идентифицирует свойство в категории. Значение этого ключа зависит от того, как нумеруются свойства в категории.

Однажды определенное, свойство содержит единственную переменную для хранения данных, и может быть либо 32-битным целым числом, либо массивом байт (дескриптор) длинной до 512 байт, либо Unicode текстом (также длинной до 512 байт), либо большим массивом байт размером до 65,536.

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

Нет необходимости в том, чтобы свойство было определено до того как к нему осуществляется доступ ('ленивое' определение (lazy definition)), поэтому не возникнет программной ошибки для свойства, которое публикуется перед тем как определяется. Это явление известно как спекулятивная публикация (speculative publishing).

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

Свойство публикуется с помощью вызова RProperty::Set(). Этот метод записывает новое значение в свойство атомарно, что гарантирует, что доступ множества потоков будет обработан правильно. Когда свойство опубликовано, все известные подписчики получают об этом извещение, даже если значение свойства не было изменено. Это позволяет использовать свойства в качестве простого широковещательного извещения.

Для того чтобы подписаться на свойство клиент обязан зарегистрировать свой интерес к нему. Для этого необходимо присоединится к свойству и вызывать асинхронный метод RProperty::Subscribe(). Извещение проходит следующие стадии:

  • Клиент регистрирует свой интерес к свойству, присоединяясь к нему (RProperty::Attach()) и вызывая асинхронный метод RProperty::Subscribe(). Для получения результата по ссылке передается объект TRequestStatus.
  • После публикации нового значения, клиент получает извещение через посыланный объекту класса TRequestStatus сигнал. Таким образом завершается выполнение асинхронного запроса.
  • Клиент получает измененное свойство вызовом RProperty::Get().
  • Клиент может подписаться заново на получение извещения об изменении свойства, снова вызвав метод Subscribe().

Присоединение к неопределенному свойству не является ошибкой. Более того, запрос Subscribe() не будет завершен, пока либо свойство не будет определено и опубликовано, либо подписчик не откажется от подписки, отменив запрос вызовом RProperty::Cancel().

P&S и безопасность платформы

Для того чтобы гарантировать разделение процессов таким образом, чтобы один процесс не мог вмешиваться в свойство другого процесса, UID категории должен совпадать с идентификатором безопасности (SID) процесса, определяющего свойство. Иначе процесс, вызывающий RProperty::Define(), обязан декларировать доступ к защищенной возможности WriteDeviceData (смотри Fundamentals of Symbian C++/Platform Security).

Свойства также должны быть определены в соответствии с политикой безопасности (используя TSecurityPolicy объекты):

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

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

Очереди сообщений

В отличие от ориентированной на соединения природой клиент-серверного механизма IPC, очереди сообщений (RMsgQueue) предлагают однонаправленный один-ко-одному или многие-ко-многим механизм общения. Очереди сообщений обеспечивают отправку сообщений всем заинтересованным сторонам без потребности в знании идентификатора получателя или даже без прослушивания каким-либо потоком; на самом деле сообщения отправляются в очередь, а не определенному получателю. Очередь может быть доступна множеству писателей и читателей.

Сообщение — это объект, который отправляется в очередь для доставки получателям. Очередь обычно создается для сообщений заданного типа. Это означает, что очередь создается для того чтобы оперировать сообщениями заданной (фиксированной) длины, которая должна быть кратна 4 байтам. Размер очереди (максимальное количество сообщений (слотов), которая она может содержать) также фиксируется при создании очереди. Максимальный размер сообщений, для которых создается очередь, и максимальный размер очереди ограничиваются только ресурсами системы.

Очередь сообщений позволяет двум или более потокам обмениваться сообщениями без установки соединения между ними, каждая очередь не имеет определенного направления. Это механизм для передачи данных:

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

Очереди сообщений действуют по принципу 'запустил и забыл', что хорошо подходит для уведомления о событии.

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

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

RPipe

Конвейеры доступны с Symbian ОС v9.3, однако на время написания статьи класс не был еще опубликован в API.

Licence icon cc-by-sa 3.0-88x31.png© 2010 Symbian Foundation Limited. This document is licensed under the Creative Commons Attribution-Share Alike 2.0 license. See http://creativecommons.org/licenses/by-sa/2.0/legalcode for the full terms of the license.
Note that this content was originally hosted on the Symbian Foundation developer wiki.

This page was last modified on 9 December 2011, at 02:58.
53 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×