PX4 FMU启动流程 2. 二、 nsh_initscript

PX4 FMU启动流程 2. 二、 nsh_initscript

PX4 FMU启动流程 2. 二、 nsh_initscript

                                                                             -------- 转载请注明出处

                                                                             -------- 2014-11-27.冷月追风

                                                                             -------- email:[email protected] 

/****************************************************************************
 * Name: nsh_initscript
 *
 * Description:
 *   Attempt to execute the configured initialization script.  This script
 *   should be executed once when NSH starts.  nsh_initscript is idempotent
 *   and may, however, be called multiple times (the script will be executed
 *   once.
 *
 ****************************************************************************/

int nsh_initscript(FAR struct nsh_vtbl_s *vtbl)
{
  static bool initialized;
  bool already;
  int ret = OK;

  /* Atomic test and set of the initialized flag */

  sched_lock();
  already     = initialized;
  initialized = true;
  sched_unlock();

  /* If we have not already executed the init script, then do so now */

  if (!already)
    {
      ret = nsh_script(vtbl, "init", NSH_INITPATH); // /etc/init.d/rcS
    }

  return ret;
}
#ifndef CONFIG_NSH_ROMFSMOUNTPT
#define CONFIG_NSH_ROMFSMOUNTPT "/etc"
#endif
#ifndef CONFIG_NSH_INITSCRIPT
#define CONFIG_NSH_INITSCRIPT "init.d/rcS"
#endif
#undef NSH_INITPATH
#define NSH_INITPATH CONFIG_NSH_ROMFSMOUNTPT "/" CONFIG_NSH_INITSCRIPT


我们很容易得出完整的路径是: “/etc/init.d/rcS”,这是一个脚本,在系统启动的时候需要调用的一个脚本,并且由 nsh进行解析。我们到不妨稍微看看这些脚本。
    但其实我们根本就不用到: “ROMFS_ROOT”目录下去查看这些脚本,去 Bulid目录下即可查看,我们可以找到一个 “romfs_scratch”目录。

set MODE autostart
set USB autoconnect
if rgbled start
then
        set HAVE_RGBLED 1
        rgbled rgb 16 16 16
else
        set HAVE_RGBLED 0
fi
echo "[init] looking for microSD..."
if mount -t vfat /dev/mmcsd0 /fs/microsd
then
 echo "[init] card mounted at /fs/microsd"
        set HAVE_MICROSD 1
 tone_alarm start
else
 echo "[init] no microSD card found"
        set HAVE_MICROSD 0
 tone_alarm 2
        if [ $HAVE_RGBLED == 1 ]
        then
                rgbled rgb 16 0 0
        fi
fi
if [ -f /fs/microsd/etc/rc ]
then
 echo "[init] reading /fs/microsd/etc/rc"
 sh /fs/microsd/etc/rc
fi
if [ -f /fs/microsd/etc/rc.txt ]
then
 echo "[init] reading /fs/microsd/etc/rc.txt"
 sh /fs/microsd/etc/rc.txt
fi
if [ $USB != autoconnect ]
then
 echo "[init] not connecting USB"
else
 if sercon
 then
  echo "[init] USB interface connected"
 else
  echo "[init] No USB connected"
 fi
fi
if [ -f /etc/init.d/rc.APM -a $HAVE_MICROSD == 1 -a ! -f /fs/microsd/APM/nostart ]
then
 echo Running rc.APM
 sh /etc/init.d/rc.APM
else
        nshterm /dev/ttyACM0 &
fi


这其中有些是 nsh内嵌的命令,有些是 “g_builtins”数组中定义的命令。
    从脚本中我们看到,先是设置 LED,接这挂载 SD卡。 SD卡挂载上来之后就执行 SD卡中的脚本。最后再通过 “/etc/init.d/rc.APM”脚本去初始化飞控。

int nsh_script(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd,
               FAR const char *path)
{
  char *fullpath;
  FILE *stream;
  char *buffer;
  char *pret;
  int ret = ERROR;

  /* The path to the script may be relative to the current working directory */

  fullpath = nsh_getfullpath(vtbl, path);// /etc/init.d/rcS
  if (!fullpath)
    {
      return ERROR;
    }

  /* Get a reference to the common input buffer */

  buffer = nsh_linebuffer(vtbl);
  if (buffer)
    {
      /* Open the file containing the script */

      stream = fopen(fullpath, "r");
      if (!stream)
        {
          nsh_output(vtbl, g_fmtcmdfailed, cmd, "fopen", NSH_ERRNO);
          nsh_freefullpath(fullpath);
          return ERROR;
        }

      /* Loop, processing each command line in the script file (or
       * until an error occurs)
       */

      do
        {
          /* Get the next line of input from the file */

          fflush(stdout);
          pret = fgets(buffer, CONFIG_NSH_LINELEN, stream);
          if (pret)
            {
              /* Parse process the command.  NOTE:  this is recursive...
               * we got to cmd_sh via a call to nsh_parse.  So some
               * considerable amount of stack may be used.
               */

              ret = nsh_parse(vtbl, buffer);
            }
        }
      while (pret && ret == OK);
      fclose(stream);
    }

  nsh_freefullpath(fullpath);
  return ret;
}


这其中调用了一个核心的函数是 “nsh_parse”,该函数用于解析 nsh脚本,而且是按行进行解析的。而前面只是获取脚本的完整路径跟 buffer,然后用文件操作函数打开脚本,再接着就是按行读入并解析。
    nsh_parse函数比较长,五六百行代码。我们也就不可能把这些代码放到这里来看。我们在这里看流程关心的主要是该函数通过哪个函数把流程继续走下去的,这个函数就是:nsh_builtin。

  /* Does this command correspond to a builtin command?
   * nsh_builtin() returns:
   *
   *   -1 (ERROR)  if the application task corresponding to 'argv[0]' could not
   *               be started (possibly because it doesn not exist).
   *    0 (OK)     if the application task corresponding to 'argv[0]' was
   *               and successfully started.  If CONFIG_SCHED_WAITPID is
   *               defined, this return value also indicates that the
   *               application returned successful status (EXIT_SUCCESS)
   *    1          If CONFIG_SCHED_WAITPID is defined, then this return value
   *               indicates that the application task was spawned successfully
   *               but returned failure exit status.
   *
   * Note the priority if not effected by nice-ness.
   */

#if defined(CONFIG_NSH_BUILTIN_APPS) && (!defined(CONFIG_NSH_FILE_APPS) || !defined(CONFIG_FS_BINFS))
  ret = nsh_builtin(vtbl, argv[0], argv, redirfile, oflags);
  if (ret >= 0)
    {
      /* nsh_builtin() returned 0 or 1.  This means that the builtin
       * command was successfully started (although it may not have ran
       * successfully).  So certainly it is not an NSH command.
       */

      /* Free the redirected output file path */

      if (redirfile)
        {
          nsh_freefullpath(redirfile);
        }

      /* Save the result:  success if 0; failure if 1 */

#ifndef CONFIG_DISABLE_ENVIRON
      if (cmdline_exp != NULL)
          free(cmdline_exp);
#endif

      return nsh_saveresult(vtbl, ret != OK);
    }

  /* No, not a built in command (or, at least, we were unable to start a
   * builtin command of that name).  Treat it like an NSH command.
   */

#endif


从注释中我们知道返回值为 0的时候表示应用成功启动。当然注释并不能够告诉我们太多的细节。
    熟悉 main函数定义形式的都知道 argv数组中 argv[0]所表示的意义。 argv数组是按下面的方式构造的:

  /* Parse out the command at the beginning of the line */

  saveptr = cmdline;
  cmd = nsh_argument(vtbl, &saveptr);

  /* Handler if-then-else-fi */

#ifndef CONFIG_NSH_DISABLESCRIPT
  if (nsh_ifthenelse(vtbl, &cmd, &saveptr) != 0)
    {
      goto errout;
    }
#endif

  /* Handle nice */

#ifndef CONFIG_NSH_DISABLEBG
  if (nsh_nice(vtbl, &cmd, &saveptr) != 0)
    {
      goto errout;
    }
#endif

  /* Check if any command was provided -OR- if command processing is
   * currently disabled.
   */

#ifndef CONFIG_NSH_DISABLESCRIPT
  if (!cmd || !nsh_cmdenabled(vtbl))
#else
  if (!cmd)
#endif
    {
      /* An empty line is not an error and an unprocessed command cannot
       * generate an error, but neither should they change the last
       * command status.
       */

#ifndef CONFIG_DISABLE_ENVIRON
      if (cmdline_exp != NULL)
          free(cmdline_exp);
#endif

      return OK;
    }

  /* Parse all of the arguments following the command name.  The form
   * of argv is:
   *
   *   argv[0]:      The command name.
   *   argv[1]:      The beginning of argument (up to CONFIG_NSH_MAXARGUMENTS)
   *   argv[argc-3]: Possibly '>' or '>>'
   *   argv[argc-2]: Possibly <file>
   *   argv[argc-1]: Possibly '&'
   *   argv[argc]:   NULL terminating pointer
   *
   * Maximum size is CONFIG_NSH_MAXARGUMENTS+5
   */

  argv[0] = cmd;
  for (argc = 1; argc < MAX_ARGV_ENTRIES-1; argc++)
    {
      argv[argc] = nsh_argument(vtbl, &saveptr);
      if (!argv[argc])
        {
          break;
        }
    }

  argv[argc] = NULL;

  /* Check if the command should run in background */

#ifndef CONFIG_NSH_DISABLEBG
  if (argc > 1 && strcmp(argv[argc-1], "&") == 0)
    {
      vtbl->np.np_bg = true;
      argv[argc-1] = NULL;
      argc--;
    }
#endif


这段代码跟注释都已经写得足够清楚了。
    举个例子,加入命令行为:“mount -t vfat /dev/mmcsd0 /fs/microsd”,那么经过解析之后在 argv数组将是:
   *   argv[0]: mount
   *   argv[1]: -t
   *   argv[2]: vfat
   *   argv[3]: /dev/mmcsd0
   *   argv[4]: /fs/microsd
   *   argv[5]: NULL terminating pointer

"&"表示把进程放到后台运行。其实熟悉 Linux的人一看到这个命令行就知道它会被解析成什么样。因为我们在 Linux应用编程中解析命令行参数的时候必须要了解这个,当然 Linux并没有要求所有的应用程序都提供解析命令行参数的功能。关于命令行参数解析的作用和意义这里就没必要说了,因为我们现在看的是流程,而不是教你怎么去解析命令行参数。

/****************************************************************************
 * Name: nsh_builtin
 *
 * Description:
 *    Attempt to execute the application task whose name is 'cmd'
 *
 * Returned Value:
 *   <0          If exec_builtin() fails, then the negated errno value
 *               is returned.
 *   -1 (ERROR)  if the application task corresponding to 'cmd' could not
 *               be started (possibly because it doesn not exist).
 *    0 (OK)     if the application task corresponding to 'cmd' was
 *               and successfully started.  If CONFIG_SCHED_WAITPID is
 *               defined, this return value also indicates that the
 *               application returned successful status (EXIT_SUCCESS)
 *    1          If CONFIG_SCHED_WAITPID is defined, then this return value
 *               indicates that the application task was spawned successfully
 *               but returned failure exit status.
 *
 ****************************************************************************/

int nsh_builtin(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd,
                FAR char **argv, FAR const char *redirfile, int oflags)
{
  int ret = OK;

  /* Lock the scheduler in an attempt to prevent the application from
   * running until waitpid() has been called.
   */

  sched_lock();

  /* Try to find and execute the command within the list of builtin
   * applications.
   */

  ret = exec_builtin(cmd, (FAR char * const *)argv, redirfile, oflags);
  if (ret >= 0)
    {

    }

  sched_unlock();

  /* If exec_builtin() or waitpid() failed, then return -1 (ERROR) with the
   * errno value set appropriately.
   */

  if (ret < 0)
    {
      return ERROR;
    }

  return ret;
}


容易看出来我把出错处理部分给去掉了。对于该函数的注释,其实要不要都无所谓,对英文不太懂的人像我就只是看一眼而已,更多的还是看代码。在 Linux应用编程中我们如果要调用一个 shell命令就会用到一个 “exec”函数。这这里作用是一样的。

int exec_builtin(FAR const char *appname, FAR char * const *argv,
                 FAR const char *redirfile, int oflags)
{
  FAR const struct builtin_s *builtin;
  posix_spawnattr_t attr;
  posix_spawn_file_actions_t file_actions;
  struct sched_param param;
  pid_t pid;
  int index;
  int ret;

  /* Verify that an application with this name exists */

  index = builtin_isavail(appname);
  if (index < 0)
    {
      ret = ENOENT;
      goto errout_with_errno;
    }

  /* Get information about the builtin */

  builtin = builtin_for_index(index);
  if (builtin == NULL)
    { 
      ret = ENOENT;
      goto errout_with_errno;
    }

  /* Initialize attributes for task_spawn(). */

  ret = posix_spawnattr_init(&attr);
  if (ret != 0)
    {
      goto errout_with_errno;
    }

  ret = posix_spawn_file_actions_init(&file_actions);
  if (ret != 0)
    {
      goto errout_with_attrs;
    }

  /* Set the correct task size and priority */

  param.sched_priority = builtin->priority;
  ret = posix_spawnattr_setschedparam(&attr, &param);
  if (ret != 0)
    {
      goto errout_with_actions;
    }

  ret = task_spawnattr_setstacksize(&attr, builtin->stacksize);
  if (ret != 0)
    {
      goto errout_with_actions;
    }

   /* If robin robin scheduling is enabled, then set the scheduling policy
    * of the new task to SCHED_RR before it has a chance to run.
    */

#if CONFIG_RR_INTERVAL > 0
  ret = posix_spawnattr_setschedpolicy(&attr, SCHED_RR);
  if (ret != 0)
    {
      goto errout_with_actions;
    }

  ret = posix_spawnattr_setflags(&attr,
                                 POSIX_SPAWN_SETSCHEDPARAM |
                                 POSIX_SPAWN_SETSCHEDULER);
  if (ret != 0)
    {
      goto errout_with_actions;
    }
#else
  ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSCHEDPARAM);
  if (ret != 0)
    {
      goto errout_with_actions;
    }
#endif

  /* Is output being redirected? */

  if (redirfile)
    {
      /* Set up to close open redirfile and set to stdout (1) */

      ret = posix_spawn_file_actions_addopen(&file_actions, 1,
                                             redirfile, O_WRONLY, 0644);
      if (ret != 0)
        {
          sdbg("ERROR: posix_spawn_file_actions_addopen failed: %d\n", ret);
          goto errout_with_actions;
        }
    }

  /* Start the built-in */

  ret = task_spawn(&pid, builtin->name, builtin->main, &file_actions,
                   &attr, (argv) ? &argv[1] : (FAR char * const *)NULL,
                   (FAR char * const *)NULL);
  if (ret != 0)
    {
      sdbg("ERROR: task_spawn failed: %d\n", ret);
      goto errout_with_actions;
    }

  /* Free attibutes and file actions.  Ignoring return values in the case
   * of an error.
   */

  /* Return the task ID of the new task if the task was sucessfully
   * started.  Otherwise, ret will be ERROR (and the errno value will
   * be set appropriately).
   */

  (void)posix_spawn_file_actions_destroy(&file_actions);
  (void)posix_spawnattr_destroy(&attr);
  return pid;

errout_with_actions:
  (void)posix_spawn_file_actions_destroy(&file_actions);

errout_with_attrs:
  (void)posix_spawnattr_destroy(&attr);

errout_with_errno:
  set_errno(ret);
  return ERROR;
}


记住这里有个变量: “struct builtin_s *builtin”,它是到数组 “g_builtins”中去取值。取到值之后就设置进程的属性,最后通过 “task_spawn”函数启动一个新的进程。
    这样我们就看到了 Nuttx是如何启动我们的进程的。
    突然发觉我们好像还漏掉了什么东西。因为我们知道 nsh命令不仅有我们写的命令,还有 Nuttx内嵌的命令,确切地所应该是 nsh内嵌的命令吧。那么这些命令有是如何被调用的呢?我们又到底遗漏了什么呢?我们又是否有一些比较好的方法去找到这些信息呢?
    有一个稍微简单却繁杂的方法是:找到脚本中有系统提供的命令如 mount,然后我们可以匹配到这样一些信息:

../PX4NuttX/apps/nshlib/nsh_mntcmds.c:302:int cmd_mount(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
../PX4NuttX/apps/nshlib/nsh_mntcmds.c:313:  /* The mount command behaves differently if no parameters are provided */
../PX4NuttX/apps/nshlib/nsh_mntcmds.c:318:      return mount_show(vtbl, argv[0]);
../PX4NuttX/apps/nshlib/nsh_mntcmds.c:322:  /* Get the mount options.  NOTE: getopt() is not thread safe nor re-entrant.
../PX4NuttX/apps/nshlib/nsh_mntcmds.c:419:  /* Perform the mount */
../PX4NuttX/apps/nshlib/nsh_mntcmds.c:421:  ret = mount(fullsource, fulltarget, filesystem, 0, NULL);
../PX4NuttX/apps/nshlib/nsh_mntcmds.c:424:      nsh_output(vtbl, g_fmtcmdfailed, argv[0], "mount", NSH_ERRNO);
../PX4NuttX/apps/nshlib/nsh_mntcmds.c:443: * Name: cmd_nfsmount


看到 “cmd_mount”函数就有一种看到希望的感觉。因为该函数的命名很特殊,以 “cmd_”开头。我们写程序在给符号命名的时候通常会遵循一个默认的规则(记住这个不是规定更不是标准),就是望文生义。这说明该函数提供了 Nuttx系统的 “mount”命令。就这样,我找到了一个数组:

static const struct cmdmap_s g_cmdmap[] =
{
#if CONFIG_NFILE_DESCRIPTORS > 0
# ifndef CONFIG_NSH_DISABLE_CAT
  { "cat",      cmd_cat,      2, CONFIG_NSH_MAXARGUMENTS, "<path> [<path> [<path> ...]]" },
# endif
#ifndef CONFIG_DISABLE_ENVIRON
# ifndef CONFIG_NSH_DISABLE_CD
  { "cd",       cmd_cd,       1, 2, "[<dir-path>|-|~|..]" },
# endif
#endif
# ifndef CONFIG_NSH_DISABLE_CP
  { "cp",       cmd_cp,       3, 3, "<source-path> <dest-path>" },
# endif

#ifdef CONFIG_NET
# ifndef CONFIG_NSH_DISABLE_IFCONFIG
  { "ifconfig", cmd_ifconfig, 1, 11, "[nic_name [<ip-address>|dhcp]] [dr|gw|gateway <dr-address>] [netmask <net-mask>] [dns <dns-address>] [hw <hw-mac>]" },
# endif
# ifndef CONFIG_NSH_DISABLE_IFUPDOWN
  { "ifdown",   cmd_ifdown,   2, 2,  "<nic_name>" },
  { "ifup",     cmd_ifup,     2, 2,  "<nic_name>" },
# endif
#endif

#if CONFIG_NFILE_DESCRIPTORS > 0
# ifndef CONFIG_NSH_DISABLE_LS
  { "ls",       cmd_ls,       1, 5, "[-lRs] <dir-path>" },
# endif
#endif

#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_FS_WRITABLE)
# ifndef CONFIG_NSH_DISABLE_MKDIR
  { "mkdir",    cmd_mkdir,    2, 2, "<path>" },
# endif
#endif

#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_FS_READABLE)
# ifndef CONFIG_NSH_DISABLE_MOUNT
#  ifdef CONFIG_NUTTX_KERNEL
  { "mount",    cmd_mount,    5, 5, "-t <fstype> [<block-device>] <mount-point>" },
#    else
  { "mount",    cmd_mount,    1, 5, "[-t <fstype> [<block-device>] <mount-point>]" },
#  endif
# endif
#endif

#if defined(CONFIG_NET) && defined(CONFIG_NET_ICMP) && defined(CONFIG_NET_ICMP_PING) && \
   !defined(CONFIG_DISABLE_CLOCK) && !defined(CONFIG_DISABLE_SIGNALS)
# ifndef CONFIG_NSH_DISABLE_PING
  { "ping",     cmd_ping,     2, 6, "[-c <count>] [-i <interval>] <ip-address>" },
# endif
#endif

#if CONFIG_NFILE_DESCRIPTORS > 0 && !defined(CONFIG_DISABLE_ENVIRON)
# ifndef CONFIG_NSH_DISABLE_PWD
  { "pwd",      cmd_pwd,      1, 1, NULL },
# endif
#endif

  { NULL,       NULL,         1, 1, NULL }
};


这个数组明显是被我裁过的,我只留下了 Linux中比较具有代表性的几个命令。其类型如下:

struct cmdmap_s
{
  const char *cmd;        /* Name of the command */
  cmd_t       handler;    /* Function that handles the command */
  uint8_t     minargs;    /* Minimum number of arguments (including command) */
  uint8_t     maxargs;    /* Maximum number of arguments (including command) */
  const char *usage;      /* Usage instructions for 'help' command */
};


跟 “g_builtins”数组一样,命令匹配由一个专门的字符串完成。既然这个数组都找到了,那么现在的问题是这个数组在哪里使用了。

bitcraze@bitcraze-vm:~/apm/PX4Firmware$ grep -nr g_cmdmap ../PX4NuttX/apps/
../PX4NuttX/apps/examples/ftpc/ftpc_main.c:81:static const struct cmdmap_s g_cmdmap[] =
../PX4NuttX/apps/examples/ftpc/ftpc_main.c:120:  for (ptr = g_cmdmap; ptr->cmd; ptr++)
../PX4NuttX/apps/examples/ftpc/ftpc_main.c:249:   for (cmdmap = g_cmdmap; cmdmap->cmd; cmdmap++)
../PX4NuttX/apps/nshlib/nsh_parse.c:97:#define NUM_CMDS      ((sizeof(g_cmdmap)/sizeof(struct cmdmap_s)) - 1)
../PX4NuttX/apps/nshlib/nsh_parse.c:147:static const struct cmdmap_s g_cmdmap[] =
../PX4NuttX/apps/nshlib/nsh_parse.c:518:          nsh_output(vtbl, "%-12s", g_cmdmap[k].cmd);
../PX4NuttX/apps/nshlib/nsh_parse.c:581:  for (cmdmap = g_cmdmap; cmdmap->cmd; cmdmap++)
../PX4NuttX/apps/nshlib/nsh_parse.c:611:  for (cmdmap = g_cmdmap; cmdmap->cmd; cmdmap++)
../PX4NuttX/apps/nshlib/nsh_parse.c:777:  for (cmdmap = g_cmdmap; cmdmap->cmd; cmdmap++)
bitcraze@bitcraze-vm:~/apm/PX4Firmware$


这个时候我才发现自己刚才匹配 “mount”是多么幸运。因为加入我匹配的是 “ls”命令,我可能就会找到 “ftpc_main.c”里边去,毕竟在那么多的匹配结果里边人是很容易犯错的。
    经过帅选,最后确定了函数 “nsh_execute”。

static int nsh_execute(FAR struct nsh_vtbl_s *vtbl, int argc, char *argv[])
{
  const struct cmdmap_s *cmdmap;
  const char            *cmd;
  cmd_t                  handler = cmd_unrecognized;
  int                    ret;

  /* The form of argv is:
   *
   * argv[0]:      The command name.  This is argv[0] when the arguments
   *               are, finally, received by the command vtblr
   * argv[1]:      The beginning of argument (up to CONFIG_NSH_MAXARGUMENTS)
   * argv[argc]:   NULL terminating pointer
   */

  cmd = argv[0];

  /* See if the command is one that we understand */

  for (cmdmap = g_cmdmap; cmdmap->cmd; cmdmap++)
    {
      if (strcmp(cmdmap->cmd, cmd) == 0)
        {
          /* Check if a valid number of arguments was provided.  We
           * do this simple, imperfect checking here so that it does
           * not have to be performed in each command.
           */

          if (argc < cmdmap->minargs)
            {
              /* Fewer than the minimum number were provided */

              nsh_output(vtbl, g_fmtargrequired, cmd);
              return ERROR;
            }
          else if (argc > cmdmap->maxargs)
            {
              /* More than the maximum number were provided */

              nsh_output(vtbl, g_fmttoomanyargs, cmd);
              return ERROR;
            }
          else
            {
              /* A valid number of arguments were provided (this does
               * not mean they are right).
               */

              handler = cmdmap->handler;
              break;
            }
        }
    }

   ret = handler(vtbl, argc, argv);
   return ret;
}


所以我们看到,最后是通过 “cmdmap->handler”找到了命令对应的函数,并通过指针 “handler”进行调用。而函数 “nsh_execute”其实有两种方式进行调用:线程和函数调用。当然这都发生在 nsh解析函数 “nsh_parse”里边。

  /* Handle the case where the command is executed in background.
   * However is app is to be started as builtin new process will
   * be created anyway, so skip this step.
   */

#ifndef CONFIG_NSH_DISABLEBG
  if (vtbl->np.np_bg)
    {
      struct sched_param param;
      struct nsh_vtbl_s *bkgvtbl;
      struct cmdarg_s *args;
      pthread_attr_t attr;
      pthread_t thread;

      /* Get a cloned copy of the vtbl with reference count=1.
       * after the command has been processed, the nsh_release() call
       * at the end of nsh_child() will destroy the clone.
       */

      /* Set up the thread attributes */

      (void)pthread_attr_init(&attr);
      (void)pthread_attr_setschedpolicy(&attr, SCHED_NSH);
      (void)pthread_attr_setschedparam(&attr, &param);

      /* Execute the command as a separate thread at the appropriate priority */

      ret = pthread_create(&thread, &attr, nsh_child, (pthread_addr_t)args);
      if (ret != 0)
        {
          nsh_output(vtbl, g_fmtcmdfailed, cmd, "pthread_create", NSH_ERRNO_OF(ret));
          nsh_releaseargs(args);
          nsh_release(bkgvtbl);
          goto errout;
        }

      /* Detach from the pthread since we are not going to join with it.
       * Otherwise, we would have a memory leak.
       */

      (void)pthread_detach(thread);

      nsh_output(vtbl, "%s [%d:%d]\n", cmd, thread, param.sched_priority);
    }
  else
#endif
    {
      uint8_t save[SAVE_SIZE];

      ret = nsh_execute(vtbl, argc, argv);
    }
#ifndef CONFIG_NSH_DISABLEBG
static pthread_addr_t nsh_child(pthread_addr_t arg)
{
  struct cmdarg_s *carg = (struct cmdarg_s *)arg;
  int ret;

  dbg("BG %s\n", carg->argv[0]);

  /* Execute the specified command on the child thread */

  ret = nsh_execute(carg->vtbl, carg->argc, carg->argv);

  /* Released the cloned arguments */

  dbg("BG %s complete\n", carg->argv[0]);
  nsh_releaseargs(carg);
  return (void*)ret;
}
#endif


代码比较多,我自然又忍不住删去了一些,只留下了基本的骨架。
    所以,如果我们要把系统命令放在后台运行就定义宏 “CONFIG_NSH_DISABLEBG”,否则就是直接调用。而这些过程,都是发生在调用 “nsh_builtin”函数之后。
    回过头来一看才发现函数 “nsh_parse”都已经看了个七七八八了。

猜你喜欢

转载自www.cnblogs.com/eastgeneral/p/10879658.html