第1階段——uboot分析之查找命令run_command函數和命令定義過程(6)


本節主要學習,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命令。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM