注:本文翻译自
QNX Software Development Platform --> Programming --> Getting Started with QNX Neutrino --> Resource Managers
http://www.qnx.com/developers/docs/7.1/index.html#com.qnx.doc.neutrino.getting_started/topic/s1_resmgr_the_resource_manager_library.html
一、示例
在深入讨论有关资源管理器的所有问题之前,我们必须熟悉 QNX Neutrino 资源管理器库。
这个“库”实际上由几个不同的部分组成:
(1) 线程池函数(我们在 Processes and Threads chapter under “Pools of threads” 中讨论过)
(2) 分发接口
(3) 资源管理器功能
(4) POSIX 库帮助函数
虽然您当然可以“从头开始”编写资源管理器(就像在 QNX 4 世界中所做的那样),但这比起它的价值要麻烦得多。
只是为了向您展示库方法的实用性,这里是“/dev/null”的单线程版本的源代码:
/* * resmgr1.c * /dev/null using the resource manager library */ #include <stdio.h> #include <stdlib.h> #include <stddef.h> #include <string.h> #include <sys/iofunc.h> #include <sys/dispatch.h> int main (int argc, char **argv) { dispatch_t *dpp; resmgr_attr_t resmgr_attr; dispatch_context_t *ctp; resmgr_connect_funcs_t connect_func; resmgr_io_funcs_t io_func; iofunc_attr_t attr; //create the dispatch structure dpp = dispatch_create_channel( -1, DISPATCH_FLAG_NOLOCK); if (dpp == NULL) { perror ("Unable to dispatch_create_channel"); exit (EXIT_FAILURE); } // initialize the various data structures memset (&resmgr_attr, 0, sizeof (resmgr_attr)); resmgr_attr.nparts_max = 1; resmgr_attr.msg_max_size = 2048; // bind default functions into the outcall tables iofunc_func_init (_RESMGR_CONNECT_NFUNCS, &connect_func, _RESMGR_IO_NFUNCS, &io_func); iofunc_attr_init (&attr, S_IFNAM | 0666, 0, 0); // establish a name in the pathname space if (resmgr_attach(dpp, &resmgr_attr, "/dev/mynull", _FTYPE_ANY, 0, &connect_func, &io_func, &attr) == -1) { perror ("Unable to resmgr_attach"); exit (EXIT_FAILURE); } ctp = dispatch_context_alloc(dpp); // wait here forever, handling messages while (1) { if ((ctp = dispatch_block(ctp)) == NULL) { perror("Unable to dispatch_block"); exit(EXIT_FAILURE); } dispatch_handler(ctp); } }
你有它! 通过几个函数调用即可实现一个完整的 /dev/null 资源管理器!
如果您要从头开始编写此代码,并让它支持该函数的所有功能(例如 stat() 工作、chown() 和 chmod() 工作等),您将看到数百个,如果不是数千行 C 代码。
The library really does what we just talked about
通过对该库的介绍,让我们(简要地)看看这些调用在 /dev/null 资源管理器中做了什么。
Behind the scenes at the library
您已经看到您的代码负责提供主消息接收循环。
二、The library really does what we just talked about
通过对该库的介绍,让我们(简要地)看看这些调用在 /dev/null 资源管理器中做了什么。
dispatch_create_channel()
创建调度结构; 这将用于阻塞在消息接收上。 有一个更简单的函数,称为 dispatch_create(),但大多数资源管理器应该设置 DISPATCH_FLAG_NOLOCK,以便使用无锁消息查找 【?】(有关详细信息,请参阅C 库参考中的 dispatch_create_channel() 条目)。
iofunc_attr_init()
初始化设备使用的属性结构。 稍后我们将更深入地讨论属性结构,但现在,简短的故事是每个设备名称都有一个属性结构,它们包含有关特定设备的信息。
iofunc_func_init()
初始化两个数据结构 cfuncs 和 ifuncs,它们分别包含指向 connect 和 I/O 函数的指针。 您可能会说这个调用最“神奇”,因为这是处理所有消息的实际“工作”例程绑定到数据结构的地方。 我们实际上没有看到任何处理连接消息的代码,或者客户端 read() 或 stat() 函数调用产生的 I/O 消息。 这是因为库为我们提供了这些函数的默认 POSIX 版本,并且 iofunc_func_init() 函数将这些相同的默认处理函数绑定到提供的两个tables中。
resmgr_attach()
创建资源管理器用于接收消息的通道, 并与进程管理器对话,告诉它我们将负责“/dev/null”。 虽然有很多参数,但我们稍后会详细了解它们。 现在,需要注意的是,这是调度句柄 (dpp)、路径名(字符串 /dev/null)以及连接 (cfuncs) 和 I/O (ifuncs) 消息处理程序全部绑定在一起的地方。
dispatch_context_alloc()
分配一个分发内部上下文块。 它包含与正在处理的消息相关的信息。
调用 dispatch_context_alloc()后,请勿调用 message_attach()或 resmgr_attach()为同一分发句柄指定更大的最大消息大小或更多数量的消息部分。 在 QNX Neutrino 7.0 或更高版本中,如果发生这种情况,这些函数会指示 EINVAL 错误。 (这不适用于 pulse_attach() 或 select_attach(),因为您无法使用这些函数指定大小。)
dispatch_block()
这是调度层的阻塞调用; 这是我们等待客户端发送消息的地方。
dispatch_handler()
一旦消息从客户端到达,就会调用该函数来处理它。
三、Behind the scenes at the library
您已经看到您的代码负责提供主消息接收循环。
while (1) { // wait here for a message if ((ctp = dispatch_block(ctp)) == NULL) { perror ("Unable to dispatch_block"); exit(EXIT_FAILURE); } // handle the message dispatch_handler(ctp); }
这非常方便,因为它允许您在接收函数上放置断点并在操作期间拦截消息(可能使用调试器)。
该库在 dispatch_handler() 函数内部实现了“魔法”,因为这是通过我们前面提到的连接函数和I/O函数表来分析和处理消息的地方。
实际上,该库由两个协作层组成:一个提供“原始”资源管理器功能的基础层,以及一个提供 POSIX 帮助程序和默认函数的 POSIX 层。 我们将简要定义这两层,然后在下面的“Resource manager structure”中,我们将了解详细信息。
(1) The base layer
最底层由名称以 resmgr_*() 开头的函数组成。 此类函数涉及使资源管理器工作的机制。
(2) The POSIX layer
资源管理器库提供的第二层是 POSIX 层。 与基础层一样,您可以在不使用资源管理器的情况下编写资源管理器代码,但这将是大量工作! 在详细讨论 POSIX 层功能之前,我们需要了解一些基础层数据结构、来自客户端的消息以及资源管理器的整体结构和职责。
3.1 The base layer
最底层由名称以 resmgr_*() 开头的函数组成。 此类函数涉及使资源管理器工作的机制。
我将简要提及可用的功能以及我们将在何处使用它们。 然后,我将建议您参阅 QNX 文档以获取有关这些函数的更多详细信息。
基础层功能包括:
resmgr_msggetv() 和 resmgr_msgget()
使用复制本地消息缓冲区数据和使用消息传递从客户端地址空间读取数据的最佳组合。 这些函数尽可能避免从客户端地址空间读取数据。
resmgr_msgwritev() 和 resmgr_msgwrite()
使用消息传递将数据写入客户端的地址空间。
resmgr_open_bind()
将上下文与连接函数相关联,以便稍后可由 I/O 函数使用。
resmgr_attach()
创建通道,将路径名、分发句柄、连接函数、I/O 函数和其他参数关联在一起。 向进程管理器发送消息以注册路径名。
resmgr_detach()
与 resmgr_attach() 相反; 解除路径名和资源管理器的绑定。
pulse_attach()
将脉冲代码与功能相关联。 由于该库实现了消息接收循环,因此这是处理脉冲“获得控制”的便捷方法。
pulse_detach()
将脉冲代码与功能分离。
除了上面列出的函数之外,还有许多处理调度接口的函数。
上面列表中值得特别提及的一个函数是 resmgr_open_bind()。 当连接消息(通常是客户端调用 open() 或 fopen() 的结果)到达时,它会关联某种形式的上下文数据,以便在处理 I/O 消息时存在该数据块。 为什么我们在 /dev/null 处理程序中没有看到这个? 因为POSIX层默认函数为我们调用了这个函数。 如果我们自己处理所有消息,我们肯定会调用这个函数。
resmgr_open_bind() 函数不仅为进一步的 I/O 消息设置上下文块,而且还初始化资源管理器库本身使用的其他数据结构。
上面列表中的其余函数有些直观 - 我们将推迟对它们的讨论,直到我们使用它们为止。
3.2 The POSIX layer
资源管理器库提供的第二层是 POSIX 层。 与基础层一样,您可以在不使用资源管理器的情况下编写资源管理器代码,但这将是大量工作! 在详细讨论 POSIX 层功能之前,我们需要了解一些基础层数据结构、来自客户端的消息以及资源管理器的整体结构和职责。
- QNX Managers Resource resource managerqnx managers resource manager qnx managers resource routines qnx managers resource writing qnx managers examples resource qnx managers resource文档 开篇qnx managers resource qnx managers advanced resource managers multiprocessing-managers generative generates managers choices