exit(EXIT_SUCCESS);
управление никогда не дойдет до выполнения exit(). Но существует еще третье допустимое значение, прямо не указанное в документации, но мельком упоминаемое в других местах документации:
•
0
— после старта пула поток, вызвавший
thread_pool_start()
, продолжает свое естественное выполнение.
Например, некоторый фрагмент кода мог бы выглядеть так:
thread_pool_attr_t att; // ...
thread_pool_t *tpp = thread_pool_create(&attr, 0);
thread_pool_start(tpp);
while (true) {
// выполнять некоторую отличную от пула работу
}
exit(EXIT_SUCCESS);
Как уже понятно из описаний,
thread_pool_create()
возвращает указатель на управляющую структуру пула потоков, которая позже будет передана
thread_pool_start()
. Если создание пула завершилось неудачей, то результатом выполнения будет
NULL
, а в
errno
будет установлен код ошибки (документацией предусмотрен только один код ошибки:
ENOMEM
— недостаточно памяти для размещения структур данных).
Управляющая структура пула потоков описана так:
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 (но которые в силу различных обстоятельств используются гораздо реже):
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]Идея техники менеджеров ресурсов столь же проста, сколь и остроумна:
• Вся система построена на тщательно проработанной в теории (и редко используемой при построении реальных ОС) концепции - коммутации сообщений. Ядро (точнее «микроядро») операционной системы при таком подходе выступает в качестве компактного коммутатора сообщений между взаимодействующими программными компонентами. При этом взаимодействующие компоненты выступают в качестве клиента, запрашивающего услугу (ресурс), и сервера, обеспечивающего эту услугу (обслуживающего ресурс).
• Большинство системных вызовов API (в том числе все «привычные» POSIX-вызовы:
open()
,
read()
,
write()
,
seek()
,
close()
…) реально посылаются обслуживающему данный ресурс сервису (например, в файловую систему типа FAT32 —
fs-dos
) в виде сообщений уровня микроядра. Код сообщения при этом определяет тип операции (например,
open()
), а последующее тело сообщения — конкретные параметры запроса, зависящие от типа операции (параметры запроса пакуются в тело сообщения).
• Раз эта схема столь универсальна, единообразна и не зависит от конкретной природы ресурса, на котором обеспечивается обслуживание, то разработчики QNX предоставляют некоторый шаблон сервера, в котором на месте обработчиков стандартных POSIX-запросов находятся пустые программные заглушки. Этот шаблон и служит базовым элементом построения разнообразных серверов услуг, называемых при выполнении в такой технике «менеджерами ресурса».
• При запуске программа менеджера ресурса регистрирует свое имя (точнее имя управляемого ею ресурса) в пространстве имен файловой системы QNX (обычно в каталоге
/dev
, но это может быть любое место файловой системы). Теперь можно обращаться с запросами к данному менеджеру так же, как и к любому реальному файлу в файловой системе.