QNX之編寫資源管理器(十)

QNX相關歷史文章:

Filesystem Resource Managers

這篇文章主要描述文件系統(tǒng)資源管理器。

1. Considerations for filesystem resource managers

由于文件系統(tǒng)資源管理器可能會收到很長的路徑名,因此它必須要能夠正確地解析和處理路徑的每個部分。
比如,一個資源管理器注冊了掛載點/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()等函數(shù)被調用時,便可對特定設備的屬性進行修改。
示例代碼如下:

/* 
 *  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]);
    }
    ...
}                            

在這個代碼中,增加了屬性結構的數(shù)組,并且多次調用了resmgr_attach()接口,其他地方不需要修改。io_readio_write處理函數(shù)不需要改動,iofunc layer默認處理函數(shù)會去處理多種設備的情況。

3. Handling directories

之前的例子路徑名都是在/dev目錄下,資源管理器不會限制只能在某個路徑下,并可以處理任何數(shù)量的路徑名,一個實際的限制在于當數(shù)量越多的時候,可能面臨內存不足以及查找速度慢等問題。
當路徑名數(shù)量很多時,一個最直接的辦法就是使用路徑名前綴,比如:

  • CD-ROM文件系統(tǒng),可以使用前綴/cdrom,當對這個路徑下的名字操作時,都會去處理CD-ROM設備;
  • 一個處理壓縮文件的文件系統(tǒng),可以使用前綴/uncompressed;
  • 網(wǎng)絡文件系統(tǒng)可以使用/mount/flipper路徑名來顯示遠程機器,當訪問這個路徑時,就像訪問本地機器;

上邊這些例子的特點是都實現(xiàn)了文件系統(tǒng),文件系統(tǒng)資源管理器與設備資源管理器在以下幾個關鍵領域有區(qū)別:

  • resmgr_attach()參數(shù)中的_RESMGR_FLAG_DIR位會通知庫,資源管理器會在掛載點路徑或掛載點路徑下接受匹配;
  • _IO_CONNECT邏輯必須根據(jù)數(shù)據(jù)權限和訪問權限來檢查路徑名的各個部分,它還必須確保在訪問特定文件名時綁定了適當?shù)膶傩裕?/li>
  • _IO_READ邏輯必須返回路徑名指定的“文件”或“路徑”數(shù)據(jù);

3.1 Matching at or below a mountpoint

在使用resmgr_attach()函數(shù)時,傳入的flags參數(shù)為_RESMGR_FLAG_DIR時,表明允許在指定的掛載點路徑或該路徑之下進行路徑名解析。如果flags參數(shù)為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處理函數(shù)。_IO_CONNECT消息的數(shù)據(jù)結構如下:

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表明資源是如何打開的。參數(shù)path_len表明路徑名占多少字節(jié),path放置實際的路徑名。注意,出現(xiàn)的路徑名是spud,而不是/sample_fsys/spud,這是因為消息只包含相對于掛載點的路徑名。還要注意的是,路徑名中不會有相對路徑(., ..)部分,也不會冗余的斜杠(/),這些都會在消息發(fā)送到資源管理器時被解析和刪除。

當編寫文件系統(tǒng)資源管理器時,在處理路徑名時可能會有一些復雜的情況,為了驗證訪問,我們需要分解傳遞的路徑名,并對每個部分進行檢查??梢允褂?code>strtok()等來分解路徑名字符串,然后調用iofunc_check_access()來進行訪問驗證。驗證名稱之后發(fā)生的綁定要求處理的每個路徑都有自己的屬性結構,如果將錯誤的屬性綁定到所提供的路徑名,將會導致意外的行為。

3.3 Returning directory entries from _IO_READ

_IO_READ處理函數(shù)被調用后,可能需要返回文件(如果S_ISDIR(ocb->attr->mode)為false),也可能需要返回目錄(如果S_ISDIR(ocb->attr->mode)為true)。
對于將目錄返回給客戶端,存在一些約束,返回的不是一個字節(jié)流,返回的是幾個struct dirent的數(shù)據(jù)結構,dirent結構必須4字節(jié)對齊。數(shù)據(jù)結構如下:

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成員包含一個掛載點唯一的文件序列號。這個序列號通常用于各種磁盤檢查程序中。在有些文件系統(tǒng)中,d_offset用于標識目錄條目本身,而在其他情況下,它是下一個目錄項的偏移量。d_reclen成員包含此目錄項的大小和任何其他相關信息。d_namelen參數(shù)指示d_name參數(shù)的大小,d_name保存該目錄項的實際名稱。
dirent結構中僅包含名稱的前四個字節(jié)的空間,_IO_READ處理程序需要返回一個更大的結構,包含名字和dirent,如下:

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

或者定義成聯(lián)合體:

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

在我們的io_read處理程序中,需要生成許多struct dirent條目,并返回給客戶端。如果在資源管理器中維護了目錄項緩存,那么構造一組IOVs來指向這些項即可。如果沒有緩存的話,則必須手動將目錄項組裝到緩沖區(qū)中,然后返回指向該緩沖區(qū)的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,以便向文件系統(tǒng)發(fā)送提示以返回額外的信息,但文件系統(tǒng)不保證這樣做。如果資源管理器提供信息,則必須將其放入到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字節(jié)邊界上對齊,dirent_extra_stat結構必須在8字節(jié)邊界上對齊,d_reclen成員必須包含這兩個結構的大小,包含路徑名和對齊所需的任何空間。最多不超過7字節(jié)的對齊填充。

客戶端必須調用_DEXTRA_*()宏來檢查額外的數(shù)據(jù),如果檢查失敗,則需要顯示調用lstat()stat()。比如,ls -l檢查額外的_DTYPE_LSTAT信息,如果不存在,ls調用lstat()。ls -L檢查額外的_DTYPE_STAT信息,如果不存在,ls調用stat()

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容