美文网首页
QNX之编写资源管理器(十)

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

作者: Loyen | 来源:发表于2019-01-12 10:50 被阅读24次

    QNX相关历史文章:

    Filesystem Resource Managers

    这篇文章主要描述文件系统资源管理器。

    1. Considerations for filesystem resource managers

    由于文件系统资源管理器可能会收到很长的路径名,因此它必须要能够正确地解析和处理路径的每个部分。
    比如,一个资源管理器注册了挂载点/mount,当用户输入:ls -l /mount/home时, 其中/mount/home是设备中的一个路径,
    那么ls会做以下事情:

    d = opendir("/mount/home");
    while (...) {
        dirent = readdir(d);
        ...
    }
    

    2. Taking over more than one device

    当资源管理器需要处理多个设备时,可以对每个设备名调用resmgr_attach()接口进行注册,此外每个设备还需要唯一的属性结构,当像chmod()等函数被调用时,便可对特定设备的属性进行修改。
    示例代码如下:

    /* 
     *  MOD [1]:  allocate multiple attribute structures,
     *            and fill in a names array (convenience)
     */
    
    #define NumDevices  2
    iofunc_attr_t     sample_attrs [NumDevices];
    char              *names [NumDevices] =
    {
        "/dev/sample1",
        "/dev/sample2"
    };
    
    main ()
    {
        ...
        /*
         *  MOD [2]:  fill in the attribute structure for each device 
         *           and call resmgr_attach for each device           
         */
        for (i = 0; i < NumDevices; i++) {
            iofunc_attr_init (&sample_attrs [i],
                              S_IFCHR | 0666, NULL, NULL);
            pathID = resmgr_attach (dpp, &resmgr_attr, name[i],
                                     _FTYPE_ANY, 0,
                                     &my_connect_funcs,
                                     &my_io_funcs,
                                     &sample_attrs [i]);
        }
        ...
    }                            
    

    在这个代码中,增加了属性结构的数组,并且多次调用了resmgr_attach()接口,其他地方不需要修改。io_readio_write处理函数不需要改动,iofunc layer默认处理函数会去处理多种设备的情况。

    3. Handling directories

    之前的例子路径名都是在/dev目录下,资源管理器不会限制只能在某个路径下,并可以处理任何数量的路径名,一个实际的限制在于当数量越多的时候,可能面临内存不足以及查找速度慢等问题。
    当路径名数量很多时,一个最直接的办法就是使用路径名前缀,比如:

    • CD-ROM文件系统,可以使用前缀/cdrom,当对这个路径下的名字操作时,都会去处理CD-ROM设备;
    • 一个处理压缩文件的文件系统,可以使用前缀/uncompressed
    • 网络文件系统可以使用/mount/flipper路径名来显示远程机器,当访问这个路径时,就像访问本地机器;

    上边这些例子的特点是都实现了文件系统,文件系统资源管理器与设备资源管理器在以下几个关键领域有区别:

    • resmgr_attach()参数中的_RESMGR_FLAG_DIR位会通知库,资源管理器会在挂载点路径或挂载点路径下接受匹配;
    • _IO_CONNECT逻辑必须根据数据权限和访问权限来检查路径名的各个部分,它还必须确保在访问特定文件名时绑定了适当的属性;
    • _IO_READ逻辑必须返回路径名指定的“文件”或“路径”数据;

    3.1 Matching at or below a mountpoint

    在使用resmgr_attach()函数时,传入的flags参数为_RESMGR_FLAG_DIR时,表明允许在指定的挂载点路径或该路径之下进行路径名解析。如果flags参数为0,表明使用默认值。

    3.2 The _IO_OPEN message for filesystems

    假设注册了一个挂载点/sample_fsys,如下:

    pathID = resmgr_attach
                 (dpp,
                 &resmgr_attr,
                 "/sample_fsys",    /* mountpoint */
                _FTYPE_ANY,
                 _RESMGR_FLAG_DIR,   /* it's a directory */
                 &connect_funcs,
                 &io_funcs,
                 &attr);
    

    当客户端调用如下代码:

    fopen ("/sample_fsys/spud", "r");
    

    资源管理器会收到_IO_CONNECT消息,并且调用io_read处理函数。_IO_CONNECT消息的数据结构如下:

    struct _io_connect {
        unsigned short  type;
        unsigned short  subtype;     /* _IO_CONNECT_*              */
        unsigned long   file_type;   /* _FTYPE_* in sys/ftype.h    */
        unsigned short  reply_max;
        unsigned short  entry_max;
        unsigned long   key;
        unsigned long   handle;
        unsigned long   ioflag;      /* O_* in fcntl.h, _IO_FLAG_* */
        unsigned long   mode;        /* S_IF* in sys/stat.h        */
        unsigned short  sflag;       /* SH_* in share.h            */
        unsigned short  access;      /* S_I in sys/stat.h          */
        unsigned short  zero;
        unsigned short  path_len;
        unsigned char   eflag;       /* _IO_CONNECT_EFLAG_*        */
        unsigned char   extra_type;  /* _IO_EXTRA_*                */
        unsigned short  extra_len;
        unsigned char   path[1];     /* path_len, null, extra_len  */
    };
    

    其中ioflat, mode, sflag, access表明资源是如何打开的。参数path_len表明路径名占多少字节,path放置实际的路径名。注意,出现的路径名是spud,而不是/sample_fsys/spud,这是因为消息只包含相对于挂载点的路径名。还要注意的是,路径名中不会有相对路径(., ..)部分,也不会冗余的斜杠(/),这些都会在消息发送到资源管理器时被解析和删除。

    当编写文件系统资源管理器时,在处理路径名时可能会有一些复杂的情况,为了验证访问,我们需要分解传递的路径名,并对每个部分进行检查。可以使用strtok()等来分解路径名字符串,然后调用iofunc_check_access()来进行访问验证。验证名称之后发生的绑定要求处理的每个路径都有自己的属性结构,如果将错误的属性绑定到所提供的路径名,将会导致意外的行为。

    3.3 Returning directory entries from _IO_READ

    _IO_READ处理函数被调用后,可能需要返回文件(如果S_ISDIR(ocb->attr->mode)为false),也可能需要返回目录(如果S_ISDIR(ocb->attr->mode)为true)。
    对于将目录返回给客户端,存在一些约束,返回的不是一个字节流,返回的是几个struct dirent的数据结构,dirent结构必须4字节对齐。数据结构如下:

    struct dirent {
    #if _FILE_OFFSET_BITS - 0 == 64
        ino_t           d_ino;          /* File serial number. */
        off_t           d_offset;
    #elif !defined(_FILE_OFFSET_BITS) || _FILE_OFFSET_BITS == 32
    #if defined(__LITTLEENDIAN__)
        ino_t           d_ino;          /* File serial number. */
        ino_t           d_ino_hi;
        off_t           d_offset;
        off_t           d_offset_hi;
    #elif defined(__BIGENDIAN__)
        ino_t           d_ino_hi;
        ino_t           d_ino;          /* File serial number. */
        off_t           d_offset_hi;
        off_t           d_offset;
    #else
     #error endian not configured for system
    #endif
    #else
     #error _FILE_OFFSET_BITS value is unsupported
    #endif
        _Int16t             d_reclen;
        _Int16t             d_namelen;
        char                d_name[1];
    };
    

    d_ino成员包含一个挂载点唯一的文件序列号。这个序列号通常用于各种磁盘检查程序中。在有些文件系统中,d_offset用于标识目录条目本身,而在其他情况下,它是下一个目录项的偏移量。d_reclen成员包含此目录项的大小和任何其他相关信息。d_namelen参数指示d_name参数的大小,d_name保存该目录项的实际名称。
    dirent结构中仅包含名称的前四个字节的空间,_IO_READ处理程序需要返回一个更大的结构,包含名字和dirent,如下:

    struct {
        struct dirent ent;
        char namebuf[NAME_MAX + 1 + offsetof(struct dirent, d_name) -
                     sizeof( struct dirent)];
    } entry
    

    或者定义成联合体:

    union {
        struct dirent ent;
        char filler[ offsetof( struct dirent, dname ) + NAME_MAX + 1];
    } entry;
    

    在我们的io_read处理程序中,需要生成许多struct dirent条目,并返回给客户端。如果在资源管理器中维护了目录项缓存,那么构造一组IOVs来指向这些项即可。如果没有缓存的话,则必须手动将目录项组装到缓冲区中,然后返回指向该缓冲区的IOV。

    3.3.1 Returning information associated with a directory structure

    除了返回_IO_READ消息中的struct dirent外,还可以返回struct stat,尽管这个可以提高效率,但是struct stat完全是可选的,如果不返回struct stat的话,客户端就必须通过stat()/lstat()来获取该信息。

    客户端可以通过将消息的xtype成员设置成_IO_XFLAG_DIR_EXTRA_HINT,以便向文件系统发送提示以返回额外的信息,但文件系统不保证这样做。如果资源管理器提供信息,则必须将其放入到struct dirent_extra_stat中,定义如下:

    struct dirent_extra_stat {
        _Uint16t            d_datalen;
        _Uint16t            d_type;
        _Uint32t            d_reserved;
        struct stat         d_stat;
    };
    

    资源管理器必须将d_type设置为_DTYPE_LSTAT_DTYPE_STAT,这取决于它是否解析符号链接。比如:

    if(msg->i.xtype & _IO_XFLAG_DIR_EXTRA_HINT) { 
        struct dirent_extra_stat    extra;
        extra.d_datalen = sizeof extra.d_stat;
        extra.d_type = _DTYPE_LSTAT;
        extra.d_reserved = 0;
        iofunc_stat(ctp, &attr, &extra.d_stat);
        ...
    }
    

    每个目录项后都有一个dirent_extra_stat

    Returning the optional struct dirent_extra_stat along with the struct dirent entry can improve efficiency
    dirent结构必须在4字节边界上对齐,dirent_extra_stat结构必须在8字节边界上对齐,d_reclen成员必须包含这两个结构的大小,包含路径名和对齐所需的任何空间。最多不超过7字节的对齐填充。

    客户端必须调用_DEXTRA_*()宏来检查额外的数据,如果检查失败,则需要显示调用lstat()stat()。比如,ls -l检查额外的_DTYPE_LSTAT信息,如果不存在,ls调用lstat()ls -L检查额外的_DTYPE_STAT信息,如果不存在,ls调用stat()

    相关文章

      网友评论

          本文标题:QNX之编写资源管理器(十)

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