5.0 NuttX File System

2023-05-16

转载请注明出处:5.0 NuttX File System_Alvin Peng的博客-CSDN博客

文章均出自个人理解

前言

      前一段时间折腾了几个驱动(PWM、Serial、I2C),这次来折腾下,Nuttx的文件系统,之前看到手册上有好多xxxFs啥的,里面有提到以个可以执行二进制文件,听起来很有意思,现在开始来看看到底是怎么实现的呢。

Pseudo Root File System

     Nuttx的根文件系统是一个简单的伪文件系统,这个文件系统存在内存中,是不需要任何存储介质或块设备驱动支持的,我的理解是在RAM中申请的一块区域,然后构建这个文件系统的,其内容是通过标准文件系统操作引用即时生成的,这句话说的容易理解点就是说,这个文件系统具备(open、close、write、read...)这些接口,还记得之前的文档中说道,nuttx是将所有的驱动向上都抽象成了这种接口的。

     同样的在Linux/proc文件系统也是伪文件系统(原文翻译,我其实不懂linux,没细致去研究过),在我们之前编写的驱动(字符型、块驱动...)通过regist_driver(...)注册驱动时候,就是注册到这个伪根文件系统的,说道这里,其实这里的文件系统其实就是一个特定结构的数组(这里挖个坑,后面列举实例佐证);

Mounted File Systems

     之前提到的这种伪文件系统(Simple in-memory file system)可以被扩展为具备访问真实存储设备的块设备(block device),Nuttx是支持标准的mount()接口,允许一个块设备绑定到伪文件系统的一个挂载点上的,目前Nuttx是支持标准的VFAT和ROMFS文件系统,以及Nuttx的NXFFS和网络文件系统客户端(NFS client),这里表达的意思有点含糊,简单的一句话应该是nuttx的伪文件系统可以为以下几种文件系统提供挂载点;

Comparison to Linux

    这里和linux做个对比就是,nuttx的根文件系统是一个伪文件系统,真实的文件系统(存在真实的介质中)可能被挂载在这个伪文件系统中,而linux的根文件系统是真实存在与介质中的; 通过读这段文字,我的理解还是猜测这里的伪文件系统就是指不会被保存,掉电就消失了,但是ta又具备文件系统所有的接口和功能罢了(这里改天还是要找出具体的例子来佐证);

以下的文字是参考了《NuttX文件系统学习之关键数据结构及设备注册》文章

     老规矩还是先来整理框架,前面翻译的文章让人没有清晰的结构,感觉有点云里雾里,直接开始梳理;

在Nuttx的文件系统中,定义了字符型驱动设备、块驱动设备、Mountpoint驱动设备...这里需要回忆下之前的字符型设备最关键的两个数据结构×_dev_s和×_ops_s,下面就先分析字符型设备注册的过程,来了解Nuttx的文件系统框架;

字符型驱动注册流程

     之前在填充了×_dev_s和×_ops_s的数据结构后就调用了register_driver来注册;

int register_driver(FAR const char *path,
                    FAR const struct file_operations *fops,
                    mode_t mode,
                    FAR void *priv)
{
  FAR struct inode *node;
  int ret;

  /* Insert a dummy node -- we need to hold the inode semaphore because we
  * will have a momentarily bad structure.
  */

  inode_semtake();
  ret = inode_reserve(path, &node);
  if (ret >= 0)
  {
  /* We have it, now populate it with driver specific information.
  * NOTE that the initial reference count on the new inode is zero.
  */

  INODE_SET_DRIVER(node);

  node->u.i_ops = fops;
#ifdef CONFIG_FILE_MODE
  node->i_mode = mode;
#endif
  node->i_private = priv;
  ret = OK;
  }

  inode_semgive();
  return ret;
}

这里很明显,我们填充的两个驱动数据结构都被注册到了struct inode这个数据结构中了,实际这inode就是我们这次分析的切入点;

/* This structure represents one inode in the Nuttx pseudo-file system */

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) */
};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) */
};

看到这里需要注意的两个关键的成员变量,红色标红的两个数据在后面分析块设备驱动时也会用得上,我继续展开;

/* These are the various kinds of operations that can be associated with
 * an inode.
 */

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
};

这里可以明显看出来两点问题,第一就是Nuttx在框架实现上都是采用了×_dev_s和×_ops_s这种套路,inode也不例外;第二就是在inode_ops_u中,定义了两类ops(file_operations、block_operations、mountpt_operations)两类后面解释;前者是我们通常的字符型型驱动和特殊驱动抽象的文件接口,后面的两个很明显是针对块设备驱动的;说到这里其实就应该了解这些驱动注册到inode里面后,是如何存在于Nuttx的文件系统里面的了;

     在补充一句就是,inode是对所有设备的接口的抽象,各类设备的区别其实就在×_ops_s,这也就很好解释了为什么是union类型的数据结构了;接口内部先根据设备名字遍历记录块设备的全局结构体链表中所有的node,如果链表中已经存在与此名字相同的node,则表明此设备已经注册,反之则根据设备名字分配一个structinode *类型的inode内存;然后根据设备的name,bops, mode以及priv初始化inode的相关成员;从而完成块设备的注册;简单来说这个inode在nuttx里面就是存在与一个树结构里面的;

/****************************************************************************
 * Name: inode_reserve
 *
 * Description:
 * Reserve an (initialized) inode the pseudo file system. The initial
 * reference count on the new inode is zero.
 *
 * Input parameters:
 * path - The path to the inode to create
 * inode - The location to return the inode pointer
 *
 * Returned Value:
 * Zero on success (with the inode point in 'inode'); A negated errno
 * value is returned on failure:
 *
 * EINVAL - 'path' is invalid for this operation
 * EEXIST - An inode already exists at 'path'
 * ENOMEM - Failed to allocate in-memory resources for the operation
 *
 * Assumptions:
 * Caller must hold the inode semaphore
 *
 ****************************************************************************/

int inode_reserve(FAR const char *path, FAR struct inode **inode)
{
  struct inode_search_s desc;
  FAR struct inode *left;
  FAR struct inode *parent;
  FAR const char *name;
  int ret;

  /* Assume failure */

  DEBUGASSERT(path != NULL && inode != NULL);
  *inode = NULL;

  /* Handle paths that are interpreted as the root directory */

  if (path[0] == '\0' || path[0] != '/')
  {
  return -EINVAL;
  }

  /* Find the location to insert the new subtree */

  SETUP_SEARCH(&desc, path, false);

  ret = inode_search(&desc);
  if (ret >= 0)
  {
  /* It is an error if the node already exists in the tree (or if it
  * lies within a mountpoint, we don't distinguish here).
  */

  ret = -EEXIST;
  goto errout_with_search;
  }

  /* Now we now where to insert the subtree */

  name = desc.path;
  left = desc.peer;
  parent = desc.parent;

  for (; ; )
  {
  FAR struct inode *node;

  /* Create a new node -- we need to know if this is the
  * the leaf node or some intermediary. We can find this
  * by looking at the next name.
  */

  FAR const char *nextname = inode_nextname(name);
  if (*nextname != '\0')
  {
  /* Insert an operationless node */

  node = inode_alloc(name);
  if (node != NULL)
  {
  inode_insert(node, left, parent);

  /* Set up for the next time through the loop */

  name = nextname;
  left = NULL;
  parent = node;
  continue;
  }
  }
  else
  {
  node = inode_alloc(name);
  if (node != NULL)
  {
  inode_insert(node, left, parent);
  *inode = node;
  ret = OK;
  break;
  }
  }

  /* We get here on failures to allocate node memory */

  ret = -ENOMEM;
  break;
  }

errout_with_search:
  RELEASE_SEARCH(&desc);
  return ret;
}

我们继续查看inode_insert函数

/****************************************************************************
 * Name: inode_insert
 ****************************************************************************/

static void inode_insert(FAR struct inode *node,
  FAR struct inode *peer,
  FAR struct inode *parent)
{
  /* If peer is non-null, then new node simply goes to the right
  * of that peer node.
  */

  if (peer)
  {
  node->i_peer = peer->i_peer;
  peer->i_peer = node;
  }

  /* If parent is non-null, then it must go at the head of its
  * list of children.
  */

  else if (parent)
  {
  node->i_peer = parent->i_child;
  parent->i_child = node;
  }

  /* Otherwise, this must be the new root_inode */

  else
  {
  node->i_peer = g_root_inode;
  g_root_inode = node;
  }
}

到此基本上我们的驱动注册就走到头了,有兴趣可以继续分析更细致了原理;接下来我们继续block的驱动设备注册原理分析;

块设备驱动的注册流程

     上面的分析其实差不多描绘了一个大体的框架了,块设备有些许的差异,但是万变不离其中,我们还是来举一反三;

和字符设备一样,块设备也是以inode的抽象形式存在VFS的树结构上,只是在×_ops_s的数据结构中有Block driver operations i_bops和Operations on a mountpoint i_mops的专门的数据结构了,这里有点区别的是多出了“Operations on a mountpoint i_mops”,这个是主要是对应文件系统的接口,例如fat/nfs/nxffs/romfs/smartfs...这写文件系统,在nuttx/fs/路径下包含上述文件系统;

     而i_mops和之前的字符驱动ops相差不大,多出了geometry这个成员,ta是用来描述块设备的物理属性的(扇区大小、扇区个数等等),既然都是以inode的形式存在,那么我们就只分析这个i_mops了;

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 *filp, 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 *filp);
  ssize_t (*read)(FAR struct file *filp, FAR char *buffer, size_t buflen);
  ssize_t (*write)(FAR struct file *filp, FAR const char *buffer, size_t buflen);
  off_t (*seek)(FAR struct file *filp, off_t offset, int whence);
  int (*ioctl)(FAR struct file *filp, 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 *filp);
  int (*dup)(FAR const struct file *oldp, FAR struct file *newp);

  /* 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);

  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.
  */
};

接下来直接进入主题,在nuttx系统中mount point和block device,其实是当block device自身作为一个设备时,和字符型设备一样,都需要先构建自己的×_dev_s(ftl_struct_s/smart_struct_s...)和×_ops_s(i_bops)数据结构,然后类似的调用register_blockdriver()函数进行设备注册;

     而相对来说mount point对我们来说就稍微陌生点,在nuttx/fs/fs_mount.c中mount函数中我们看到了这样的定义:FAR struct inode *mountpt_inode;这说明mountpoint其实也是被抽象成了inode形式,那么我接下来其实也就很容易理解了;

union inode_ops_u
{
  FAR const struct file_operations *i_ops; /* Driver operations for inode */
#ifndef CONFIG_DISABLE_MOUNTPOUNT
  FAR const struct block_operations *i_bops; /* Block driver operations */
  FAR const struct mountpt_operations *i_mops; /* Operations on a mountpoint */
#endif
};

这样来说我们也就可以理解为在nuttx中inode其实定义了三种类型,从struct mountpt_operations来看,其成员又是对于文件系统抽象的接口罢了;

文件系统mount流程

上面了解了块设备的注册流程,接下就只剩下文件系统的Mount流程了;

/****************************************************************************
 * Name: mount
 *
 * Description:
 * mount() attaches the filesystem specified by the 'source' block device
 * name into the root file system at the path specified by 'target.'
 *
 * Return:
 * Zero is returned on success; -1 is returned on an error and errno is
 * set appropriately:
 *
 * EACCES A component of a path was not searchable or mounting a read-only
 * filesystem was attempted without giving the MS_RDONLY flag.
 * EBUSY 'source' is already mounted.
 * EFAULT One of the pointer arguments points outside the user address
 * space.
 * EINVAL 'source' had an invalid superblock.
 * ENODEV 'filesystemtype' not configured
 * ENOENT A pathname was empty or had a nonexistent component.
 * ENOMEM Could not allocate a memory to copy filenames or data into.
 * ENOTBLK 'source' is not a block device
 *
 ****************************************************************************/

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;
  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)
  {
  fdbg("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 */
  {
  fdbg("Failed to find file system %s\n", filesystemtype);
  errcode = ENODEV;
  goto errout;
  }

  /* Insert a dummy node -- we need to hold the inode semaphore
  * to do this because we will have a momentarily bad structure.
  */

  inode_semtake();
  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
  */

  fdbg("Failed to reserve inode\n");
  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 ??? */

  fdbg("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.
  */

  fdbg("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 umount() is called.
  */

#ifdef BDFS_SUPPORT
#ifdef NONBDFS_SUPPORT
  if (blkdrvr_inode)
#endif
  {
  inode_release(blkdrvr_inode);
  }
#endif

  return OK;

  /* A lot of goto's! But they make the error handling much simpler */

errout_with_mountpt:
  mountpt_inode->i_crefs = 0;
  inode_remove(target);
  inode_semgive();
#ifdef BDFS_SUPPORT
#ifdef NONBDFS_SUPPORT
  if (blkdrvr_inode)
#endif
  {
  inode_release(blkdrvr_inode);
  }
#endif

  inode_release(mountpt_inode);
  goto errout;

errout_with_semaphore:
  inode_semgive();
#ifdef BDFS_SUPPORT
#ifdef NONBDFS_SUPPORT
  if (blkdrvr_inode)
#endif
  {
  inode_release(blkdrvr_inode);
  }
#endif

errout:
  set_errno(errcode);
  return ERROR;

#else
  fdbg("No filesystems enabled\n");
  set_errno(ENOSYS);
  return error;
#endif /* BDFS_SUPPORT || NONBDFS_SUPPORT */
}

Source:块设备节点名字,比如/dev/ram1

Target:mount到文件系统的路径,比如/mnt/ramdisk0

Filesystemtype:文件系统类型,比如procfs, tmpfs, vfat, smartfs等等

Mountflags:文件系统被mount时的属性,比如只读等

Data: private data,用于扩展,未使用时传NULL

     接口内部先根据文件系统类型,去遍历记录各种文件系统属性(各种文件系统的类型和操作方法)的结构体全局数组,找出待mount类型文件系统的operation,即读写等操作的方法;

static const struct fsmap_t g_bdfsmap[] =
{

  { "vfat",&fat_operations },

  { "romfs",&romfs_operations },

  { "smartfs",&smartfs_operations },

  { NULL, NULL },

};

static const struct fsmap_t g_nonbdfsmap[] =

{

  { "nxffs",&nxffs_operations },

  { "tmpfs",&tmpfs_operations },

  { "nfs",&nfs_operations },

  { "binfs",&binfs_operations },

  { "procfs",&procfs_operations },

  { "hostfs",&hostfs_operations },

  { NULL, NULL },

};

根据mount的设备节点名字比如/dev/ram1,找到描述该设备的inode节点(即到g_root_inode链表中去遍历);

根据要Mount的路径名字target,为mountpt新分配一个inode节点,并添加至链表g_root_node中;

根据步骤1中获取的文件系统operation方法,调用其中的bind接口,进行文件系统初始化;

对于mount,每种文件系统都有管理他们文件系统的内部数据结构,虽然管理各自内部文件系统的数据结构不同;

但是他们内部均有一个相同的成员,那就是struct inode *fs_blkdriver,用于指向管理设备的inode成员,

Bind函数内部先分配一块管理各自文件系统的内部数据结构,初始化如下成员

fs->fs_blkdriver = blkdriver

fs->fs_sem

然后调用各种文件系统的mout函数,比如smartfs_mount/procfs_mount/fat_mount函数进行各自文件系统真正的Mount操作;

最后把上述管理各自文件系统内部数据结构,通过参数fshandle指针返回;

说明:每种文件系统内部mount实现均是根据各种类型文件系统原理实现,差异比较大,这里略去说明;

最后初始化mountpt_inode的如下成员,完成磁盘Mount工作;

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

5.0 NuttX File System 的相关文章

  • 如何在scala的specs2测试中使用jUnit的TemporaryFolder?

    我正在使用 Playframework 编写测试 我需要创建一个临时文件 RunWith classOf JUnitRunner class DiagnosticSpec extends Specification Rule val tem
  • php 的问题:读取文件名,生成 javascript 和 html

    UPDATE 再一次问好 我发现自己遇到了一个新问题 php代码在我的PC wamp服务器 上完美运行 但我现在已将其上传到免费的网络主机服务器上 虽然php部分运行完美 它生成数组 但javascript函数本身不起作用 因为没有照片在网
  • 从文件中读取第n行的快速方法

    介绍 我有一个名为的 C 进程MyProcess我称之为nbLines时间 地点nbLines是一个名为的大文件的行数InputDataFile txt在其中可以找到输入数据 例如调用 MyProcess InputDataFile txt
  • 在 Ruby 中获取 system() 调用的输出

    如果我使用调用命令内核 系统 http ruby doc org core 2 2 0 Kernel html method i system在 Ruby 中 如何获取其输出 system ls 我想扩展和澄清混沌的答案 https sta
  • 如何使用 Retrofit 2 下载 pdf 文件

    我在下载带改造的 pdf 文件时遇到困难 我的代码生成了一个文件 但它的大小错误 并且当 pdf 打开时它是空白的 这就是我的 php web 服务返回 pdf 文件的方式 param Slim Slim app param String
  • 从相对路径读取文件

    我知道这个问题之前已经被问过 1000 次了 我确实尝试了所有解决方案 Java项目中如何从相对路径读取文件 java io File 找不到指定的路径 https stackoverflow com questions 3844307 h
  • 在 Android 中下载文件

    我正在使用以下代码在 Android 中下载文件 public class FileDownloadActivity extends Activity ProgressDialog mProgressDialog Called when t
  • 如何在Java中读取文件的最后“n”个字节

    如何在不使用 RandomAccessFile 的情况下从文件中读取最后 n 个字节 我的文件中的最后 6 个字节包含写回文件时的重要信息 我需要写入原始文件 然后将最后 6 个字节附加到其他地方 有什么指导吗 谢谢 你必须使用随机存取文件
  • Unix 上的常规文件是什么

    我看到了手册页test http pubs opengroup org onlinepubs 009695399 utilities test html 其中提到了下面的内容 e pathname True if pathname reso
  • CMake:将为 lib 构建的对象文件重用到另一个 lib 目标中

    我正在尝试将我的项目转移到CMake 同时对编译过程进行一些优化 这是交易 我有几个子目录 必须 每个子目录都编译成静态库 这有效 我想将每个子目录中的所有目标文件收集到另一个更大的 完整的静态库中 它看起来像这样 libBig a mad
  • 从文件中读取单词并放入列表中

    本质上 我有一个巨大的文件 所有文件包含每行多个单词 每个单词用空格分隔 有点像这样 WORD WORD WORD WORD ANOTHER WORD SCRABBLE BLAH YES NO 我想要做的是将文件中的所有单词放入一个巨大的列
  • 读取一个文本文件,替换其中的单词,输出到另一个文本文件

    所以我试图在 GO 中编写一个程序来获取一个充满代码的文本文件并将其转换为 GO 代码 然后将该文件保存到 GO 文件或文本文件中 我一直在试图弄清楚如何保存对文本文件所做的更改 但我可以看到更改的唯一方法是通过 println 语句 因为
  • Linux shell 命令逐块读取/打印文件

    是否有一个标准的 Linux 命令可以用来逐块读取文件 例如 我有一个大小为 6kB 的文件 我想读取 打印第一个 1kB 然后是第二个 1kB 看来猫 头 尾在这种情况下不起作用 非常感谢 你可以这样做read n在循环中 while r
  • java IO将一个文件复制到另一个文件

    我有两个 Java io File 对象 file1 和 file2 我想将 file1 的内容复制到 file2 有没有一种标准方法可以做到这一点 而无需我创建一个读取 file1 并写入 file2 的方法 不 没有内置方法可以做到这一
  • 将 PHP 变量保存到文本文件

    我想知道如何将 PHP 变量保存到 txt 文件 然后 再次检索它们 Example 有一个输入框 提交后写的东西 输入框将被保存到文本文件中 稍后需要结果 作为变量带回 假设变量是 text I 需要将其保存到文本文件并能够将其检索回来
  • 文件保存在文件系统中 VS 保存在数据库中

    我正在设计一个 servlet 或 Struts2 中的操作 用于文件 图像 文档等 下载 但我想知道哪种更好的方法可以将文件保留在文件系统和数据库中 只需保留文件的路径或将文件保留在数据库中 如 BLOB 我知道当我查询数据库时 哪里的
  • QT 中只获取文件而不获取目录?

    当我这样做时 QDir myDir home some location QStringList filesList myDir entryList 它返回该位置内的文件和目录 但我只想要文件 并且这些文件可以具有任意扩展名 有任何想法吗
  • C#,System.Timers.Timer,每 15 分钟运行一次,与系统时钟同步

    如何让 System Timers Timer 每 15 分钟触发一次与系统时钟同步的事件 换句话说 我希望它恰好在 xx 00 xx 15 xx 30 xx 45 触发 其中 xx 表示任何小时 您可以让它每秒流逝一次 并检查当前时间是否
  • PHP preg_match_all 100 MB 文件

    我读到 preg match all 不是为解析大文件而设计的 但我需要这样做 我增加了 pcre backtrack limit 1000000000 pcre recursion limit 1000000000 我的 PHP memo
  • java JFileChooser 文件大小过滤器

    我知道我可以按文件类型进行过滤 但是可以按文件大小进行过滤吗 例如 JFileChooser 仅显示 3 MB 以内的图片 简短的回答应该是 你尝试过什么 长答案是肯定的 JFileChooser fc new JFileChooser f

随机推荐

  • 毫米波雷达和视觉融合简记

    毫米波雷达和视觉传感器融合笔记 毫米波雷达和摄像头概述毫米波雷达和视觉传感器融合时间融合空间融合 写在前面 xff1a 1 按照信息抽象的五个层次 xff0c 融合可分成五个级别 xff0c 即 xff1a 检测级融合 位置级融合 属性 x
  • 多目标跟踪-DeepSort分析(一)论文解读及代码架构

    先引入多目标跟踪DeepSort的论文地址及代码链接 xff08 Python版 xff09 xff1a 论文地址 xff1a https arxiv org pdf 1703 07402 pdf 代码链接 xff1a https gith
  • stm32f4xx 加密

    文章参考http www 61ic com Technology embed 201311 50853 html xff0c 一 ID获取 34 设备电子签名 电子签名存储在 Flash 区 可以使用 JTAG SWD 或 CPU 对其进行
  • C++为什么要学习STL和Boost库

    最近一年我电话面试了数十位 C 43 43 应聘者 xff0c 惯用的暖场问题是 工作中使用过 STL 的哪些组件 xff1f 使用过 Boost 的哪些组件 xff1f 得到的答案大多集中在 vector map 和 shared ptr
  • 智能硬件新产品开发逻辑

  • 移动机器人入门介绍

    移动机器人技术应用 xff1a 天上飞的 xff0c 水里游的 xff0c 地上跑的 xff0c 都可以应用移动机器人领域的技术 比如说 xff0c 1 工业机器人 xff0c 搬运机器人 xff08 AGV xff09 xff1b 2 商
  • 占据栅格地图(occupancy grid maps) -- 二值贝叶斯滤波应用

    其实 xff0c 我想讲的关键点是二值状态的最优估计问题 xff0c 而不仅仅是栅格地图 Anyway xff0c that is a good example 机器人的地图表示方式有多种 xff0c 如拓扑地图 特征地图 直接表征法 栅格
  • 粒子滤波实现及推导

    一 应用 example xff1a 机器人全局定位 粒子滤波实现过程解读 二 推导 从贝叶斯到粒子滤波 三 工程化细节探讨 这是草稿 xff0c 待完善
  • 卫星信号的上行下行学习笔记

    gt gt gt gt gt gt 我的博客目录导航 最近被卫星电视信号的发射接收过程弄的头疼 xff0c 于是潜心找了本书 卫星电视接收DIY 细细看看下 1 地面信号上星 以前在家弄个 锅 看看电视 xff0c 电视里主持人录节目 xf
  • 信息与不确定性

    最近学习了吴军的 信息论40讲 xff0c 深有感触 xff0c 有感而发 xff01 本博文想要给你传递 xff08 或者说洗脑 xff09 两个主要观点 xff1a 信息是可以量化的 xff0c 用事件的不确定性表示 xff0c 增加相
  • AlexeyAB/darknet (YOLO)的编译 和 作为动态库进行使用 以及 训练 自定义 数据(检测网络和分类网络)

    ubuntu 编译 https github com AlexeyAB darknet 这个库很贴心了 xff0c 当然 xff0c 主要是 darknet的实现也很硬核 xff0c 自带图像编解码 xff0c 各种造轮子 文档 把 各种用
  • LoRa和NB-IoT有什么区别?LoRa的优势在哪些方面?

    对于LoRa技术 xff0c 行业内人士都不会陌生 xff0c 它也经常会被拿来和NB IoT技术比较 作为低功耗广域网 xff08 LPWAN xff09 的新兴技术 xff0c 两种技术都备受关注 对于LoRa技术 xff0c 行业内人
  • Source Insight 4.0最好看的主题

    推荐一款sourceinsight主题 xff0c 4 0适用 xff0c 配色本人觉得非常舒服 使用方法 xff1a 1 安装Sourceinsight 2 安装字体 xff1a YaHei Consolas Hybrid 1 12 tt
  • Notepad++最好看的主题

    Notepad 43 43 最好看的主题 xff0c 收藏了很久 xff0c 现在拿出来和大家分享 配色如下 xff1a 使用方法 xff1a 1 安装Notepad 43 43 2 将KamiTheme xml放到 Notepad 43
  • DTLS协议中client/server的认证过程和密钥协商过程

    1 DTLS介绍 1 1 DTLS的作用 互联网先驱们最开始在设计互联网协议时主要考虑的是可用性 xff0c 安全性是没有考虑在其中的 xff0c 所以传输层的TCP UDP协议本身都不具备安全性 SSL TLS协议是基于TCP socke
  • Ubuntu18.04 实现串口通信

    最近由于项目需要 xff0c 研究了关于在ubuntu下串口通信的功能实现 期间遇到一些问题 xff0c 跟大家分享下 1 代码 comm service h ifndef comm service h define comm servic
  • [STM32] 串口数据帧处理(第一弹)

    文章目录 1 串口使用的常用场景2 字节帧处理总结 1 串口使用的常用场景 使用串口的主要目的是实现数据的交互 xff0c 数据的交互的方法脱身于常用的场景 这里描述一个比较典型的场景 xff1a MCU作为主控制器通过串口和外部的设备或者
  • 串口编程—(1)串口基本知识

    串口是用来干什么的 xff1f 串行接口 简称串口 xff0c 也称 串行通信 接口或 串行通讯接口 xff08 通常指 COM接口 xff09 xff0c 是采用 串行通信 方式的扩展接口 串行接口 Serial Interface 是指
  • C语言操作redis数据库

    文章目录 1 开发环境2 C语言redis库 hiredi安装配置2 1 下载并且解压hiredis2 2 hiredis安装 3 hiredis简单测试4 运行出错解决办法5 验证5 1 运行程序5 2 redis客户端验证 1 开发环境
  • 5.0 NuttX File System

    转载请注明出处 xff1a 5 0 NuttX File System Alvin Peng的博客 CSDN博客 文章均出自个人理解 前言 前一段时间折腾了几个驱动 xff08 PWM Serial I2C xff09 xff0c 这次来折