uboot以tag结构体的方式给内核传参,cmdline,bootargs,以及uboot如何启动内核

news2024/11/25 10:30:48

uboot以tag结构体的方式给内核传参,cmdline,bootargs,以及uboot如何启动内核

一、u-boot启动流程

1、第一阶段

cpu/s3c24xx/start.S
主要是一些依赖于 CPU 体系结构的代码,比如硬件设备初始化代码 等。这一阶段的代码主要是通过汇编来实现的,已达到短小精悍和高效的目的。为位置无关代码,通常在 Flash 中运行。所以有的指令为相对寻址,可 以在任何位置运行。

1)、soc内部cpu硬件初始化

关闭 Watchdog、关闭中断、设置 CPU 的速度和时钟 频率、配置 SDRAM 存储控制器及 IO、关闭处理器内部指令/数据
Cache 等;

mmu映射
第一阶段的东西我认为只要能知道个大概就行,但是mmu还是必须得单独拿出来说一下

sdram物理地址
在这里插入图片描述
顺便提一下,为啥uboot的烧录地址是0x3000000,就是这个sram的寻址地址空间,是如图。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

1、第二阶段
lib_arm/board.c

通常用 C 语言来实现,这样可以实现更复杂的功能,而且代码 会具有更好的可读性和可移植性。
初始使到的硬件设备(如串口、Flash 和网卡等)
检测系统内存映射
把内核从 Flash 读到 RAM 空间中
为内核设置启动参数
调用内核

第二阶段主要是对开发板级别的硬件、软件数据结构进行初始化。
(2)
init_sequence
cpu_init 空的
board_init 网卡、机器码、内存传参地址
dm9000_pre_init 网卡
gd->bd->bi_arch_number 机器码
gd->bd->bi_boot_params 内存传参地址
interrupt_init 定时器
env_init
init_baudrate gd数据结构中波特率
serial_init 空的
console_init_f 空的
display_banner 打印启动信息
print_cpuinfo 打印CPU时钟设置信息
checkboard 检验开发板名字
dram_init gd数据结构中DDR信息
display_dram_config 打印DDR配置信息表
mem_malloc_init 初始化uboot自己维护的堆管理器的内存
mmc_initialize inand/SD卡的SoC控制器和卡的初始化
env_relocate 环境变量重定位
gd->bd->bi_ip_addr gd数据结构赋值
gd->bd->bi_enetaddr gd数据结构赋值
devices_init 空的
jumptable_init 不用关注的
console_init_r 真正的控制台初始化
enable_interrupts 空的
loadaddr、bootfile 环境变量读出初始化全局变量
board_late_init 空的
eth_initialize 空的
x210_preboot_init LCD初始化和显示logo
check_menu_update_from_sd 检查自动更新
main_loop 主循环

2.6.17.2、启动过程特征总结
(1)第一阶段为汇编阶段、第二阶段为C阶段
(2)第一阶段在SRAM中、第二阶段在DRAM中
(3)第一阶段注重SoC内部、第二阶段注重SoC外部Board内部

在系统上电后,片子自动将U-boot的前4k拷到内部ram中开始执行,但不知什么时候开始转到外部ram执行代码?

前4k代码负责将U-Boot剩余的部分复制到外部ram。然后通过下面这一句:ldr pc, _start_armboot ,跳转到lib_arm/board.c中定义的start_armboot函数。这就已经转到外部ram执行了。

void start_armboot (void)
gd = (gd_t*)gd_base;
分配gd
global data gd_t结构体包括了u-boot中所有重要全局变量(也包括了bd变量)

#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")

include/asm-arm/global_data.h
声明一个寄存器变量gd占用r8。这个宏在所有需要引用全局数据指针gd_t *gd的源码中都有声明,这个声明也避免编译器把r8分配给其他的变量,所以gd就是r8,这个指针变量不占用内存。
定义gd为gd_t类型指针,存储在寄存器r8中
register 表示变量对于执行速度非常重要,因此应该放在机器的寄存器中(寄存器独立于内存,通常在处理器芯片上)
volatile 用于指定变量的值可以由外部过程异步修改,例如中断例程
asm__ volatile(“”: : :“memory”);

告诉编译器内存地址已经修改过了
asm 指示编译器在此插入汇编语句。
volatile 告诉编译器,严禁将此处的汇编语句与其它的语句重组合优化。即:原原本本按原来的样子处理这这里
的汇编。
“”::: 空指令。barrier()不用在此插入一条串行化汇编指令。
memory 强制gcc编译器假设RAM所有内存单元均被汇编指令修改,这样cpu中的registers和cache中已缓存的内存单
元中的数据将作废。
cpu将不得不在需要的时候重新读取内存中的数据。这就阻止了cpu又将registers,cache中的数据用于去
优化指令,而避免去访问内存。

init_fnc_t *init_sequence
init_fnc_t结构体里面的函数都在board/samsung/smdk2416/smdk2416.c内被定义

size = flash_init ();
nand_init();

在这里插入图片描述
函数原型

int run_command (const char *cmd, int flag)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

内核启动流程
在这里插入图片描述

启动内核

操作系统的内核映像一般是存储在 Flash 上的,当 Bootloader 将内核复制到 RAM 里之后可能还需要对其解压。
但是对于有自解压能力的内核而言是不需要 Bootloader 来解压的。
在这里插入图片描述

根据前面调用流程中的分析我们只要看common/cmd_bootm.c中的do_bootm

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

uboot(universal boot)是通用的启动代码,支持多种架构的CPU,并且是开源的。uboot是高度定制的,大致分为Soc级资源管理和板级资源管理。不同的CPU或者同款CPU不同的开发板,uboot都是不同的,要根据硬件电路进行移植。

bootLoader是嵌入式设备中用来引导内核启动的一段代码。内核启动是需要一定条件的,当设备上电后会首先运行BootLoader,BootLoader会初始化必要的硬件,比如DDR、Flash、串口等,相关初始化完成后就会去启动内核。我的理解,BootLoader是一个概念并不是具体的代码,只要满足在设备启动初期启动内核功能的代码都可以叫做BootLoader。

uboot和bootLoader的关系
uboot是BootLoader的一个具象化的表现,uboot是BootLoader,但BootLoader不仅仅指uboot,比如bios也是属于BootLoader。但是在我们日常的嵌入式开发中,经常把BootLoader和uboot混在一起,很多时候BootLoader和uboot都指的是uboot,因为在嵌入式开发中,使用的BootLoader基本都是uboot。
————————————————

                        版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/weixin_42031299/article/details/123452708

cmdline是uboot引导内核启动时传递给内核的,作用是指导内核启动。内核启动阶段会去解析cmdline,并根据cmdline去指导内核启动。
uboot启动内核的大致步骤

(1)首先uboot要通过读取SD卡/flash等外存 或者 通过tftp、nfs等网络下载方式,将内核加载到内存的链接地址处;
(2)解析加载的内核的头信息,区分出当前启动方式是zImage、uImage还是设备树方式,构建出描述该内核的image_header_t结构体;
(3)将上一步得到的image_header_t结构体和内核所在地址传入do_bootm_linux函数,启动Linux内核;
(4)boot->bootm->do_bootm_linux->内核启动,uboot结束

2、加载内核镜像到DDR的链接地址处
2.1、内核有三种状态:
(1)静态:没有启动内核时,内核以镜像的方式存放在外存中;
(2)动态:内核在DDR中运行时;
(3)静态->动态:把内核从外存加载到DDR,并启动内核;

2.2、内核的重定位
内核从外存加载到DDR的过程就叫做重定位,必须加载DDR的特定地址(链接地址),因为启动的时候就是去链接地址启动。内核的重定位是uboot完成的,根据启动方式的不同,uboot可能从flash等外存去读取内核,也可能通过tftp、nfs等网络下载方式读取内核,但不管何种读取内核的方式,最终内核都是被加载到链接地址。链接地址在编译脚本、环境变量bootcmd、配置文件的CONFIG_BOOTCOMMAND宏定义可以查到。

3、启动内核的相关命令
(1)boot:该命令会先将内核重定位,然后调用bootm命令启动内核;
(2)bootm:这是直接启动内核的命令,只能启动已经加载到DDR的内核,在调用时传入内核在DDR中的地址(一般是内核的链接地址)即可启动内核。在bootm命令的实现代码里,其实主要完成的是启动方式的判断,判断出启动的操作系统类型后,完成初始化就会去调用相关操作系统的启动函数。
(2)do_bootm_linux函数:这是启动linux系统的函数,功能包括:准备给内核的传参、找到内核程序入口、启动内核。

start_armboot()
	env_init()--init_sequence[]
	env_relocate()
		env_relocate_spec ()
			env_relocate_spec_movinand()
				movi_read_env()
				use_default()
					mmc_bread()

1)环境变量的地址存放在变量gd->env_addr中,gd全局变量参见博客:《uboot中重要的全局变量——gd》;
(2)实际上gd->env_addr == (ulong)&(env_ptr->data),但是env_ptr->data可以指向默认环境变量default_environment[]或者从SD卡读取的环境变量;

2、saveenv命令的代码调用关系

do_saveenv
	saveenv
		saveenv_movinand
			movi_write_env
				movi_write
					mmc_bwrite
#define __REG(x)	(*(vu_long *)(x))
#define INF_REG3_REG			__REG(INF_REG_BASE+INF_REG3_OFFSET)

//路径:./common/env_auto.c
int saveenv(void)
{
	if (INF_REG3_REG == 2)
		saveenv_nand();
	else if (INF_REG3_REG == 3) //开发板接的iNand/SD卡,所以INF_REG3_REG == 3
		saveenv_movinand();
	else if (INF_REG3_REG == 1)
		saveenv_onenand();
	else if (INF_REG3_REG == 4)
		saveenv_nor();

	else
		printf("Unknown boot device\n");

	return 0;
}

//从外存中读取环境变量保存到env_ptr变量中
int saveenv_movinand(void)
{
        movi_write_env(virt_to_phys((ulong)env_ptr));
        puts("done\n");

        return 1;
}

void movi_write_env(ulong addr)
{
	movi_write(raw_area_control.image[2].start_blk,
		   raw_area_control.image[2].used_blk, addr);
}

ulong movi_write(ulong start, lbaint_t blkcnt, void *src)
{
	//第一个参数是0,说明从SD卡通道0去读环境变量,这在代码里写死了
	//start:开始读取的扇区数
	//blkcnt:要写入的扇区个数
	//src:将从SD卡读取到的数据加载到该地址处
	
	return mmc_bwrite(0, start, blkcnt, src);
}


uboot以tag方式给内核传参
(1)struct tag,tag是一个数据结构,在uboot和linux kernel中都有定义tag数据机构,而且定义是一样的。
(2)tag_header和tag_xxx。tag_header中有这个tag的size和类型编码,kernel拿到一个tag后先分析tag_header得到tag的类型和大小,然后将tag中剩余部分当作一个tag_xxx来处理。
(3)tag_start与tag_end。kernel接收到的传参是若干个tag构成的,这些tag由ATAG_CORE类型的tag起始,到ATAG_NONE类型的tag结束。
(4)tag传参的方式是由linux kernel发明的,kernel定义了这种向我传参的方式,uboot只是实现了这种传参方式从而可以支持给kernel传参。
、内核如何接收tag参数
启动内核的代码:theKernel (0, machid, bd->bi_boot_params);其中bd->bi_boot_params就是所有tag结构体所在的首地址,这个地址是保存在全局变量gd->bd中的,在uboot启动的前期会指定内存地址用于存放tag结构体,然后在启动内核的时候传给内核,内核拿到地址就会从该地址去遍历tag结构体,内核会判断tag的类型,如果是ATAG_CORE类型的tag则是起始的tag,如果是ATAG_NONE则是最后一个tag结构体,不用再往后遍历。

tag结构体

struct tag_header {
		u32 size;	//结构体的大小
		u32 tag;	//结构体的类型
	};

struct tag {
       struct tag_header hdr;
		union { 	//此枚举体包含了uboot传给内核参数的所有类型
				struct tag_core         core;
				struct tag_mem32        mem;
				struct tag_videotext    videotext;
				struct tag_ramdisk      ramdisk;
				struct tag_initrd       initrd;
				struct tag_serialnr     serialnr;
				struct tag_revision     revision;
				struct tag_videolfb     videolfb;
				struct tag_cmdline      cmdline;
				
				/*
				* Acorn specific
				*/
				struct tag_acorn        acorn;
				
				/*
				 * DC21285 specific
				 */
				struct tag_memclk       memclk;
				
				struct tag_mtdpart      mtdpart_info;
		} u;
};

构建tag结构体

	/* The list must start with an ATAG_CORE node */
	#define ATAG_CORE	0x54410001
	
	/* The list ends with an ATAG_NONE node. */
	#define ATAG_NONE	0x00000000
	
	#define tag_size(type)	((sizeof(struct tag_header) + sizeof(struct type)) >> 2)
	#define tag_next(t)	((struct tag *)((u32 *)(t) + (t)->hdr.size))
	
	static struct tag *params;
	
	static void setup_start_tag (bd_t *bd)
	{
		params = (struct tag *) bd->bi_boot_params;//bd->bi_boot_params是专门用于保存tag结构体的内存首地址

		params->hdr.tag = ATAG_CORE;	//ATAG_CORE类型是tag结构体的开始
		params->hdr.size = tag_size (tag_core);	//结构体的大小

		params->u.core.flags = 0;
		params->u.core.pagesize = 0;
		params->u.core.rootdev = 0;

		params = tag_next (params);	//将指针偏移params->hdr.size个字节,让params指向下一个可用的内存地址
	}
	
	`````````中间省略掉其他类型tag结构体的构建
	
	static void setup_end_tag (bd_t *bd)
	{
		params->hdr.tag = ATAG_NONE;
		params->hdr.size = 0;
	}

内核中解析cmdline的函数调用关系

	start_kernel()
		parse_early_param()
			parse_early_options()
				parse_args();
					next_arg()
					parse_one()
						do_early_param()

parse_early_param()函数

void __init parse_early_param(void)
{
	static __initdata int done = 0;
	static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];

	if (done)
		return;

	/* All fall through to do_early_param. */
	strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
	parse_early_options(tmp_cmdline);
	done = 1;
}

parse_early_options()函数
void __init parse_early_options(char *cmdline)
{
parse_args(“early options”, cmdline, NULL, 0, do_early_param);
}
1
2
3
4
调用parse_args()函数并将cmdline和do_early_param函数指针传入。
parse_args()函数
int parse_args(const char *name,
char *args,
struct kernel_param *params,
unsigned num,
int (*unknown)(char *param, char *val))
{
char *param, *val;

/* Chew leading spaces */
args = skip_spaces(args);

while (*args) {
	int ret;
	int irq_was_disabled;

	args = next_arg(args, &param, &val);//解析cmdline的格式解析出cmdline的每个单元
	irq_was_disabled = irqs_disabled();
	ret = parse_one(param, val, params, num, unknown);
	if (irq_was_disabled && !irqs_disabled()) {
		······
	}
	switch (ret) {
		······
	}
}
return 0;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
(1)next_arg()函数:按照cmdline的格式逐个解析;
比如cmdline=“root=/dev/mmcblk0p2 rootfstype=ext3”
第一次解析结果:param=“root”,val=“/dev/mmcblk0p2”
第二次解析结果:param=“rootfstype”,val=“ext3”
(2)parse_one()函数:
对每个从cmdline解析出的单元进行处理;

parse_one()函数

static int parse_one(char *param,
		     char *val,
		     struct kernel_param *params, 
		     unsigned num_params,
		     int (*handle_unknown)(char *param, char *val))
{
	······
	//handle_unknown函数指针就是do_early_param()函数
	if (handle_unknown) {
		DEBUGP("Unknown argument: calling %p\n", handle_unknown);
		return handle_unknown(param, val);
	}

	DEBUGP("Unknown argument `%s'\n", param);
	return -ENOENT;
}

对next_arg()函数解析出的param和val进行处理,handle_unknown函数指针就是do_early_param()函数。具体的解析工作都是do_early_param()函数做的。
do_early_param()函数
static int __init do_early_param(char *param, char *val)
{
struct obs_kernel_param *p;

for (p = __setup_start; p < __setup_end; p++) { //遍历".init.setup"段,寻找匹配的obs_kernel_param结构体
	if ((p->early && strcmp(param, p->str) == 0) ||
	    (strcmp(param, "console") == 0 &&
	     strcmp(p->str, "earlycon") == 0)
	) {
		if (p->setup_func(val) != 0)	//匹配到obs_kernel_param结构体就执行绑定的处理函数
			printk(KERN_WARNING
			       "Malformed early option '%s'\n", param);
	}
}
/* We accept everything at this stage. */
return 0;

}
(1)__setup_start和__setup_end是".init.setup"段的起始/结束地址;
(2)".init.setup"段的遍历结合__setup宏和链接脚本进行理解;

cmdline常用参数
例如:mem=1408M console=ttyS0,115200 root=/dev/mmcblk0p7 rootfstype=squashfs mtdparts=xxx
(1)mem=
用来告诉内核当前系统的内存有多少;
(2)console=
指定控制台使用的串口已经波特率;
(3)root=
根文件系统的位置,比如上面就是指定根文件系统在mmcblk0p7分区,内核挂载根文件系统时会用到;
根文件系统也可以通过nfs远程挂载:
root=/dev/nfs nfsroot=192.168.1.141:/root/s3c2440/build_rootfs/aston_rootfs ip=192.168.1.10:192.168.1.141:192.168.1.1:255.255.255.0::eth0:off
(4)rootfstype=
指明文件系统的格式和权限;
(5)mtdparts=
指明存储设备的分区情况;
如:mtdparts=nand0:0x140000@0x000000(param),4m(uboot),2m(env),49408k(romfs),2m(custom),25m(web),2m(logo),512k(dgs),31m(ext_usr),2m(config_fw),-(config)

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

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

相关文章

3. 轴指令(omron 机器自动化控制器)——>MC_MoveRelative

机器自动化控制器——第三章 轴指令 5 MC_MoveRelative变量▶输入变量▶输出变量▶输入输出变量 功能说明▶指令详情▶时序图▶重启运动指令▶多重启动运动指令▶异常 MC_MoveRelative 指定自指令当前位置起的移动距离&#xff0c;进行定位。 指令名称FB/FUN图形表现ST表现MC…

如何查看线程

1、首先找到我们的电脑安装jdk的位置&#xff0c;这里给大家展示一下博主本人的电脑jdk路径下的jconsole位置。 2、 ok&#xff0c;那么找到这个jconsole程序我们直接双击打开就可以查看我们电脑的本地进程&#xff1a; jconsole 这里能够罗列出你系统上的 java 进程&#xff0…

学习threejs,绘制二维线

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言二、&#x1f340;绘制二维线1. ☘️…

软件测试岗位的经典面试题

在我近十年的测试职业生涯中&#xff0c;我也面试过不少软件测试岗位的候选人&#xff0c;这几个问题我也经常问到。但很多同学在这几个高频面试题上&#xff0c;其实并不太了解面试官提问背后的逻辑和目的。 这篇文章&#xff0c;聊聊这几道经典面试题考查的目的&#xff0c;…

花朵识别系统Python+卷积神经网络算法+人工智能+深度学习+计算机课设项目+TensorFlow+模型训练

一、介绍 花朵识别系统。本系统采用Python作为主要编程语言&#xff0c;基于TensorFlow搭建ResNet50卷积神经网络算法模型&#xff0c;并基于前期收集到的5种常见的花朵数据集&#xff08;向日葵、玫瑰、蒲公英、郁金香、菊花&#xff09;进行处理后进行模型训练&#xff0c;最…

生成式AI赋能:对话式BI引领数据分析新潮流

引言&#xff1a;数据交互的革新之旅 在信息爆炸的今天&#xff0c;我们与数据交互的方式正经历着前所未有的变革。静态的仪表盘&#xff0c;尽管曾以视觉上的革新引领一时风尚&#xff0c;但如今已难以满足用户对动态、深度数据洞察的迫切需求。用户不再满足于仅仅观赏精美的…

如何恢复被删除的 GitLab 项目?

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料&#xff1a; 极狐GitLab 官网极狐…

攻防世界--->backup

学习笔记。 备份文件&#xff1f; 没听过&#xff0c;&#xff0c;&#xff0c;搜呗。 我看的这个&#xff1a; CTF——Web网站备份源码泄露_web常见备份文件有哪些-CSDN博客https://blog.csdn.net/weixin_51735061/article/details/124238304 进入靶场。 嗯&#xff1f; -…

实操学习——文章和评论的设计

实操学习——文章和评论的设计 1.文章表的设计2.文章表接口演示基础权限创建文章修改文章删除文章浏览所有文章 3.评论表的设计4.评论表接口演示1. 查询指定文章下的所有评论 1.文章表的设计 创建一个community的app 在settings中 完成注册 定义模型 创建文章表 from django.…

2024 Redis 全部

1. 单机部署 1.1 检查环境&#xff0c;创建目录。 # 本地运行&#xff0c;不需要考虑安装的原因&#xff0c;可以卸载防火墙 # 关闭防火墙 systemctl stop firewalld.service# 查看防火强状态 firewall-cmd --state# redis 是基于gcc 环境的&#xff0c;查看是否有 gcc 环境 …

线程基础、状态及常用方法

多线程基础 进程和线程 什么是程序&#xff1f; 程序是包含有计算机指令和数据的文件。 什么是进程&#xff1f; 进程是程序的一次执行过程&#xff0c;是计算机分配资源的最小单位。 什么是线程&#xff1f; 一个进程中会包含若干个线程&#xff0c;每个线程都是一个独立的…

OpenCV图像文件读写(2) 检查 OpenCV 是否支持某种图像格式的写入功能函数haveImageWriter()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 haveImageWriter 函数用于检查 OpenCV 是否支持某种图像格式的写入功能。这个函数可以帮助开发者在编写代码时确定是否可以成功地将图像写入特定…

【Linux】Linux基本命令

目录 文件和目录操作&#xff1a; ls cd pwd cp mv rm mkdir rmdir touch clear history which/whereis 文件查看和编辑&#xff1a; cat less head tail vi 或 vim sz/rz echo 系统信息和管理&#xff1a; su uname hostname df free top ps ki…

fopen与fwrite在C语言中写文件及open、write的对比

fopen与fwrite在C语言中写文件及open、write的对比 一、fopen与fwrite写文件1. fopen函数介绍2. fwrite函数介绍3. 示例代码4. 注意事项二、open与write写文件1. open函数介绍2. write函数介绍3. 示例代码4. 注意事项三、fopen/fwrite与open/write的对比1. 跨平台性2. 功能与灵…

android和ios双端应用性能的测试工具

1.工具介绍 基于日常工作的需要&#xff0c;开发了一款新的android和ios端应用性能测试工具&#xff0c;本工具在数据测试方面与所流行的工具没有区别。欢迎下载使用体验。 本工具为筋斗云&#xff0c;工具说明 本工具无侵入&#xff0c;不需要root&#xff0c;低延迟…

统一建模语言(UML)在软件研发过程中常用图接受:类图、用例图、时序图、状态图、活动图、流程图、顺序图

UML具有许多不同类型的图表&#xff0c;包括&#xff1a; 静态图&#xff1a;用例图、类图、对象图、组件图、部署图动态图&#xff1a;活动图、状态图、时序图&#xff08;又叫顺序图、序列图&#xff09;、协作图 软件工程&#xff08;软件工程中的各种图一般用于以下三个阶段…

线上报名小程序怎么做

在这个数字化、智能化的时代&#xff0c;信息技术的发展正以前所未有的速度改变着我们的生活。无论是学习、工作还是娱乐&#xff0c;互联网都成为了我们不可或缺的一部分。而在线上报名这一领域&#xff0c;小程序的出现更是为广大用户带来了前所未有的便捷与高效。今天&#…

# 高可用的并发解决方案nginx+keepalived(四)

高可用的并发解决方案nginxkeepalived&#xff08;四&#xff09; 一、Keepalived安装 1、keepalived 介绍 Keepalived 是一种高性能的服务器高可用或热备解决方案&#xff0c;Keepalived 可以用来防止服务器单点故障的发生&#xff0c;通过配合 Nginx 可以实现 web 前端服务…

SOMEIP_ETS_134: SD_Option_Length_ends_past_Options_Array_Var_A

测试目的&#xff1a; 验证DUT能够处理一个其选项长度超出所指示的选项数组长度的SubscribeEventgroup消息&#xff0c;并以SubscribeEventgroupNAck作为响应或完全忽略该请求。 描述 在变体A中&#xff0c;通过将SOME/IP头部中指示的总长度从60字节略微减少到48字节&#x…

Tomcat后台弱口令部署war包

1.环境搭建 cd /vulhub/tomcat/tomcat8 docker-compose up -d 一键启动容器 2.访问靶场 点击Manager App tomcat8的默认用户名和密码都是tomcat进行登录 3.制作war包 先写一个js的一句话木马 然后压缩成zip压缩包 最后修改后缀名为war 4.在网站后台上传war文件 上传war文件…