_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]Идея техники менеджеров ресурсов столь же проста, сколь и остроумна:
• Вся система построена на тщательно проработанной в теории (и редко используемой при построении реальных ОС) концепции - коммутации сообщений. Ядро (точнее «микроядро») операционной системы при таком подходе выступает в качестве компактного коммутатора сообщений между взаимодействующими программными компонентами. При этом взаимодействующие компоненты выступают в качестве клиента, запрашивающего услугу (ресурс), и сервера, обеспечивающего эту услугу (обслуживающего ресурс).
• Большинство системных вызовов API (в том числе все «привычные» POSIX-вызовы:
open
,
read
,
write
,
seek
,
close
…) реально посылаются обслуживающему данный ресурс сервису (например, в файловую систему типа FAT32 —
fs-dos
) в виде сообщений уровня микроядра. Код сообщения при этом определяет тип операции (например,
open
), а последующее тело сообщения — конкретные параметры запроса, зависящие от типа операции (параметры запроса пакуются в тело сообщения).
• Раз эта схема столь универсальна, единообразна и не зависит от конкретной природы ресурса, на котором обеспечивается обслуживание, то разработчики QNX предоставляют некоторый шаблон сервера, в котором на месте обработчиков стандартных POSIX-запросов находятся пустые программные заглушки. Этот шаблон и служит базовым элементом построения разнообразных серверов услуг, называемых при выполнении в такой технике «менеджерами ресурса».
• При запуске программа менеджера ресурса регистрирует свое имя (точнее имя управляемого ею ресурса) в пространстве имен файловой системы QNX (обычно в каталоге
/dev
, но это может быть любое место файловой системы). Теперь можно обращаться с запросами к данному менеджеру так же, как и к любому реальному файлу в файловой системе.
• Программисту, пишущему свой драйвер услуги, ресурса, устройства или псевдоустройства, остается только переопределить программное наполнение тех программных заглушек, которые ответственны за интересующие его вызовы (например,
open
,
read
,
close
), никак не затрагивая вызовы, не обеспечиваемые этим ресурсом (например,
write
,
seek
и др.).
В наши цели не входит детальное обсуждение техники написания менеджеров ресурсов (этому посвящено специальное исчерпывающее руководство в составе технической документации QNX объемом более 80 страниц
[42]). Поэтому, как и ранее с динамическим пулом потоков, начнем с примера. Приведем простейший код менеджера ресурса, который использовался нами для тестирования наследования приоритетов в QNX (
#include
#include
#include
#include
#include
#include
#include
#include
#include
// обработчик запроса от клиента 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, ¶m);
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);