Чтение онлайн

ЖАНРЫ

QNX/UNIX: Анатомия параллелизма
Шрифт:
Примечание

Управляющая структура пула потоков описана так:

typedef struct _thread_pool thread_pool_t;

struct _thread_pool {

thread_pool_attr_t pool_attr;

unsigned created;

unsigned waiting;

unsigned flags;

unsigned reserved[3];

};

3. Последний шаг в процедуре запуска пула потоков:

int thread_pool_start(void* pool);

где

pool
— это указатель, возвращаемый
thread_pool_create
. [40]

При

успешном завершении (которого почти никогда не происходит, за исключением значения флага
0
; об этом см. выше) функция возвращает
EOK
, в противном случае (что происходит гораздо чаще) — значение
– 1
.

4. Другие, относящиеся к библиотеке динамического пула потоков функции, которые целесообразно посмотреть в документации QNX (но которые в силу различных обстоятельств используются гораздо реже):

40

Вы спросите, почему указатель, возвращаемый

thread_pool_create
, имеет тип
thread_pool_t*
, а получающий его параметр
thread_pool_start
определен как
void*
? Это или неаккуратность разработчиков QNX, или глубокая сермяжная правда, которую мы пока не понимаем.

int thread_pool_destroy(thread_pool_t* pool);

int thread_pool_control(thread_pool_t* pool, thread_pool_attr_t* attr,

_Uint16t lower, _Uint16t upper, unsigned flags);

int thread_pool_limits(thread_pool_t* pool,

int lowater, int hiwater, int maximum, int increment, unsigned flags);

Менеджеры ресурсов

QNX вводит технику программирования, которая единообразно проходит сквозь всю систему. [41] Идея техники менеджеров ресурсов столь же проста, сколь и остроумна:

• Вся система построена на тщательно проработанной в теории (и редко используемой при построении реальных ОС) концепции - коммутации сообщений. Ядро (точнее «микроядро») операционной системы при таком подходе выступает в качестве компактного коммутатора сообщений между взаимодействующими программными компонентами. При этом взаимодействующие компоненты выступают в качестве клиента, запрашивающего услугу (ресурс), и сервера, обеспечивающего эту услугу (обслуживающего ресурс).

41

Эта техника возникла не «сразу» и не случайно: ее идеология практически сложилась за почти 20 лет развития системы QNX, но не была представлена в виде формальных механизмов. В QNX 6.X оставалось только придать ей формальный вид и обеспечить ее поддержание написанием специальных библиотек.

• Большинство системных вызовов API (в том числе все «привычные» POSIX-вызовы:

open
,
read
,
write
,
seek
,
close
…) реально посылаются обслуживающему данный ресурс сервису (например, в файловую систему типа FAT32 —
fs-dos
) в виде сообщений уровня микроядра. Код сообщения при этом определяет тип операции (например,
open
), а последующее тело сообщения — конкретные параметры запроса, зависящие от типа операции (параметры запроса пакуются в тело сообщения).

• Раз эта схема столь универсальна, единообразна и не зависит от конкретной природы ресурса, на котором обеспечивается обслуживание, то разработчики QNX предоставляют некоторый шаблон сервера, в котором на месте обработчиков стандартных POSIX-запросов находятся пустые программные заглушки. Этот шаблон и служит базовым элементом построения разнообразных серверов услуг, называемых при выполнении в такой технике «менеджерами ресурса».

• При запуске программа менеджера ресурса

регистрирует свое имя (точнее имя управляемого ею ресурса) в пространстве имен файловой системы QNX (обычно в каталоге
/dev
, но это может быть любое место файловой системы). Теперь можно обращаться с запросами к данному менеджеру так же, как и к любому реальному файлу в файловой системе.

• Программисту, пишущему свой драйвер услуги, ресурса, устройства или псевдоустройства, остается только переопределить программное наполнение тех программных заглушек, которые ответственны за интересующие его вызовы (например,

open
,
read
,
close
), никак не затрагивая вызовы, не обеспечиваемые этим ресурсом (например,
write
,
seek
и др.).

В наши цели не входит детальное обсуждение техники написания менеджеров ресурсов (этому посвящено специальное исчерпывающее руководство в составе технической документации QNX объемом более 80 страниц [42] ). Поэтому, как и ранее с динамическим пулом потоков, начнем с примера. Приведем простейший код менеджера ресурса, который использовался нами для тестирования наследования приоритетов в QNX ( файл prior.cc):

42

В Интернете доступен прекрасный перевод этого документа, выполненный Владимиром Зайцевым и отредактированный к публикации Сергеем Малышевым: http://qnxclub.net/files/articles/resmgr/resmgr.pdf.gz.

Однопоточный менеджер ресурса

#include <errno.h>

#include <stdio.h>

#include <stddef.h>

#include <stdlib.h>

#include <unistd.h>

#include <string.h>

#include <pthread.h>

#include <sys/iofunc.h>

#include <sys/dispatch.h>

// обработчик запроса от клиента read,

// возвращающий текущий приоритет обслуживания

static int prior_read(resmgr_context_t *ctp, io_read_t *msg,

RESMGR_OCB_T *ocb) {

static bool odd = true;

int status = iofunc_read_verify(ctp, msg, ocb, NULL);

if (status != EOK) return status;

if (msg->i.xtype & _IO_XTYPE_MASK != _ID_XTYPE_NONE)

return ENOSYS;

if (odd) {

struct sched_param param;

sched_getparam(0, &param);

static char rbuf[4];

sprintf(rbuf, "%d\n", param.sched_curpriority);

MsgReply(ctp->rcvid, strlen(rbuf) + 1, rbuf, strlen(rbuf) + 1);

} else MsgReply(ctp->rcvid, EOK, NULL, 0);

odd = !odd;

return _RESMGR_NOREPLY;

}

// главная программа запуска менеджера

main(int argc, char **argv) {

resmgr_attr_t resmgr_attr;

dispatch_t *dpp;

dispatch_context_t *ctp;

int id;

// инициализация интерфейса диспетчеризации

if ((dpp = dispatch_create) == NULL)

perror("allocate dispatch"), exit(EXIT_FAILURE);

Поделиться с друзьями: