uboot, s5pv210 , main_loop 分析(16)

main_loop 的代码如下:

    44
    43 void main_loop (void)
    42 {
    41 #ifndef CONFIG_SYS_HUSH_PARSER
E   40 ▎   static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };     ■ Use of undeclared identifier 'CONFIG_SYS_CBSIZE'
    39 ▎   int len;
    38 ▎   int rc = 1;
    37 ▎   int flag;
    36 #endif
    35 ▎
    34 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    33 ▎   char *s;
    32 ▎   int bootdelay;
    31 #endif
    30 #ifdef CONFIG_PREBOOT
    29 ▎   char *p;
    28 #endif
    27 #ifdef CONFIG_BOOTCOUNT_LIMIT
    26 ▎   unsigned long bootcount = 0;
    25 ▎   unsigned long bootlimit = 0;
    24 ▎   char *bcs;
    23 ▎   char bcs_set[16];
    22 #endif /* CONFIG_BOOTCOUNT_LIMIT */
    21 ▎
    20 #ifdef CONFIG_BOOTCOUNT_LIMIT
    19 ▎   bootcount = bootcount_load();
    18 ▎   bootcount++;
    17 ▎   bootcount_store (bootcount);
    16 ▎   sprintf (bcs_set, "%lu", bootcount);
    15 ▎   setenv ("bootcount", bcs_set);
    14 ▎   bcs = getenv ("bootlimit");
    13 ▎   bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0;
    12 #endif /* CONFIG_BOOTCOUNT_LIMIT */
    11 ▎
    10 #ifdef CONFIG_MODEM_SUPPORT
     9 ▎   debug ("DEBUG: main_loop:   do_mdm_init=%d\n", do_mdm_init);
     8 ▎   if (do_mdm_init) {
     7 ▎   ▎   char *str = strdup(getenv("mdm_cmd"));
     6 ▎   ▎   setenv ("preboot", str);  /* set or delete definition */
     5 ▎   ▎   if (str != NULL)
     4 ▎   ▎   ▎   free (str);
     3 ▎   ▎   mdm_init(); /* wait for modem connection */
     2 ▎   }
     1 #endif  /* CONFIG_MODEM_SUPPORT */
  322  ▎
     1 #ifdef CONFIG_VERSION_VARIABLE
     2 ▎   {
     3 ▎   ▎   setenv ("ver", version_string);  /* set version variable */
     4 ▎   }
     5 #endif /* CONFIG_VERSION_VARIABLE */
     6 ▎
     7 #ifdef CONFIG_SYS_HUSH_PARSER
     8 ▎   u_boot_hush_start ();
     9 #endif
 #if defined(CONFIG_HUSH_INIT_VAR)
    42 ▎   hush_init_var ();
    41 #endif
    40 ▎
    39 #ifdef CONFIG_PREBOOT
    38 ▎   if ((p = getenv ("preboot")) != NULL) {
    37 # ifdef CONFIG_AUTOBOOT_KEYED
    36 ▎   ▎   int prev = disable_ctrlc(1);    /* disable Control C checking */
    35 # endif
    34 ▎   ▎
    33 ▎   ▎   run_command_list(p, -1, 0);
    32 ▎   ▎
    31 # ifdef CONFIG_AUTOBOOT_KEYED
    30 ▎   ▎   disable_ctrlc(prev);    /* restore Control C checking */
    29 # endif
    28 ▎   }
    27 #endif /* CONFIG_PREBOOT */
    26 ▎
    25 #if defined(CONFIG_UPDATE_TFTP)
    24 ▎   update_tftp (0UL);
    23 #endif /* CONFIG_UPDATE_TFTP */
    22 ▎
    21 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    20 ▎   s = getenv ("bootdelay");
    19 ▎   bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
    18 ▎
    17 ▎   debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);
    16 ▎
    15 #if defined(CONFIG_MENU_SHOW)
    14 ▎   bootdelay = menu_show(bootdelay);
    13 #endif
    12 # ifdef CONFIG_BOOT_RETRY_TIME
    11 ▎   init_cmd_timeout ();
    10 # endif /* CONFIG_BOOT_RETRY_TIME */
     9 ▎
     8 #ifdef CONFIG_POST
     7 ▎   if (gd->flags & GD_FLG_POSTFAIL) {
     6 ▎   ▎   s = getenv("failbootcmd");
     5 ▎   }
     4 ▎   else
     3 #endif /* CONFIG_POST */
     2 #ifdef CONFIG_BOOTCOUNT_LIMIT
     1 ▎   if (bootlimit && (bootcount > bootlimit)) {
  376  ▎   ▎   printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
     1 ▎   ▎   ▎       (unsigned)bootlimit);
     2 ▎   ▎   s = getenv ("altbootcmd");
     3 ▎   }
     4 ▎   else
     5 #endif /* CONFIG_BOOTCOUNT_LIMIT */
     6 ▎   ▎   s = getenv ("bootcmd");
     7 ▎
     8 ▎   debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
     9 ▎
  if (bootdelay != -1 && s && !abortboot(bootdelay)) {
    42 # ifdef CONFIG_AUTOBOOT_KEYED
    41 ▎   ▎   int prev = disable_ctrlc(1);    /* disable Control C checking */
    40 # endif
    39 ▎   ▎
    38 ▎   ▎   run_command_list(s, -1, 0);
    37 ▎   ▎
    36 # ifdef CONFIG_AUTOBOOT_KEYED
    35 ▎   ▎   disable_ctrlc(prev);    /* restore Control C checking */
    34 # endif
    33 ▎   }
    32 ▎
    31 # ifdef CONFIG_MENUKEY
    30 ▎   if (menukey == CONFIG_MENUKEY) {
    29 ▎   ▎   s = getenv("menucmd");
    28 ▎   ▎   if (s)
    27 ▎   ▎   ▎   run_command_list(s, -1, 0);
    26 ▎   }
    25 #endif /* CONFIG_MENUKEY */
    24 #endif /* CONFIG_BOOTDELAY */
    23 ▎
    22 ▎   /*
    21 ▎   ▎* Main Loop for Monitor Command Processing
    20 ▎   ▎*/
    19 #ifdef CONFIG_SYS_HUSH_PARSER
    18 ▎   parse_file_outer();
    17 ▎   /* This point is never reached */
    16 ▎   for (;;);
    15 #else
    14 ▎   for (;;) {
    13 #ifdef CONFIG_BOOT_RETRY_TIME
    12 ▎   ▎   if (rc >= 0) {
    11 ▎   ▎   ▎   /* Saw enough of a valid command to
    10 ▎   ▎   ▎   ▎* restart the timeout.
     9 ▎   ▎   ▎   ▎*/
     8 ▎   ▎   ▎   reset_cmd_timeout();
     7 ▎   ▎   }
     6 #endif
E    5 ▎   ▎   len = readline (CONFIG_SYS_PROMPT);     ■■ Use of undeclared identifier 'CONFIG_SYS_PROMPT'
     4 ▎   ▎
     3 ▎   ▎   flag = 0;   /* assume no special flags for now */
     2 ▎   ▎   if (len > 0)
E    1 ▎   ▎   ▎   strcpy (lastcommand, console_buffer);     ■ Call to undeclared library function 'strcpy' with type 'char *(char *, const char *)'; ISO C99 and later do not s
  429  ▎   ▎   else if (len == 0)
E    1 ▎   ▎   ▎   flag |= CMD_FLAG_REPEAT;     ■ Use of undeclared identifier 'CMD_FLAG_REPEAT'
     2 #ifdef CONFIG_BOOT_RETRY_TIME
     3 ▎   ▎   else if (len == -2) {
     4 ▎   ▎   ▎   /* -2 means timed out, retry autoboot
     5 ▎   ▎   ▎   ▎*/
     6 ▎   ▎   ▎   puts ("\nTimed out waiting for command\n");
     7 # ifdef CONFIG_RESET_TO_RETRY
     8 ▎   ▎   ▎   /* Reinit board to run initialization code again */
     9 ▎   ▎   ▎   do_reset (NULL, 0, 0, NULL);
    43 # else
    42 ▎   ▎   ▎   return;     /* retry autoboot */
    41 # endif
    40 ▎   ▎   }
    39 #endif
    38 ▎   ▎
    37 ▎   ▎   if (len == -1)
    36 ▎   ▎   ▎   puts ("<INTERRUPT>\n");
    35 ▎   ▎   else
E   34 ▎   ▎   ▎   rc = run_command(lastcommand, flag);     ■ Call to undeclared function 'run_command'; ISO C99 and later do not support implicit function declarations
    33 ▎   ▎
    32 ▎   ▎   if (rc <= 0) {
    31 ▎   ▎   ▎   /* invalid command or not repeatable, forget it */
    30 ▎   ▎   ▎   lastcommand[0] = 0;
    29 ▎   ▎   }
    28 ▎   }
    27 #endif /*CONFIG_SYS_HUSH_PARSER*/
    26 }
    25

这段代码 表示 所有的命令使用哈希表。

  281  #ifndef CONFIG_SYS_HUSH_PARSER
E    1 ▎   static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };     ■ Use of undeclared identifier 'CONFIG_SYS_CBSIZE'
     2 ▎   int len;
     3 ▎   int rc = 1;
     4 ▎   int flag;
     5 #endif

但是我们没有 使能这个宏定义,我们使用的是 顺序查找的方式。

所以这段不用看了。

据这个老师讲 哈希表的方式比顺序查找的方式 更有效率。

这段代码 ,我们定义了, s 指向的是一个 字符串, 而 bootdelay 是一个整数。

    36 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    35 ▎   char *s;
    34 ▎   int bootdelay;
    33 #endif

直接跳转到这段代码出执行

    13 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    12 ▎   s = getenv ("bootdelay");
    11 ▎   bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
    10 ▎
     9 ▎   debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);

getenv ("bootdelay");  这句没有 找到 getenv 的定义

bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;  这句中,是在将 字符串 转换为数字。它的函数是 首先看一下 能不能从环境变量中 拿到 bootdelya , 如果能拿到的话,就开始转换成数字, 如果拿不到的话,就使用宏定义的值。

看一下 函数  simple_strtol 的代码

  94  long simple_strtol(const char *cp,char **endp,unsigned int base)
    1 {
    2 ▎   if(*cp=='-')
    3 ▎   ▎   return -simple_strtoul(cp+1,endp,base);
    4 ▎   return simple_strtoul(cp,endp,base);
    5 }

我们肯定是执行 if 之外的代码。

在看一下 函数 simple_strtout函数

   19 unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
   18 {
   17 ▎   unsigned long result = 0,value;
   16 ▎
   15 ▎   if (*cp == '0') {
   14 ▎   ▎   cp++;
E  13 ▎   ▎   if ((*cp == 'x') && isxdigit(cp[1])) {     ■ Call to undeclared library function 'isxdigit' with type 'int (int)';
   12 ▎   ▎   ▎   base = 16;
   11 ▎   ▎   ▎   cp++;
   10 ▎   ▎   }
    9 ▎   ▎   if (!base) {
    8 ▎   ▎   ▎   base = 8;
    7 ▎   ▎   }
    6 ▎   }
    5 ▎   if (!base) {
    4 ▎   ▎   base = 10;
    3 ▎   }
E   2 ▎   while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)     ■■ Call to undeclared library function 'isl
E   1 ▎   ▎   ? toupper(*cp) : *cp)-'A'+10) < base) {     ■ Call to undeclared library function 'toupper' with type 'int (int)';   62  ▎   ▎   result = result*base + value;
    1 ▎   ▎   cp++;                                                                                                                  2 ▎   }
    3 ▎   if (endp)                                                                                                                  4 ▎   ▎   *endp = (char *)cp;
    5 ▎   return result;                                                                                                             6 }

首先将 base 设置成了 0

然后就是这段代码

E   2 ▎   while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)     ■■ Call to undeclared library function 'isl
E   1 ▎   ▎   ? toupper(*cp) : *cp)-'A'+10) < base) {     ■ Call to undeclared library function 'toupper' with type 'int (int)';   62  ▎   ▎   result = result*base + value;
    1 ▎   ▎   cp++;                                                                                                                  2 ▎   }

我整理了一下。

isxdigit  判断是不是16进制数, isdigit 判断是不是 10 进制数, 具体实现我不管。

后面难看懂的地方我 整理了一下。

while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp) ? toupper(*cp) : *cp)-'A'+10) < base)

这一通下来 ,实际上我是得到了一个10 以内的 整数。

然后关键的是这一句。 result = result*base + value;

这是 从最大的数 往 最小的数 在遍历。

每往后挪一位 ,前面的数字要 乘以 10

然后最后 返回一个整数 。

这部分分析完了,

接下来 继续往下分析代码。

代码如下:

s = getenv ("bootcmd");
    35 ▎
    34 ▎   debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
    33 ▎
    32 ▎   if (bootdelay != -1 && s && !abortboot(bootdelay)) {
    31 # ifdef CONFIG_AUTOBOOT_KEYED
    30 ▎   ▎   int prev = disable_ctrlc(1);    /* disable Control C checking */
    29 # endif
    28 ▎   ▎
    27 ▎   ▎   run_command_list(s, -1, 0);
    26 ▎   ▎
    25 # ifdef CONFIG_AUTOBOOT_KEYED
    24 ▎   ▎   disable_ctrlc(prev);    /* restore Control C checking */
    23 # endif
    22 ▎   }
    21 ▎
    20 # ifdef CONFIG_MENUKEY
    19 ▎   if (menukey == CONFIG_MENUKEY) {
    18 ▎   ▎   s = getenv("menucmd");
    17 ▎   ▎   if (s)
    16 ▎   ▎   ▎   run_command_list(s, -1, 0);
    15 ▎   }
    14 #endif /* CONFIG_MENUKEY */
    13 #endif /* CONFIG_BOOTDELAY */
    12 ▎
    11 ▎   /*
    10 ▎   ▎* Main Loop for Monitor Command Processing
     9 ▎   ▎*/
     8 #ifdef CONFIG_SYS_HUSH_PARSER
     7 ▎   parse_file_outer();
     6 ▎   /* This point is never reached */
     5 ▎   for (;;);
     4 #else
     3 ▎   for (;;) {
     2 #ifdef CONFIG_BOOT_RETRY_TIME
     1 ▎   ▎   if (rc >= 0) {
  418  ▎   ▎   ▎   /* Saw enough of a valid command to
     1 ▎   ▎   ▎   ▎* restart the timeout.
     2 ▎   ▎   ▎   ▎*/
     3 ▎   ▎   ▎   reset_cmd_timeout();
     4 ▎   ▎   }
     5 #endif
 len = readline (CONFIG_SYS_PROMPT);     ■■ Use of undeclared identifier 'CONFIG_SYS_PROMPT'
    41 ▎   ▎
    40 ▎   ▎   flag = 0;   /* assume no special flags for now */
    39 ▎   ▎   if (len > 0)
E   38 ▎   ▎   ▎   strcpy (lastcommand, console_buffer);     ■ Call to undeclared library function 'strcpy' with type 'char *(char *, const char *)'; ISO C99 and later do not s
    37 ▎   ▎   else if (len == 0)
E   36 ▎   ▎   ▎   flag |= CMD_FLAG_REPEAT;     ■ Use of undeclared identifier 'CMD_FLAG_REPEAT'
    35 #ifdef CONFIG_BOOT_RETRY_TIME
    34 ▎   ▎   else if (len == -2) {
    33 ▎   ▎   ▎   /* -2 means timed out, retry autoboot
    32 ▎   ▎   ▎   ▎*/
    31 ▎   ▎   ▎   puts ("\nTimed out waiting for command\n");
    30 # ifdef CONFIG_RESET_TO_RETRY
    29 ▎   ▎   ▎   /* Reinit board to run initialization code again */
    28 ▎   ▎   ▎   do_reset (NULL, 0, 0, NULL);
    27 # else
    26 ▎   ▎   ▎   return;     /* retry autoboot */
    25 # endif
    24 ▎   ▎   }
    23 #endif
    22 ▎   ▎
    21 ▎   ▎   if (len == -1)
    20 ▎   ▎   ▎   puts ("<INTERRUPT>\n");
    19 ▎   ▎   else
E   18 ▎   ▎   ▎   rc = run_command(lastcommand, flag);     ■ Call to undeclared function 'run_command'; ISO C99 and later do not support implicit function declarations
    17 ▎   ▎
    16 ▎   ▎   if (rc <= 0) {
    15 ▎   ▎   ▎   /* invalid command or not repeatable, forget it */
    14 ▎   ▎   ▎   lastcommand[0] = 0;
    13 ▎   ▎   }
    12 ▎   }
    11 #endif /*CONFIG_SYS_HUSH_PARSER*/
    10 }

来看一下 这段代码:

    30 ▎   ▎   s = getenv ("bootcmd");
    29 ▎
    28 ▎   debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
    27 ▎
    26 ▎   if (bootdelay != -1 && s && !abortboot(bootdelay)) {
    25 # ifdef CONFIG_AUTOBOOT_KEYED
    24 ▎   ▎   int prev = disable_ctrlc(1);    /* disable Control C checking */
    23 # endif
    22 ▎   ▎
    21 ▎   ▎   run_command_list(s, -1, 0);
    20 ▎   ▎
    19 # ifdef CONFIG_AUTOBOOT_KEYED
    18 ▎   ▎   disable_ctrlc(prev);    /* restore Control C checking */
    17 # endif
    16 ▎   }

这段的含义是, 如果说 在规定的时间内,没有按下键盘,那么就执行了  if 里面的内容。

接下来一句一句的分析。

s = getenv ("bootcmd");  这句得到了 bootcmd 的值,并且保存到了 s 中。

我们来看一下 bootcmd 是什么。

文件位置:common/env_common.c

那么 BOOTCOMMAND 又是什么呢?

文件位置: include/configs/s5p_goni.h

可以看到是 run ubifastboot .

接下来分析这段代码 :if (bootdelay != -1 && s && !abortboot(bootdelay))

S是有值的,  bootdelay != -1 这句我不用管。

!abortboot(bootdelay) 这句的意思是 ,如果倒计时到 0 , 那么就返回0 , 那么 

if 条件成立, 执行, if 的内容。

接下里看一下 abortboot() 函数。

代码如下:


    42 #ifndef CONFIG_MENU
    41 static inline
    40 #endif
    39 int abortboot(int bootdelay)
    38 {
    37 ▎   int abort = 0;
    36 ▎
    35 #ifdef CONFIG_MENUPROMPT
    34 ▎   printf(CONFIG_MENUPROMPT);
    33 #else
    32 ▎   if (bootdelay >= 0)
    31 ▎   ▎   printf("Hit any key to stop autoboot: %2d ", bootdelay);
    30 #endif
    29 ▎
    28 #if defined CONFIG_ZERO_BOOTDELAY_CHECK
    27 ▎   /*
    26 ▎   ▎* Check if key already pressed
    25 ▎   ▎* Don't check if bootdelay < 0
    24 ▎   ▎*/
    23 ▎   if (bootdelay >= 0) {
    22 ▎   ▎   if (tstc()) {   /* we got a key press   */
    21 ▎   ▎   ▎   (void) getc();  /* consume input    */
    20 ▎   ▎   ▎   puts ("\b\b\b 0");
    19 ▎   ▎   ▎   abort = 1;  /* don't auto boot  */
    18 ▎   ▎   }
    17 ▎   }
    16 #endif
    15 ▎
    14 ▎   while ((bootdelay > 0) && (!abort)) {
    13 ▎   ▎   int i;
    12 ▎   ▎
    11 ▎   ▎   --bootdelay;
    10 ▎   ▎   /* delay 100 * 10ms */
     9 ▎   ▎   for (i=0; !abort && i<100; ++i) {
     8 ▎   ▎   ▎   if (tstc()) {   /* we got a key press   */
     7 ▎   ▎   ▎   ▎   abort  = 1; /* don't auto boot  */
     6 ▎   ▎   ▎   ▎   bootdelay = 0;  /* no more delay    */
     5 # ifdef CONFIG_MENUKEY
     4 ▎   ▎   ▎   ▎   menukey = getc();
     3 # else
     2 ▎   ▎   ▎   ▎   (void) getc();  /* consume input    */
     1 # endif
  257  ▎   ▎   ▎   ▎   break;
     1 ▎   ▎   ▎   }
     2 ▎   ▎   ▎   udelay(10000);
     3 ▎   ▎   }
     4 ▎   ▎
     5 ▎   ▎   printf("\b\b\b%2d ", bootdelay);
     6 ▎   }
     7 ▎
     8 ▎   putc('\n');

    16 #ifdef CONFIG_SILENT_CONSOLE
    15 ▎   if (abort)
    14 ▎   ▎   gd->flags &= ~GD_FLG_SILENT;
    13 #endif
    12 ▎
    11 ▎   return abort;
    10 }
     9 # endif /* CONFIG_AUTOBOOT_KEYED */
     8 #endif  /* CONFIG_BOOTDELAY >= 0  */

接下来分析这段代码:

    14 ▎   while ((bootdelay > 0) && (!abort)) {
    13 ▎   ▎   int i;
    12 ▎   ▎
    11 ▎   ▎   --bootdelay;
    10 ▎   ▎   /* delay 100 * 10ms */
     9 ▎   ▎   for (i=0; !abort && i<100; ++i) {
     8 ▎   ▎   ▎   if (tstc()) {   /* we got a key press   */
     7 ▎   ▎   ▎   ▎   abort  = 1; /* don't auto boot  */
     6 ▎   ▎   ▎   ▎   bootdelay = 0;  /* no more delay    */
     5 # ifdef CONFIG_MENUKEY
     4 ▎   ▎   ▎   ▎   menukey = getc();
     3 # else
     2 ▎   ▎   ▎   ▎   (void) getc();  /* consume input    */
     1 # endif
  257  ▎   ▎   ▎   ▎   break;
     1 ▎   ▎   ▎   }
     2 ▎   ▎   ▎   udelay(10000);
     3 ▎   ▎   }
     4 ▎   ▎
     5 ▎   ▎   printf("\b\b\b%2d ", bootdelay);
     6 ▎   }

 while ((bootdelay > 0) && (!abort)) 这句中 bootdelay 大于0 , abort 是0 , 所以执行 while

这个for 循环的代码如下:

    10 ▎   ▎   /* delay 100 * 10ms */
     9 ▎   ▎   for (i=0; !abort && i<100; ++i) {
     8 ▎   ▎   ▎   if (tstc()) {   /* we got a key press   */
     7 ▎   ▎   ▎   ▎   abort  = 1; /* don't auto boot  */
     6 ▎   ▎   ▎   ▎   bootdelay = 0;  /* no more delay    */
     5 # ifdef CONFIG_MENUKEY
     4 ▎   ▎   ▎   ▎   menukey = getc();
     3 # else
     2 ▎   ▎   ▎   ▎   (void) getc();  /* consume input    */
     1 # endif
  257  ▎   ▎   ▎   ▎   break;
     1 ▎   ▎   ▎   }
     2 ▎   ▎   ▎   udelay(10000);
     3 ▎   ▎   }

也就是说,他假定 ,执行命令是不花时间的, 没存循环的时间 是 udelay 在起作用,并且他不是一直 循环遍历, 而是没10ms 遍历一次。

来看一下 tstc() 函数的实现。

   20 int tstc(void)
   19 {
   18 #ifdef CONFIG_DISABLE_CONSOLE
   17 ▎   if (gd->flags & GD_FLG_DISABLE_CONSOLE)
   16 ▎   ▎   return 0;
   15 #endif
   14 ▎
   13 ▎   if (!gd->have_console)
   12 ▎   ▎   return 0;
   11 ▎
   10 ▎   if (gd->flags & GD_FLG_DEVINIT) {
    9 ▎   ▎   /* Test the standard input */
    8 ▎   ▎   return ftstc(stdin);
    7 ▎   }
    6 ▎
    5 ▎   /* Send directly to the handler */
    4 ▎   return serial_tstc();
    3 }

只会 执行下面的代码,

   10 ▎   if (gd->flags & GD_FLG_DEVINIT) {
    9 ▎   ▎   /* Test the standard input */
    8 ▎   ▎   return ftstc(stdin);
    7 ▎   }

也就是执行    ftstc(stdin)

来看一下 代码

    7 int ftstc(int file)
    6 {
    5 ▎   if (file < MAX_FILES)
    4 ▎   ▎   return console_tstc(file);
    3 ▎
    2 ▎   return -1;
    1 }

那么也就是会执行,   console_tstc(file)

  176 static inline int console_tstc(int file)
    1 {
    2 ▎   return stdio_devices[file]->tstc();
    3 }

可以看到 , 这里 的file 只是一个数字, 最终反映到  stdio_devices[file] 中第几个结构体。

我们知道 , 所有的结构体都是指向  serial0 的。

也就是调用的 serial0 的 tstc() 函数。

那么看一下 这个函数。

最终会调用  serail_tstc_dev 函数。

   8 /*
    7 ▎* Test whether a character is in the RX buffer
    6 ▎*/
    5 int serial_tstc_dev(const int dev_index)
    4 {
    3 ▎   struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
    2 ▎
    1 ▎   return (int)(readl(&uart->utrstat) & 0x1);
  169 }

也就是 最终会使用 (int)(readl(&uart->utrstat) & 0x1); 函数查看 是不是 1 。

readl 函数的代码如下:

        

   18 /*
   17 ▎* TODO: The kernel offers some more advanced versions of barriers, it might
   16 ▎* have some advantages to use them instead of the simple one here.
   15 ▎*/
   14 #define dmb()       __asm__ __volatile__ ("" : : : "memory")
   13 #define __iormb()   dmb()
   12 #define __iowmb()   dmb()
   11
   10 #define writeb(v,c) ({ u8  __v = v; __iowmb(); __arch_putb(__v,c); __v; })
    9 #define writew(v,c) ({ u16 __v = v; __iowmb(); __arch_putw(__v,c); __v; })
    8 #define writel(v,c) ({ u32 __v = v; __iowmb(); __arch_putl(__v,c); __v; })
    7
    6 #define readb(c)    ({ u8  __v = __arch_getb(c); __iormb(); __v; })
    5 #define readw(c)    ({ u16 __v = __arch_getw(c); __iormb(); __v; })
    4 #define readl(c)    ({ u32 __v = __arch_getl(c); __iormb(); __v; })
    2 #define __arch_getb(a)          (*(volatile unsigned char *)(a))
    1 #define __arch_getw(a)          (*(volatile unsigned short *)(a))
  72  #define __arch_getl(a)          (*(volatile unsigned int *)(a))
    1 ▎
    2 #define __arch_putb(v,a)        (*(volatile unsigned char *)(a) = (v))
    3 #define __arch_putw(v,a)        (*(volatile unsigned short *)(a) = (v))
    4 #define __arch_putl(v,a)        (*(volatile unsigned int *)(a) = (v))

readl 最后的实现 就是 根据地址 去 内存中找 值。

&uart->utrstat  这句就是  Uart寄存器中  state 寄存器的地址。

也就是看看这一位 有没有置 1 .

如果置1 , 说明 有了 信息的传输。

看一下 这个寄存器。

可以看到 第0 位 就是管 接收的。

如果是 有按键按下的话,那么 就进入的 if 内。

就执行这一段。

     7 ▎   ▎   ▎   ▎   abort  = 1; /* don't auto boot  */
     6 ▎   ▎   ▎   ▎   bootdelay = 0;  /* no more delay    */
     5 # ifdef CONFIG_MENUKEY
     4 ▎   ▎   ▎   ▎   menukey = getc();
     3 # else
     2 ▎   ▎   ▎   ▎   (void) getc();  /* consume input    */
     1 # endif
  257  ▎   ▎   ▎   ▎   break;

分析一下 getc() 函数。

   12 int getc(void)
   11 {
   10 #ifdef CONFIG_DISABLE_CONSOLE
    9 ▎   if (gd->flags & GD_FLG_DISABLE_CONSOLE)
    8 ▎   ▎   return 0;
    7 #endif
    6 ▎
    5 ▎   if (!gd->have_console)
    4 ▎   ▎   return 0;
    3 ▎
    2 ▎   if (gd->flags & GD_FLG_DEVINIT) {
    1 ▎   ▎   /* Get from the standard input */
  306 ▎   ▎   return fgetc(stdin);
    1 ▎   }
    2 ▎
    3 ▎   /* Send directly to the handler */
    4 ▎   return serial_getc();
    5 }

这个函数 最终 会执行  fgetc(stdin);

然后进入 到  serial_fgetc_dev 函数

    5 /*
    4 ▎* Read a single byte from the serial port. Returns 1 on success, 0
    3 ▎* otherwise. When the function is succesfull, the character read is
    2 ▎* written into its argument c.
    1 ▎*/
  128 int serial_getc_dev(const int dev_index)
    1 {
    2 ▎   struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
    3 ▎
    4 ▎   /* wait for character to arrive */                                                                                                                                        5 ▎   while (!(readl(&uart->utrstat) & 0x1)) {
    6 ▎   ▎   if (serial_err_check(dev_index, 0))
    7 ▎   ▎   ▎   return 0;
    8 ▎   }
    9 ▎
   10 ▎   return (int)(readb(&uart->urxh) & 0xff);
   11 }

这句中 (int)(readb(&uart->urxh) & 0xff) ,  uart->urxh 代表的是接收寄存器。

就是这个。

如果他 拿到了一个值, 会将这个值 返回去。

但是这里显示 他什么也没做,直接退出循环了。

总结:也就是说,如果有 键盘上的任意按键 按下的话,我就跳出 倒计时的循环了。

执行的是下面的代码。

    25 ▎   for (;;) {
    24 #ifdef CONFIG_BOOT_RETRY_TIME
    23 ▎   ▎   if (rc >= 0) {
    22 ▎   ▎   ▎   /* Saw enough of a valid command to
    21 ▎   ▎   ▎   ▎* restart the timeout.
    20 ▎   ▎   ▎   ▎*/
    19 ▎   ▎   ▎   reset_cmd_timeout();
    18 ▎   ▎   }
    17 #endif
E   16 ▎   ▎   len = readline (CONFIG_SYS_PROMPT);     ■■ Use of undeclared identifier 'CONFIG_SYS_PROMPT'
    15 ▎   ▎
    14 ▎   ▎   flag = 0;   /* assume no special flags for now */
    13 ▎   ▎   if (len > 0)
E   12 ▎   ▎   ▎   strcpy (lastcommand, console_buffer);     ■ Call to undeclared library function 'strcpy' with type 'char *(char *, const char *)'; ISO C99 and later do not s
    11 ▎   ▎   else if (len == 0)
E   10 ▎   ▎   ▎   flag |= CMD_FLAG_REPEAT;     ■ Use of undeclared identifier 'CMD_FLAG_REPEAT'
     9 #ifdef CONFIG_BOOT_RETRY_TIME
     8 ▎   ▎   else if (len == -2) {
     7 ▎   ▎   ▎   /* -2 means timed out, retry autoboot
     6 ▎   ▎   ▎   ▎*/
     5 ▎   ▎   ▎   puts ("\nTimed out waiting for command\n");
     4 # ifdef CONFIG_RESET_TO_RETRY
     3 ▎   ▎   ▎   /* Reinit board to run initialization code again */
     2 ▎   ▎   ▎   do_reset (NULL, 0, 0, NULL);
     1 # else
  440  ▎   ▎   ▎   return;     /* retry autoboot */
     1 # endif
     2 ▎   ▎   }
     3 #endif

这就是在 等待 用户的输入,然后解析命令,然后执行命令。

我接下来 分析一下 他是怎么解析命令的,

首先是这句代码的分析

len = readline (CONFIG_SYS_PROMPT);

readline 函数如下:

    16 /*
    15 ▎* Prompt for input and read a line.
    14 ▎* If  CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0,
    13 ▎* time out when time goes past endtime (timebase time in ticks).
    12 ▎* Return:  number of read characters
    11 ▎*      -1 if break
    10 ▎*      -2 if timed out
     9 ▎*/
     8 int readline (const char *const prompt)
     7 {
     6 ▎   /*
     5 ▎   ▎* If console_buffer isn't 0-length the user will be prompted to modify
     4 ▎   ▎* it instead of entering it from scratch as desired.
     3 ▎   ▎*/
     2 ▎   console_buffer[0] = '\0';
     1 ▎
E 926  ▎   return readline_into_buffer(prompt, console_buffer, 0);     ■ Call to undeclared function 'readline_into_buffer'; ISO C99 and later do not support implicit function
     1 }
     2

它的作用就是 读取你在键盘上输入的一行命令。

然后看一下  readline_into_buffer(prompt, console_buffer, 0); 函数。


    42 int readline_into_buffer(const char *const prompt, char *buffer, int timeout)
    41 {
    40 ▎   char *p = buffer;
    39 #ifdef CONFIG_CMDLINE_EDITING
    38 ▎   unsigned int len = CONFIG_SYS_CBSIZE;
    37 ▎   int rc;
    36 ▎   static int initted = 0;
    35 ▎
    34 ▎   /*
    33 ▎   ▎* History uses a global array which is not
    32 ▎   ▎* writable until after relocation to RAM.
    31 ▎   ▎* Revert to non-history version if still
    30 ▎   ▎* running from flash.
    29 ▎   ▎*/
    28 ▎   if (gd->flags & GD_FLG_RELOC) {
    27 ▎   ▎   if (!initted) {
    26 ▎   ▎   ▎   hist_init();
    25 ▎   ▎   ▎   initted = 1;
    24 ▎   ▎   }
    23 ▎   ▎
    22 ▎   ▎   if (prompt)
    21 ▎   ▎   ▎   puts (prompt);
    20 ▎   ▎
    19 ▎   ▎   rc = cread_line(prompt, p, &len, timeout);
    18 ▎   ▎   return rc < 0 ? rc : len;
    17 ▎   ▎
    16 ▎   } else {
    15 #endif  /* CONFIG_CMDLINE_EDITING */
    14 ▎   char * p_buf = p;
    13 ▎   int n = 0;              /* buffer index     */
    12 ▎   int plen = 0;           /* prompt length    */
    11 ▎   int col;                /* output column cnt    */
    10 ▎   char    c;
     9 ▎
     8 ▎   /* print prompt */
     7 ▎   if (prompt) {
E    6 ▎   ▎   plen = strlen (prompt);     ■ Call to undeclared library function 'strlen' with type 'unsigned long (const char *)'; ISO C99 and later do not support implicit fu
     5 ▎   ▎   puts (prompt);
     4 ▎   }
     3 ▎   col = plen;
     2 ▎

    43 ▎   for (;;) {
    42 #ifdef CONFIG_BOOT_RETRY_TIME
    41 ▎   ▎   while (!tstc()) {   /* while no incoming data */
    40 ▎   ▎   ▎   if (retry_time >= 0 && get_ticks() > endtime)
    39 ▎   ▎   ▎   ▎   return (-2);    /* timed out */
    38 ▎   ▎   ▎   WATCHDOG_RESET();
    37 ▎   ▎   }
    36 #endif
E   35 ▎   ▎   WATCHDOG_RESET();       /* Trigger watchdog, if needed */     ■ Call to undeclared function 'WATCHDOG_RESET'; ISO C99 and later do not support implicit function
    34 ▎   ▎
    33 #ifdef CONFIG_SHOW_ACTIVITY
    32 ▎   ▎   while (!tstc()) {
    31 ▎   ▎   ▎   show_activity(0);
    30 ▎   ▎   ▎   WATCHDOG_RESET();
    29 ▎   ▎   }
    28 #endif
E   27 ▎   ▎   c = getc();     ■ Too few arguments to function call, single argument '__stream' was not specified
    26 ▎   ▎
    25 ▎   ▎   /*
    24 ▎   ▎   ▎* Special character handling
    23 ▎   ▎   ▎*/
    22 ▎   ▎   switch (c) {
    21 ▎   ▎   case '\r':              /* Enter        */
    20 ▎   ▎   case '\n':
    19 ▎   ▎   ▎   *p = '\0';
    18 ▎   ▎   ▎   puts ("\r\n");
    17 ▎   ▎   ▎   return (p - p_buf);
    16 ▎   ▎   ▎
    15 ▎   ▎   case '\0':              /* nul          */
    14 ▎   ▎   ▎   continue;
    13 ▎   ▎   ▎
    12 ▎   ▎   case 0x03:              /* ^C - break       */
    11 ▎   ▎   ▎   p_buf[0] = '\0';    /* discard input */
    10 ▎   ▎   ▎   return (-1);
     9 ▎   ▎   ▎
     8 ▎   ▎   case 0x15:              /* ^U - erase line  */
     7 ▎   ▎   ▎   while (col > plen) {
     6 ▎   ▎   ▎   ▎   puts (erase_seq);
     5 ▎   ▎   ▎   ▎   --col;
     4 ▎   ▎   ▎   }
     3 ▎   ▎   ▎   p = p_buf;
     2 ▎   ▎   ▎   n = 0;
     1 ▎   ▎   ▎   continue;
  1014 ▎   ▎   ▎
     1 ▎   ▎   case 0x17:              /* ^W - erase word  */
     2 ▎   ▎   ▎   p=delete_char(p_buf, p, &col, &n, plen);
     3 ▎   ▎   ▎   while ((n > 0) && (*p != ' ')) {
     4 ▎   ▎   ▎   ▎   p=delete_char(p_buf, p, &col, &n, plen);
     5 ▎   ▎   ▎   }
     6 ▎   ▎   ▎   continue;
     7 ▎   ▎   ▎
     8 ▎   ▎   case 0x08:              /* ^H  - backspace  */
     9 ▎   ▎   case 0x7F:              /* DEL - backspace  */
   39 ▎   ▎   ▎   p=delete_char(p_buf, p, &col, &n, plen);
    38 ▎   ▎   ▎   continue;
    37 ▎   ▎
    36 ▎   ▎   default:
    35 ▎   ▎   ▎   /*
    34 ▎   ▎   ▎   ▎* Must be a normal character then
    33 ▎   ▎   ▎   ▎*/
E   32 ▎   ▎   ▎   if (n < CONFIG_SYS_CBSIZE-2) {     ■ Use of undeclared identifier 'CONFIG_SYS_CBSIZE'
    31 ▎   ▎   ▎   ▎   if (c == '\t') {    /* expand TABs      */
    30 #ifdef CONFIG_AUTO_COMPLETE
    29 ▎   ▎   ▎   ▎   ▎   /* if auto completion triggered just continue */
    28 ▎   ▎   ▎   ▎   ▎   *p = '\0';
    27 ▎   ▎   ▎   ▎   ▎   if (cmd_auto_complete(prompt, console_buffer, &n, &col)) {
    26 ▎   ▎   ▎   ▎   ▎   ▎   p = p_buf + n;  /* reset */
    25 ▎   ▎   ▎   ▎   ▎   ▎   continue;
    24 ▎   ▎   ▎   ▎   ▎   }
    23 #endif
    22 ▎   ▎   ▎   ▎   ▎   puts (tab_seq+(col&07));
    21 ▎   ▎   ▎   ▎   ▎   col += 8 - (col&07);
    20 ▎   ▎   ▎   ▎   } else {
    19 ▎   ▎   ▎   ▎   ▎   ++col;      /* echo input       */
E   18 ▎   ▎   ▎   ▎   ▎   putc (c);     ■ Too few arguments to function call, expected 2, have 1
    17 ▎   ▎   ▎   ▎   }
    16 ▎   ▎   ▎   ▎   *p++ = c;
    15 ▎   ▎   ▎   ▎   ++n;
    14 ▎   ▎   ▎   } else {            /* Buffer full      */
E   13 ▎   ▎   ▎   ▎   putc ('\a');     ■ Too few arguments to function call, expected 2, have 1
    12 ▎   ▎   ▎   }
    11 ▎   ▎   }
    10 ▎   }
     9 #ifdef CONFIG_CMDLINE_EDITING
     8 ▎   }
     7 #endif
     6 }

我们有定义

 CONFIG_CMDLINE_EDITING 

这个宏定义 可以 通过键盘 翻看 历史命令,相当于 windows 的 上下键。

所以只会执行这一段代码

    30 ▎   unsigned int len = CONFIG_SYS_CBSIZE;
    29 ▎   int rc;
    28 ▎   static int initted = 0;
    27 ▎
    26 ▎   /*
    25 ▎   ▎* History uses a global array which is not
    24 ▎   ▎* writable until after relocation to RAM.
    23 ▎   ▎* Revert to non-history version if still
    22 ▎   ▎* running from flash.
    21 ▎   ▎*/
    20 ▎   if (gd->flags & GD_FLG_RELOC) {
    19 ▎   ▎   if (!initted) {
    18 ▎   ▎   ▎   hist_init();
    17 ▎   ▎   ▎   initted = 1;
    16 ▎   ▎   }
    15 ▎   ▎
    14 ▎   ▎   if (prompt)
    13 ▎   ▎   ▎   puts (prompt);
    12 ▎   ▎
    11 ▎   ▎   rc = cread_line(prompt, p, &len, timeout);
    10 ▎   ▎   return rc < 0 ? rc : len;
     9 ▎   ▎

unsigned int len = CONFIG_SYS_CBSIZE;  这句定义了最大的字符数量,就是 256 .

由于  gd->flags & GD_FLG_RELOC 有值。所以执行下面的代码

    20 ▎   if (gd->flags & GD_FLG_RELOC) {
    19 ▎   ▎   if (!initted) {
    18 ▎   ▎   ▎   hist_init();
    17 ▎   ▎   ▎   initted = 1;
    16 ▎   ▎   }

hist_init() 代码如下, 这是在初始化,历史命令的设置。

    24 char* hist_list[HIST_MAX];
    23 char hist_lines[HIST_MAX][HIST_SIZE + 1];    /* Save room for NULL */
    22
    21 #define add_idx_minus_one() ((hist_add_idx == 0) ? hist_max : hist_add_idx-1)
    20
    19 static void hist_init(void)
    18 {
    17 ▎   int i;
    16 ▎
    15 ▎   hist_max = 0;
    14 ▎   hist_add_idx = 0;
    13 ▎   hist_cur = -1;
    12 ▎   hist_num = 0;
    11 ▎
    10 ▎   for (i = 0; i < HIST_MAX; i++) {
     9 ▎   ▎   hist_list[i] = hist_lines[i];
     8 ▎   ▎   hist_list[i][0] = '\0';
     7 ▎   }
     6 }

相当于 将二维数组的每一行都初始化为0 。

 疑问: 但他为什么用两个数组呢? 一个不就行了。

这句代码 打印出了 提示符。

     3 ▎   ▎   if (prompt)
     2 ▎   ▎   ▎   puts (prompt);

接下来分析 这段代码  ,

   ▎   rc = cread_line(prompt, p, &len, timeout);

代码如下:


    38 static int cread_line(const char *const prompt, char *buf, unsigned int *len,
    37 ▎   ▎   int timeout)
    36 {
    35 ▎   unsigned long num = 0;
    34 ▎   unsigned long eol_num = 0;
    33 ▎   unsigned long wlen;
    32 ▎   char ichar;
    31 ▎   int insert = 1;
    30 ▎   int esc_len = 0;
    29 ▎   char esc_save[8];
    28 ▎   int init_len = strlen(buf);
    27 ▎   int first = 1;
    26 ▎
    25 ▎   if (init_len)
    24 ▎   ▎   cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len);
    23 ▎
    22 ▎   while (1) {
    21 #ifdef CONFIG_BOOT_RETRY_TIME
    20 ▎   ▎   while (!tstc()) {   /* while no incoming data */
    19 ▎   ▎   ▎   if (retry_time >= 0 && get_ticks() > endtime)
    18 ▎   ▎   ▎   ▎   return (-2);    /* timed out */
    17 ▎   ▎   ▎   WATCHDOG_RESET();
    16 ▎   ▎   }
    15 #endif
    14 ▎   ▎   if (first && timeout) {
    13 ▎   ▎   ▎   uint64_t etime = endtick(timeout);
    12 ▎   ▎   ▎
    11 ▎   ▎   ▎   while (!tstc()) {   /* while no incoming data */
    10 ▎   ▎   ▎   ▎   if (get_ticks() >= etime)
     9 ▎   ▎   ▎   ▎   ▎   return -2;  /* timed out */
     8 ▎   ▎   ▎   ▎   WATCHDOG_RESET();
     7 ▎   ▎   ▎   }
     6 ▎   ▎   ▎   first = 0;
     5 ▎   ▎   }
     4 ▎   ▎
     3 ▎   ▎   ichar = getcmd_getch();
     2 ▎   ▎
     1 ▎   ▎   if ((ichar == '\n') || (ichar == '\r')) {
  715  ▎   ▎   ▎   putc('\n');
     1 ▎   ▎   ▎   break;
     2 ▎   ▎   }
 ▎
    43 ▎   ▎   /*
    42 ▎   ▎   ▎* handle standard linux xterm esc sequences for arrow key, etc.
    41 ▎   ▎   ▎*/
    40 ▎   ▎   if (esc_len != 0) {
    39 ▎   ▎   ▎   if (esc_len == 1) {
    38 ▎   ▎   ▎   ▎   if (ichar == '[') {
    37 ▎   ▎   ▎   ▎   ▎   esc_save[esc_len] = ichar;
    36 ▎   ▎   ▎   ▎   ▎   esc_len = 2;
    35 ▎   ▎   ▎   ▎   } else {
    34 ▎   ▎   ▎   ▎   ▎   cread_add_str(esc_save, esc_len, insert,
    33 ▎   ▎   ▎   ▎   ▎   ▎         &num, &eol_num, buf, *len);
    32 ▎   ▎   ▎   ▎   ▎   esc_len = 0;
    31 ▎   ▎   ▎   ▎   }
    30 ▎   ▎   ▎   ▎   continue;
    29 ▎   ▎   ▎   }
    28 ▎   ▎   ▎
    27 ▎   ▎   ▎   switch (ichar) {
    26 ▎   ▎   ▎
    25 ▎   ▎   ▎   case 'D':   /* <- key */
    24 ▎   ▎   ▎   ▎   ichar = CTL_CH('b');
    23 ▎   ▎   ▎   ▎   esc_len = 0;
    22 ▎   ▎   ▎   ▎   break;
    21 ▎   ▎   ▎   case 'C':   /* -> key */
    20 ▎   ▎   ▎   ▎   ichar = CTL_CH('f');
    19 ▎   ▎   ▎   ▎   esc_len = 0;
    18 ▎   ▎   ▎   ▎   break;  /* pass off to ^F handler */
    17 ▎   ▎   ▎   case 'H':   /* Home key */
    16 ▎   ▎   ▎   ▎   ichar = CTL_CH('a');
    15 ▎   ▎   ▎   ▎   esc_len = 0;
    14 ▎   ▎   ▎   ▎   break;  /* pass off to ^A handler */
    13 ▎   ▎   ▎   case 'A':   /* up arrow */
    12 ▎   ▎   ▎   ▎   ichar = CTL_CH('p');
    11 ▎   ▎   ▎   ▎   esc_len = 0;
    10 ▎   ▎   ▎   ▎   break;  /* pass off to ^P handler */
     9 ▎   ▎   ▎   case 'B':   /* down arrow */
     8 ▎   ▎   ▎   ▎   ichar = CTL_CH('n');
     7 ▎   ▎   ▎   ▎   esc_len = 0;
     6 ▎   ▎   ▎   ▎   break;  /* pass off to ^N handler */
     5 ▎   ▎   ▎   default:
     4 ▎   ▎   ▎   ▎   esc_save[esc_len++] = ichar;
     3 ▎   ▎   ▎   ▎   cread_add_str(esc_save, esc_len, insert,
     2 ▎   ▎   ▎   ▎   ▎         &num, &eol_num, buf, *len);
     1 ▎   ▎   ▎   ▎   esc_len = 0;
  762  ▎   ▎   ▎   ▎   continue;
     1 ▎   ▎   ▎   }
     2 ▎   ▎   }
  switch (ichar) {
    43 ▎   ▎   case 0x1b:
    42 ▎   ▎   ▎   if (esc_len == 0) {
    41 ▎   ▎   ▎   ▎   esc_save[esc_len] = ichar;
    40 ▎   ▎   ▎   ▎   esc_len = 1;
    39 ▎   ▎   ▎   } else {
    38 ▎   ▎   ▎   ▎   puts("impossible condition #876\n");
    37 ▎   ▎   ▎   ▎   esc_len = 0;
    36 ▎   ▎   ▎   }
    35 ▎   ▎   ▎   break;
    34 ▎   ▎   ▎
    33 ▎   ▎   case CTL_CH('a'):
    32 ▎   ▎   ▎   BEGINNING_OF_LINE();
    31 ▎   ▎   ▎   break;
    30 ▎   ▎   case CTL_CH('c'):   /* ^C - break */
    29 ▎   ▎   ▎   *buf = '\0';    /* discard input */
    28 ▎   ▎   ▎   return (-1);
    27 ▎   ▎   case CTL_CH('f'):
    26 ▎   ▎   ▎   if (num < eol_num) {
    25 ▎   ▎   ▎   ▎   getcmd_putch(buf[num]);
    24 ▎   ▎   ▎   ▎   num++;
    23 ▎   ▎   ▎   }
    22 ▎   ▎   ▎   break;
    21 ▎   ▎   case CTL_CH('b'):
    20 ▎   ▎   ▎   if (num) {
    19 ▎   ▎   ▎   ▎   getcmd_putch(CTL_BACKSPACE);
    18 ▎   ▎   ▎   ▎   num--;
    17 ▎   ▎   ▎   }
    16 ▎   ▎   ▎   break;
    15 ▎   ▎   case CTL_CH('d'):
    14 ▎   ▎   ▎   if (num < eol_num) {
    13 ▎   ▎   ▎   ▎   wlen = eol_num - num - 1;
    12 ▎   ▎   ▎   ▎   if (wlen) {
    11 ▎   ▎   ▎   ▎   ▎   memmove(&buf[num], &buf[num+1], wlen);
    10 ▎   ▎   ▎   ▎   ▎   putnstr(buf + num, wlen);
     9 ▎   ▎   ▎   ▎   }
     8 ▎   ▎   ▎   ▎
     7 ▎   ▎   ▎   ▎   getcmd_putch(' ');
     6 ▎   ▎   ▎   ▎   do {
     5 ▎   ▎   ▎   ▎   ▎   getcmd_putch(CTL_BACKSPACE);
     4 ▎   ▎   ▎   ▎   } while (wlen--);
     3 ▎   ▎   ▎   ▎   eol_num--;
     2 ▎   ▎   ▎   }
     1 ▎   ▎   ▎   break;
  810  ▎   ▎   case CTL_CH('k'):
     1 ▎   ▎   ▎   ERASE_TO_EOL();
     2 ▎   ▎   ▎   break;
     3 ▎   ▎   case CTL_CH('e'):
     4 ▎   ▎   ▎   REFRESH_TO_EOL();
     5 ▎   ▎   ▎   break;
     6 ▎   ▎   case CTL_CH('o'):
     7 ▎   ▎   ▎   insert = !insert;
     8 ▎   ▎   ▎   break;
     9 ▎   ▎   case CTL_CH('x'):
    10 ▎   ▎   case CTL_CH('u'):
  ▎   BEGINNING_OF_LINE();
    39 ▎   ▎   ▎   ERASE_TO_EOL();
    38 ▎   ▎   ▎   break;
    37 ▎   ▎   case DEL:
    36 ▎   ▎   case DEL7:
    35 ▎   ▎   case 8:
    34 ▎   ▎   ▎   if (num) {
    33 ▎   ▎   ▎   ▎   wlen = eol_num - num;
    32 ▎   ▎   ▎   ▎   num--;
    31 ▎   ▎   ▎   ▎   memmove(&buf[num], &buf[num+1], wlen);
    30 ▎   ▎   ▎   ▎   getcmd_putch(CTL_BACKSPACE);
    29 ▎   ▎   ▎   ▎   putnstr(buf + num, wlen);
    28 ▎   ▎   ▎   ▎   getcmd_putch(' ');
    27 ▎   ▎   ▎   ▎   do {
    26 ▎   ▎   ▎   ▎   ▎   getcmd_putch(CTL_BACKSPACE);
    25 ▎   ▎   ▎   ▎   } while (wlen--);
    24 ▎   ▎   ▎   ▎   eol_num--;
    23 ▎   ▎   ▎   }
    22 ▎   ▎   ▎   break;
    21 ▎   ▎   case CTL_CH('p'):
    20 ▎   ▎   case CTL_CH('n'):
    19 ▎   ▎   {
    18 ▎   ▎   ▎   char * hline;
    17 ▎   ▎   ▎
    16 ▎   ▎   ▎   esc_len = 0;
    15 ▎   ▎   ▎
    14 ▎   ▎   ▎   if (ichar == CTL_CH('p'))
    13 ▎   ▎   ▎   ▎   hline = hist_prev();
    12 ▎   ▎   ▎   else
    11 ▎   ▎   ▎   ▎   hline = hist_next();
    10 ▎   ▎   ▎
     9 ▎   ▎   ▎   if (!hline) {
     8 ▎   ▎   ▎   ▎   getcmd_cbeep();
     7 ▎   ▎   ▎   ▎   continue;
     6 ▎   ▎   ▎   }
     5 ▎   ▎   ▎
     4 ▎   ▎   ▎   /* nuke the current line */
     3 ▎   ▎   ▎   /* first, go home */
     2 ▎   ▎   ▎   BEGINNING_OF_LINE();
     1 ▎   ▎   ▎
  861  ▎   ▎   ▎   /* erase to end of line */
     1 ▎   ▎   ▎   ERASE_TO_EOL();
     2 ▎   ▎   ▎
     3 ▎   ▎   ▎   /* copy new line into place and display */
     4 ▎   ▎   ▎   strcpy(buf, hline);
     5 ▎   ▎   ▎   eol_num = strlen(buf);
     6 ▎   ▎   ▎   REFRESH_TO_EOL();
     7 ▎   ▎   ▎   continue;
     8 ▎   ▎   }
    39 #ifdef CONFIG_AUTO_COMPLETE
    38 ▎   ▎   case '\t': {
    37 ▎   ▎   ▎   int num2, col;
    36 ▎   ▎   ▎
    35 ▎   ▎   ▎   /* do not autocomplete when in the middle */
    34 ▎   ▎   ▎   if (num < eol_num) {
    33 ▎   ▎   ▎   ▎   getcmd_cbeep();
    32 ▎   ▎   ▎   ▎   break;
    31 ▎   ▎   ▎   }
    30 ▎   ▎   ▎
    29 ▎   ▎   ▎   buf[num] = '\0';
    28 ▎   ▎   ▎   col = strlen(prompt) + eol_num;
    27 ▎   ▎   ▎   num2 = num;
    26 ▎   ▎   ▎   if (cmd_auto_complete(prompt, buf, &num2, &col)) {
    25 ▎   ▎   ▎   ▎   col = num2 - num;
    24 ▎   ▎   ▎   ▎   num += col;
    23 ▎   ▎   ▎   ▎   eol_num += col;
    22 ▎   ▎   ▎   }
    21 ▎   ▎   ▎   break;
    20 ▎   ▎   }
    19 #endif
    18 ▎   ▎   default:
    17 ▎   ▎   ▎   cread_add_char(ichar, insert, &num, &eol_num, buf, *len);
    16 ▎   ▎   ▎   break;
    15 ▎   ▎   }
    14 ▎   }
    13 ▎   *len = eol_num;
    12 ▎   buf[eol_num] = '\0';    /* lose the newline */
    11 ▎
    10 ▎   if (buf[0] && buf[0] != CREAD_HIST_CHAR)
     9 ▎   ▎   cread_add_to_hist(buf);
     8 ▎   hist_cur = hist_add_idx;
     7 ▎
     6 ▎   return 0;
     5 }
     4
     3 #endif /* CONFIG_CMDLINE_EDITING */

它的逻辑是这样的,如果说我一直输入的是 普通的字符, 那么它会进入这个分支,

     3 ▎   ▎   default:
     2 ▎   ▎   ▎   cread_add_char(ichar, insert, &num, &eol_num, buf, *len);
     1 ▎   ▎   ▎   break;
  894  ▎   ▎   }

如果说 我命令输入完了, 按了一下回车 ,他会进入这个分支。

    20 ▎   ▎   if ((ichar == '\n') || (ichar == '\r')) {
    19 ▎   ▎   ▎   putc('\n');
    18 ▎   ▎   ▎   break;
    17 ▎   ▎   }

直接退出 while 循环了, 然后执行最后的命令。

    15 ▎   *len = eol_num;
    14 ▎   buf[eol_num] = '\0';    /* lose the newline */
    13 ▎
    12 ▎   if (buf[0] && buf[0] != CREAD_HIST_CHAR)
    11 ▎   ▎   cread_add_to_hist(buf);
    10 ▎   hist_cur = hist_add_idx;
     9 ▎
     8 ▎   return 0;

这个命令就是 将读取的命令,直接放到了 二位数组中。

好接下来, 开始一句一句的分析。

这句直接跳过, 因为 init_len 是最开始应该是0

    33 ▎   if (init_len)
    32 ▎   ▎   cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len);

这个代码应该是 说 有没有 重复执行的命令,不如想 ping -t 3 之类了。

    35 #ifdef CONFIG_BOOT_RETRY_TIME
    34 ▎   ▎   while (!tstc()) {   /* while no incoming data */
    33 ▎   ▎   ▎   if (retry_time >= 0 && get_ticks() > endtime)
    32 ▎   ▎   ▎   ▎   return (-2);    /* timed out */
    31 ▎   ▎   ▎   WATCHDOG_RESET();
    30 ▎   ▎   }
    29 #endif

这段代码 不理解。

    33 ▎   ▎   if (first && timeout) {
    32 ▎   ▎   ▎   uint64_t etime = endtick(timeout);
    31 ▎   ▎   ▎
    30 ▎   ▎   ▎   while (!tstc()) {   /* while no incoming data */
    29 ▎   ▎   ▎   ▎   if (get_ticks() >= etime)
    28 ▎   ▎   ▎   ▎   ▎   return -2;  /* timed out */
    27 ▎   ▎   ▎   ▎   WATCHDOG_RESET();
    26 ▎   ▎   ▎   }
    25 ▎   ▎   ▎   first = 0;
    24 ▎   ▎   }

这个代码 应该就是 去读取 键盘了。

   22 ▎   ▎   ichar = getcmd_getch();

这个函数 定义如下;

 #define getcmd_getch()      getc()

这个getc() 最后应该是 调用的  这个函数。

    5 /*
    4 ▎* Read a single byte from the serial port. Returns 1 on success, 0
    3 ▎* otherwise. When the function is succesfull, the character read is
    2 ▎* written into its argument c.
    1 ▎*/
  128 int serial_getc_dev(const int dev_index)
    1 {
    2 ▎   struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
    3 ▎
    4 ▎   /* wait for character to arrive */
    5 ▎   while (!(readl(&uart->utrstat) & 0x1)) {
    6 ▎   ▎   if (serial_err_check(dev_index, 0))
    7 ▎   ▎   ▎   return 0;
    8 ▎   }
    9 ▎
   10 ▎   return (int)(readb(&uart->urxh) & 0xff);
   11 }
   12

这个是判断是不是 回车

    24 ▎   ▎   if ((ichar == '\n') || (ichar == '\r')) {
    23 ▎   ▎   ▎   putc('\n');
    22 ▎   ▎   ▎   break;
    21 ▎   ▎   }

这段代码是在 兼容 xterm ,不懂。

    40 ▎   ▎   /*
    39 ▎   ▎   ▎* handle standard linux xterm esc sequences for arrow key, etc.
    38 ▎   ▎   ▎*/
    37 ▎   ▎   if (esc_len != 0) {
    36 ▎   ▎   ▎   if (esc_len == 1) {
    35 ▎   ▎   ▎   ▎   if (ichar == '[') {
    34 ▎   ▎   ▎   ▎   ▎   esc_save[esc_len] = ichar;
    33 ▎   ▎   ▎   ▎   ▎   esc_len = 2;
    32 ▎   ▎   ▎   ▎   } else {
    31 ▎   ▎   ▎   ▎   ▎   cread_add_str(esc_save, esc_len, insert,
    30 ▎   ▎   ▎   ▎   ▎   ▎         &num, &eol_num, buf, *len);
    29 ▎   ▎   ▎   ▎   ▎   esc_len = 0;
    28 ▎   ▎   ▎   ▎   }
    27 ▎   ▎   ▎   ▎   continue;
    26 ▎   ▎   ▎   }
    25 ▎   ▎   ▎
    24 ▎   ▎   ▎   switch (ichar) {
    23 ▎   ▎   ▎
    22 ▎   ▎   ▎   case 'D':   /* <- key */
    21 ▎   ▎   ▎   ▎   ichar = CTL_CH('b');
    20 ▎   ▎   ▎   ▎   esc_len = 0;
    19 ▎   ▎   ▎   ▎   break;
    18 ▎   ▎   ▎   case 'C':   /* -> key */
    17 ▎   ▎   ▎   ▎   ichar = CTL_CH('f');
    16 ▎   ▎   ▎   ▎   esc_len = 0;
    15 ▎   ▎   ▎   ▎   break;  /* pass off to ^F handler */
    14 ▎   ▎   ▎   case 'H':   /* Home key */
    13 ▎   ▎   ▎   ▎   ichar = CTL_CH('a');
    12 ▎   ▎   ▎   ▎   esc_len = 0;
    11 ▎   ▎   ▎   ▎   break;  /* pass off to ^A handler */
    10 ▎   ▎   ▎   case 'A':   /* up arrow */
     9 ▎   ▎   ▎   ▎   ichar = CTL_CH('p');
     8 ▎   ▎   ▎   ▎   esc_len = 0;
     7 ▎   ▎   ▎   ▎   break;  /* pass off to ^P handler */
     6 ▎   ▎   ▎   case 'B':   /* down arrow */
     5 ▎   ▎   ▎   ▎   ichar = CTL_CH('n');
     4 ▎   ▎   ▎   ▎   esc_len = 0;
     3 ▎   ▎   ▎   ▎   break;  /* pass off to ^N handler */
     2 ▎   ▎   ▎   default:
     1 ▎   ▎   ▎   ▎   esc_save[esc_len++] = ichar;
  759  ▎   ▎   ▎   ▎   cread_add_str(esc_save, esc_len, insert,
     1 ▎   ▎   ▎   ▎   ▎         &num, &eol_num, buf, *len);
     2 ▎   ▎   ▎   ▎   esc_len = 0;
     3 ▎   ▎   ▎   ▎   continue;
     4 ▎   ▎   ▎   }
     5 ▎   ▎   }

从这里开始 的一坨,就是 对于 特殊字符的判断。

▎   ▎
    41 ▎   ▎   switch (ichar) {
    40 ▎   ▎   case 0x1b:
    39 ▎   ▎   ▎   if (esc_len == 0) {
    38 ▎   ▎   ▎   ▎   esc_save[esc_len] = ichar;
    37 ▎   ▎   ▎   ▎   esc_len = 1;
    36 ▎   ▎   ▎   } else {
    35 ▎   ▎   ▎   ▎   puts("impossible condition #876\n");
    34 ▎   ▎   ▎   ▎   esc_len = 0;
    33 ▎   ▎   ▎   }
    32 ▎   ▎   ▎   break;
    31 ▎   ▎
    30 ▎   ▎   case CTL_CH('a'):
    29 ▎   ▎   ▎   BEGINNING_OF_LINE();
    28 ▎   ▎   ▎   break;
    27 ▎   ▎   case CTL_CH('c'):   /* ^C - break */
    26 ▎   ▎   ▎   *buf = '\0';    /* discard input */
    25 ▎   ▎   ▎   return (-1);
    24 ▎   ▎   case CTL_CH('f'):
    23 ▎   ▎   ▎   if (num < eol_num) {
    22 ▎   ▎   ▎   ▎   getcmd_putch(buf[num]);
    21 ▎   ▎   ▎   ▎   num++;
    20 ▎   ▎   ▎   }
    19 ▎   ▎   ▎   break;
    18 ▎   ▎   case CTL_CH('b'):
    17 ▎   ▎   ▎   if (num) {
    16 ▎   ▎   ▎   ▎   getcmd_putch(CTL_BACKSPACE);
    15 ▎   ▎   ▎   ▎   num--;
    14 ▎   ▎   ▎   }
    13 ▎   ▎   ▎   break;

感觉这个应该是跟 补全相关的。

    30 #ifdef CONFIG_AUTO_COMPLETE
    29 ▎   ▎   case '\t': {
    28 ▎   ▎   ▎   int num2, col;
    27 ▎   ▎   ▎
    26 ▎   ▎   ▎   /* do not autocomplete when in the middle */
    25 ▎   ▎   ▎   if (num < eol_num) {
    24 ▎   ▎   ▎   ▎   getcmd_cbeep();
    23 ▎   ▎   ▎   ▎   break;
    22 ▎   ▎   ▎   }
    21 ▎   ▎   ▎
    20 ▎   ▎   ▎   buf[num] = '\0';
    19 ▎   ▎   ▎   col = strlen(prompt) + eol_num;
    18 ▎   ▎   ▎   num2 = num;
    17 ▎   ▎   ▎   if (cmd_auto_complete(prompt, buf, &num2, &col)) {
    16 ▎   ▎   ▎   ▎   col = num2 - num;
    15 ▎   ▎   ▎   ▎   num += col;
    14 ▎   ▎   ▎   ▎   eol_num += col;
    13 ▎   ▎   ▎   }
    12 ▎   ▎   ▎   break;
    11 ▎   ▎   }
    10 #endif

接下里分析一下 这个代码

  896  ▎   *len = eol_num;
     1 ▎   buf[eol_num] = '\0';    /* lose the newline */
     2 ▎
     3 ▎   if (buf[0] && buf[0] != CREAD_HIST_CHAR)
     4 ▎   ▎   cread_add_to_hist(buf);
     5 ▎   hist_cur = hist_add_idx;

*len = eol_num; 这句中  eol_num 是在上面的 if 判断中 赋值的。

继续往下分析。

buf[eol_num] = '\0';  这句添加了 字符串的结束。

这段代码是在操作 存储命令的数组。

     1 ▎   if (buf[0] && buf[0] != CREAD_HIST_CHAR)
  900  ▎   ▎   cread_add_to_hist(buf);
     1 ▎   hist_cur = hist_add_idx;
     2 ▎

大致看一下 函数   cread_add_to_hist(buf);

  533  static void cread_add_to_hist(char *line)
     1 {
     2 ▎   strcpy(hist_list[hist_add_idx], line);
     3 ▎
     4 ▎   if (++hist_add_idx >= HIST_MAX)
     5 ▎   ▎   hist_add_idx = 0;
     6 ▎
     7 ▎   if (hist_add_idx > hist_max)
     8 ▎   ▎   hist_max = hist_add_idx;
     9 ▎
    10 ▎   hist_num++;
    11 }

他这种处理方式挺有意思的, hist_list[hist_add_idx],  实际上是  指向 二维数组的 一个 指针。

剩下的我就不分析了。

然后继续往下分析代码

     3 ▎   ▎   flag = 0;   /* assume no special flags for now */
     2 ▎   ▎   if (len > 0)
E    1 ▎   ▎   ▎   strcpy (lastcommand, console_buffer);     ■ Call to undeclared library function 'strcpy' with type 'char *(char *, const char *)'; ISO C99 and later do not s
  429  ▎   ▎   else if (len == 0)
E    1 ▎   ▎   ▎   flag |= CMD_FLAG_REPEAT;     ■ Use of undeclared identifier 'CMD_FLAG_REPEAT'
     2 #ifdef CONFIG_BOOT_RETRY_TIME
     3 ▎   ▎   else if (len == -2) {
     4 ▎   ▎   ▎   /* -2 means timed out, retry autoboot
     5 ▎   ▎   ▎   ▎*/
     6 ▎   ▎   ▎   puts ("\nTimed out waiting for command\n");
     7 # ifdef CONFIG_RESET_TO_RETRY
     8 ▎   ▎   ▎   /* Reinit board to run initialization code again */
     9 ▎   ▎   ▎   do_reset (NULL, 0, 0, NULL);
    10 # else
    11 ▎   ▎   ▎   return;     /* retry autoboot */
    12 # endif
    13 ▎   ▎   }
    14 #endif

 strcpy (lastcommand, console_buffer); 这句表示 将指令拷贝到 lastcommand 中。

然后继续往下分析。

    16 ▎   ▎   if (len == -1)
    15 ▎   ▎   ▎   puts ("<INTERRUPT>\n");
    14 ▎   ▎   else
E   13 ▎   ▎   ▎   rc = run_command(lastcommand, flag);     ■ Call to undeclared function 'run_command'; ISO C99 and later do not support implicit function declarations
    12 ▎   ▎
    11 ▎   ▎   if (rc <= 0) {
    10 ▎   ▎   ▎   /* invalid command or not repeatable, forget it */
     9 ▎   ▎   ▎   lastcommand[0] = 0;
     8 ▎   ▎   }
     7 ▎   }
     6 #endif /*CONFIG_SYS_HUSH_PARSER*/
     5 }
     4

最关键的就是 这句代码了。

rc = run_command(lastcommand, flag);  

代码如下:

     8 /*
     7 ▎* Run a command using the selected parser.
     6 ▎*
     5 ▎* @param cmd   Command to run
     4 ▎* @param flag  Execution flags (CMD_FLAG_...)
     3 ▎* @return 0 on success, or != 0 on error.
     2 ▎*/
     1 int run_command(const char *cmd, int flag)
  1367 {
     1 #ifndef CONFIG_SYS_HUSH_PARSER
     2 ▎   /*
     3 ▎   ▎* builtin_run_command can return 0 or 1 for success, so clean up
     4 ▎   ▎* its result.
     5 ▎   ▎*/
     6 ▎   if (builtin_run_command(cmd, flag) == -1)
     7 ▎   ▎   return 1;
     8 ▎
     9 ▎   return 0;
    10 #else
    11 ▎   return parse_string_outer(cmd,
    12 ▎   ▎   ▎   FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP);
    13 #endif
    14 }

由于没有定义 , CONFIG_SYS_HUSH_PARSER , 所以会执行。

builtin_run_command(cmd, flag)  这句代码。


    34 static int builtin_run_command(const char *cmd, int flag)
    33 {
    32 ▎   char cmdbuf[CONFIG_SYS_CBSIZE]; /* working copy of cmd      */
    31 ▎   char *token;            /* start of token in cmdbuf */
    30 ▎   char *sep;          /* end of token (separator) in cmdbuf */
    29 ▎   char finaltoken[CONFIG_SYS_CBSIZE];
    28 ▎   char *str = cmdbuf;
    27 ▎   char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated  */
    26 ▎   int argc, inquotes;
    25 ▎   int repeatable = 1;
    24 ▎   int rc = 0;
    23 ▎
    22 #ifdef DEBUG_PARSER
    21 ▎   printf ("[RUN_COMMAND] cmd[%p]=\"", cmd);
    20 ▎   puts (cmd ? cmd : "NULL");  /* use puts - string may be loooong */
    19 ▎   puts ("\"\n");
    18 #endif
    17 ▎
    16 ▎   clear_ctrlc();      /* forget any previous Control C */
    15 ▎
    14 ▎   if (!cmd || !*cmd) {
    13 ▎   ▎   return -1;  /* empty command */
    12 ▎   }
    11 ▎
    10 ▎   if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {
     9 ▎   ▎   puts ("## Command too long!\n");
     8 ▎   ▎   return -1;
     7 ▎   }
     6 ▎
     5 ▎   strcpy (cmdbuf, cmd);
     4 ▎
     3 ▎   /* Process separators and check for invalid
     2 ▎   ▎* repeatable commands
     1 ▎   ▎*/

    40 #ifdef DEBUG_PARSER
    39 ▎   printf ("[PROCESS_SEPARATORS] %s\n", cmd);
    38 #endif
    37 ▎   while (*str) {
    36 ▎   ▎
    35 ▎   ▎   /*
    34 ▎   ▎   ▎* Find separator, or string end
    33 ▎   ▎   ▎* Allow simple escape of ';' by writing "\;"
    32 ▎   ▎   ▎*/
    31 ▎   ▎   for (inquotes = 0, sep = str; *sep; sep++) {
    30 ▎   ▎   ▎   if ((*sep=='\'') &&
    29 ▎   ▎   ▎   ▎   (*(sep-1) != '\\'))
    28 ▎   ▎   ▎   ▎   inquotes=!inquotes;
    27 ▎   ▎   ▎
    26 ▎   ▎   ▎   if (!inquotes &&
    25 ▎   ▎   ▎   ▎   (*sep == ';') &&    /* separator        */
    24 ▎   ▎   ▎   ▎   ( sep != str) &&    /* past string start    */
    23 ▎   ▎   ▎   ▎   (*(sep-1) != '\\')) /* and NOT escaped  */
    22 ▎   ▎   ▎   ▎   break;
    21 ▎   ▎   }
    20 ▎   ▎
    19 ▎   ▎   /*
    18 ▎   ▎   ▎* Limit the token to data between separators
    17 ▎   ▎   ▎*/
    16 ▎   ▎   token = str;
    15 ▎   ▎   if (*sep) {
    14 ▎   ▎   ▎   str = sep + 1;  /* start of command for next pass */
    13 ▎   ▎   ▎   *sep = '\0';
    12 ▎   ▎   }
    11 ▎   ▎   else
    10 ▎   ▎   ▎   str = sep;  /* no more commands for next pass */
     9 #ifdef DEBUG_PARSER
     8 ▎   ▎   printf ("token: \"%s\"\n", token);
     7 #endif
     6 ▎   ▎
     5 ▎   ▎   /* find macros in this token and replace them */
     4 ▎   ▎   process_macros (token, finaltoken);
     3 ▎   ▎
     2 ▎   ▎   /* Extract arguments */
     1 ▎   ▎   if ((argc = parse_line (finaltoken, argv)) == 0) {
  1343 ▎   ▎   ▎   rc = -1;    /* no command at all */
     1 ▎   ▎   ▎   continue;
     2 ▎   ▎   }
     3 ▎   ▎
     4 ▎   ▎   if (cmd_process(flag, argc, argv, &repeatable))
     5 ▎   ▎   ▎   rc = -1;
     6 ▎   ▎
     7 ▎   ▎   /* Did the user stop this? */
     8 ▎   ▎   if (had_ctrlc ())
     9 ▎   ▎   ▎   return -1;  /* if stopped then not repeatable */
    10 ▎   }
    20 ▎   return rc ? rc : repeatable;
    19 }
    18 #endif

clear_ctrlc();   这句代码实现如下:

   1 void clear_ctrlc(void)
  493 {
    1 ▎   ctrlc_was_pressed = 0;
    2 }

只是做了一个标记, 不知道 要干什么。

strcpy (cmdbuf, cmd);  这句是将 命令 拷贝到了 临时的变量中。

接下来就是 这一段代码了。

    40 ▎   while (*str) {
    39 ▎   ▎
    38 ▎   ▎   /*
    37 ▎   ▎   ▎* Find separator, or string end
    36 ▎   ▎   ▎* Allow simple escape of ';' by writing "\;"
    35 ▎   ▎   ▎*/
    34 ▎   ▎   for (inquotes = 0, sep = str; *sep; sep++) {
    33 ▎   ▎   ▎   if ((*sep=='\'') &&
    32 ▎   ▎   ▎   ▎   (*(sep-1) != '\\'))
    31 ▎   ▎   ▎   ▎   inquotes=!inquotes;
    30 ▎   ▎   ▎
    29 ▎   ▎   ▎   if (!inquotes &&
    28 ▎   ▎   ▎   ▎   (*sep == ';') &&    /* separator        */
    27 ▎   ▎   ▎   ▎   ( sep != str) &&    /* past string start    */
    26 ▎   ▎   ▎   ▎   (*(sep-1) != '\\')) /* and NOT escaped  */
    25 ▎   ▎   ▎   ▎   break;
    24 ▎   ▎   }
    23 ▎   ▎
    22 ▎   ▎   /*
    21 ▎   ▎   ▎* Limit the token to data between separators
    20 ▎   ▎   ▎*/
    19 ▎   ▎   token = str;
    18 ▎   ▎   if (*sep) {
    17 ▎   ▎   ▎   str = sep + 1;  /* start of command for next pass */
    16 ▎   ▎   ▎   *sep = '\0';
    15 ▎   ▎   }
    14 ▎   ▎   else
    13 ▎   ▎   ▎   str = sep;  /* no more commands for next pass */
    12 #ifdef DEBUG_PARSER
    11 ▎   ▎   printf ("token: \"%s\"\n", token);
    10 #endif
     9 ▎   ▎
     8 ▎   ▎   /* find macros in this token and replace them */
     7 ▎   ▎   process_macros (token, finaltoken);
     6 ▎   ▎
     5 ▎   ▎   /* Extract arguments */
     4 ▎   ▎   if ((argc = parse_line (finaltoken, argv)) == 0) {
     3 ▎   ▎   ▎   rc = -1;    /* no command at all */
     2 ▎   ▎   ▎   continue;
     1 ▎   ▎   }
  1346 ▎   ▎
     1 ▎   ▎   if (cmd_process(flag, argc, argv, &repeatable))
     2 ▎   ▎   ▎   rc = -1;
     3 ▎   ▎
     4 ▎   ▎   /* Did the user stop this? */
     5 ▎   ▎   if (had_ctrlc ())
     6 ▎   ▎   ▎   return -1;  /* if stopped then not repeatable */
     7 ▎   }

这段是在  处理 分号  ; ,我就不看了。

    34 ▎   ▎   for (inquotes = 0, sep = str; *sep; sep++) {
    33 ▎   ▎   ▎   if ((*sep=='\'') &&
    32 ▎   ▎   ▎   ▎   (*(sep-1) != '\\'))
    31 ▎   ▎   ▎   ▎   inquotes=!inquotes;
    30 ▎   ▎   ▎
    29 ▎   ▎   ▎   if (!inquotes &&
    28 ▎   ▎   ▎   ▎   (*sep == ';') &&    /* separator        */
    27 ▎   ▎   ▎   ▎   ( sep != str) &&    /* past string start    */
    26 ▎   ▎   ▎   ▎   (*(sep-1) != '\\')) /* and NOT escaped  */
    25 ▎   ▎   ▎   ▎   break;
    24 ▎   ▎   }

这几句 让 token 指向了 字符串

    27 ▎   ▎   token = str;
    26 ▎   ▎   if (*sep) {
    25 ▎   ▎   ▎   str = sep + 1;  /* start of command for next pass */
    24 ▎   ▎   ▎   *sep = '\0';
    23 ▎   ▎   }
    22 ▎   ▎   else
    21 ▎   ▎   ▎   str = sep;  /* no more commands for next pass */
    20 #ifdef DEBUG_PARSER
    19 ▎   ▎   printf ("token: \"%s\"\n", token);
    18 #endif

接下来就是 这段代码了。

     9 ▎   ▎   /* find macros in this token and replace them */
     8 ▎   ▎   process_macros (token, finaltoken);
     7 ▎   ▎
     6 ▎   ▎   /* Extract arguments */
     5 ▎   ▎   if ((argc = parse_line (finaltoken, argv)) == 0) {
     4 ▎   ▎   ▎   rc = -1;    /* no command at all */
     3 ▎   ▎   ▎   continue;
     2 ▎   ▎   }
     1 ▎   ▎
  1347 ▎   ▎   if (cmd_process(flag, argc, argv, &repeatable))
     1 ▎   ▎   ▎   rc = -1;
     2 ▎   ▎
     3 ▎   ▎   /* Did the user stop this? */
     4 ▎   ▎   if (had_ctrlc ())
     5 ▎   ▎   ▎   return -1;  /* if stopped then not repeatable */
     6 ▎   }
     7 ▎

process_macros (token, finaltoken);  这句是在 替换 命令中的宏, 不知道什么意思。

先跳过。

然后就是  if ((argc = parse_line (finaltoken, argv)) == 0)  这一句了。

看一下 pase_line() 函数。

    38 int parse_line (char *line, char *argv[])
    37 {
    36 ▎   int nargs = 0;
    35 ▎
    34 #ifdef DEBUG_PARSER
    33 ▎   printf ("parse_line: \"%s\"\n", line);
    32 #endif
E   31 ▎   while (nargs < CONFIG_SYS_MAXARGS) {     ■ Use of undeclared identifier 'CONFIG_SYS_MAXARGS'
    30 ▎   ▎
    29 ▎   ▎   /* skip any white space */
E   28 ▎   ▎   while (isblank(*line))     ■ Call to undeclared library function 'isblank' with type 'int (int)'; ISO C99 and later do not support implicit function declarations
    27 ▎   ▎   ▎   ++line;
    26 ▎   ▎
    25 ▎   ▎   if (*line == '\0') {    /* end of line, no more args    */
    24 ▎   ▎   ▎   argv[nargs] = NULL;
    23 #ifdef DEBUG_PARSER
    22 ▎   ▎   printf ("parse_line: nargs=%d\n", nargs);
    21 #endif
    20 ▎   ▎   ▎   return (nargs);
    19 ▎   ▎   }
    18 ▎   ▎
    17 ▎   ▎   argv[nargs++] = line;   /* begin of argument string */
    16 ▎   ▎
    15 ▎   ▎   /* find end of string */
    14 ▎   ▎   while (*line && !isblank(*line))
    13 ▎   ▎   ▎   ++line;
    12 ▎   ▎
    11 ▎   ▎   if (*line == '\0') {    /* end of line, no more args    */
    10 ▎   ▎   ▎   argv[nargs] = NULL;
     9 #ifdef DEBUG_PARSER
     8 ▎   ▎   printf ("parse_line: nargs=%d\n", nargs);
     7 #endif
     6 ▎   ▎   ▎   return (nargs);
     5 ▎   ▎   }
     4 ▎   ▎
     3 ▎   ▎   *line++ = '\0';     /* terminate current arg     */
     2 ▎   }
     1 ▎
E 1131 ▎   printf ("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS);     ■ Use of undeclared identifier 'CONFIG_SYS_MAXARGS'
     1 ▎
     2 #ifdef DEBUG_PARSER
     3 ▎   printf ("parse_line: nargs=%d\n", nargs);
     4 #endif
     5 ▎   return (nargs);
     6 }

这段代码是 值得我去学习的。

他将一句话 ,依据 空格 分割出单词。

每个单词 都放到了 argv[] 中。 argv[] 实际上是 字符指针数组。

  char *argv[CONFIG_SYS_MAXARGS + 1]; /*

这些个 字符串的真正的存放位置 是 finaltoken.  它实际上是 字符数组,是一个局部变量。

▎   char finaltoken[CONFIG_SYS_CBSIZE];

这个函数最后返回的是 参数的个数,即 argc .

注意 :由于字符存放在 字符数组中,所以是可以这样修改的。

这样做 实际上 是将 原本的 空格变成了 \0 。

继续往下分析代码。

然后就是 这句代码, if (cmd_process(flag, argc, argv, &repeatable)) 

代码如下:


   33 enum command_ret_t cmd_process(int flag, int argc, char * const argv[],
   32 ▎   ▎   ▎          int *repeatable)
   31 {
   30 ▎   enum command_ret_t rc = CMD_RET_SUCCESS;
   29 ▎   cmd_tbl_t *cmdtp;
   28 ▎
   27 ▎   /* Look up command in command table */
   26 ▎   cmdtp = find_cmd(argv[0]);
   25 ▎   if (cmdtp == NULL) {
   24 ▎   ▎   printf("Unknown command '%s' - try 'help'\n", argv[0]);
   23 ▎   ▎   return 1;
   22 ▎   }
   21 ▎
   20 ▎   /* found - check max args */
   19 ▎   if (argc > cmdtp->maxargs)
   18 ▎   ▎   rc = CMD_RET_USAGE;
   17 ▎
   16 #if defined(CONFIG_CMD_BOOTD)
   15 ▎   /* avoid "bootd" recursion */
   14 ▎   else if (cmdtp->cmd == do_bootd) {
   13 ▎   ▎   if (flag & CMD_FLAG_BOOTD) {
   12 ▎   ▎   ▎   puts("'bootd' recursion detected\n");
   11 ▎   ▎   ▎   rc = CMD_RET_FAILURE;
   10 ▎   ▎   } else {
    9 ▎   ▎   ▎   flag |= CMD_FLAG_BOOTD;
    8 ▎   ▎   }
    7 ▎   }
    6 #endif
    5 ▎
    4 ▎   /* If OK so far, then do the command */
    3 ▎   if (!rc) {
    2 ▎   ▎   rc = cmd_call(cmdtp, flag, argc, argv);
    1 ▎   ▎   *repeatable &= cmdtp->repeatable;
  545 ▎   }
    1 ▎   if (rc == CMD_RET_USAGE)
    2 ▎   ▎   rc = cmd_usage(cmdtp);
    3 ▎   return rc;
    4 }

enum command_ret_t rc = CMD_RET_SUCCESS;  这句代码中,  command_ret_t 代码如下:

    8 /*
    7 ▎* Error codes that commands return to cmd_process(). We use the standard 0
    6 ▎* and 1 for success and failure, but add one more case - failure with a
    5 ▎* request to call cmd_usage(). But the cmd_process() function handles
    4 ▎* CMD_RET_USAGE itself and after calling cmd_usage() it will return 1.
    3 ▎* This is just a convenience for commands to avoid them having to call
    2 ▎* cmd_usage() all over the place.
    1 ▎*/
  127 enum command_ret_t {
    1 ▎   CMD_RET_SUCCESS,    /* 0 = Success */
    2 ▎   CMD_RET_FAILURE,    /* 1 = Failure */
    3 ▎   CMD_RET_USAGE = -1, /* Failure, please report 'usage' error */
    4 };

cmd_tbl_t *cmdtp;  这句中, cmd_tbl_t 代码如下:

   16 struct cmd_tbl_s {
   15 ▎   char        *name;      /* Command Name         */
   14 ▎   int     maxargs;    /* maximum number of arguments  */
   13 ▎   int     repeatable; /* autorepeat allowed?      */
   12 ▎   ▎   ▎   ▎   ▎   /* Implementation function  */
   11 ▎   int     (*cmd)(struct cmd_tbl_s *, int, int, char * const []);
   10 ▎   char        *usage;     /* Usage message    (short) */
    9 #ifdef  CONFIG_SYS_LONGHELP
    8 ▎   char        *help;      /* Help  message    (long)  */
    7 #endif
    6 #ifdef CONFIG_AUTO_COMPLETE
    5 ▎   /* do auto completion on the arguments */
    4 ▎   int     (*complete)(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]);
    3 #endif
    2 };
    1
  62  typedef struct cmd_tbl_s    cmd_tbl_t;

这个结构体 , 就是 关于 uboot 的命令的结构体, 所有的Uboot的命令都存放在这个段中。

这个段 如下图所示。

接下来 , 我们就看一下, 关于 Uboot的命令的存储吧。

以 version 这个命令为例子。

代码如下:

   14
E  13 #include <common.h>     ■ 'common.h' file not found
   12 #include <command.h>
   11 #include <version.h>
   10 #include <linux/compiler.h>
    9
E   8 const char __weak version_string[] = U_BOOT_VERSION_STRING;     ■ Expected ';' after top level declarator (fix available)
    7
E   6 int do_version(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])     ■ Unknown type name 'cmd_tbl_t'
    5 {
E   4 ▎   printf("\n%s\n", version_string);     ■■ Use of undeclared identifier 'version_string'
    3 #ifdef CC_VERSION_STRING
    2 ▎   puts(CC_VERSION_STRING "\n");
    1 #endif
  37  #ifdef LD_VERSION_STRING
    1 ▎   puts(LD_VERSION_STRING "\n");
    2 #endif
    3 ▎
    4 ▎   return 0;
    5 }
    6
E   7 U_BOOT_CMD(     ■ Type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int (fix available)
E   8 ▎   version,    1,      1,  do_version,     ■ Expected identifier
    9 ▎   "print monitor, compiler and linker version",
   10 ▎   ""
   11 );

主要就是这个 U_BOOT_CMD 红顶以的发展开。

它的定义在这里。

    1 #define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
    2 ▎   U_BOOT_CMD_COMPLETE(name,maxargs,rep,cmd,usage,help,NULL)

完全展开之后, 是这样的。

#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)    cmd_tbl_t __u_boot_cmd_##name  __attribute__((unused, section(".u_boot_cmd"), aligned(4))) =  {#name, maxargs, rep, cmd, usage, _CMD_HELP(help) _CMD_COMPLETE(comp)}

实际上 就是 它 定义了 一个 cmd_tbl_t 的结构体,并给这个结构体赋上了值。

疑问: 那么是谁把这些 命令 拷贝到这个段上去的呢?

我觉得是这样。

首先  c 文件在编译的时候,会将, 这个结构体的说明,放到 .u_boot_cmd 段中,但是 命令的实现不放这里。

然后,不是有 连接脚本吗,连接脚本会将代码中 所有  有这些段的  放到   .u_boot_cmd 段中。

接下来继续往下分析代码。

接下来是这段代码

    1 ▎   /* Look up command in command table */
  519 ▎   cmdtp = find_cmd(argv[0]);
    1 ▎   if (cmdtp == NULL) {
    2 ▎   ▎   printf("Unknown command '%s' - try 'help'\n", argv[0]);
    3 ▎   ▎   return 1;
    4 ▎   }

cmdtp = find_cmd(argv[0]);   这句中 find_cmd 代码如下:

   20 cmd_tbl_t *find_cmd (const char *cmd)
   19 {
   18 ▎   int len = &__u_boot_cmd_end - &__u_boot_cmd_start;
   17 ▎   return find_cmd_tbl(cmd, &__u_boot_cmd_start, len);
   16 }

继续往下看。

   35 /***************************************************************************
   34 ▎* find command table entry for a command
   33 ▎*/
   32 cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len)
   31 {
   30 ▎   cmd_tbl_t *cmdtp;
   29 ▎   cmd_tbl_t *cmdtp_temp = table;  /*Init value */
   28 ▎   const char *p;
   27 ▎   int len;
   26 ▎   int n_found = 0;
   25 ▎
   24 ▎   if (!cmd)
   23 ▎   ▎   return NULL;
   22 ▎   /*
   21 ▎   ▎* Some commands allow length modifiers (like "cp.b");
   20 ▎   ▎* compare command name only until first dot.
   19 ▎   ▎*/
   18 ▎   len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);
   17 ▎
   16 ▎   for (cmdtp = table;
   15 ▎   ▎    cmdtp != table + table_len;
   14 ▎   ▎    cmdtp++) {
   13 ▎   ▎   if (strncmp (cmd, cmdtp->name, len) == 0) {
   12 ▎   ▎   ▎   if (len == strlen (cmdtp->name))
   11 ▎   ▎   ▎   ▎   return cmdtp;   /* full match */
   10 ▎   ▎   ▎
    9 ▎   ▎   ▎   cmdtp_temp = cmdtp; /* abbreviated command ? */
    8 ▎   ▎   ▎   n_found++;
    7 ▎   ▎   }
    6 ▎   }
    5 ▎   if (n_found == 1) {         /* exactly one match */
    4 ▎   ▎   return cmdtp_temp;
    3 ▎   }
    2 ▎
    1 ▎   return NULL;    /* not found or ambiguous command */
  136 }

这个函数就是 一条一条的找, 就不分析了,如果找到了,就返回这个结构体的头部。

继续分析代码,接下来就是 这段了。

    3 ▎   /* If OK so far, then do the command */
    2 ▎   if (!rc) {
    1 ▎   ▎   rc = cmd_call(cmdtp, flag, argc, argv);
  544 ▎   ▎   *repeatable &= cmdtp->repeatable;
    1 ▎   }

代码如下:

   19 ▎* Call a command function. This should be the only route in U-Boot to call
   18 ▎* a command, so that we can track whether we are waiting for input or
   17 ▎* executing a command.
   16 ▎*
   15 ▎* @param cmdtp     Pointer to the command to execute
   14 ▎* @param flag      Some flags normally 0 (see CMD_FLAG_.. above)
   13 ▎* @param argc      Number of arguments (arg 0 must be the command text)
   12 ▎* @param argv      Arguments
   11 ▎* @return 0 if command succeeded, else non-zero (CMD_RET_...)
   10 ▎*/
    9 static int cmd_call(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
    8 {
    7 ▎   int result;
    6 ▎
    5 ▎   result = (cmdtp->cmd)(cmdtp, flag, argc, argv);
    4 ▎   if (result)
    3 ▎   ▎   debug("Command failed, result=%d", result);
    2 ▎   return result;
    1 }
  511

实际上 就是 调用了 结构体中的函数指针

这样 ,如果 按下按键的流程就分析完了,

接下来去分析 如果是等到 倒计时为0 的时候,执行流程。

那么它会执行 这段代码


 

run_command_list(s, -1, 0);

代码如下:

    35 int run_command_list(const char *cmd, int len, int flag)
    34 {
    33 ▎   int need_buff = 1;
    32 ▎   char *buff = (char *)cmd;   /* cast away const */
    31 ▎   int rcode = 0;
    30 ▎
    29 ▎   if (len == -1) {
    28 ▎   ▎   len = strlen(cmd);
    27 #ifdef CONFIG_SYS_HUSH_PARSER
    26 ▎   ▎   /* hush will never change our string */
    25 ▎   ▎   need_buff = 0;
    24 #else
    23 ▎   ▎   /* the built-in parser will change our string if it sees \n */
    22 ▎   ▎   need_buff = strchr(cmd, '\n') != NULL;
    21 #endif
    20 ▎   }
    19 ▎   if (need_buff) {
    18 ▎   ▎   buff = malloc(len + 1);
    17 ▎   ▎   if (!buff)
    16 ▎   ▎   ▎   return 1;
    15 ▎   ▎   memcpy(buff, cmd, len);
    14 ▎   ▎   buff[len] = '\0';
    13 ▎   }
    12 #ifdef CONFIG_SYS_HUSH_PARSER
    11 ▎   rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON);
    10 #else
     9 ▎   /*
     8 ▎   ▎* This function will overwrite any \n it sees with a \0, which
     7 ▎   ▎* is why it can't work with a const char *. Here we are making
     6 ▎   ▎* using of internal knowledge of this function, to avoid always
     5 ▎   ▎* doing a malloc() which is actually required only in a case that
     4 ▎   ▎* is pretty rare.
     3 ▎   ▎*/
     2 ▎   rcode = builtin_run_command_list(buff, flag);
     1 ▎   if (need_buff)
  1461 ▎   ▎   free(buff);
     1 #endif
     2 ▎
     3 ▎   return rcode;
     4 }
     5

然后再去分析如果是 没有 按下按键的话,他是怎么解析命令的。

这个我就不分析了。这个老师也没有分析这块。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/942598.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

信号强劲,通信清晰:北斗三号多模对讲机TD70——专业通信解决方案

在边防海防等国家安全的关键领域&#xff0c;通信的稳定性和可靠性关乎着任务的成败和战士们的安全。北斗三号多模对讲机TD70&#xff0c;凭借其卓越的性能和全面的功能&#xff0c;成为了边防海防通信的利器&#xff0c;守护着国家安全的前沿哨兵。 一、三网融合&#xff0c;…

Arduino驱动DS18B20测量环境温度

DS18B20是一款高精度的单总线数字温度传感器&#xff0c;具体参数如下表所示&#xff1a; 参数名称 参数特性 测温范围 -55~125℃ 测量精度 在-10~85℃范围内的误差为0.5℃ 分辨率 9~12位数字信号&#xff0c;分辨率分别为0.5℃、0.25℃、0.125℃和0.0625℃ 通信方式 …

vector快慢指针+例题详解

1.快慢指针 例题 给定一个链表&#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;我们使用整数 pos 来表示链表尾连接到链表中的位置&#xff08;索引从…

C++--------效率和表示

C 效率和表示 效率 时间效率&#xff1a;在 C 中&#xff0c;不同的数据结构和算法有着各异的时间复杂度。例如&#xff0c;访问数组元素的时间复杂度是 O ( 1 ) O(1) O(1)&#xff0c;而遍历链表查找元素的时间复杂度最坏情况下是 O ( n ) O(n) O(n)。选择合适的算法与数据…

【Mac】终端改色-让用户名和主机名有颜色

效果图 配置zsh 1.打开终端&#xff0c;进入.zshrc配置 cd ~ vim .zshrc2.添加如下配置并保存 # 启用命令行颜色显示 export CLICOLOR1 ## 加载颜色支持 autoload -U colors && colors # 配置 zsh 提示符 PROMPT"%{$fg_bold[red]%}%n%{$reset_color%}%{$fg_bol…

模拟——郑益慧_笔记1_绪论

B站视频链接 模电是数电的基础&#xff1b;参考书&#xff1a; 模拟电子技术基础&#xff08;第四版&#xff09;华成英、童诗白主编&#xff0c;高等教育出版社&#xff1b;电子技术基础 模拟部分 康华光主编&#xff0c;高等教育出版社&#xff1b; 电子技术的发展史 电子…

YOLOv11模型改进-模块-引入多尺度大核注意力Multi-scale Large Kernel Attention

MLKA 的提出源于图像超分辨率任务的挑战性&#xff0c;该任务需重建低质量图像缺失的高频信息&#xff0c;但因 LR 与 HR 图像对应关系复杂&#xff0c;寻找像素相关性困难。此前模型扩展容量的方法增加了训练负担和数据收集成本&#xff0c;而采用的注意力机制无法同时获取局部…

【gym】给定的强化学习环境简介(二)

文章目录 环境介绍一 box2dbipedal_walkercar_dynamicscar_racinglunar_lander 二、 classic_controlacrobotCartPolecontinuous_mountain_carmountain_carpendulum 三、toy_textblackjackcliffwalkingfrozentaxi 四、mujocoAnt&#xff1a;HalfCheetah&#xff1a;Hopper&…

基于支付宝百宝箱构建自己的Agent的基本简易流程(Datawhale AI冬令营)

一&#xff0c;使用支付宝百宝箱 官网地址&#xff1a;百宝箱 (alipay.com) 二&#xff0c;应用构建 点击左上角的新建应用 然后按自己的需求选择对应的模块 以下是我的示例 点击确认之后&#xff0c;进入模型设置界面 按需设计便可以&#xff0c;以下是我的设计 当你写好…

攻防世界 - Web - Level 1 unseping

关注这个靶场的其它相关笔记&#xff1a;攻防世界&#xff08;XCTF&#xff09; —— 靶场笔记合集-CSDN博客 0x01&#xff1a;Write UP 本关是一个 PHP 代码审计关卡&#xff0c;考察的是 PHP 反序列化漏洞以及命令执行的一些绕过手段&#xff0c;下面笔者将带你一步步过关。…

Java进阶学习笔记|面向对象

第一章.类和对象 1.面向对象的介绍 1.面向过程:自己的事情自己干,代表语言C语言洗衣服:每一步自己要亲力亲为 -> 找个盆,放点水,找个搓衣板,搓搓搓 2.面向对象:自己的事情别人帮忙去干,代表语言Java语言 洗衣服:自己的事情别人干 -> 全自动洗衣机3.为啥要使用面向对…

前端性能优化之大文件上传

大文件上传是前端开发中常见的需求之一&#xff0c;特别是在需要处理较大的Excel表格数据、高清图片、视频或其他大型文件时。优化大文件上传不仅可以提升用户体验&#xff0c;还能有效减轻服务器负担。本文将深入探讨大文件上传的几种常见优化技术&#xff0c;包括文件切片与并…

数据结构之线性表之顺序表

定义&#xff1a; 由n&#xff08;n>0&#xff09;个数据特性相同的元素构成的有限序列称为线性表 简单来说n个相同数据类型的数据组wsw合在一起的这么一个集合就是一个线性表 线性表包括顺序表和链表 1. 顺序表&#xff08;我们所有的代码实现都用函数来封装&#xff09…

Matlab 和 R 语言的数组索引都是从 1 开始,并且是左闭右闭的

文章目录 一、前言二、主要内容三、小结 &#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一、前言 在早期的计算机科学中&#xff0c;数组索引从 1 开始是很常见的。例如&#xff0c;Fortran 和 Pascal 等编程语言也采用了从 1 开始的索引。 这种索引…

突发!!!GitLab停止为中国大陆、港澳地区提供服务,60天内需迁移账号否则将被删除

GitLab停止为中国大陆、香港和澳门地区提供服务&#xff0c;要求用户在60天内迁移账号&#xff0c;否则将被删除。这一事件即将引起广泛的关注和讨论。以下是对该事件的扩展信息&#xff1a; 1. 背景介绍&#xff1a;GitLab是一家全球知名的软件开发平台&#xff0c;提供代码托…

瑞吉外卖项目学习笔记(八)修改菜品信息、批量启售/停售菜品

瑞吉外卖项目学习笔记(一)准备工作、员工登录功能实现 瑞吉外卖项目学习笔记(二)Swagger、logback、表单校验和参数打印功能的实现 瑞吉外卖项目学习笔记(三)过滤器实现登录校验、添加员工、分页查询员工信息 瑞吉外卖项目学习笔记(四)TableField(fill FieldFill.INSERT)公共字…

C++小碗菜之五:关键字static

“一个人的命运啊&#xff0c;当然要靠自我奋斗&#xff0c;但也要考虑到历史的行程。” ——2009年4月23日在视察中国联合工程公司时的讲话 目录 ​编辑 前言 static在局部作用域中的作用 给出例子&#xff1a; 修改上面给出的例子&#xff1a; 为什么不使用全局变量…

开源云原生数据仓库ByConity ELT 的测试体验

ByConity 是分布式的云原生SQL数仓引擎&#xff0c;擅长交互式查询和即席查询&#xff0c;具有支持多表关联复杂查询、集群扩容无感、离线批数据和实时数据流统一汇总等特点。 ByConity 是一个云原生的、高性能的实时数据仓库&#xff0c;而 ELT&#xff08;Extract&#xff0c…

Linux -- 线程的优点、pthread 线程库

目录 线程的优点 pthread 线程库 前言 认识线程库 简单验证线程的独立栈空间 线程的优点 与进程之间的切换相比&#xff0c;线程之间的切换需要操作系统做的工作要少得多。 调度进程时&#xff0c;CPU 中有一个 cache&#xff08;缓存&#xff0c;提高运行效率&#xff0…

数字IC前端学习笔记:脉动阵列的设计方法学(四)

相关阅读 数字IC前端https://blog.csdn.net/weixin_45791458/category_12173698.html?spm1001.2014.3001.5482 引言 脉动结构&#xff08;也称为脉动阵列&#xff09;表示一种有节奏地计算并通过系统传输数据的处理单元(PEs)网络。这些处理单元有规律地泵入泵出数据以保持规则…