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

QNX相关历史文章:

  • QNX简介
  • QNX Neutrino微内核
  • QNX IPC机制
  • QNX进程管理器
  • QNX资源管理器
  • QNX字符I/O
  • QNX之编写资源管理器(一)
  • QNX之编写资源管理器(二)
  • QNX之编写资源管理器(三)
  • QNX之编写资源管理器(四)
  • QNX之编写资源管理器(五)
  • QNX之编写资源管理器(六)
  • QNX之编写资源管理器(七)
  • QNX之编写资源管理器(八)
  • 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

QNX之编写资源管理器(十)_第1张图片
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之编写资源管理器(十))