QNX之编写资源管理器(二)

QNX相关历史文章:

  • QNX简介
  • QNX Neutrino微内核
  • QNX IPC机制
  • QNX进程管理器
  • QNX资源管理器
  • QNX字符I/O
  • QNX之编写资源管理器(一)

Fleshing Out the Skeleton

在讲完大体框架后,这篇文章将涉及到更多细节:消息类型;资源管理器属性;添加功能;安全事项等。

1. Message types

在前文中也提到过,资源管理器需要处理两类消息:

  • connect message,连接消息
  • I/O message, IO消息

1.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_tresmgr_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

1.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一样,对应到客户端的不同请求,及消息类型。

1.3 Default message handling

由于资源管理器接收的大量消息处理的是一组公共的属性,因此QNX提供了一个iofunc_*()共享库,实现了一些默认的消息处理函数。目前实现的默认函数可用于处理客户端的以下请求:

  • chmod()
  • chown()
  • close()
  • devctl()
  • fpathconf()
  • fseek()
  • fstat()
  • lockf()
  • lseek()
  • mmap()
  • open()
  • pathconf()
  • stat()
  • utime()

2. Setting resource manager attribute

除了定义connectI/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;

各个成员介绍如下:

  1. 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上。

3. Ways of adding functionality to the resource manager

3.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处理程序中。

3.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(),释放锁;

3.3 Writing the entire function yourself

有时候默认的处理函数对特定的资源管理器来说没有用处,可以自己实现处理函数,在这些实现的处理函数中,可以去调用帮助函数,比如iofunc_read_verify()

4. Security

资源管理器通常是一个特权进程,因此需要小心,防止客户端迫使它耗尽资源或损耗系统。在设计资源管理器时,应该考虑以下几点:

  • 管理路径名空间中资源管理器条目的权限,可以将权限指定为iofunc_attr_init()的参数;
  • 资源管理器通常需要运行在root权限,以便能与路径名空间绑定,但是更好的方式是运行在非root权限,而使用procmgr_ability()去获取特权的能力。
  • 如果资源管理器不是一个没有资源约束阈值的关键进程,它可以简单的运行在约束模式下,而如果是一个关键进程,应该保持PROCMGR_AID_RCONSTRAINT能力,需要确保受约束的客户端不使用它来分配超过当前阈值的资源。
  • 对客户端的能力进行检查,通常可以调用ConnectClientInfoAble()iofunc_client_info_able()来检查。

你可能感兴趣的:(QNX之编写资源管理器(二))