// статичность 3-х последующих описаний принципиально важна!
 // (также они могут быть сделаны глобальными переменными файла):
 static resmgr_connect_funcs_t connect_funcs;
 static resmgr_io_funcs_t io_funcs;
 static iofunc_attr_t attr;
 iofunc_func_init(_RESMGR_CONNECT_NFUNCS, &connect_funcs,
  _RESMGR_IO_NFUNCS, &io_funcs);
 // переопределение обработчиков по умолчанию
 io_funcs.read = line_read;
 io_funcs.write = line_write;
 io_funcs.lock_ocb = nolock;
 iofunc_attr_init(&attr, S_IFNAM | 0666, NULL, NULL);
 // через это поле осуществляется связь с новой
 // структурой OCB.
 attr.mount = &mountpoint;
 if (resmgr_attach(dpp, &resmgr_attr, sResName, _FTYPE_ANY, 0,
  &connect_funcs, &io_funcs, &attr) == -1)
  perror('name attach'), exit(EXIT_FAILURE);
 // создание пула потоков (многопоточность)
 thread_pool_attr_t pool_attr;
 memset(&pool_attr, 0, sizeof pool_attr);
 pool_attr.handle = dpp;
 pool_attr.context_alloc = dispatch_context_alloc;
 pool_attr.block_func = dispatch_block;
 pool_attr.handler_func = dispatch_handler;
 pool_attr.context_free = dispatch_context_free;
 pool_attr.lo_water = 2;
 pool_attr.hi_water = 6;
 pool_attr.increment = 1;
 pool_attr.maximum = 50;
 thread_pool_t* tpp;
 if ((tpp = thread_pool_create(&pool_attr, POOL_FLAG_EXIT_SELF)) == NULL)
  perror('pool create'), exit(EXIT_FAILURE);
 thread_pool_start(tpp);
 // к этой точке return управление уже никогда не подойдет...
}
int main(int argc, char *argv[]) {
 // проверка, не загружен ли ранее экземпляр менеджера,
 // 2 экземпляра нам ни к чему...
 char sDirName[_POSIX_NAME_MAX + 1];
 int nDirLen = strrchr((const char*)sResName, '/') - (char*)sResName;
 strncpy(sDirName, sResName, nDirLen);
 sDirName[nDirLen] = ' ';
 DIR *dirp = opendir(sDirName);
 if (dirp == NULL)
  perror('directory not found'), exit(EXIT_FAILURE);
 struct dirent *direntp;
 while (true) {
  if ((direntp = readdir(dirp)) == NULL) break;
  if (strcmp(direntp->d_name, strrchr(sResName, '/') + 1) == 0)
   cout << 'second copy of manager' << endl, exit(EXIT_FAILURE);
 }
 closedir(dirp);
 // старт менеджера
 StartResMng();
 // ... к этой точке мы уже никогда не подойдем...
 exit(EXIT_SUCCESS);
}
В отличие от типового и привычного шаблона многопоточного менеджера, мы проделали здесь дополнительно следующее:
• Определили собственную структуру OCB, новый экземпляр которой должен создаваться для каждого нового подключающегося клиента:
class ownocb : public iofunc_ocb_t { ... };
• Переопределили описание структуры OCB, используемое библиотеками менеджера ресурсов:
#define IOFUNC_OCB_T struct ownocb
• Заполняя атрибутную запись устройства:
attr.mount = &mountpoint;
мы к точке монтирования «привязываем» функции создания и уничтожения вновь определенной структуры OCB (по умолчанию библиотека менеджера станет размещать только стандартный OCB):
iofunc_funcs_t ownocb_funcs = {
 _IOFUNC_NFUNCS, ownocb_calloc, ownocb_free
};
iofunc_mount_t mountpoint = { 0, 0, 0, 0, &ownocb_funcs };
(_IOFUNC_NFUNCS — это просто константа, определяющая число функций и равная 2.)
• Определяем собственные функции размещения и уничтожения структуры OCB с прототипами:
IOFUNC_OCB_T* ownocb_calloc(resmgr_context_t*, IOFUNC_ATTR_T*);
void ownocb_free(IOFUNC_OCB_T *o);
В нашем случае это: а) интерфейс из C-понятия «создать-удалить», в C++ — «конструктор- деструктор» и б) именно здесь создается и инициализируется сколь угодно сложная структура экземпляра OCB.
• В функциях обработки запросов клиента (операций менеджера) мы позже будем в качестве 3-го параметра вызова обработчика получать указатель именно того экземпляра, для которого требуется выполнить операцию, например:
int read(resmgr_context_t*, io_read_t*, IOFUNC_OCB_T*) {...}
Дополнительно мы проделываем еще один трюк, запрещая менеджеру блокировать атрибутную запись устройства при выполнении операций (что он делает по умолчанию; для реальных устройств это резонно, но для программного псевдоустройства это, как правило, не является необходимым). Для этого:
• В таблице операций ввода/вывода переназначаем функцию-обработчик операции блокирования атрибутной записи:
io_funcs.lock_ocb = nolock;
• В качестве такого обработчика предлагаем «пустую» операцию:
static int nolock(resmgr_context_t*, void*, IOFUNC_OCB_T*) {
 return EOK;
}
Запустим менеджер и проверим, как происходит его установка в системе:
/dev # ls -l /dev/w*

 
                