Nuttx相關的歷史文章:
1. 介紹
文件系統(tǒng),是對一個存儲設備上的數(shù)據(jù)和元數(shù)據(jù)進行組織的機制,它是操作系統(tǒng)管理持久性數(shù)據(jù)的子系統(tǒng),提供數(shù)據(jù)存儲和訪問功能。
將一個文件系統(tǒng)與一個存儲設備關聯(lián)起來的過程叫做掛載(mount),掛載時會將一個文件系統(tǒng)附著到當前文件系統(tǒng)層次結構中(根),在執(zhí)行掛載時,需要提供文件系統(tǒng)類型、文件系統(tǒng)和一個掛載點。
1.1 Nuttx文件系統(tǒng)介紹
Nuttx包含了一個可選的、可擴展的文件系統(tǒng),這個文件系統(tǒng)可以完全省略掉,Nuttx不依賴于任何文件系統(tǒng)的存在。
偽根文件系統(tǒng)
可以通過將CONFIG_NFILE_DESCRIPTOS設置成非零值,來使能這個內存中的偽文件系統(tǒng)。它是一個內存文件系統(tǒng),因為它不需要任何存儲介質或塊驅動程序的支持。文件系統(tǒng)內容是通過標準文件系統(tǒng)操作(open, close, read, write, etc.)實時生成的。在這個意義上,它是一個偽文件系統(tǒng)(Linux的/proc也稱為偽文件系統(tǒng))。
可以通過偽文件系統(tǒng)訪問用戶提供的任何數(shù)據(jù)或邏輯。支持對字符設備驅動及塊設備驅動節(jié)點在偽文件系統(tǒng)任何目錄中的內建,不過按照慣例,都習慣放在/dev偽文件系統(tǒng)目錄中。文件系統(tǒng)掛載
簡單的內存文件系統(tǒng),可通過掛載塊設備來擴展,這些塊設備提供大容量存儲設備支持以實現(xiàn)真正的文件系統(tǒng)訪問。Nuttx支持標準的mount()命令,該命令允許塊驅動程序將文件系統(tǒng)綁定到偽文件系統(tǒng)中的掛載點上。目前,Nuttx支持VFAT文件系統(tǒng)。與Linux比較
從編程的角度來看,Nuttx文件系統(tǒng)看起來與Linux文件系統(tǒng)非常類似,但是,有一個根本的區(qū)別:Nuttx根文件系統(tǒng)是一個偽文件系統(tǒng),而真正的文件系統(tǒng)可以掛載在偽文件系統(tǒng)中;相比之下,在典型的Linux安裝中,Linux根文件系統(tǒng)是一個真正的文件系統(tǒng),偽文件系統(tǒng)掛載在真正的根文件系統(tǒng)中。Nuttx選擇的方法,旨在提供從非常小的平臺到中等平臺等的支持,以便具備更好的可擴展性。
2. 數(shù)據(jù)結構
2.1 struct inode
inode是文件系統(tǒng)中最重要的結構,存放基本的數(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會將inode組織成樹狀結構; -
i_flags用于表明文件類型,比如Character driver/Block driver/Mount point/Special OS type/Named semaphore/Message Queue/Shared memory region/Soft link等 -
i_private在驅動中,通常用于存放私有數(shù)據(jù); -
union inode_ops_u u,存放對inode的操作函數(shù)集,而針對不同的inode類型,對應不同的操作函數(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
};
主要有三個操作函數(shù)集,此外由于VFS也維護了像Named semaphores/Message Queues/Shared memory等資源,但是這些資源又不像其他類型的文件有函數(shù)操作集,算是special case,也放在這個結構中。
-
struct file_operations,存放對驅動的操作,一般在實現(xiàn)驅動程序中,都會去實現(xiàn)對應的函數(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ù)集,用于文件系統(tǒng)轉換;
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,由一個文件系統(tǒng)提供,用于描述掛載點;
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
- 一個打開的文件對應一個
struct file結構,在該結構中包含了inode,用于描述文件的類型以及對應的函數(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 */
};
- 在每個進程中,
struct tcb_s結構中都有一個struct filelist結構,用于維護打開的文件,當一個進程調用POSIX接口open來打開時,會得到文件描述符,文件描述符對應的就是這個文件數(shù)組的索引值。
struct filelist
{
sem_t fl_sem; /* Manage access to the file list */
struct file fl_files[CONFIG_NFILE_DESCRIPTORS];
}
3. 原理分析
3.1 框架分析
架構框圖如下所示:

- 用戶層,通過系統(tǒng)調用調到VFS層的通用接口;
- VFS層,相當于一個適配層,用于對接不同的實際文件系統(tǒng);
- 實際文件系統(tǒng)層,典型的情況下一個文件系統(tǒng)都需要綁定到塊設備驅動程序上,而一些不太典型的情況是不需要塊設備驅動,比如偽文件系統(tǒng)(
BINFS、PROCFS)和MTD文件系統(tǒng)(NXFFS)。在Nuttx中,需要塊設備驅動的文件系統(tǒng)為:FAT、ROMFS、SMARTFS;而不需要塊設備驅動的文件系統(tǒng)為:NXFFS、BINFS、PROCFS、NFS、TMPFS等; - MTD,
Memory Technology Devices,向上提供MTD接口,向下對接不同的硬件設備;
3.2 mount流程
mount()函數(shù)用于將source塊設備指定的文件系統(tǒng)與根文件系統(tǒng)中target指定的路徑名關聯(lián)在一起。
在講mount()之前,需要先了解一下數(shù)據(jù)結構:
- struct fsmap_t
struct fsmap_t
{
FAR const char *fs_filesystemtype;
FAR const struct mountpt_operations *fs_mops;
};
這個結構完成的就是文件系統(tǒng)名字和對應的操作函數(shù)集的映射,在mount()函數(shù)中會根據(jù)對應的文件系統(tǒng)名字去查找struct mountpt_operations。
- struct mountpt_operations
這個數(shù)據(jù)結構在上文中介紹過了,有一個函數(shù)需要特別注意一下:
int (*bind)(FAR struct inode *blkdriver, FAR const void *data, FAR void **handle);
該函數(shù)用于將文件系統(tǒng)與某個塊設備驅動進行綁定。
mount()關鍵代碼如下:
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.
*/
...
}
完成的工作主要有:
- 調用
mount_findfs()函數(shù),根據(jù)傳入的參數(shù)filesystemtype來找到對應的文件系統(tǒng)操作函數(shù)集mops,如果是需要塊設備支持的文件系統(tǒng),則需要調用find_blockdriver()來查找傳入?yún)?shù)source對應的塊設備驅動; - 根據(jù)傳入?yún)?shù)
target,來查找需要mount的路徑對應的inode節(jié)點,如果沒有的話需要調用inode_reserve()創(chuàng)建一個mountpt_inode; - 調用
mops->bind()函數(shù)將文件系統(tǒng)與塊設備驅動進行綁定,如果不需要塊設備支持的文件系統(tǒng),bind()函數(shù)可能不需要做特殊處理, 而在需要塊設備支持的文件系統(tǒng)中,bind()函數(shù)最終會將該文件系統(tǒng)的整體狀態(tài)都傳出來,保存在fshandle中; - 更新掛載點
mountpt_inode的內容,包括操作函數(shù)集mops,以及將fshandle保存的文件系統(tǒng)的整體狀態(tài)放置到mountpt_inode結構中的i_private字段中。當打開這個掛載點mountpt_inode時,便可以根據(jù)這個字段來取出對應的信息。