一、u-boot对命令行的命令解析过程分析

u-boot的主线运行过程中,在board_init_r()函数进行u-boot初始化过程的最后一个过程则是:运行主函数循环。该函数名为

run_main_loop()。定义如下:

static int run_main_loop(void)
{
#ifdef CONFIG_SANDBOX
	sandbox_main_loop_init();
#endif
	/* main_loop() can return to retry autoboot, if so just run it again */
	for (;;)
		main_loop();    //此处是一个死循环调用main_loop()函数
	return 0;
}

main_loop()函数定义在文件./common/main.c中,如下所示:

/* We come here after U-Boot is initialised and ready to process commands */
void main_loop(void)
{
	const char *s;

	bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");

#ifndef CONFIG_SYS_GENERIC_BOARD
	puts("Warning: Your board does not use generic board. Please read\n");
	puts("doc/README.generic-board and take action. Boards not\n");
	puts("upgraded by the late 2014 may break or be removed.\n");
#endif

#ifdef CONFIG_VERSION_VARIABLE
	setenv("ver", version_string);  /* set version variable */
#endif /* CONFIG_VERSION_VARIABLE */
	//初始化hush shell相关的变量
	cli_init();
	//获取环境变量preboot的内容,preboot是一些预启动命令。
	run_preboot_environment_command();

#if defined(CONFIG_UPDATE_TFTP)
	update_tftp(0UL, NULL, NULL);
#endif /* CONFIG_UPDATE_TFTP */

	s = bootdelay_process();
	if (cli_process_fdt(&s))
		cli_secure_boot_cmd(s);
	
	//该函数检测在u-boot启动过程中的【倒计时过程】是否结束?【倒计时】结束之前是否被打断
	autoboot_command(s);
	//该函数是u-boot的命令行处理函数,当在终端输入命令时,就是依靠该函数来处理
	cli_loop();
}

由以上代码可见,有两个重要函数:

(1)autoboot_command(s)函数:用于检测u-boot启动过程中的【倒计时过程】是否结束。【倒计时】结束之前是否被打断。

(2)cli_loop()函数:该函数是u-boot的命令行处理函数,当我们在u-boot命令行输入命令时,则依靠该函数来处理。

接下来,就来分析以下这两个函数:

autoboot_command()函数定义如下:

void autoboot_command(const char *s)
{
	debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");

	if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {
#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
		int prev = disable_ctrlc(1);	/* disable Control C checking */
#endif

		run_command_list(s, -1, 0);

#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
		disable_ctrlc(prev);	/* restore Control C checking */
#endif
	}

#ifdef CONFIG_MENUKEY
	if (menukey == CONFIG_MENUKEY) {
		s = getenv("menucmd");
		if (s)
			run_command_list(s, -1, 0);
	}
#endif /* CONFIG_MENUKEY */
}

cli_loop()函数的定义如下:

void cli_loop(void)
{
#ifdef CONFIG_SYS_HUSH_PARSER
	parse_file_outer();
	/* This point is never reached */
	for (;;);
#else
	cli_simple_loop();
#endif /*CONFIG_SYS_HUSH_PARSER*/
}

parse_file_outer()函数定义如下:

int parse_file_outer(void)
#endif
{
	int rcode;
	struct in_str input;
#ifndef __U_BOOT__
	setup_file_in_str(&input, f); //用于初始化变量input的成员变量值
#else
	setup_file_in_str(&input);
#endif
	//负责接收命令行输入,然后解析并执行相应的命令	【重要函数】
	rcode = parse_stream_outer(&input, FLAG_PARSE_SEMICOLON);
	return rcode;
}

parse_stream_outer()函数定义如下:

/* most recursion does not come through here, the exeception is
 * from builtin_source() */
static int parse_stream_outer(struct in_str *inp, int flag)
{
	struct p_context ctx;
	o_string temp=NULL_O_STRING;
	int rcode;
#ifdef __U_BOOT__
	int code = 1;
#endif
	do {
		ctx.type = flag;
		initialize_context(&ctx);
		update_ifs_map();
		if (!(flag & FLAG_PARSE_SEMICOLON) || (flag & FLAG_REPARSING)) mapset((uchar *)";$&|", 0);
		inp->promptmode=1;
		rcode = parse_stream(&temp, &ctx, inp,
				     flag & FLAG_CONT_ON_NEWLINE ? -1 : '\n');
#ifdef __U_BOOT__
		if (rcode == 1) flag_repeat = 0;
#endif
		if (rcode != 1 && ctx.old_flag != 0) {
			syntax();
#ifdef __U_BOOT__
			flag_repeat = 0;
#endif
		}
		if (rcode != 1 && ctx.old_flag == 0) {
			done_word(&temp, &ctx);
			done_pipe(&ctx,PIPE_SEQ);
#ifndef __U_BOOT__
			run_list(ctx.list_head);
#else
			code = run_list(ctx.list_head);
			if (code == -2) {	/* exit */
				b_free(&temp);
				code = 0;
				/* XXX hackish way to not allow exit from main loop */
				if (inp->peek == file_peek) {
					printf("exit not allowed from main input shell.\n");
					continue;
				}
				break;
			}
			if (code == -1)
			    flag_repeat = 0;
#endif
		} else {
			if (ctx.old_flag != 0) {
				free(ctx.stack);
				b_reset(&temp);
			}
#ifdef __U_BOOT__
			if (inp->__promptme == 0) printf("<INTERRUPT>\n");
			inp->__promptme = 1;
#endif
			temp.nonnull = 0;
			temp.quote = 0;
			inp->p = NULL;
			free_pipe_list(ctx.list_head,0);
		}
		b_free(&temp);
	/* loop on syntax errors, return on EOF */
	} while (rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP) &&
		(inp->peek != static_peek || b_peek(inp)));
#ifndef __U_BOOT__
	return 0;
#else
	return (code != 0) ? 1 : 0;
#endif /* __U_BOOT__ */
}

在parse_stream_outer()函数中,有个大的do-while用于处理输入命令。在其中会调用parse_stream函数进行命令解析,调用run_list函数【该函数调用逻辑特别长】来执行解析出来的命令操作。经过一系列函数调用操作后,run_list()函数将会调用到cmd_process()函数来处理命令。cmd_process函数定义如下:

enum command_ret_t cmd_process(int flag, int argc, char * const argv[],
			       int *repeatable, ulong *ticks)
{
	enum command_ret_t rc = CMD_RET_SUCCESS;
	cmd_tbl_t *cmdtp;

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

	/* found - check max args */
	if (argc > cmdtp->maxargs)
		rc = CMD_RET_USAGE;

#if defined(CONFIG_CMD_BOOTD)
	/* avoid "bootd" recursion */
	else if (cmdtp->cmd == do_bootd) {
		if (flag & CMD_FLAG_BOOTD) {
			puts("'bootd' recursion detected\n");
			rc = CMD_RET_FAILURE;
		} else {
			flag |= CMD_FLAG_BOOTD;
		}
	}
#endif

	/* If OK so far, then do the command */
	if (!rc) {
		if (ticks)
			*ticks = get_timer(0);
		rc = cmd_call(cmdtp, flag, argc, argv); //执行命令操作
		if (ticks)
			*ticks = get_timer(*ticks);
		*repeatable &= cmdtp->repeatable;
	}
	if (rc == CMD_RET_USAGE)
		rc = cmd_usage(cmdtp);
	return rc;
}

当cmd_process()函数中找到了命令后,会调用cmd_call()函数来执行命令了,该函数定义如下:

static int cmd_call(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
	int result;
	//调用注册的具体的命令执行函数来执行命令操作
	result = (cmdtp->cmd)(cmdtp, flag, argc, argv);
	if (result)
		debug("Command failed, result=%d\n", result);
	return result;
}

在以上代码中:

result = (cmdtp->cmd)(cmdtp, flag, argc, argv);

则是调用注册的具体命令执行函数来执行命令操作了。


二、总结

在开发中,如果使用u-boot作为硬件板卡的引导加载程序,那么肯定会接触到其命令行,u-boot的命令行非常好用,但u-boot对输入命令的解析过程较为复杂,u-boot对命令的解析过程大致如下图所示【从源码角度分析】:

uos 如何执行jnlp_cmd_process

(1)在u-boot启动主线中,被board_init_r()函数调用的run_main_loop()函数承载了命令行解析的顶层入口。在run_main_loop()函数中会调用main_loop()函数,命令的解析则在该函数中展开。

(2)在u-boot启动过程中,【倒计时过程】是由autoboot_command(s)函数完成。

(3)cmd_process()函数是核心的命令解析函数。

(4)当找到对应的命令后,则调用cmd_call()函数来执行对应命令下的具体操作。