QNX相關(guān)歷史文章:
Filesystem Resource Managers
這篇文章主要描述文件系統(tǒng)資源管理器。
1. Considerations for filesystem resource managers
由于文件系統(tǒng)資源管理器可能會收到很長的路徑名,因此它必須要能夠正確地解析和處理路徑的每個部分。
比如,一個資源管理器注冊了掛載點(diǎn)/mount,當(dāng)用戶輸入:ls -l /mount/home時, 其中/mount/home是設(shè)備中的一個路徑,
那么ls會做以下事情:
d = opendir("/mount/home");
while (...) {
dirent = readdir(d);
...
}
2. Taking over more than one device
當(dāng)資源管理器需要處理多個設(shè)備時,可以對每個設(shè)備名調(diào)用resmgr_attach()接口進(jìn)行注冊,此外每個設(shè)備還需要唯一的屬性結(jié)構(gòu),當(dāng)像chmod()等函數(shù)被調(diào)用時,便可對特定設(shè)備的屬性進(jìn)行修改。
示例代碼如下:
/*
* 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]);
}
...
}
在這個代碼中,增加了屬性結(jié)構(gòu)的數(shù)組,并且多次調(diào)用了resmgr_attach()接口,其他地方不需要修改。io_read或io_write處理函數(shù)不需要改動,iofunc layer默認(rèn)處理函數(shù)會去處理多種設(shè)備的情況。
3. Handling directories
之前的例子路徑名都是在/dev目錄下,資源管理器不會限制只能在某個路徑下,并可以處理任何數(shù)量的路徑名,一個實際的限制在于當(dāng)數(shù)量越多的時候,可能面臨內(nèi)存不足以及查找速度慢等問題。
當(dāng)路徑名數(shù)量很多時,一個最直接的辦法就是使用路徑名前綴,比如:
-
CD-ROM文件系統(tǒng),可以使用前綴/cdrom,當(dāng)對這個路徑下的名字操作時,都會去處理CD-ROM設(shè)備; - 一個處理壓縮文件的文件系統(tǒng),可以使用前綴
/uncompressed; - 網(wǎng)絡(luò)文件系統(tǒng)可以使用
/mount/flipper路徑名來顯示遠(yuǎn)程機(jī)器,當(dāng)訪問這個路徑時,就像訪問本地機(jī)器;
上邊這些例子的特點(diǎn)是都實現(xiàn)了文件系統(tǒng),文件系統(tǒng)資源管理器與設(shè)備資源管理器在以下幾個關(guān)鍵領(lǐng)域有區(qū)別:
-
resmgr_attach()參數(shù)中的_RESMGR_FLAG_DIR位會通知庫,資源管理器會在掛載點(diǎn)路徑或掛載點(diǎn)路徑下接受匹配; -
_IO_CONNECT邏輯必須根據(jù)數(shù)據(jù)權(quán)限和訪問權(quán)限來檢查路徑名的各個部分,它還必須確保在訪問特定文件名時綁定了適當(dāng)?shù)膶傩裕?/li> -
_IO_READ邏輯必須返回路徑名指定的“文件”或“路徑”數(shù)據(jù);
3.1 Matching at or below a mountpoint
在使用resmgr_attach()函數(shù)時,傳入的flags參數(shù)為_RESMGR_FLAG_DIR時,表明允許在指定的掛載點(diǎn)路徑或該路徑之下進(jìn)行路徑名解析。如果flags參數(shù)為0,表明使用默認(rèn)值。
3.2 The _IO_OPEN message for filesystems
假設(shè)注冊了一個掛載點(diǎn)/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);
當(dāng)客戶端調(diào)用如下代碼:
fopen ("/sample_fsys/spud", "r");
資源管理器會收到_IO_CONNECT消息,并且調(diào)用io_read處理函數(shù)。_IO_CONNECT消息的數(shù)據(jù)結(jié)構(gòu)如下:
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,這是因為消息只包含相對于掛載點(diǎn)的路徑名。還要注意的是,路徑名中不會有相對路徑(., ..)部分,也不會冗余的斜杠(/),這些都會在消息發(fā)送到資源管理器時被解析和刪除。
當(dāng)編寫文件系統(tǒng)資源管理器時,在處理路徑名時可能會有一些復(fù)雜的情況,為了驗證訪問,我們需要分解傳遞的路徑名,并對每個部分進(jìn)行檢查??梢允褂?code>strtok()等來分解路徑名字符串,然后調(diào)用iofunc_check_access()來進(jìn)行訪問驗證。驗證名稱之后發(fā)生的綁定要求處理的每個路徑都有自己的屬性結(jié)構(gòu),如果將錯誤的屬性綁定到所提供的路徑名,將會導(dǎo)致意外的行為。
3.3 Returning directory entries from _IO_READ
當(dāng)_IO_READ處理函數(shù)被調(diào)用后,可能需要返回文件(如果S_ISDIR(ocb->attr->mode)為false),也可能需要返回目錄(如果S_ISDIR(ocb->attr->mode)為true)。
對于將目錄返回給客戶端,存在一些約束,返回的不是一個字節(jié)流,返回的是幾個struct dirent的數(shù)據(jù)結(jié)構(gòu),dirent結(jié)構(gòu)必須4字節(jié)對齊。數(shù)據(jù)結(jié)構(gòu)如下:
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成員包含一個掛載點(diǎn)唯一的文件序列號。這個序列號通常用于各種磁盤檢查程序中。在有些文件系統(tǒng)中,d_offset用于標(biāo)識目錄條目本身,而在其他情況下,它是下一個目錄項的偏移量。d_reclen成員包含此目錄項的大小和任何其他相關(guān)信息。d_namelen參數(shù)指示d_name參數(shù)的大小,d_name保存該目錄項的實際名稱。
dirent結(jié)構(gòu)中僅包含名稱的前四個字節(jié)的空間,_IO_READ處理程序需要返回一個更大的結(jié)構(gòu),包含名字和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條目,并返回給客戶端。如果在資源管理器中維護(hù)了目錄項緩存,那么構(gòu)造一組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成員設(shè)置成_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設(shè)置為_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:

dirent結(jié)構(gòu)必須在4字節(jié)邊界上對齊,dirent_extra_stat結(jié)構(gòu)必須在8字節(jié)邊界上對齊,d_reclen成員必須包含這兩個結(jié)構(gòu)的大小,包含路徑名和對齊所需的任何空間。最多不超過7字節(jié)的對齊填充。
客戶端必須調(diào)用_DEXTRA_*()宏來檢查額外的數(shù)據(jù),如果檢查失敗,則需要顯示調(diào)用lstat()或stat()。比如,ls -l檢查額外的_DTYPE_LSTAT信息,如果不存在,ls調(diào)用lstat()。ls -L檢查額外的_DTYPE_STAT信息,如果不存在,ls調(diào)用stat()。