QNX-9—QNX官网文档翻译—Resource Managers—Handler routines

发布时间 2023-07-09 20:47:34作者: Hello-World3

注:本文翻译自
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_routines.html


并非所有outcalls都对应于客户端消息 —— 有些是由内核合成的,有些是由库合成的

我将本节分为以下几部分:

general notes
connect functions notes

本节后面是按字母顺序排列的连接和 I/O 消息列表。

(1) General notes

每个处理函数都会传递一个内部上下文块(ctp 参数),该块应被视为“只读”(iov 成员除外)。 该上下文块包含一些感兴趣的项目,如上面“resmgr_context_t internal context block.”中所述。 此外,每个函数都会传递一个指向消息的指针(在 msg 参数中)。 您将广泛使用此消息指针,因为它包含客户端的 C 库调用放置在那里供您使用的参数。

(2) Connect functions notes

然而,在我们深入研究各个消息之前,值得指出的是,连接函数都具有相同的消息结构(稍微重新排列,请参阅 <sys/iomsg.h> 了解原始消息):


一、General notes

每个处理函数都会传递一个内部上下文块(ctp 参数),该块应被视为“只读”(iov 成员除外)。 该上下文块包含一些感兴趣的项目,如上面“resmgr_context_t 内部上下文块”中所述。 此外,每个函数都会传递一个指向消息的指针(在 msg 参数中)。 您将广泛使用此消息指针,因为它包含客户端的 C 库调用放置在那里供您使用的参数 ########。

您提供的函数必须返回一个值(所有函数的原型都以 int 形式返回)。 这些值是从以下列表中选择的:

_RESMGR_NOREPLY

向资源管理器库指示它不应执行 MsgReplyv() — 假设您已在处理程序函数中自行执行了该操作,或者稍后将执行该操作。

_RESMGR_NPARTS ( n )

资源管理器库在执行 MsgReplyv() 时应返回 n 部分 IOV(IOV 位于 ctp->iov 中)。 您的函数负责填充 ctp 结构的 iov 成员,然后返回具有正确数量的部分的 _RESMGR_NPARTS()。

ctp 的 iov 成员是动态分配的,因此它必须足够大才能容纳您写入 iov 成员的数组元素数量! 有关设置 nparts_max 成员的信息,请参阅上面的“resmgr_attr_t 控制结构”部分。

_RESMGR_DEFAULT

这指示资源管理器库执行低级默认函数(这与 iofunc_*_default() 函数不同!)您很少使用此返回值。 一般来说,它会导致资源管理器库向客户端返回ENOSYS的errno,这表明该功能不受支持。

An errno value

指示资源管理器库应使用该值作为错误参数来调用 MsgError()。 这通常会导致客户端函数(例如 open())返回 -1 并将客户端的 errno 设置为返回值。

_RESMGR_ERRNO(errno)

(已弃用)此返回值已用于“包装” errno 编号作为消息的返回值。 例如,如果客户端向只读设备发出 open() 请求,则返回错误值 EROFS 是适当的。 由于此函数已被弃用,因此您可以直接返回错误号,而不是用 _RESMGR_ERRNO() 宏包装它(例如, return (EROFS); 而不是更麻烦的 return (_RESMGR_ERRNO (EROFS));。)

_RESMGR_PTR ( ctp, addr, len )

这是一个方便的宏,它接受上下文指针 ctp,并填充其第一个 IOV 元素以指向 addr 指定的地址,长度为 len 指定,然后将 _RESMGR_NPARTS (1) 的等效项返回到库。 如果您从函数返回单部分 IOV,则通常会使用此选项。

Locking, unlocking, and combine message handling

当我们查看 readblock() 时(在“组合消息”中),我们看到了组合消息的客户端。 客户端能够自动构造一条消息,其中包含与函数 lseek() 和 read() 相对应的多个资源管理器“子消息”。 从客户端的角度来看,两个(或更多)函数至少是原子发送的(并且,由于消息传递的性质,将由资源管理器原子接收)。 我们还没有讨论的是如何确保消息被原子处理。


1.1 Locking, unlocking, and combine message handling

当我们查看 readblock() 时(在 “Combine messages” 中),我们看到了组合消息的客户端。 客户端能够自动构造一条消息,其中包含与函数 lseek() 和 read() 相对应的多个资源管理器“子消息”。 从客户端的角度来看,两个(或更多)函数至少是原子发送的(并且,由于消息传递的性质,将由资源管理器原子接收)。 我们还没有讨论的是如何确保消息被原子处理。

此讨论不仅适用于组合消息,还适用于资源管理器库接收到的所有 I/O 消息(关闭消息除外,我们稍后会讨论该消息)。

资源管理器库所做的第一件事是锁定与接收到的消息所使用的资源相对应的属性结构。 然后,它处理传入消息中的一条或多条子消息。 最后,解锁属性结构。#########

这确保了传入消息被原子处理,因为资源管理器中的其他线程(当然,在多线程资源管理器的情况下)不能在线程忙于使用资源时“跳入”并修改资源。 ######## 如果没有适当的锁定,两个客户端线程都可以发出它们认为是原子组合消息的消息(例如 lseek() 和 read())。 由于资源管理器可能有两个不同的线程在其中运行并处理消息,因此这两个资源管理器线程可能会相互抢占,并且 lseek() 组件可能会相互干扰。 通过锁定和解锁,可以防止这种情况,因为访问资源的每条消息都将以原子方式完整完成。

锁定和解锁资源由默认辅助函数(iofunc_lock_ocb_default() 和 iofunc_unlock_ocb_default())处理,这些函数位于 I/O 表中的 lock_ocb 和 unlock_ocb 位置。 当然,如果您想在此锁定和解锁阶段执行进一步的操作,您可以覆盖这些函数。

请注意,在调用关闭 OCB I/O 函数处理程序之前,资源已解锁。 这是必要的,因为这个特定的函数处理程序释放了 OCB,这将有效地使用于访问属性结构的指针无效,属性结构是存储锁的位置! 另请注意,没有一个连接函数执行此锁定,因为传递给它们的句柄不必是属性结构(并且锁存储在属性结构中)。


二、Connect functions notes

然而,在我们深入研究各个消息之前,值得指出的是,连接函数都具有相同的消息结构(稍微重新排列,请参阅 <sys/iomsg.h> 了解原始消息):

struct _io_connect {
    // Internal use
    uint16_t type;
    uint16_t subtype;
    uint32_t file_type;
    uint16_t reply_max;
    uint16_t entry_max;
    uint32_t key;
    uint32_t handle;
    uint32_t ioflag;
    uint32_t mode;
    uint16_t sflag;
    uint16_t access;
    uint16_t zero;
    uint8_t  eflag;

    // End-user parameters
    uint16_t path_len;
    uint8_t  extra_type;
    uint16_t extra_len;
    char     path [1];
};

您会注意到,我已将 struct _io_connect 结构分为两个区域,“内部使用”部分和“最终用户参数”部分。


2.1 Internal use part

第一部分由资源管理器库用于以下目的的字段组成:

a. 确定客户端发送的消息类型
b. 验证(确保消息不是欺骗的)
c. 跟踪访问模式(由辅助函数使用)

为了简单起见,我建议您始终在所有连接函数中使用辅助函数(iofunc_*_default() 函数)。 这些将返回通过/失败指示,之后,您可以在连接函数中使用 “End-user parameters” 成员。


2.2 End-user parameter part

成员的后半部分直接涉及连接功能的实现:

path_len 和 path

路径长度和作为操作数的路径名(即您正在操作的路径名)。

extra_type 和 extra_len

与连接函数相关的附加参数(例如路径名)。

为了了解如何将路径成员用作“正在操作的路径名”,让我们检查一下类似 rename() 函数的内容。 该函数需要两个路径名; “原始”路径名和“新”路径名。 原始路径名在路径中传递,因为它是正在处理的东西(它是正在进行名称更改的文件名)。 新路径名是操作的参数。 您将看到传递给 connect 函数的额外参数方便地包含指向操作参数的指针 - 在本例中为新路径名。 (在实现方面,新路径名存储在路径指针中原始路径名的后面,并考虑了对齐情况,但您不必对此执行任何操作 - 额外的参数可以方便地为您提供正确的指针。)