思路是命令是一个结构体,结构体中有name 和function,name表示command_name,function表示如何执行和处理这个command_name。具体的结构体形式是这样的:
struct cmd_tbl_s {
char *name;
int maxargs;
int repeatable; 这里指的是执行完命令之后如果再次摁下回车能不能自动执行。
int (*cmd)(struct cmd_tbl_s *, int, int, char *[]);这个是实现函数的函数指针
char *usage; 指的是短的帮助信息,例如输入help命令后,出现的所有命令的简单的介绍
#ifdef CFG_LONGHELP
char *help; 指的是长的帮助信息,例如输入help md就会显示出来md命令详细的信息。
#endif
#ifdef CONFIG_AUTO_COMPLETE
int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
下面分析run_command如何实现:
run_command函数是在command/main.c中
---------------------------run_command函数开始-------------------------------------
int run_command (const char *cmd, int flag)
{
cmd_tbl_t *cmdtp;
char cmdbuf[CFG_CBSIZE];
char *token;
char *sep;
char finaltoken[CFG_CBSIZE];
char *str = cmdbuf;
char *argv[CFG_MAXARGS + 1];
int argc, inquotes;
int repeatable = 1;
int rc = 0;
#ifdef DEBUG_PARSER
printf ("[RUN_COMMAND] cmd[%p]=\"", cmd);
puts (cmd ? cmd : "NULL");
puts ("\"\n");
#endif
clear_ctrlc();
if (!cmd || !*cmd) {
return -1;
}
if (strlen(cmd) >= CFG_CBSIZE) {
puts ("## Command too long!\n");
return -1;
}
strcpy (cmdbuf, cmd);
#ifdef DEBUG_PARSER
printf ("[PROCESS_SEPARATORS] %s\n", cmd);
#endif
while (*str) {这里实现把命令解析出来,一个一个提取出来
for (inquotes = 0, sep = str; *sep; sep++) {
if ((*sep=='\'') &&
(*(sep-1) != '\\'))
inquotes=!inquotes;
if (!inquotes &&
(*sep == ';') && 这里分号表示如果有两个命令的话用分号隔开,也就是说处理了输入多个命令的情况。
( sep != str) &&
(*(sep-1) != '\\'))
break;
}
token = str;
if (*sep) {
str = sep + 1;
*sep = '\0';
}
else
str = sep;
#ifdef DEBUG_PARSER
printf ("token: \"%s\"\n", token);
#endif
process_macros (token, finaltoken);处理宏,例如当我们用网卡或者usb下载文件到内存中时候会临时生成一些环境变量或者宏,
if ((argc = parse_line (finaltoken, argv)) == 0) {解析输入的命令文字,比如md.w 0,成为一个字符数组如argc[0]="md.w" argc[1]="0",英文是extract arguments,提取参数。
rc = -1;
continue;
}
if ((cmdtp = find_cmd(argv[0])) == NULL) {这里是查找命令,一般argv[0]中存放的是命令,后面的几个是命令的参数,
printf ("Unknown command '%s' - try 'help'\n", argv[0]);
rc = -1;
continue;
}
if (argc > cmdtp->maxargs) {
printf ("Usage:\n%s\n", cmdtp->usage);
rc = -1;
continue;
}
#if (CONFIG_COMMANDS & CFG_CMD_BOOTD)
if (cmdtp->cmd == do_bootd) {
#ifdef DEBUG_PARSER
printf ("[%s]\n", finaltoken);
#endif
if (flag & CMD_FLAG_BOOTD) {
puts ("'bootd' recursion detected\n");
rc = -1;
continue;
} else {
flag |= CMD_FLAG_BOOTD;
}
}
#endif
if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
rc = -1;
}
repeatable &= cmdtp->repeatable;
if (had_ctrlc ())
return 0;
}
return rc ? rc : repeatable;
}
---------------------------run_command函数结束-------------------------------------
---------------------------find_cmd函数开始---------------------------------------
* find command table entry for a command
*/
cmd_tbl_t *find_cmd (const char *cmd)
{
cmd_tbl_t *cmdtp;
cmd_tbl_t *cmdtp_temp = &__u_boot_cmd_start;
const char *p;
int len;
int n_found = 0;
len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);
for (cmdtp = &__u_boot_cmd_start;这两个参数在文件中是没有的,但是查看链接脚本有这样几句话_u_boot_cmd_start = .;
.u_boot_cmd:{ *(.u_boot_cmd) }
_u_boot_cmd_end = .;
是一个地址,这个循环实现从命令开始存放地址到结束地址遍历命令进行查找。所有的命令全都会被集中到u_boot_cmd这个区域里面来,具体的实现办法是:#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd"))),这句话,同理可以找到如何将所有文件的代码段放到.text的, 关键的是结构体cmd_tbl_t,也即cmd_table,t表示结构体,有这个属性attribute。
cmdtp != &__u_boot_cmd_end;
cmdtp++) {
if (strncmp (cmd, cmdtp->name, len) == 0) {
if (len == strlen (cmdtp->name))
return cmdtp;
cmdtp_temp = cmdtp;
n_found++;
}
}
if (n_found == 1) {
return cmdtp_temp;
}
return NULL;
}
---------------------------find_cmd函数开始---------------------------------------
例如U_BOOT_CMD命令的定义
U_BOOT_CMD(
bootm, CFG_MAXARGS, 1, do_bootm,
"bootm - boot application image from memory\n",
"[addr [arg ...]]\n - boot application image stored in memory\n"
"\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"
"\t'arg' can be the address of an initrd image\n"
#ifdef CONFIG_OF_FLAT_TREE
"\tWhen booting a Linux kernel which requires a flat device-tree\n"
"\ta third argument is required which is the address of the of the\n"
"\tdevice-tree blob. To boot that kernel without an initrd image,\n"
"\tuse a '-' for the second argument. If you do not pass a third\n"
"\ta bd_info struct will be passed instead\n"
#endif
);按照结构体中去拆开就是 bootm是name,最大参数个数,可以repeat,具体实现函数指针,简要帮助信息,详细帮助信息。
分析如何实现添加一个hello命令,然后输出helloworld。