QNX资源管理器

  1. Introduction
    QNX Neutrino允许用户编写进程充当资源管理器,并且可以动态的启动和停止。这样的好处是可以降低系统在运行时的内存需求,并且可以灵活的应对定制化嵌入式系统中的各种设备。
    资源管理器通常负责向各类设备提供接口,这些设备可能涉及管理实际的硬件设备(比如串口、并口、网卡或磁盘驱动器)或虚拟设备(比如/dev/null、网络文件系统、伪ttys等)
    在其他操作系统中,这个功能通常与设备驱动程序相关联,与设备驱动程序不同的是资源管理器不需要与内核进行耦合,看起来更像是用户级程序。
  2. What is a resource manager?
    资源管理器是用户级的服务器程序,它接收来自其他程序的请求服务,并可选的与硬件进行通信。QNX Neutrino强大和灵活的本地IPC机制,可以让资源管理器与内核解耦合。
    由于QNX Neutrino是一个分布式微内核操作系统,几乎所有非内核功能都由用户可安装的程序提供,因此客户端程序与资源管理器之间需要一个定义清晰与良好的接口,并对资源管理器的功能进行文档化。
    在路径名空间映射中,进程管理器会将路径名和资源管理器进行映射绑定,比如串口可以由资源管理器devc-ser*管理,但是实际的路径名空间中名称为dev/ser1,可以通过打开dev/ser1来获取串口服务。
    2.1 Why write a resource manager?
    编写一个资源管理器有以下几个原因:
  • 客户端使用POSIX接口与资源管理器通信;
  • 可以减少接口类型的数量,当有很多服务器进程时,将服务器进程都编写成资源管理器,可以最大化减少客户端需要使用的接口数量;
  • 可以使用命令行工具与资源管理器通信,比如cat /proc/my_status,也可以使用命令去测试驱动程序;
    2.2 The types of resource managers
    一般可以将资源管理器分为两类:
  • 设备资源管理器
  • 文件系统资源管理器
  1. 设备资源管理器
    设备资源管理器只在文件系统中创建单个文件条目,每个条目都向进程管理器注册,每个名称通常代表一个设备。
  2. 文件系统资源管理器
    文件系统资源管理器会向进程管理器注册一个挂载点,挂载点是注册到进程管理器中的路径的一部分。路径的其他部分由文件系统资源管理器管理。比如挂载点是/mount,路径是/mount/home/thomasf,由进程管理器来标识/mount,而由文件系统管理器来标识home/thomasf。
    2.3 Communication via native IPC
    当一个资源管理器绑定好对应的路径名后,它便可以收到客户端的请求信息了,比如io_open、io_read等。
    客户端程序与资源管理器之间的所有通信都是通过本机IPC消息传递完成的,它有许多独特的功能:
  • 定义良好的应用程序接口,客户端与资源管理器分工明确;
  • 资源管理器的接口简单,与OS交互也是通过本地IPC,不需要担心其他模块的影响;
  • 网络传递,底层原生IPC机制本质是网络分布式的,程序可以无缝的访问网络中其他节点的资源;
    所有QNX Neutrino的设备驱动程序和文件系统都是作为资源管理器实现的,这意味着“原生”QNX Neutrino设备驱动程序或文件系统能做的一切,用户编写的资源管理器也能做到
  1. Resource manager architecture
    资源管理器的核心如下:
    initialize the dispatch interface
    register the pathname with the process manager
    DO forever
    receive a message
    SWITCH on the type of message
    CASE io_open:
    perform io_open processing
    ENDCASE
    CASE io_read:
    perform io_read processing
    ENDCASE
    CASE io_write:
    perform io_write processing
    ENDCASE
    . // etc. handle all other messages
    . // that may occur, performing
    . // processing as appropriate
    ENDSWITCH
    ENDDO
    资源管理器架构包括三部分:
  2. 创建一个通道,以便客户端程序可以连接到资源管理器上,并发送消息;
  3. 资源管理器需要向进程管理器注册路径名,以便能在对该特定的路径名请求打开时,能解析到该资源管理器;
  4. 接收并处理消息;
    每个资源管理器都需要消息处理功能(上文代码中的switch/case部分),QNX Neutrino提供了一组库函数来方便的处理这个功能,当然也能处理其他关键功能。
    3.1 Message types
    资源管理器会收到两种类型的消息:
  • connect messages,客户端在操作路径名时(比如io_open)时会发送连接消息,这可能会涉及到权限检查(客户端是否有打开设备的权限),并为该请求设置上下文;
  • I/O messages,基于上下文(客户端和资源管理器之间创建的)来发送I/O消息,比如io_read;
    3.2 The resource manager shared library
    QNX Neutrino提供了一下共享库,可以让资源管理器编写变得相对简单一点。
  1. 自动默认消息处理
    当资源管理器不想处理某些消息时,可以使用默认的动作,目前有两个级别的默认动作:
  • 给客户端返回ENOSYS,表明不支持特定功能;
  • iofunc_*()共享库,允许资源管理器自动处理多种功能;
    由于资源管理器接收的大量消息都处理一组公共属性,iofunc_*共享库允许资源管理器自动处理stat()、chmod()、chown()、lseek()等函数,无需再编写额外代码。有三个主要的结构需要考虑:
  • context,保存每次打开时使用的数据,例如文件中的当前位置(lseek()偏移量);
  • attributes structure,保存每个设备的数据,例如设备所有者的用户和组ID、最后修改时间等;
  • mount structure,对于文件系统管理器来说,需要mount structure,包含对整个挂载设备都可全局访问的数据项;

[图片]

  • A resource manager is responsible for three data structures
    当有多个客户端程序在特定资源上打开多种设备时,数据结构如下图:

[图片]
Multiple clients opening various devices
iofunc_*()默认函数的运行假设是:使用了context块和attribute结构的默认定义,这是一个可靠的假设,原因有二:

  • 默认的context和attribute结构为大多数应用程序提供了足够的信息;
  • 如果默认结构没有包含足够的信息,它们可以封装在自定义的结构中;
    在自定义数据结构时,需要把attribute的结构放在前边,以便iofunc_attr_t *()函数能使用,如下图:
    [图片]
  1. 资源管理器共享库提供了跟踪open()/dup()/close()消息的默认处理函数,并且只对最后一个close执行操作;
  2. 多线程处理
    QNX Neutrino提供多线程,可以基于这个来构造资源管理器,多个线程等待消息并同时处理它们。资源管理器共享库不仅可以跟踪创建的线程数量和等待线程的数量,还负责维护最佳的线程数量。
  3. dispatch功能
    操作系统提供一套`dispatch*函数集,可用于:
  • 给需要多种消息类型的资源管理器和客户端提供一个公共阻塞点;
  • 给没有绑定到资源管理器的消息类型提供灵活的接口;
  • 在线程中将阻塞和处理代码解耦合;
  1. Combine messages
    QNX支持将IO消息或connect消息组合成一个消息,从而进行一些类似原子性的操作。比如通过将io_lseek和io_read消息组合成一个消息,资源管理器在收到这个消息时,readblock()函数就会允许线程去原子性的执行lseek()和read()操作。

  2. Message types详细介绍
    在前文中也提到过,资源管理器需要处理两类消息:

  • connect message,连接消息

  • I/O message, IO消息
    4.1 connect message
    客户端发出connect message来执行基于路径名的操作。当调用resmgr_attach()函数时,会将一个指针传递给resmgr_connect_funcs_t结构,该结构定义了一系列连接函数:
    typedef struct _resmgr_connect_funcs {

    unsigned nfuncs;

    int (*open) (resmgr_context_t *ctp, io_open_t *msg,
    RESMGR_HANDLE_T *handle, void *extra);

    int (*unlink) (resmgr_context_t *ctp, io_unlink_t *msg,
    RESMGR_HANDLE_T *handle, void *reserved);

    int (*rename) (resmgr_context_t *ctp, io_rename_t *msg,
    RESMGR_HANDLE_T *handle,
    io_rename_extra_t *extra);

    int (*mknod) (resmgr_context_t *ctp, io_mknod_t *msg,
    RESMGR_HANDLE_T *handle, void *reserved);

    int (*readlink) (resmgr_context_t *ctp, io_readlink_t *msg,
    RESMGR_HANDLE_T *handle, void *reserved);

    int (*link) (resmgr_context_t *ctp, io_link_t *msg,
    RESMGR_HANDLE_T *handle,
    io_link_extra_t *extra);

    int (*unblock) (resmgr_context_t *ctp, io_pulse_t *msg,
    RESMGR_HANDLE_T *handle, void *reserved);

    int (*mount) (resmgr_context_t *ctp, io_mount_t *msg,
    RESMGR_HANDLE_T *handle,
    io_mount_extra_t *extra);
    } resmgr_connect_funcs_t;
    可以调用iofunc_func_init()接口来用默认的处理程序指针来初始化这个结构。自己也可以重写某些接口,进行覆盖即可。
    需要注意的是,resmgr_attach()接口只是将函数指针拷贝到resmgr_connect_func_t和resmgr_io_funcs_t结构中,而不是拷贝整个结构。需要分配这些结构,将它们声明为静态的或者全局变量。如果资源管理器用于具有不同处理程序的多个设备,则应该分开定义独立的结构。
    这些连接消息都有一个_IO_CONNECT类型,此外还有子类型来进行分类,各个字段介绍如下:

  • nfuncs,结构中函数的个数,可用于扩展;

  • open,处理客户端的open()/fopen()/sopen()等请求,消息子类型包括_IO_CONNECT_COMBINE, _IO_CONNECT_COMBINE_CLOSE, _IO_CONNECT_OPEN等;

  • unlink,处理客户端unlink()请求,消息子类型为_IO_CONNECT_UNLINK;

  • rename,处理客户端rename()请求,消息子类型为_IO_CONNECT_RENAME;

  • mknod,处理客户端mkdir()/mkfifo()/mknod()请求,消息子类型为_IO_CONNECT_MKNOD;

  • readlink,处理客户端readlink()请求,消息子类型为_IO_CONNECT_READLINK;

  • link,处理客户端link()请求,消息子类型为_IO_CONNECT_LINK;

  • unlock,处理来自内核的请求,以便在连接消息阶段接触对客户端的阻塞;

  • mount,处理客户端mount()请求,消息子类型为_IO_CONNECT_MOUNT;
    4.2 I/O messages
    I/O消息依赖于客户端和资源管理器之间已有的绑定关系,比如当客户端调用read()函数发送_IO_READ消息时,需要先通过open()函数来与资源管理器建立绑定关系,进而获取到文件描述符。
    regmgr_io_funcs_t结构体定义了I/O消息处理函数:
    typedef struct resmgr_io_funcs {
    unsigned nfuncs;
    int (*read) (resmgr_context_t *ctp, io_read_t *msg,
    RESMGR_OCB_T *ocb);
    int (*write) (resmgr_context_t *ctp, io_write_t *msg,
    RESMGR_OCB_T *ocb);
    int (*close_ocb) (resmgr_context_t *ctp, void *reserved,
    RESMGR_OCB_T *ocb);
    int (*stat) (resmgr_context_t *ctp, io_stat_t *msg,
    RESMGR_OCB_T *ocb);
    int (*notify) (resmgr_context_t *ctp, io_notify_t *msg,
    RESMGR_OCB_T *ocb);
    int (*devctl) (resmgr_context_t *ctp, io_devctl_t *msg,
    RESMGR_OCB_T *ocb);
    int (*unblock) (resmgr_context_t *ctp, io_pulse_t *msg,
    RESMGR_OCB_T *ocb);
    int (*pathconf) (resmgr_context_t *ctp, io_pathconf_t *msg,
    RESMGR_OCB_T *ocb);
    int (*lseek) (resmgr_context_t *ctp, io_lseek_t *msg,
    RESMGR_OCB_T *ocb);
    int (*chmod) (resmgr_context_t *ctp, io_chmod_t *msg,
    RESMGR_OCB_T *ocb);
    int (*chown) (resmgr_context_t *ctp, io_chown_t *msg,
    RESMGR_OCB_T *ocb);
    int (*utime) (resmgr_context_t *ctp, io_utime_t *msg,
    RESMGR_OCB_T *ocb);
    int (*openfd) (resmgr_context_t *ctp, io_openfd_t *msg,
    RESMGR_OCB_T *ocb);
    int (*fdinfo) (resmgr_context_t *ctp, io_fdinfo_t *msg,
    RESMGR_OCB_T *ocb);
    int (*lock) (resmgr_context_t *ctp, io_lock_t *msg,
    RESMGR_OCB_T *ocb);
    int (*space) (resmgr_context_t *ctp, io_space_t *msg,
    RESMGR_OCB_T *ocb);
    int (*shutdown) (resmgr_context_t *ctp, io_shutdown_t *msg,
    RESMGR_OCB_T *ocb);
    int (*mmap) (resmgr_context_t *ctp, io_mmap_t *msg,
    RESMGR_OCB_T *ocb);
    int (*msg) (resmgr_context_t *ctp, io_msg_t *msg,
    RESMGR_OCB_T *ocb);
    int (*reserved) (resmgr_context_t *ctp, void *msg,
    RESMGR_OCB_T *ocb);
    int (*dup) (resmgr_context_t *ctp, io_dup_t *msg,
    RESMGR_OCB_T *ocb);
    int (*close_dup) (resmgr_context_t *ctp, io_close_t *msg,
    RESMGR_OCB_T *ocb);
    int (*lock_ocb) (resmgr_context_t *ctp, void *reserved,
    RESMGR_OCB_T *ocb);
    int (*unlock_ocb) (resmgr_context_t *ctp, void *reserved,
    RESMGR_OCB_T *ocb);
    int (*sync) (resmgr_context_t *ctp, io_sync_t *msg,
    RESMGR_OCB_T *ocb);
    int (*power) (resmgr_context_t *ctp, io_power_t *msg,
    RESMGR_OCB_T *ocb);
    } resmgr_io_funcs_t;
    这个结构的使用与resmgr_connect_funcs_t一样,对应到客户端的不同请求,及消息类型。
    4.3 Default message handling
    由于资源管理器接收的大量消息处理的是一组公共的属性,因此QNX提供了一个iofunc
    *()共享库,实现了一些默认的消息处理函数。目前实现的默认函数可用于处理客户端的以下请求:

  • chmod()

  • chown()

  • close()

  • devctl()

  • fpathconf()

  • fseek()

  • fstat()

  • lockf()

  • lseek()

  • mmap()

  • open()

  • pathconf()

  • stat()

  • utime()

  1. Setting resource manager attribute
    除了定义connect和I/O函数结构体之外,resmgr_attach()函数还需要用到resmgr_attr_t来指定资源管理器的属性。定义如下:
    typedef struct _resmgr_attr {
    unsigned flags;
    unsigned nparts_max;
    unsigned msg_max_size;
    int (*other_func)(resmgr_context_t *,
    void *msg);
    unsigned reserved[4];
    } resmgr_attr_t;
    各个成员介绍如下:
  2. flags
    可用于修改资源管理器接口的行为,可以将其设置为0,或者是以下不同状态的组合:
  • RESMGR_FLAG_ATTACH_LOCAL,设置资源管理器,不向procnto注册路径,可以向资源管理器通道发送消息;
  • RESMGR_FLAG_ATTACH_OTHERFUNC,该结构中的other_func成员指向一个用于未处理I/O消息的函数;
  • RESMGR_FLAG_CROSS_ENDIAN,服务器支持跨端处理,可以在服务器端做必要的转换,客户端不需要做任何事情;
  • RESMGR_FLAG_NO_DEFAULT,未实现;
  • RESMGR_FLAG_RCM,在处理请求时自动采取客户端的资源约束模式;
  1. nparts_max
    分配给IOV数组的组件数量。

  2. msg_max_size
    消息缓冲的大小。
    这些成员在实现自己的处理函数时很重要。

  3. other_func
    other_func可用于指定一个例程,在资源管理器接收到不能理解的I/O消息时调用。要使用这个成员,需要在flag字段里置上RESMGR_FLAG_ATTACH_OTHERFUNC。如果other_func成员设置成NULL的话,资源管理器在收到不能理解的消息时,便会返回ENOSYS错误给客户端。
    对于非I/O消息类型时,需要使用message_attach()接口来将消息绑定到dispatch handle上。

  4. Ways of adding functionality to the resource manager
    6.1 Using the default functions
    下边是一个使用自己的io_open处理程序的示例:
    main (int argc, char **argv)
    {

    /* install all of the default functions /
    iofunc_func_init (_RESMGR_CONNECT_NFUNCS, &connect_funcs,
    _RESMGR_IO_NFUNCS, &io_funcs);

    / take over the open function */
    connect_funcs.open = io_open;

    }

int
io_open (resmgr_context_t *ctp, io_open_t *msg,
RESMGR_HANDLE_T *handle, void extra)
{
return (iofunc_open_default (ctp, msg, handle, extra));
}
上述的代码只是一个增量步骤,可以允许在调用默认处理函数之前或者之后执行某些操作,比如可以实现如下代码:
/
example of doing something before */

extern int accepting_opens_now;

int
io_open (resmgr_context_t *ctp, io_open_t *msg,
RESMGR_HANDLE_T *handle, void extra)
{
if (!accepting_opens_now) {
return (EBUSY);
}

/ 
 *  at this point, we're okay to let the open happen,
 *  so let the default function do the "work".
 */

return (iofunc_open_default (ctp, msg, handle, extra));

}
或者:
/* example of doing something after */

int
io_open (resmgr_context_t *ctp, io_open_t *msg,
RESMGR_HANDLE_T *handle, void extra)
{
int sts;

/ 
 * have the default function do the checking 
 * and the work for us
 /

sts = iofunc_open_default (ctp, msg, handle, extra);

/ 
 *  if the default function says it's okay to let the open
 *  happen, we want to log the request
 */

if (sts == EOK) {
    log_open_request (ctp, msg);
}
return (sts);

}
这种方法的优势是,只需要很少的工作就可以添加到标准的默认POSIX处理程序中。
6.2 Using the helper functions
在很多默认处理函数中,都调用到了帮助函数,比如下边的iofunc_chmod_default()和iofunc_stat_default():
int
iofunc_chmod_default (resmgr_context_t *ctp, io_chmod_t *msg,
iofunc_ocb_t *ocb)
{
return (iofunc_chmod (ctp, msg, ocb, ocb -> attr));
}

int
iofunc_stat_default (resmgr_context_t *ctp, io_stat_t *msg,
iofunc_ocb_t *ocb)
{
iofunc_time_update (ocb -> attr);
iofunc_stat (ocb -> attr, &msg -> o);
return (_RESMGR_PTR (ctp, &msg -> o,
sizeof (msg -> o)));
}
在上边的代码中分别都调用到了iofunc_chmod()、iofunc_time_update()、iofunc_stat()等帮助函数。
更复杂的case如下:
int
iofunc_open_default (resmgr_context_t *ctp, io_open_t *msg,
iofunc_attr_t *attr, void *extra)
{
int status;

iofunc_attr_lock (attr);

if ((status = iofunc_open (ctp, msg, attr, 0, 0)) != EOK) {
    iofunc_attr_unlock (attr);
    return (status);
}

if ((status = iofunc_ocb_attach (ctp, msg, 0, attr, 0)) 
    != EOK) {
    iofunc_attr_unlock (attr);
    return (status);
}

iofunc_attr_unlock (attr);
return (EOK);

}
调用了以下帮助函数:

  • iofunc_attr_lock()接口,获取锁,用于互斥访问属性结构;
  • iofunc_open(),进行权限验证;
  • iofunc_ocb_attach(),绑定OCB结构;
  • iofunc_attr_unlock(),释放锁;
    6.3 Writing the entire function yourself
    有时候默认的处理函数对特定的资源管理器来说没有用处,可以自己实现处理函数,在这些实现的处理函数中,可以去调用帮助函数,比如iofunc_read_verify()。
  1. Security
    资源管理器通常是一个特权进程,因此需要小心,防止客户端迫使它耗尽资源或损耗系统。在设计资源管理器时,应该考虑以下几点:
  • 管理路径名空间中资源管理器条目的权限,可以将权限指定为iofunc_attr_init()的参数;
  • 资源管理器通常需要运行在root权限,以便能与路径名空间绑定,但是更好的方式是运行在非root权限,而使用procmgr_ability()去获取特权的能力。
  • 如果资源管理器不是一个没有资源约束阈值的关键进程,它可以简单的运行在约束模式下,而如果是一个关键进程,应该保持PROCMGR_AID_RCONSTRAINT能力,需要确保受约束的客户端不使用它来分配超过当前阈值的资源。
  • 对客户端的能力进行检查,通常可以调用ConnectClientInfoAble()或iofunc_client_info_able()来检查。
  1. POSIX-Layer Data Structures
    这篇文章主要讲述和POSIX-Layer例程支持相关的几个关键数据结构。
    8.1 Introduction
    资源管理器定义了三个与POSIX-Layer相关的数据结构:
  • iofunc_ocb_t,包含了每次打开的数据,比如打开文件的偏移等;
  • iofunc_attr_t,资源管理器有时可能得管理多个设备,比如devc-ser*对应到/dev/ser1,/dev/ser2等,这个结构包含一个名字对应的数据;
  • iofunc_mount_t,主要用于文件系统,设备通常不需要这个结构;
    三者之间的关系如下:
    [图片]
  • A resource manager is responsible for three data structures
    [图片]
    Multiple clients with multiple OCBs, all linked to one mount structure
    8.2 iofunc_ocb_t结构
    OCB(Open Control Block)结构,用于维护客户端和资源管理器之间特定会话的状态信息,在open()时创建,在close()时退出。这个数据结构用于iofunc layer中的帮助函数。该结构至少包含以下内容:
    typedef struct _iofunc_ocb {
    IOFUNC_ATTR_T *attr;
    int32_t ioflag;
    off_t offset;
    uint16_t sflag;
    uint16_t flags;
    } iofunc_ocb_t;
  • attr,指向属性结构体;
  • ioflag,包含了资源打开模式:O_RDONLY/O_RDWR/O_WRONLY,对应的ioflag值为_IO_FLAG_RD/_IO_FLAG_RD | _IO_FLAG_WR/_IO_FLAG_WR,这些信息从io_connect_t结构体中继承得来;
  • offset,读写资源的偏移,资源管理器可以修改这个成员;
  • sflag,定义共享模式,从io_connect_t结构中继承得来;
  • flags,当设置IOFUNC_OCB_PRIVILEGED位时,特权进程来执行open()操作,此外也可以使用IOFUNC_OCB_FLAGS_PRIVATE范围之内的flag。资源管理器可以修改这个成员;
    8.3 iofunc_attr_t结构
    这个结构为资源管理器提供了设备的特征,与OCB结构结合使用。
    typedef struct _iofunc_attr {
    IOFUNC_MOUNT_T *mount;
    uint32_t flags;
    int32_t lock_tid;
    uint16_t lock_count;
    uint16_t count;
    uint16_t rcount;
    uint16_t wcount;
    uint16_t rlocks;
    uint16_t wlocks;
    struct _iofunc_mmap_list *mmap_list;
    struct _iofunc_lock_list *lock_list;
    void *list;
    uint32_t list_size;
    off_t nbytes;
    ino_t inode;
    uid_t uid;
    gid_t gid;
    time_t mtime;
    time_t atime;
    time_t ctime;
    mode_t mode;
    nlink_t nlink;
    dev_t rdev;
    } iofunc_attr_t;
  • mount,指向挂载结构体;
  • flags,可以是不同比特位的组合,包括:IOFUNC_ATTR_ATIME/IOFUNC_ATTR_CTIME/IOFUNC_ATTR_DIRTY_NLINK/IOFUNC_ATTR_DIRTY_MODE/IOFUNC_ATTR_DIRTY_OWNER/IOFUNC_ATTR_DIRTY_RDEV/IOFUNC_ATTR_DIRTY_SIZE/IOFUNC_ATTR_DIRTY_TIME/IOFUNC_ATTR_MTIME,用于标识修改记录。
  • lock_tid和lock_count,用于多线程的加锁和统计;
  • count, rcount, wcount, rlocks and wlocks,计数值及锁;
  • mmap_list and lock_list,mmap_list用于iofunc_mmap()和iofunc_mmap_default()函数,lock_list用于iofunc_lock_default()函数,通常用户不需要修改会检查这两个成员;
  • list and list_size,保留字段;
  • nbytes,资源的字节数,比如对于文件来说,放置的就是文件的大小;
  • inode,特定挂载点的inode,每个挂载点都必须是唯一的;
  • uid and gid,资源的用户ID和组ID,通常由chown()等函数来更新;
  • mtime, atime, and ctime,修改时间、访问时间,以及状态改变时间,是三个POSIX时间成员;
  • mode,资源的模式,定义在中,以S_*开头;
  • nlink,链接数量;
  • rdev,包含字符特殊设备的设备号,以及指定专用设备的rdev号;
    8.4 iofunc_mount_t结构(可选)
    这个结构中的成员,尤其是conf和flags,可以修改某些iofunc层函数的行为。这个结构中至少包含以下内容:
    typedef struct _iofunc_mount {
    uint32_t flags;
    uint32_t conf;
    dev_t dev;
    int32_t blocksize;
    iofunc_funcs_t *funcs;
    } iofunc_mount_t;
  • flags,包含一个相关的位用于标识资源管理器使用的偏移量是32-bit的(与扩展的64-bit偏移相反);
  • conf,包含以下位:IOFUNC_PC_CHOWN_RESTRICTED/IOFUNC_PC_NO_TRUNC/IOFUNC_PC_SYNC_IO/IOFUNC_PC_LINK_DIR/IOFUNC_PC_ACL,这些选项是由iofunc层_IO_PATHCONF默认处理程序返回的;
  • dev,包含文件系统对应的设备号,当客户端调用stat()函数时,会将该值填充到struct stat st_dev成员中;
  • blocksize,包含了设备的块大小;
  • funcs,这是一个struct _iofunc_funcs结构,用于扩展OCB;

你可能感兴趣的:(qnx)