run_main_loop 到 cmd_process处理说明三

本文详细解析了U-Boot启动过程中cmd_process函数如何处理接收到的命令,包括函数调用链、命令查找、cmd_call的执行逻辑以及错误处理。

一.  run_main_loop 到 cmd_process处理过程

之前文章了解了 uboot的命令格式组成。本文简单分析下 cmd_process函数对 uboot命令的处理过程。

本文继上一篇文章的学习,地址如下:

uboot启动流程-run_main_loop 到 cmd_process处理说明二-优快云博客

二.  cmd_process函数处理命令过程

1.  函数调用关系

这里主要分析一下 uboot 的命令行处理过程。

通过分析可知, cli_loop 函数通过一系列调用,最终调用 cmd_process函数处理命令。调用关系如下:

2.  cmd_process函数处理命令过程

cmd_process 函数定义在文件 common/command.c 中,函数内容如下:
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 函数 调用函数 find_cmd函数 在命令表中找到指定的命令, find_cmd 函数内容如下:
 
cmd_tbl_t *find_cmd(const char *cmd)
{
	cmd_tbl_t *start = ll_entry_start(cmd_tbl_t, cmd);
	const int len = ll_entry_count(cmd_tbl_t, cmd);
	return find_cmd_tbl(cmd, start, len);
}

参数 cmd 就是所查找的命令名字, uboot 中的命令表其实就是 cmd_tbl_t 结构体数组,通 ll_entry_star t 函数得到数组的第一个元素,也就是命令表起始地址。
通过 ll_entry_count 函数得到数组长度,也就是命令表的长度。
最终通过 find_cmd_tbl 函数在命令表中找到所需的命 令,每个命令都有一个 name 成员,所以将参数 cmd 与命令表中每个成员的 name 字段都对比 一下,如果相等的话就说明找到了这个命令,找到以后就返回这个命令。

  cmd_process 函数中,找到命令以后肯定就要执行这个命令了, 调用 cmd_call函数来执行具体的命令,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;
}

在前面的分析中我们知道, cmd_tbl_t cmd 成员就是具体的命令处理函数,所以,cmd_call 函数中,第5行 调用 cmdtp cmd 成员来处理具体的命令,返回值为命令的执行结果。
cmd_process 中会检测 cmd_tbl 的返回值,如果返回值为 CMD_RET_USAGE ,就会调用
cmd_usage 函数输出命令的用法,其实就是输出 cmd_tbl_t usage 成员变量。

### run_sys 是否会造成阻塞 `run_sys` 函数是否会阻塞取决于其实现方式以及调用的具体命令。如果 `run_sys` 是通过 Python 的 `subprocess.run()` 方法来执行系统命令,那么默认情况下此方法将会等待命令完成才会继续执行后续代码[^1]。 当在多线程环境中使用时,如果 `run_sys` 阻塞,则意味着该线程会被挂起直到外部命令结束。这不会影响其他线程的执行,除非这些线程依赖于被阻塞线程的结果或资源。然而,在多进程环境下,即使 `run_sys` 调用了阻塞操作,也不会阻止同一应用程序中的其他进程继续运行,因为每个进程都拥有独立的地址空间和系统资源[^3]。 为了防止潜在的阻塞问题,可以考虑采用异步的方式执行系统命令,比如利用 Python 中的 asyncio 库配合 subprocess 来实现非阻塞的任务提交与管理。下面是一个简单的例子展示如何创建不阻塞主线程/进程的子进程: ```python import asyncio import sys async def run_command(cmd): process = await asyncio.create_subprocess_shell( cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) stdout, stderr = await process.communicate() if process.returncode == 0: print(f'[{cmd!r} exited with {process.returncode}]') if stdout: print(f'[stdout]\n{stdout.decode()}') else: print(f'[{cmd!r} failed]') if stderr: print(f'[stderr]\n{stderr.decode()}', file=sys.stderr) def main(): loop = asyncio.get_event_loop() try: command_to_run = "echo Hello World" loop.run_until_complete(run_command(command_to_run)) finally: loop.close() if __name__ == '__main__': main() ``` 这段代码展示了怎样定义一个名为 `run_command` 的协程函数用于启动并监控另一个程序的执行情况而不必担心它可能会造成的任何阻塞效应。这种方式非常适合那些希望保持响应性的 GUI 或 Web 应用程序[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值