res loop hello 字符串 获取 保存 style 调用
本节主要学习, run_command 函数命令查找过程, 命令生成过程
1.run_command 函数命令查找过程分析:
在 u-boot 界面中 (main_loop(); 位于 u-boot-1.1.6/common/main.c ):
a 输入命令字符串
b 将命令字符串代入函数 run_command()
c run_command(): 判断命令字符串, 在 argv[0] 里保存命令名,并调用 find_cmd(argv[0])) 函数查找内存中该命令结构体, 判断各个参数, 执行命令等
d find_cmd(argv[0])): 查找. u_boot_cmd 命令段中所有命令是否与 argv[0] 这个命令名字相等,
1.1 首先查看 run_command() 函数分析, 如何判断判断命令:
- int run_command(const char * cmd, int flag) //*cmd:入口字符串命令 flag:参数
- {
- cmd_tbl_t * cmdtp;
- char cmdbuf[CFG_CBSIZE]; //cmdbuf:用来备份的
- char * str = cmdbuf; //*str指向备份的入口命令
- ...
- if (!cmd || !*cmd) { //先对字符串命令cmd的有效性进行检测,判断该命令是否为空
- return - 1;
- /* empty command */
- }
- if (strlen(cmd) >= CFG_CBSIZE) { //判断字符串命令的长度是否在CFG_CBSIZE(256)范围之内
- puts("## Command too long!\n");
- return - 1;
- }
- strcpy(cmdbuf, cmd); //备份入口命令cmd到cmdbuf
- while ( * str) { //str指向cmdbuf备份命令,循环判断字符串命令有几个
- /*因为uboot允许一次输入多个命令,以下就是分析 是否有多个命令,*/
- /* 比如在uboot界面输入"print;md.w 0"回车后,先打印出环境参数后,后打印地址0里存放的数据。*/
- for (inquotes = 0, sep = str; * sep; sep++) //sep指向当前命令str的开头,一直for寻找当前命令结尾处
- {
- if (( * sep == '\'') && //对"\"解析,分割成多个命令
- ( * (sep - 1) != '\\')) inquotes = !inquotes;
- if (!inquotes && ( * sep == ';') && //判断当前等于";"
- (sep != str) && //且当前指向的位置不是命令的开头,
- ( * (sep - 1) != '\\')) break; //停止本次for循环,sep指向当前这个命令结尾处
- }
- token = str; //token指向当前命令的开头
- if ( * sep) { //将当前";"分割符处替换成'\0'空字符
- str = sep + 1; //str命令指向下个命令,若下个命令为空,退出while (*str)循环.
- * sep = '\0'; //将当前命令结尾";"处替换成'\0'空字符
- } else str = sep; //如果没有命令了,就指向当前命令底部
- process_macros(token, finaltoken); //token=当前命令开头,将当前命令中的宏替换掉,
- //例如命令"nand write .yaffs 30000000 0X00260000 $(kernelsize)":其中$(kernelsize)就是宏,这里将替换成文件大小长度
- if ((argc = parse_line(finaltoken, argv)) == 0)
- //argc等于参数的个数。
- //parse_line函数:解析当前命令用argv数组保存并返回当前命令参数个数,例如"md.w 0"->argv[0]="md.w"(保存命令), argv[1]="0"(保存参数)
- {
- rc = -1;
- /* no command at all */
- continue;
- }
- if ((cmdtp = find_cmd(argv[0])) == NULL) {
- /* find_cmd(argv[0])) :查找.u_boot_cmd段中是否有这个命令argv[0]名字,若有的话返回这个命令的结构体,否则返回NULL。*/
- /* cmdtp: 指向argv[0]命令名字的结构体. */
- printf("Unknown command '%s' - try 'help'\n", argv[0]); //输出提示,未找到命令
- rc = -1;
- /* give up after bad command */
- continue;
- }
- /*其中find_cmd()返回值和cmdtp都是一个cmd_tbl_s型结构体,其中成员如下所示:
- struct cmd_tbl_s {
- char *name; //命令的名字
- int maxargs; //命令后带的最大参数个数
- int repeatable; //定义命令是否可重复,例如:在uboot界面输入"md.w 0"打印后,再次敲回车键继续运行该命令.
- int (*cmd)(struct cmd_tbl_s *, int, int, char *[]); //函数指针,用于命令执行时需要调用什么函数
- char *usage; // 该命令所对应得较短的使用说明,例如输入"help",每行命令后面都跟着较短的使用说明
- #ifdef CFG_LONGHELP
- char *help; // 该命令所对应得较详细的使用说明,例如输入"help md",会打印出该命令详细的使用说明
- #endif
- };
- */
- if (argc > cmdtp - >maxargs) { //检查当前命令的参数个数argc是否在最大参数个数范围内
- printf("Usage:\n%s\n", cmdtp - >usage);
- rc = -1;
- continue;
- }
- if ((cmdtp - >cmd)(cmdtp, flag, argc, argv) != 0) { //cmdtp:当前命令结构体,判断cmdtp->cmd执行命令有没有函数
- rc = -1; //执行命令
- }
- repeatable &= cmdtp - >repeatable; //设置命令重复执行标志
- if (had_ctrlc()) //检查是否有ctrl+c按键按下,如果有的话,取消当前命令执行。
- return 0;
- /* if stopped then not repeatable */
- }
- return rc ? rc: repeatable;
- }
1.2 进入 find_cmd() 函数分析, 如何查找命令:
- cmd_tbl_t * find_cmd(const char * cmd) //*cmd:字符串命令名
- {
- cmd_tbl_t * cmdtp;
- cmd_tbl_t * cmdtp_temp = &__u_boot_cmd_start;
- /*Init value */
- const char * p;
- int len;
- int n_found = 0;
- len = ((p = strchr(cmd, '.')) == NULL) ? strlen(cmd) : (p - cmd); //查找'.'这个字符,用来获得当前字符串命令长度len
- for (cmdtp = &__u_boot_cmd_start; cmdtp != &__u_boot_cmd_end; cmdtp++)
- //上面的__u_boot_cmd_start和__u_boot_cmd_end在board/100ask24x0/u-boot.lds连接脚本里已定义.
- //所以for循环是将*cmd入口参数从所有命令起始段找到命令结束段,直到找到为止。
- {
- if (strncmp(cmd, cmdtp - >name, len) == 0) { //比较len长度的*cmd和cmdtp->name,若相等表示找到一样的实际命令。
- if (len == strlen(cmdtp - >name)) //再次获取实际命令的长度,判断是否和当前命令的长度一样。
- return cmdtp; //已找到,返回实际命令的结构体
- cmdtp_temp = cmdtp;
- /* abbreviated command ? */
- n_found++;
- }
- }
- if (n_found == 1) {
- /* exactly one match */
- return cmdtp_temp;
- }
- return NULL;
- /* not found or ambiguous command */
- }
2 命令定义分析, 分析命令是怎么定义出来的:
例如:"boodcmd=nand read.jffs2 0x30007FC0 kernel;bootm 0x30007FC0" 中 bootm 命令 (定义过程, 如何定义的) 分析:
2.1"bootm 0x30007FC0" 是使用 bootm 命令, 参数为 0x30007FC0, 该命令位于 Cmd_bootm.C
先搜索 bootm 命令, 位于./common/Cmd_bootm.C (命令文件都存在 common 文件里,Cmd_bootm.C 就是定义 bootm 命令的文件)
进入./common/Cmd_bootm.C:
其中执行 bootm 这个命令时所对应的函数就是:
- int do_bootm(cmd_tbl_t * cmdtp, int flag, int argc, char * argv[]);
这个 do_bootm 函数等于 bootm 命令里的宏 U_BOOT_CMD->cmd
do_bootm() 成员:
- cmd_tbl_t *cmdtp : 指向命令段里的bootm命令结构体
- flag : 参数
- argc : 参数个数,例如"bootm 0x30007FC0",那么argc=2。
- argv : 存参数的数组,共argc个.例如"bootm 0x30007FC0",那么argv[0]="bootm",argv[1]="0x30007FC0".
它在 U_BOOT_CMD 宏里, 是因为每个命令都是通过 U_BOOT_CMD 宏定义调用的, 如下:
- U_BOOT_CMD( //U_BOOT_CMD宏里有bootm成员,CFG_MAXARGS成员等
- bootm, CFG_MAXARGS, 1, do_bootm, // do_bootm是一个函数名
- "bootm - boot application image from memory\n",
- "[addr [arg ...]]\n - boot application image stored in memory\n" //usage成员,较短的帮助说明
- "\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" //help成员,详细的帮助说明
- #endif
- );
2.2 再来看看 U_BOOT_CMD 宏是怎么定义的,宏 U_BOOT_CMD 在./include/command.h 定义, 如下所示:
- #define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
- cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
这里定义了全局变量: U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) 等于 "cmd_tbl_t __u_boot_cmd_##name Struct_Section={#name, maxargs, rep, cmd, usage, help}"
2.3 其中 U_BOOT_CMD 宏各个参数意义如下:
cmd_tbl_t: 定义__u_boot_cmd_bootm 的类型为 cmd_tbl_t 结构体
##name: 指向 name, 其中 name 指向第 2.1 节里面 U_BOOT_CMD 宏里的第一个成员 bootm.
Struct_Section : 保存命令的段位置参数, 在./include/Command.h 中定义, "#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))",section 表示强制将 段属性设为. u_boot_cmd 命令段
#name: U_BOOT_CMD 宏里第一个成员, 命令名字, 等于 "bootm"
maxargs: 最大参数个数, 等于 CFG_MAXARGS
rep: [repeat] 是否支持重复, 等于 1, 表示支持
cmd: 执行命令后对应的函数指针, 执行命令时就会使用该指针 , 在 2.1 节里,
usage: 保存字符串, 用于较短的帮助说明, 等于 "bootm - boot application image from memory\n"
help: 用于详细的帮助说明,等于 U_BOOT_CMD 宏里 usage 成员后剩下的几行字符串 (它们之间没有加逗号,所以那几行字符串都是连接在一起的).
2.4 所以对于 bootm 命令, 最终扩展开为: cmd_tbl_t __u_boot_cmd_bootm __attribute__ ((unused,section (".u_boot_cmd"))) /
={bootm, CFG_MAXARGS, 1, do_bootm," 字符串 1","" 字符串 2"}
所有 uboot 中命令定义都是通过 U_BOOT_CMD 宏 保存在. u_boot_cmd 段中, 通过 run_command() 函数调用.
接下来学习怎么仿照 bootm 命令来制作 hello 命令。
第 1 阶段——uboot 查找命令 run_command 函数和命令定义分析 (6)
来源: http://www.bubuko.com/infodetail-2251959.html