美文网首页QNX操作系统
QNX之编写资源管理器(二)

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

作者: Loyen | 来源:发表于2019-01-08 22:36 被阅读0次

    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之编写资源管理器(二)

        本文链接:https://www.haomeiwen.com/subject/zvavrqtx.html