Nuttx文件系統(tǒng)

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_peeri_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)框圖
  • 用戶層,通過系統(tǒng)調用調到VFS層的通用接口;
  • VFS層,相當于一個適配層,用于對接不同的實際文件系統(tǒng);
  • 實際文件系統(tǒng)層,典型的情況下一個文件系統(tǒng)都需要綁定到塊設備驅動程序上,而一些不太典型的情況是不需要塊設備驅動,比如偽文件系統(tǒng)(BINFS、PROCFS)和MTD文件系統(tǒng)(NXFFS)。在Nuttx中,需要塊設備驅動的文件系統(tǒng)為:FAT、ROMFSSMARTFS;而不需要塊設備驅動的文件系統(tǒng)為:NXFFSBINFS、PROCFSNFS、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.
   */
...
}

完成的工作主要有:

  1. 調用mount_findfs()函數(shù),根據(jù)傳入的參數(shù)filesystemtype來找到對應的文件系統(tǒng)操作函數(shù)集mops,如果是需要塊設備支持的文件系統(tǒng),則需要調用find_blockdriver()來查找傳入?yún)?shù)source對應的塊設備驅動;
  2. 根據(jù)傳入?yún)?shù)target,來查找需要mount的路徑對應的inode節(jié)點,如果沒有的話需要調用inode_reserve()創(chuàng)建一個mountpt_inode
  3. 調用mops->bind()函數(shù)將文件系統(tǒng)與塊設備驅動進行綁定,如果不需要塊設備支持的文件系統(tǒng),bind()函數(shù)可能不需要做特殊處理, 而在需要塊設備支持的文件系統(tǒng)中,bind()函數(shù)最終會將該文件系統(tǒng)的整體狀態(tài)都傳出來,保存在fshandle中;
  4. 更新掛載點mountpt_inode的內容,包括操作函數(shù)集mops,以及將fshandle保存的文件系統(tǒng)的整體狀態(tài)放置到mountpt_inode結構中的i_private字段中。當打開這個掛載點mountpt_inode時,便可以根據(jù)這個字段來取出對應的信息。
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容