Nuttx相關(guān)的歷史文章:
1. 介紹
文件系統(tǒng),是對(duì)一個(gè)存儲(chǔ)設(shè)備上的數(shù)據(jù)和元數(shù)據(jù)進(jìn)行組織的機(jī)制,它是操作系統(tǒng)管理持久性數(shù)據(jù)的子系統(tǒng),提供數(shù)據(jù)存儲(chǔ)和訪問功能。
將一個(gè)文件系統(tǒng)與一個(gè)存儲(chǔ)設(shè)備關(guān)聯(lián)起來的過程叫做掛載(mount),掛載時(shí)會(huì)將一個(gè)文件系統(tǒng)附著到當(dāng)前文件系統(tǒng)層次結(jié)構(gòu)中(根),在執(zhí)行掛載時(shí),需要提供文件系統(tǒng)類型、文件系統(tǒng)和一個(gè)掛載點(diǎn)。
1.1 Nuttx文件系統(tǒng)介紹
Nuttx包含了一個(gè)可選的、可擴(kuò)展的文件系統(tǒng),這個(gè)文件系統(tǒng)可以完全省略掉,Nuttx不依賴于任何文件系統(tǒng)的存在。
偽根文件系統(tǒng)
可以通過將CONFIG_NFILE_DESCRIPTOS設(shè)置成非零值,來使能這個(gè)內(nèi)存中的偽文件系統(tǒng)。它是一個(gè)內(nèi)存文件系統(tǒng),因?yàn)樗恍枰魏未鎯?chǔ)介質(zhì)或塊驅(qū)動(dòng)程序的支持。文件系統(tǒng)內(nèi)容是通過標(biāo)準(zhǔn)文件系統(tǒng)操作(open, close, read, write, etc.)實(shí)時(shí)生成的。在這個(gè)意義上,它是一個(gè)偽文件系統(tǒng)(Linux的/proc也稱為偽文件系統(tǒng))。
可以通過偽文件系統(tǒng)訪問用戶提供的任何數(shù)據(jù)或邏輯。支持對(duì)字符設(shè)備驅(qū)動(dòng)及塊設(shè)備驅(qū)動(dòng)節(jié)點(diǎn)在偽文件系統(tǒng)任何目錄中的內(nèi)建,不過按照慣例,都習(xí)慣放在/dev偽文件系統(tǒng)目錄中。文件系統(tǒng)掛載
簡單的內(nèi)存文件系統(tǒng),可通過掛載塊設(shè)備來擴(kuò)展,這些塊設(shè)備提供大容量存儲(chǔ)設(shè)備支持以實(shí)現(xiàn)真正的文件系統(tǒng)訪問。Nuttx支持標(biāo)準(zhǔn)的mount()命令,該命令允許塊驅(qū)動(dòng)程序?qū)⑽募到y(tǒng)綁定到偽文件系統(tǒng)中的掛載點(diǎn)上。目前,Nuttx支持VFAT文件系統(tǒng)。與Linux比較
從編程的角度來看,Nuttx文件系統(tǒng)看起來與Linux文件系統(tǒng)非常類似,但是,有一個(gè)根本的區(qū)別:Nuttx根文件系統(tǒng)是一個(gè)偽文件系統(tǒng),而真正的文件系統(tǒng)可以掛載在偽文件系統(tǒng)中;相比之下,在典型的Linux安裝中,Linux根文件系統(tǒng)是一個(gè)真正的文件系統(tǒng),偽文件系統(tǒng)掛載在真正的根文件系統(tǒng)中。Nuttx選擇的方法,旨在提供從非常小的平臺(tái)到中等平臺(tái)等的支持,以便具備更好的可擴(kuò)展性。
2. 數(shù)據(jù)結(jié)構(gòu)
2.1 struct inode
inode是文件系統(tǒng)中最重要的結(jié)構(gòu),存放基本的數(shù)據(jù):
struct inode
{
FAR struct inode *i_peer; /* Link to same level inode */
FAR struct inode *i_child; /* Link to lower level inode */
int16_t i_crefs; /* References to inode */
uint16_t i_flags; /* Flags for inode */
union inode_ops_u u; /* Inode operations */
#ifdef CONFIG_FILE_MODE
mode_t i_mode; /* Access mode flags */
#endif
FAR void *i_private; /* Per inode driver private data */
char i_name[1]; /* Name of inode (variable) */
};
-
i_peer和i_child會(huì)將inode組織成樹狀結(jié)構(gòu); -
i_flags用于表明文件類型,比如Character driver/Block driver/Mount point/Special OS type/Named semaphore/Message Queue/Shared memory region/Soft link等 -
i_private在驅(qū)動(dòng)中,通常用于存放私有數(shù)據(jù); -
union inode_ops_u u,存放對(duì)inode的操作函數(shù)集,而針對(duì)不同的inode類型,對(duì)應(yīng)不同的操作函數(shù);
2.2 union inode_ops_u
union inode_ops_u
{
FAR const struct file_operations *i_ops; /* Driver operations for inode */
#ifndef CONFIG_DISABLE_MOUNTPOINT
FAR const struct block_operations *i_bops; /* Block driver operations */
FAR const struct mountpt_operations *i_mops; /* Operations on a mountpoint */
#endif
#ifdef CONFIG_FS_NAMED_SEMAPHORES
FAR struct nsem_inode_s *i_nsem; /* Named semaphore */
#endif
#ifndef CONFIG_DISABLE_MQUEUE
FAR struct mqueue_inode_s *i_mqueue; /* POSIX message queue */
#endif
#ifdef CONFIG_PSEUDOFS_SOFTLINKS
FAR char *i_link; /* Full path to link target */
#endif
};
主要有三個(gè)操作函數(shù)集,此外由于VFS也維護(hù)了像Named semaphores/Message Queues/Shared memory等資源,但是這些資源又不像其他類型的文件有函數(shù)操作集,算是special case,也放在這個(gè)結(jié)構(gòu)中。
-
struct file_operations,存放對(duì)驅(qū)動(dòng)的操作,一般在實(shí)現(xiàn)驅(qū)動(dòng)程序中,都會(huì)去實(shí)現(xiàn)對(duì)應(yīng)的函數(shù)操作;
struct file_operations
{
/* The device driver open method differs from the mountpoint open method */
int (*open)(FAR struct file *filep);
/* The following methods must be identical in signature and position because
* the struct file_operations and struct mountp_operations are treated like
* unions.
*/
int (*close)(FAR struct file *filep);
ssize_t (*read)(FAR struct file *filep, FAR char *buffer, size_t buflen);
ssize_t (*write)(FAR struct file *filep, FAR const char *buffer, size_t buflen);
off_t (*seek)(FAR struct file *filep, off_t offset, int whence);
int (*ioctl)(FAR struct file *filep, int cmd, unsigned long arg);
/* The two structures need not be common after this point */
#ifndef CONFIG_DISABLE_POLL
int (*poll)(FAR struct file *filep, struct pollfd *fds, bool setup);
#endif
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
int (*unlink)(FAR struct inode *inode);
#endif
};
-
struct block_operations,存放塊設(shè)備的操作函數(shù)集,用于文件系統(tǒng)轉(zhuǎn)換;
struct block_operations
{
int (*open)(FAR struct inode *inode);
int (*close)(FAR struct inode *inode);
ssize_t (*read)(FAR struct inode *inode, FAR unsigned char *buffer,
size_t start_sector, unsigned int nsectors);
ssize_t (*write)(FAR struct inode *inode, FAR const unsigned char *buffer,
size_t start_sector, unsigned int nsectors);
int (*geometry)(FAR struct inode *inode, FAR struct geometry *geometry);
int (*ioctl)(FAR struct inode *inode, int cmd, unsigned long arg);
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
int (*unlink)(FAR struct inode *inode);
#endif
};
-
struct mountpt_operations,由一個(gè)文件系統(tǒng)提供,用于描述掛載點(diǎn);
struct inode;
struct fs_dirent_s;
struct stat;
struct statfs;
struct mountpt_operations
{
/* The mountpoint open method differs from the driver open method
* because it receives (1) the inode that contains the mountpoint
* private data, (2) the relative path into the mountpoint, and (3)
* information to manage privileges.
*/
int (*open)(FAR struct file *filep, FAR const char *relpath,
int oflags, mode_t mode);
/* The following methods must be identical in signature and position
* because the struct file_operations and struct mountp_operations are
* treated like unions.
*/
int (*close)(FAR struct file *filep);
ssize_t (*read)(FAR struct file *filep, FAR char *buffer, size_t buflen);
ssize_t (*write)(FAR struct file *filep, FAR const char *buffer,
size_t buflen);
off_t (*seek)(FAR struct file *filep, off_t offset, int whence);
int (*ioctl)(FAR struct file *filep, int cmd, unsigned long arg);
/* The two structures need not be common after this point. The following
* are extended methods needed to deal with the unique needs of mounted
* file systems.
*
* Additional open-file-specific mountpoint operations:
*/
int (*sync)(FAR struct file *filep);
int (*dup)(FAR const struct file *oldp, FAR struct file *newp);
int (*fstat)(FAR const struct file *filep, FAR struct stat *buf);
/* Directory operations */
int (*opendir)(FAR struct inode *mountpt, FAR const char *relpath,
FAR struct fs_dirent_s *dir);
int (*closedir)(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir);
int (*readdir)(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir);
int (*rewinddir)(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir);
/* General volume-related mountpoint operations: */
int (*bind)(FAR struct inode *blkdriver, FAR const void *data,
FAR void **handle);
int (*unbind)(FAR void *handle, FAR struct inode **blkdriver,
unsigned int flags);
int (*statfs)(FAR struct inode *mountpt, FAR struct statfs *buf);
/* Operations on paths */
int (*unlink)(FAR struct inode *mountpt, FAR const char *relpath);
int (*mkdir)(FAR struct inode *mountpt, FAR const char *relpath,
mode_t mode);
int (*rmdir)(FAR struct inode *mountpt, FAR const char *relpath);
int (*rename)(FAR struct inode *mountpt, FAR const char *oldrelpath,
FAR const char *newrelpath);
int (*stat)(FAR struct inode *mountpt, FAR const char *relpath,
FAR struct stat *buf);
/* NOTE: More operations will be needed here to support: disk usage
* stats file stat(), file attributes, file truncation, etc.
*/
};
2.3
struct file
- 一個(gè)打開的文件對(duì)應(yīng)一個(gè)
struct file結(jié)構(gòu),在該結(jié)構(gòu)中包含了inode,用于描述文件的類型以及對(duì)應(yīng)的函數(shù)操作集。
struct file
{
int f_oflags; /* Open mode flags */
off_t f_pos; /* File position */
FAR struct inode *f_inode; /* Driver or file system interface */
void *f_priv; /* Per file driver private data */
};
- 在每個(gè)進(jìn)程中,
struct tcb_s結(jié)構(gòu)中都有一個(gè)struct filelist結(jié)構(gòu),用于維護(hù)打開的文件,當(dāng)一個(gè)進(jìn)程調(diào)用POSIX接口open來打開時(shí),會(huì)得到文件描述符,文件描述符對(duì)應(yīng)的就是這個(gè)文件數(shù)組的索引值。
struct filelist
{
sem_t fl_sem; /* Manage access to the file list */
struct file fl_files[CONFIG_NFILE_DESCRIPTORS];
}
3. 原理分析
3.1 框架分析
架構(gòu)框圖如下所示:

- 用戶層,通過系統(tǒng)調(diào)用調(diào)到VFS層的通用接口;
- VFS層,相當(dāng)于一個(gè)適配層,用于對(duì)接不同的實(shí)際文件系統(tǒng);
- 實(shí)際文件系統(tǒng)層,典型的情況下一個(gè)文件系統(tǒng)都需要綁定到塊設(shè)備驅(qū)動(dòng)程序上,而一些不太典型的情況是不需要塊設(shè)備驅(qū)動(dòng),比如偽文件系統(tǒng)(
BINFS、PROCFS)和MTD文件系統(tǒng)(NXFFS)。在Nuttx中,需要塊設(shè)備驅(qū)動(dòng)的文件系統(tǒng)為:FAT、ROMFS、SMARTFS;而不需要塊設(shè)備驅(qū)動(dòng)的文件系統(tǒng)為:NXFFS、BINFS、PROCFS、NFS、TMPFS等; - MTD,
Memory Technology Devices,向上提供MTD接口,向下對(duì)接不同的硬件設(shè)備;
3.2 mount流程
mount()函數(shù)用于將source塊設(shè)備指定的文件系統(tǒng)與根文件系統(tǒng)中target指定的路徑名關(guān)聯(lián)在一起。
在講mount()之前,需要先了解一下數(shù)據(jù)結(jié)構(gòu):
- struct fsmap_t
struct fsmap_t
{
FAR const char *fs_filesystemtype;
FAR const struct mountpt_operations *fs_mops;
};
這個(gè)結(jié)構(gòu)完成的就是文件系統(tǒng)名字和對(duì)應(yīng)的操作函數(shù)集的映射,在mount()函數(shù)中會(huì)根據(jù)對(duì)應(yīng)的文件系統(tǒng)名字去查找struct mountpt_operations。
- struct mountpt_operations
這個(gè)數(shù)據(jù)結(jié)構(gòu)在上文中介紹過了,有一個(gè)函數(shù)需要特別注意一下:
int (*bind)(FAR struct inode *blkdriver, FAR const void *data, FAR void **handle);
該函數(shù)用于將文件系統(tǒng)與某個(gè)塊設(shè)備驅(qū)動(dòng)進(jìn)行綁定。
mount()關(guān)鍵代碼如下:
int mount(FAR const char *source, FAR const char *target,
FAR const char *filesystemtype, unsigned long mountflags,
FAR const void *data)
{
#if defined(BDFS_SUPPORT) || defined(NONBDFS_SUPPORT)
#ifdef BDFS_SUPPORT
FAR struct inode *blkdrvr_inode = NULL;
#endif
FAR struct inode *mountpt_inode;
FAR const struct mountpt_operations *mops;
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
struct inode_search_s desc;
#endif
void *fshandle;
int errcode;
int ret;
/* Verify required pointer arguments */
DEBUGASSERT(target && filesystemtype);
/* Find the specified filesystem. Try the block driver file systems first */
#ifdef BDFS_SUPPORT
if (source && (mops = mount_findfs(g_bdfsmap, filesystemtype)) != NULL)
{
/* Make sure that a block driver argument was provided */
DEBUGASSERT(source);
/* Find the block driver */
ret = find_blockdriver(source, mountflags, &blkdrvr_inode);
if (ret < 0)
{
ferr("ERROR: Failed to find block driver %s\n", source);
errcode = -ret;
goto errout;
}
}
else
#endif /* BDFS_SUPPORT */
#ifdef NONBDFS_SUPPORT
if ((mops = mount_findfs(g_nonbdfsmap, filesystemtype)) != NULL)
{
}
else
#endif /* NONBDFS_SUPPORT */
{
ferr("ERROR: Failed to find file system %s\n", filesystemtype);
errcode = ENODEV;
goto errout;
}
inode_semtake();
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
/* Check if the inode already exists */
SETUP_SEARCH(&desc, target, false);
ret = inode_find(&desc);
if (ret >= 0)
{
/* Successfully found. The reference count on the inode has been
* incremented.
*/
mountpt_inode = desc.node;
DEBUGASSERT(mountpt_inode != NULL);
/* But is it a directory node (i.e., not a driver or other special
* node)?
*/
if (INODE_IS_SPECIAL(mountpt_inode))
{
ferr("ERROR: target %s exists and is a special node\n", target);
errcode = -ENOTDIR;
inode_release(mountpt_inode);
goto errout_with_semaphore;
}
}
else
#endif
/* Insert a dummy node -- we need to hold the inode semaphore
* to do this because we will have a momentarily bad structure.
* NOTE that the new inode will be created with an initial reference
* count of zero.
*/
{
ret = inode_reserve(target, &mountpt_inode);
if (ret < 0)
{
/* inode_reserve can fail for a couple of reasons, but the most likely
* one is that the inode already exists. inode_reserve may return:
*
* -EINVAL - 'path' is invalid for this operation
* -EEXIST - An inode already exists at 'path'
* -ENOMEM - Failed to allocate in-memory resources for the operation
*/
ferr("ERROR: Failed to reserve inode for target %s\n", target);
errcode = -ret;
goto errout_with_semaphore;
}
}
/* Bind the block driver to an instance of the file system. The file
* system returns a reference to some opaque, fs-dependent structure
* that encapsulates this binding.
*/
if (!mops->bind)
{
/* The filesystem does not support the bind operation ??? */
ferr("ERROR: Filesystem does not support bind\n");
errcode = EINVAL;
goto errout_with_mountpt;
}
/* Increment reference count for the reference we pass to the file system */
#ifdef BDFS_SUPPORT
#ifdef NONBDFS_SUPPORT
if (blkdrvr_inode)
#endif
{
blkdrvr_inode->i_crefs++;
}
#endif
/* On failure, the bind method returns -errorcode */
#ifdef BDFS_SUPPORT
ret = mops->bind(blkdrvr_inode, data, &fshandle);
#else
ret = mops->bind(NULL, data, &fshandle);
#endif
if (ret != 0)
{
/* The inode is unhappy with the blkdrvr for some reason. Back out
* the count for the reference we failed to pass and exit with an
* error.
*/
ferr("ERROR: Bind method failed: %d\n", ret);
#ifdef BDFS_SUPPORT
#ifdef NONBDFS_SUPPORT
if (blkdrvr_inode)
#endif
{
blkdrvr_inode->i_crefs--;
}
#endif
errcode = -ret;
goto errout_with_mountpt;
}
/* We have it, now populate it with driver specific information. */
INODE_SET_MOUNTPT(mountpt_inode);
mountpt_inode->u.i_mops = mops;
#ifdef CONFIG_FILE_MODE
mountpt_inode->i_mode = mode;
#endif
mountpt_inode->i_private = fshandle;
inode_semgive();
/* We can release our reference to the blkdrver_inode, if the filesystem
* wants to retain the blockdriver inode (which it should), then it must
* have called inode_addref(). There is one reference on mountpt_inode
* that will persist until umount2() is called.
*/
...
}
完成的工作主要有:
- 調(diào)用
mount_findfs()函數(shù),根據(jù)傳入的參數(shù)filesystemtype來找到對(duì)應(yīng)的文件系統(tǒng)操作函數(shù)集mops,如果是需要塊設(shè)備支持的文件系統(tǒng),則需要調(diào)用find_blockdriver()來查找傳入?yún)?shù)source對(duì)應(yīng)的塊設(shè)備驅(qū)動(dòng); - 根據(jù)傳入?yún)?shù)
target,來查找需要mount的路徑對(duì)應(yīng)的inode節(jié)點(diǎn),如果沒有的話需要調(diào)用inode_reserve()創(chuàng)建一個(gè)mountpt_inode; - 調(diào)用
mops->bind()函數(shù)將文件系統(tǒng)與塊設(shè)備驅(qū)動(dòng)進(jìn)行綁定,如果不需要塊設(shè)備支持的文件系統(tǒng),bind()函數(shù)可能不需要做特殊處理, 而在需要塊設(shè)備支持的文件系統(tǒng)中,bind()函數(shù)最終會(huì)將該文件系統(tǒng)的整體狀態(tài)都傳出來,保存在fshandle中; - 更新掛載點(diǎn)
mountpt_inode的內(nèi)容,包括操作函數(shù)集mops,以及將fshandle保存的文件系統(tǒng)的整體狀態(tài)放置到mountpt_inode結(jié)構(gòu)中的i_private字段中。當(dāng)打開這個(gè)掛載點(diǎn)mountpt_inode時(shí),便可以根據(jù)這個(gè)字段來取出對(duì)應(yīng)的信息。