这一回来分析一下uboot中命令行的解释, 所以我们直接从main_loop开始分析.
1. 从汇编阶段进入c阶段的第一个函数是start_xxx,如/lib_unicore/board.c中的start_unicoreboot.前半部分调用了若干初始化函数来进行部分硬件的初始化, 并设置一下环境. 这里不是我们本回要讨论的所以一一跳过.在start_xxx的最后调用了main_loop(), 而且还是被一个死循环死死圈住了;
|
for
(;;){
main_loop ();
}
|
2. 现在我们已经进入了这个圈套那么只能往里钻了. common/main.c文件中的main_loop().
|
//从环境变量里找bootdelay
s =getenv
("bootdelay");
//如果环境变量中有定义,将delay数值赋值给变量bootdelay, 否则赋值CONFIG_BOOTDELAY.
bootdelay = s?
(int)simple_strtol(s,NULL,
10): CONFIG_BOOTDELAY;
//获取bootcmd
s =getenv
("bootcmd");
debug ("###main_loop: bootcmd=\"%s\"\n", s
? s:
"<UNDEFINED>");
//如果bootdelay和bootcmd都存在,那么在abortboot()中延时bootdelay,调用run_command执行在bootcmd中定义的默认命令
if(bootdelay
>= 0
&& s &&
!abortboot (bootdelay)){
run_command (s,0);
}
|
上面代码主要是对自启动部分的描述, 其中命令执行部分是在run_command中进行的, 这个等在后文分析.如果我们没有bootcmd或者在延时中被打断, 那么代码会继续向下执行
|
//下面for循环是uboot处理终端命令的主循环, 这时等待我们从键盘中输入一行命令,真正的人机交互从这里开始
for (;;){
//CFG_PROMPT中定义的就是我们在命令行显示的题头部分,如"sep611=>",
//在readline中首先先显示CFG_PROMPT定义的字符串, 然后等待键盘输入
//每次从终端读入一个字符, 先判断是否是正常字符(ctrl+c, 回车等属于非正常字符)
//对与正常字符那么将其存入console_uffer中, 并在终端回显
len = readline
(CFG_PROMPT);
//这时我们已经收到屏幕上的一行输入, 而且命令已经存console_uffer
flag = 0;
if (len> 0)
strcpy
(lastcommand, console_buffer);
else if(len
==0)
flag|= CMD_FLAG_REPEAT;
//命令从console_buffer搬运到lastcommand中, 乾坤大挪移
rc = run_command
(lastcommand, flag);
|
3.read_line()读取到命令行后会调用common/main.c文件中的run_command().现在是分析run_command()的时候了,不管是从环境变量还是终端获得命令,都是由run_command()来处理的.
|
//下面if语句判断命令是否太长,还是避免有些变态的家伙输入了超过CFG_CBSIZE个字符的命令
if (strlen(cmd)>=
CFG_CBSIZE) {
puts ("## Commandtoo long!\n");
return
-1;
}
//这个世界到处体现了自私,对命令又进行了一次拷贝, 安全吗?
strcpy(cmdbuf, cmd);
|
中场休息, 下面要进入处理cmdbuf的循环中了, 长征马上开始
|
//str就是指向cmdbuf的指针, 其实这些东西都是针对的刚才那行命令
while(*str){
//注释很清楚,找到;作为命令结束符, 因为多个命令可以一次输入, 并以;分割. 忽略'\;'
for (inquotes
= 0,sep
= str; *sep;sep++){
if
((*sep=='\'')&&
(*(sep-1)!=
'\\'))
inquotes=!inquotes;
if
(!inquotes
&&
(*sep==
';')&&
(sep
!= str)&&
(*(sep-1)!=
'\\'))
break;
}
//如果上面for循环找到一条以';'结束的命令,那么sep指向命令末尾
token = str;
if (*sep){
str= sep
+ 1;
*sep
='\0';
}
else
str= sep;
process_macros (token,finaltoken);
if ((argc= parse_line
(finaltoken, argv))== 0){
rc=
-1;
continue;
}
if ((cmdtp= find_cmd(argv[0]))==
NULL){
printf
("Unknowncommand '%s' - try 'help'\n", argv[0]);
rc=
-1;
continue;
}
|
4.就此打断一下, 我们要分析一下find_cmd了, 不能再跳过了.find_cmd()在.u_boot_cmd段中寻找该命令的cmd_tbl_t结构, 找到后返回该结构.该命令的结构是通过定义在include/command.h中的宏定义U_BOOT_CMD登记进.u_boot_cmd段中的.
|
//先研究一下这个神奇的U_BOOT_CMD, 定义如下, 其中cmd_tbl_t是命令的结构体类型,这里不罗列了,成员包括命令名字, 参数的最大个数, 使用说明等等.Struct_Section定义为
#defineStruct_Section
__attribute__ ((unused,section
(".u_boot_cmd")))
//所以,U_BOOT_CMD将会创建一个类型为cmd_tbl_t的结构体对象, 名字为__u_boot_cmd_name, 存储在__.u_boot_cmd区段中
#defineU_BOOT_CMD(name,maxargs,rep,cmd,usage,help)\
cmd_tbl_t__u_boot_cmd_##name Struct_Section
= {#name,maxargs, rep, cmd,usage,
help}
//这个for循环是find_cmd()的核心,__u_boot_cmd_start在lds连接脚本中, 指定的是在__.u_boot_cmd的开始地址,__u_boot_cmd_end就是相对应的结束地址.注意:for中的cmdtp++每次加的不是1二是sizeof(cmd_tbl_t).所以在这个循环中将会遍历在__.u_boot_cmd段中的所有命令,并查找与本次命令向对应的那个命令的结构体.
for(cmdtp
= &__u_boot_cmd_start;
cmdtp!=
&__u_boot_cmd_end;
cmdtp++){
if (strncmp
(cmd,cmdtp->name, len)==
0){
if
(len==
strlen (cmdtp->name))
returncmdtp;
cmdtp_temp= cmdtp;
n_found++;
}
}
|
5. 刚才我们在长征的半路翻越了一座雪山, 现在继续回到while循环中
|
if (argc
> cmdtp->maxargs)
{
printf
("Usage:\n%s\n", cmdtp->usage);
rc=
-1;
continue;
}
//这是啥, 我不管它了, 掠过这条小溪
#if defined(CONFIG_CMD_BOOTD)
if (cmdtp->cmd
==do_bootd)
{
if
(flag &CMD_FLAG_BOOTD)
{
puts("'bootd' recursion detected\n");
rc=
-1;
continue;
}
else{
flag|= CMD_FLAG_BOOTD;
}
}
#endif
//长征马上结束,胜利就在眼前! 调用结构体中注册的cmd函数, 何时注册的呢?上面不远处介绍的U_BOOT_CMD!
if ((cmdtp->cmd)
(cmdtp,flag, argc, argv)!=
0){
rc=
-1;
}
repeatable &= cmdtp->repeatable;
if (had_ctrlc
())
return
-1;
}
本文转至: http://blog.sina.com.cn/s/blog_4cabc5460100ptui.html
|