QNX/UNIX: Анатомия параллелизма
Шрифт:
// из фактической длины блока передаваемых данных
const unsigned int DCMD_CMD = 1,
DCMD_SRR = _POSIX_DEVDIR_TOFROM + (_DCMD_NET << 8) + DCMD_CMD;
// структура ответов менеджера ресурса по запросу read
struct result {
pid_t pid;
int chid;
uint64_t cps;
result(void) {
pid = getpid;
//
если уж возвращать, то и служебную информацию ;)
cps = SYSPAGE_ENTRY(qtime)->cycles_per_sec;
}
}
// завершение с извещением кода причины
inline void exit(const char *msg) {
cout << '\r';
perror(msg);
exit(EXIT_FAILURE);
}
В этой части каких-либо комментариев заслуживает разве что структура
result
. Наш сервер устроен «наоборот»: информационный обмен данными он осуществляет по запросу devctl
, запрос read
нам «не нужен», и мы используем его только для возврата информации (PID + CHID) об автономном канале обмена сообщениями. Заодно мы передаем в поле cps этой структуры значение тактовой частоты процессора сервера, что позволяет знать, «с кем мы имеем дело» при экспериментах в сети. Теперь мы вполне готовы написать код сервера. Этот сервер ( файл srv.cc), в отличие от традиционных, имеет два независимых канала подключения: как менеджер ресурсов и как сервер простого обмена сообщениями. Как менеджер он по запросу
read
возвращает адресные компоненты (PID, CHID) для обмена сообщениями (ND клиент должен восстановить самостоятельно). По запросу devctl
, а также по запросу по автономному каналу обмена сообщениями сервер просто ретранслирует обратно полученный от клиента блок данных (в каком-то смысле по обоим каналам обмена наш сервер является «зеркалом», отражающим данные). Сервер запросов
result data;
//---------------------------------------------------------
// реализация обработчика read
static int readfunc(resmgr_context_t *ctp, io_read_t *msg,
iofunc_ocb_t *ocb) {
int sts = iofunc_read_verify(ctp, msg, ocb, NULL);
if (sts != EOK) return sts;
// возвращать одни и те же статические данные...
MsgReply(ctp->rcvid, sizeof(result), &data, sizeof(result));
return _RESMGR_NOREPLY;
}
//---------------------------------------------------------
// реализация обработчика devctl.
static int devctlfunc(resmgr_context_t *ctp, io_devctl_t *msg,
iofunc_ocb_t *ocb) {
int sts = iofunc_devctl_default(ctp, msg, ocb);
if (sts != _RESMGR_DEFAULT) return sts;
//
разбор динамически создаваемого кода devctl,
// извлечение из него длины принятого блока
unsigned int nbytes = (msg->i.dcmd - DCMD_SRR) >> 16;
msg->o.nbytes = nbytes;
// и тут же ретрансляция блока назад
return _RESMGR_PTR(ctp, &msg->i, sizeof(msg->i) + nbytes);
}
//---------------------------------------------------------
// установка однопоточного менеджера, выполняемая
// в отдельном потоке
static void* install(void* data) {
dispatch_t *dpp;
if ((dpp = dispatch_create) == NULL)
exit("dispatch allocate");
resmgr_attr_t resmgr_attr;
memset(&resmgr_attr, 0, sizeof(resmgr_attr));
resmgr_attr.nparts_max = 1;
resmgr_attr.msg_max_size = 2048;
static resmgr_connect_funcs_t connect_funcs;
static resmgr_io_funcs_t io_funcs;
iofunc_func_init(_RESMGR_CONNECT_NFUNCS, &connect_funcs,
_RESMGR_IO_NFUNCS, &io_funcs);
// определяем обработку, отличную от обработки по умолчанию,
// только для двух команд: read & devctl
io_funcs.read = &readfunc;
io_funcs.devctl = &devctlfunc;
static iofunc_attr_t attr;
iofunc_attr_init(&attr, S_IFNAM | 0666, 0, 0);
// связываем менеджер с его префиксным именем
if (resmgr_attach(dpp, &resmgr_attr, DEVNAME,
_FTYPE_ANY, 0, &connect_funcs, &io_funcs, &attr) == -1)
exit("prefix attach");
dispatch_context_t* ctp = dispatch_context_alloc(dpp);
while (true) {
if ((ctp = dispatch_block(ctp)) == NULL)
exit("block error");
dispatch_handler(ctp);
}
}
// размер буфера для обмена сообщениями,
// этого нам хватит с большим запасом и надолго ;)
Поделиться с друзьями: