Nuttx文件系統(tǒng)

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_peeri_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)框圖
  • 用戶層,通過系統(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、ROMFSSMARTFS;而不需要塊設(shè)備驅(qū)動(dòng)的文件系統(tǒng)為:NXFFS、BINFS、PROCFSNFSTMPFS等;
  • 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.
   */
...
}

完成的工作主要有:

  1. 調(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);
  2. 根據(jù)傳入?yún)?shù)target,來查找需要mount的路徑對(duì)應(yīng)的inode節(jié)點(diǎn),如果沒有的話需要調(diào)用inode_reserve()創(chuàng)建一個(gè)mountpt_inode;
  3. 調(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中;
  4. 更新掛載點(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)的信息。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容