11_Uboot启动流程_3

news2024/12/25 22:30:08

目录

run_main_loop函数详解

cli_loop函数详解

cmd_process函数详解 


run_main_loop函数详解

uboot启动以后会进入3秒倒计时,如果在3秒倒计时结束之前按下按下回车键,那么就,会进入uboot的命令模式,如果倒计时结束以后都没有按下回车键,那么就会自动启动Linux内核,这个功能就是由run_main_loop函数来完成的。run_main_loop函数定义在文件common/board r.c中,函数内容如下:

第759行和第760行是个死循环, "for(;;)”和"while(1)”功能一样,死循环里面就一个main_loop 函数,main_loop函数定义在文件common/main.c 里面,代码如下: 

 第48行,调用bootstage_mark_name函数,打印出启动进度。第57行,如果定义了宏CONFIG_VERSION-VARIABLE的话就会执行函数setenv,设置换将变量ver的值为version_string,也就是设置版本号环境变量。version_string定义在文件cmd/version.c 中,定义如下:

 const char_weak version_string[] = U_BOOT_VERSION_STRING;

 U_BOOT_VERSION_STRING是个宏,定义在文件include/version.h,如下:

#define U_BOOT_VERSION_STRING U_BOOT_VERSION"(" U_BOOT_DATE "-"VU BOOT TIME""U BOOT TZ")" CONFIG IDENT STRING 

U_BOOT_VERSION定义在文件include/generated/version_autogenerated.h中,文件version_autogenerated.h 内如如下: 

 可以看出,U_BOOT_VERSION 为“U-boot 2016.03”,U_BOOT_DATEU_BOOT TIME

U_BOOT_TZ这定义在文件include/generated/timestamp_autogenerated.h中,如下所示:

宏CONFIG_IDENT_STRING为空,所以U_BOOT_VERSION_STRING为“U-Boot 2016.03(Apr 25 2019-21:10:53 +0800)",进入uboot命令模式,输入命令“version"查看版本号,如图所示: 

图中的第一行就是uboot版本号,和我们分析的一致。

接着回到示例代码32.2.9.2中,第60行,cli_init函数,跟命令初始化有关,初始化hushshell相关的变量。

第62行, run_preboot_environment_command函数,获取环境变量perboot的内容, perboot是一些预启动命令,一般不使用这个环境变量。

第68行, bootdelay_process函数,此函数会读取环境变量bootdelay和bootemd的内容,然后将bootdelay的值赋值给全局变量stored_bootdelay,返回值为环境变量bootemd的值。

第69行,如果定义了CONFIG_OF_CONTROL的话函数cli_process_fdt就会实现,如果没有定义CONFIG_OF_CONTROL的话函数cli_process_fdt直接返回一个false。在本uboot中没有定义CONFIG_OF_CONTROL,因此cli_process_fdt函数返回值为false。

第72行, autoboot_command函数,此函数就是检查倒计时是否结束?倒计时结束之前有没有被打断?此函数定义在文件common/autoboot.c中,内容如下:

 可以看出, autoboot_command函数里面有很多条件编译,条件编译一多就不利于我们阅读程序(所以正点原子的例程基本是不用条件编译的,就是为了方便大家阅读源码)宏CONFIG _AUTOBOOT_KEYEDCONFIG_AUTOBOOT_KEYED_CTRLC和CONFIG_MENUKEY这三个宏在I.MX6ULL里面没有定义,所以讲示例代码32.2.9.5进行精简,得到如下代码:

当一下三条全部成立的话,就会执行函数run_command_list

1.stored_bootdelay不等于-1

2.s不为空。

3.函数abortboot返回值为0。

stored_bootdelay等于环境变量bootdelay的值: s是环境变量bootemd的值,一般不为空,因此前两个成立,就剩下了函数abortboot的返回值, abortboot函数也定义在文件common/autoboot.c中,内容如下:

 因为宏CONFIG_AUTOBOOT_KEYE未定义,因此执行函数abortboot_normal,好吧,绕来绕去的!接着来看函数abortboot_normal,此函数也定义在文件common/autoboot.c中,内容如下:

 函数abortboot-normal同样很多条件编译,删除掉条件编译相关代码后abortboot_normal函数内容如下:

第3行的变量abort是函数abortboot normal的返回值,默认值为0。

第7行通过串口输出"Hit any key to stop autoboot"字样,如图所示:

 第9-21行就是倒计时的具体实现。

第14行判断键盘是否有按下,也就是是否打断了倒计时,如果键盘按下的话就执行相应的分支。比如设置abort为1,设置bootdelay为0等,最后跳出倒计时循环。

第26行,返回abort的值,如果倒计时自然结束,没有被打断abort就为0,否则的话abort的值就为1。

回到示例代码32.2.9.6的autoboot_command函数中,如果倒计时自然结束那么就执行函数run_command_list,此函数会执行参数s指定的一系列命令,也就是环境变量bootemd的命令,bootemd里面保存着默认的启动命令,因此linux内核启动!这个就是uboot中倒计时结束以后自动启动linux内核的原理。如果倒计时结束之前按下了键盘上的按键,那么run_command_list函数就不会执行,相当于autoboot_command是个空函数。

回到“遥远”的示例代码32.2.9.2中的main_loop函数中,如果倒计时结束之前按下按键,那么就会执行第74行的cli_loop函数,这个就是命令处理函数,负责接收好处理输入的命令。

cli_loop函数详解

cli_loop函数是uboot的命令行处理函数,我们在uboot中输入各种命令,进行各种操作就是有cli_loop来处理的,此函数定义在文件common/cli.c中,函数内容如下:

 在文件include/configs/mx6_common.h中有定义宏CONFIG_SYS_HUSH_PARSER,而正点原子的I.MX6ULL开发板配置头文件mx6ullevk.h里面会引用mx_common.h这个头文件,因此宏CONFIG_SYS_HUSH_PARSER有定义。

第205行调用函数parse_file_outer。

第207行是个死循环,永远不会执行到这里。

函数parse_file_outer定义在文件common/cli_hush.c中,去掉条件编译内容以后的函数内容如下:

第6行调用函数setup_file_in_str初始化变量input的成员变量。

第7行调用函数parse_stream_outer,这个函数就是hush shell的命令解释器,负责接收命令行输入,然后解析并执行相应的命令,函数parse_stream_outer定义在文件common/cli_hush.c中,精简版的函数内容如下:

第7~21行中的do-while循环就是处理输入命令的。

第9行调用函数parse_stream进行命令解析。

第14行调用run_list函数来执行解析出来的命令。

函数run_list会经过一系列的函数调用,最终通过调用cmd_process函数来处理命令,过程如下:

 第 5 行,run_list调用run_list_real函数。

第16行, run_list_real函数调用run_pipe_real函数。

第36行, run_pipe_real函数调用 cmd_process函数。

最终通过函数cmd_process来处理命令,接下来就是分析cmd_process函数。

 

cmd_process函数详解 

在学习cmd_process之前先看一下uboot中命令是如何定义的。uboot使用宏U_BOOT_CMD来定义命令,宏U_BOOT_CMD定义在文件include/command.h中,定义如下: 

 可以看出U_BOOT_CMD是U_BOOT_CMD_COMPLETE的特例, 将U_BOOT_CMD_COMPLETE的最后一个参数设置成NULL就是U_BOOT_CMD。宏U_BOOT_ CMD_COMPLETE如下:

宏U_BOOT_CMD_COMPLETE用到了ll_entry_declare和U-BOOT_CMD_MKENT_COMPLETE。ll_entry_declar定义在文件include/linker_lists.h中,定义如下: 

 _type为cmd_tbl_t,因此ll_entry_declare就是定义了一个cmd_tbl_t变量,这里用到了C语言中的“#”连接符符。其中的“##list”表示用_list的值来替换,"##_name”就是用_name的值来替换。

宏U_BOOT_CMD_MKENT_COMPLETE定义在文件include/command.h中,内容如下:

 上述代码中的“#”表示将name传递过来的值字符串化,U-BOOT_CMD_MKENT_COMPLETE又用到了宏_CMD_HELP和-CMD_COMPLETE,这两个宏的定义如下:

 可以看出,如果定义了宏CONFIG_AUTO_COMPLETE和CONFIG_SYS_LONGHELP的话,CMD COMPLETE和CMD_HELP就是取自身的值,然后在加上一个‘,’。CONFIG_AUTO_COMPLETE和CONFIG_SYS_LONGHELP这两个宏有定义在文件mx6_common.h中。

U_BOOT_CMD宏的流程我们已经清楚了(一个U_BOOT_CMD 宏就如此的绕来绕去的!),我们就以一个具体的命令为例,来看一下 U_BOOT_CMD经过展开以后究竟是个什么模样的。以命令dhep为例,dhcp命令定义如下:

 将其展开,结果如下:

从示例代码32.2.11.7可以看出,dhep命令最终展开结果为: 

 第1行定义了一个cmd_tbl_t类型的变量,变量名为_u_boot_list_2_cmd_2_dhcp,此变量4字节对齐。

2,使用_attribute_关键字设置变量_u_boot_list_2_cmd_2_dhep存储在

u_boot_list_2_cmd_2_dhcp 段中。u-boot.lds 链接脚本中有一个名为“.u_boot_list"的段,所有.uboot_list开头的段都存放到.u_boot.list中,如图所示:

因此,第2行就是设置变量_u_boot_list_2_cmd_2_dhep的存储位置。

第3-6 行,cmd_tbl_t 是个结构体,因此第3-6行是初始化emd_tbl_t这个结构体的各个成员变量。cmd_tbl_t结构体定义在文件include/command.h中,内容如下:

 结合实例代码32.2.11.8,可以得出变量_u_boot_list_2_cmd_2_dhep的各个成员的值如下所示:

当我们在uboot的命令行中输入"dhep”这个命令的时候,最终执行的是do-dhep这个函数。总结一下, uboot中使用U BOOT CMD来定义一个命令,最终的目的就是为了定义一个cmd_tbl_t类型的变量,并初始化这个变量的各个成员。uboot中的每个命令都存储在.u_boot_list段中,每个命令都有一个名为do_xxx(xxx为具体的命令名)的函数,这个do_xxx函数就是具体的命令处理函数。

了解了uboot 中命令的组成以后,再来看一下cmd_process函数的处理过程,cmd_process函数定义在文件common/command.c中,函数内容如下:

第507行,调用函数find_cmd在命令表中找到指定的命令,find_cmd 函数内容如下: 

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

回到示例代码32.2.11.10的cmd_process函数中,找到命令以后肯定就要执行这个命令了,第533行调用函数cmd call来执行具体的命令, cmd call函数内容如下:

 在前面的分析中我们知道, cmd_tbl_t的cmd成员就是具体的命令处理函数,所以第494行调用cmdtp的cmd成员来处理具体的命令,返回值为命令的执行结果。

cmd_process中会检测cmd_tbl的返回值,如果返回值为CMD_RET_USAGE的话就会调用cmd_usage函数输出命令的用法,其实就是输出cmd_tbl_t的usage成员变量。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/507174.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQ ---- 函数

函数 函数是指一段可以直接被另一段程序调用的程序或代码。MySQL 内置了很多函数,开发人员只需要调用使用即可。查询语句中已经使用过了一些聚合函数。下面还有些常用的函数需要掌握。 函数应用场景举例 函数的分类 ① 字符串函数 ② 数值函数 ③ 日期函数 ④ 流程…

chap和ppp认证配置+MGRE实验

题目要求 1.R2为ISP,其上只能配置IP地址 2.R1-R2之间为HDLC封装 3.R2-R3之间为ppp封装,pap认证,R2为主认证方 4.R2-R4之间为ppp封装,chap认证,R2为主认证方 5.R1,R2,R3构建MGRE环境,仅R1的IP地址固定 6.内…

OpenPCDet系列 | 4.KITTI数据集数据加载流程代码解析

文章目录 数据加载流程0. create_kitti_infos1. __getitem__函数2. prepare_data函数3. collate_batch函数数据加载流程 这里记录一下具体用到的那些数据形式,整个kitti数据集的处理框架图如下所示: 在数据集处理到获取一个batch数据的整个流程的入口如下: # 开始迭代每…

STL常用容器

目录 一、string容器 1、基本概念 2、构造函数 3、赋值操作 4、字符串拼接 5、查找和替换 6、字符串比较 7、字符存取 8、插入与删除 9、获取字串 二、vector容器 1、基本概念 2、构造函数 3、赋值操作 4、容量和大小 5、插入和删除 6、数据存取 7、互换容器…

hadoop shell操作HDFS文件

一.常用的 hadoop shell 文件路径需要自己有才行,示例中的文件路径是本人自己的文件路径,不是公共文件路径,如何建立自己的数仓,查看本人 大数据单机学习环境搭建 相关文章 1.1查看 创建 删除 # 列出当前hdfs所存贮的文件 hado…

模式串匹配算法(朴素模式匹配与KMP)的机算与手算。

一.朴素模式匹配 1.机算 其实就是暴力匹配。 使用双指针 i (指向主串) j (指向模式串) 从主串 S 第一字符起,与模式串 T, 第一个字符比较,   ①若相同,则 i 与 j 统一向后移   ②若遇到 i 与 j 指向字符不同,回溯 i j 指针。继续如此&a…

经验分享|如何搭建产品帮助文档

作为一款优秀的产品,除了功能强大、易于使用等特点外,相应的使用说明和帮助文档也是至关重要的,这些说明和文档可以帮助用户更好地使用这款产品,并解决在使用过程中的问题。本篇文章将为大家详细介绍如何搭建一份优秀的产品帮助文…

12-Vue技术栈之Vuex的使用

目录 1、理解 vue1.1 vuex 是什么1.2 什么时候使用 Vue1.3 图解两种方式实现数据共享 2、搭建vuex环境2.1 下载vuex2.2 配置文件 3、基本使用3.1 求和案例纯vue写法3.2 求和案例vuex写法 4、getters的使用5、四个map方法的使用5.1 求和案例 6、 模块化命名空间6.1求和案例改造 …

C++“static“成员使用

1.static 成员概念 声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化。 1.2 static特性 静态成员为所有类对…

JVM学习(十):方法区

目录 一、栈、堆和方法区的交互关系 二、对方法区的理解 2.1 方法区在哪里 2.2 方法区的基本概念 2.3 Hotspot中方法区的演进 三、方法区的大小 3.1 设置参数 3.1.1 jdk7及以前 3.1.2 jdk8以后: 3.2 配置参数演示OOM 四、方法区的内部结构 4.1 方法区里…

【Java虚拟机】JVM调优和分析案例综合实战

1.什么是JVM性能优化 jvm性能优化涉及到两个很重要的概念:吞吐量和响应时间。jvm调优主要是针对他们进行调整优化,达到一个理想的目标,根据业务确定目标是吞吐量优先还是响应时间优先。 吞吐量:用户代码执行时间/(用户代码执行时…

C语言学习分享(第六次)------数组

💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:C语言学习分享⏪   🚚代码仓库:NEO的学习日记🚚   🌹关注我🫵带你学习更多C语言知识   🔝🔝 数组详解 1. 前言🔶2. …

使用 spring 的 IoC 的实现账户的CRUD(2)双层实现

spring实现service和dao的数据的查找 dao层设置接口实现dao层的接口service设置接口通过注入dao层,来实现接口 //dao层的接口,定义了根据id查询的方法 public interface Accountdao {Account findByid(int id); }实现接口:实现了查询的方法 …

【模板】拓扑排序

import java.util.Scanner; import java.util.*;// 注意类名必须为 Main, 不要有任何 package xxx 信息 public class Main {public static void main(String[] args) {Scanner in new Scanner(System.in);int point in.nextInt();int side in.nextInt();int[][] arr new i…

MacOS下安装和配置Nginx

一、安装brew /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"按回车后,根据提示操作:输入镜像序号 --> 输入Y,回车等待brew安装完成即可。 在终端输入brew -v后,会提示…

【牛客刷题专栏】0x25:JZ24 反转链表(C语言编程题)

前言 个人推荐在牛客网刷题(点击可以跳转),它登陆后会保存刷题记录进度,重新登录时写过的题目代码不会丢失。个人刷题练习系列专栏:个人CSDN牛客刷题专栏。 题目来自:牛客/题库 / 在线编程 / 剑指offer: 目录 前言问…

Jedis客户端和SpringDataRedis客户端

目录 3.Redis的Java客户端 3.1.Jedis客户端 3.1.1.快速入门 3.1.2.连接池 3.2.SpringDataRedis客户端 3.2.1.快速入门 3.2.2.自定义序列化 3.2.3.StringRedisTemplate 3.Redis的Java客户端 3.1.Jedis客户端 Jedis的官网地址: GitHub - redis/jedis: Redis…

单片机中时钟分析与快速读懂时序图的方法

目录 一、时钟电路 二、周期 三、时序 我们都知道在学校是通过铃声来控制所有班级的上下课时间,那个单片机是通过什么样的办法进行取指令,执行指令和其它操作的呢?在这里引入了一个时序的概念。 一、时钟电路 单片机时钟电路有三种方式…

LoadRunner的简单使用

目录 1、LoadRunner工具介绍 2、VUG的使用 3、Controller的使用 3.1、场景设计 3.2、场景运行及结果 4、Analysis的使用 1、LoadRunner工具介绍 Virtual User Generator:主要用来生成性能测试脚本Controller:创建测试场景,运行测试脚本、…

民用电力远程监控解决方案

民用电力远程监控解决方案 项目背景 随着我国城市现代化的飞速发展,城市配电系统的不断改造更新,信息化、网络化和智能化的快速发展,要求箱变安全稳定运行,出现故障能够及时排除保证快速供电。 但是,电力行业的监控…