当前位置: 首页 > news >正文

ARM uboot 源码分析8 - uboot的环境变量

一、uboot 的环境变量基础

1、环境变量的作用

(1) 让我们可以不用修改 uboot 的源代码,而是通过修改环境变量,来影响 uboot 运行时的一些数据和特性。譬如说,通过修改 bootdelay 环境变量,就可以更改系统开机自动启动时倒数的秒数。


2、环境变量的优先级

(1) uboot 代码当中有一个值,环境变量中也有一个值。uboot 程序实际运行时规则是:如果环境变量为空,则使用代码中的值;如果环境变量不为空,则优先使用环境变量对应的值。

(2) 譬如 machid(机器码)。uboot 中在 x210_sd.h 中定义了一个机器码 2456,写死在程序中的,不能更改。如果要修改 uboot 中配置的机器码,可以修改 x210_sd.h 中的机器码,但是修改源代码后需要重新编译烧录,很麻烦;

在这里插入图片描述

在这里插入图片描述

比较简单的方法就是,使用环境变量 machidset machid 0x998 类似这样,有了 machid 环境变量后,系统启动时会优先使用 machid 对应的环境变量,这就是优先级问题。


3、环境变量在 uboot 中工作方式

(1) 默认环境变量,在 uboot/common/env_common.c 中 default_environment,这东西本质是一个字符数组,大小为 CFG_ENV_SIZE(16 kByte),里面内容就是很多个环境变量连续分布组成的,每个环境变量最末端以 ‘\0’ 结束。

在这里插入图片描述


(2) SD 卡中的环境变量分区,在 uboot 的 raw 分区中。SD 卡中其实就是给了个分区,专门用来存储而已。存储时其实是把 DDR 中的环境变量整体的写入 SD 卡中分区里。所以当我们 saveenv 时,其实整个所有的环境变量都被保存了一遍,而不是只保存更改了的。

(3) DDR 中环境变量,在 default_environment 中,实质是字符数组。在 uboot 中其实是一个全局变量,链接时在数据段;重定位时,default_environment 就被重定位到 DDR 中一个内存地址处了。这个地址处,这个全局字符数组,就是我们 uboot 运行时的 DDR 中的环境变量了。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


总结:

  • 刚烧录的系统中,环境变量分区是空白的,uboot 第一次运行时,加载的是 uboot 代码中自带的一份环境变量,叫默认环境变量 default_environment
  • 我们在 saveenv 时,DDR 中的环境变量会被更新到 SD 卡中的环境变量中,就可以被保存下来,下次开机会在环境变量 relocate 时,SD 卡中的环境变量会被加载到 DDR 中去。
  • default_environment 中的内容虽然被 uboot 源代码初始化为一定的值(这个值就是我们的默认环境变量),但是在 uboot 启动的第二阶段,env_relocate 时代码会去判断 SD 卡中的 env 分区的 crc 是否通过。如果 crc 校验通过,说明 SD 卡中有正确的环境变量存储,则 relocate 函数会从 SD 卡中读取环境变量来覆盖 default_environment 字符数组,从而每次开机可以保持上一次更改过的环境变量。

二、环境变量相关命令源码解析1

1、printenv

(1) 找到 printenv 命令所对应的函数。通过 printenv 的 help 可以看出,这个命令有 2 种使用方法。第一种直接使用不加参数,则打印所有的环境变量;第二种是 printenv name ,则只打印出 name 这个环境变量的值。

在这里插入图片描述


(2) 分析 do_printenv 函数。

int do_printenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{int i, j, k, nxt;int rcode = 0;if (argc == 1) {		/* Print all env variables	*/for (i=0; env_get_char(i) != '\0'; i=nxt+1) {for (nxt=i; env_get_char(nxt) != '\0'; ++nxt);for (k=i; k<nxt; ++k)putc(env_get_char(k));putc  ('\n');if (ctrlc()) {puts ("\n ** Abort\n");return 1;}}printf("\nEnvironment size: %d/%ld bytes\n",i, (ulong)ENV_SIZE);return 0;}for (i=1; i<argc; ++i) {	/* print single env variables	*/char *name = argv[i];k = -1;for (j=0; env_get_char(j) != '\0'; j=nxt+1) {for (nxt=j; env_get_char(nxt) != '\0'; ++nxt);k = envmatch((uchar *)name, j);if (k < 0) {continue;}puts (name);putc ('=');while (k < nxt)putc(env_get_char(k++));putc ('\n');break;}if (k < 0) {printf ("## Error: \"%s\" not defined\n", name);rcode ++;}}return rcode;
}

在这里插入图片描述

在这里插入图片描述

不论 SD 卡中是否有环境变量,以及是否发生环境变量覆盖,环境变量的首地址总是 default_environment 字符数组的首地址。


(3) do_printenv 函数首先区分 argc=1 还是不等于 1 的情况,若 argc=1 ,那么就循环打印所有的环境变量出来;如果 argc 不等于 1,则后面的参数就是要打印的环境变量,给哪个就打印哪个。

(4) argc=1 时,用双重 for 循环来依次处理所有的环境变量的打印。第一重 for 循环就是处理各个环境变量。所以有多少个环境变量,则第一重就执行循环多少圈。

在这里插入图片描述

在这里插入图片描述


(5) 这个函数要看懂,首先要明白整个环境变量在内存中如何存储的问题。

(6) 关键点:第一,要明白环境变量在内存中存储的方式;第二,要 C 语言处理字符串的功底要好。


三、环境变量相关命令源码解析 2

在这里插入图片描述

1、setenv

(1) 命令定义和对应的函数在 uboot/common/cmd_nvedit.c 中,对应的函数为 do_setenv

在这里插入图片描述


(2) setenv 的思路就是:先去 DDR 中的环境变量处寻找原来有没有这个环境变量,如果原来就有,则需要覆盖原来的环境变量,如果原来没有则在最后新增一个环境变量即可。

第1步:遍历 DDR 中环境变量的数组,找到原来就有的那个环境变量对应的地址。168-174 行。
在这里插入图片描述

第2步:擦除原来的环境变量,259-265 行。
第3步:写入新的环境变量,266-273 行。


(3)本来 setenv 做完上面的就完了,但是还要考虑一些附加的问题。

问题一:环境变量太多,超出 DDR 中的字符数组,溢出的解决方法。
问题二:有些环境变量如 baudrate、ipaddr 等,在 gd 中有对应的全局变量。这种环境变量在 set 更新的时候,要同时去更新对应的全局变量,否则就会出现在本次运行中,环境变量和全局变量的值不一致的情况。

在这里插入图片描述


四、环境变量相关命令源码解析 2

1、saveenv

(1) 在 uboot/common/cmd_nvedit.c 中,对应函数为 do_saveenv

在这里插入图片描述


(2) 从 uboot 实际执行 saveenv 命令的输出,和 x210_sd.h 中的配置(#define CFG_ENV_IS_IN_AUTO)可以分析出:我们实际使用的是 env_auto.c 中相关的内容。没有一种芯片叫 auto 的,env_auto.c 中是使用宏定义的方式,去条件编译了各种常见的 flash 芯片(如 movinand、norflash、nand 等)。然后在程序中读取 INF_REG(OMpin 内部对应的寄存器)从而知道我们的启动介质,然后调用这种启动介质对应的操作函数来操作。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


(3) do_saveenv 内部调用 env_auto.c 中的 saveenv 函数来执行实际的环境变量保存操作。

在这里插入图片描述


(4) 寄存器地址:E010F000 + 0C=E010_F00C,含义是用户自定义数据。我们在 start.S 中判断启动介质后,将 #BOOT_MMCSD(就是 3,定义在x210_sd.h)写入了这个寄存器,所以这里读出的肯定是 3,经过判断就是 movinand。所以实际执行的函数是:saveenv_movinand。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


(5) 真正执行保存环境变量操作的是:cpu/s5pc11x/movi.c 中的 movi_write_env 函数,这个函数肯定是写 sd卡,将 DDR 中的环境变量数组(其实就是 default_environment 这个数组,大小 16kb,刚好 32 个扇区)写入 iNand 中的 ENV 分区中

在这里插入图片描述

在这里插入图片描述


(6) raw_area_control 是 uboot 中规划 iNnad/SD 卡的原始分区表,这个里面记录了我们对 iNand 的分区,env 分区也在这里,下标是2。 追到这一层就够了,再里面就是调用驱动部分的写 SD卡/iNand 的底层函数了。

在这里插入图片描述

在这里插入图片描述

int init_raw_area_table (block_dev_desc_t * dev_desc)
{struct mmc *host = find_mmc_device(dev_desc->dev);/* when last block does not have raw_area definition. */if (raw_area_control.magic_number != MAGIC_NUMBER_MOVI) {int i = 0;member_t *image;u32 capacity;if (host->high_capacity) {capacity = host->capacity;#ifdef CONFIG_S3C6410if(IS_SD(host))capacity -= 1024;#endif} else {capacity = host->capacity;}dev_desc->block_read(dev_desc->dev,capacity - (eFUSE_SIZE/MOVI_BLKSIZE) - 1,1, &raw_area_control);if (raw_area_control.magic_number == MAGIC_NUMBER_MOVI) {return 0;}dbg("Warning: cannot find the raw area table(%p) %08x\n",&raw_area_control, raw_area_control.magic_number);/* add magic number */raw_area_control.magic_number = MAGIC_NUMBER_MOVI;/* init raw_area will be 16MB */raw_area_control.start_blk = 16*1024*1024/MOVI_BLKSIZE;raw_area_control.total_blk = capacity;raw_area_control.next_raw_area = 0;strcpy(raw_area_control.description, "initial raw table");image = raw_area_control.image;#if defined(CONFIG_EVT1)#if defined(CONFIG_FUSED)/* image 0 should be fwbl1 */image[0].start_blk = (eFUSE_SIZE/MOVI_BLKSIZE);image[0].used_blk = MOVI_FWBL1_BLKCNT;image[0].size = FWBL1_SIZE;image[0].attribute = 0x0;strcpy(image[0].description, "fwbl1");dbg("fwbl1: %d\n", image[0].start_blk);#endif
#endif/* image 1 should be bl2 */
#if defined(CONFIG_EVT1)#if defined(CONFIG_FUSED)image[1].start_blk = image[0].start_blk + MOVI_FWBL1_BLKCNT;#elseimage[1].start_blk = (eFUSE_SIZE/MOVI_BLKSIZE);#endif
#elseimage[1].start_blk = capacity - (eFUSE_SIZE/MOVI_BLKSIZE) -MOVI_BL1_BLKCNT;
#endifimage[1].used_blk = MOVI_BL1_BLKCNT;image[1].size = SS_SIZE;image[1].attribute = 0x1;strcpy(image[1].description, "u-boot parted");dbg("bl1: %d\n", image[1].start_blk);/* image 2 should be environment */
#if defined(CONFIG_EVT1)image[2].start_blk = image[1].start_blk + MOVI_BL1_BLKCNT;
#elseimage[2].start_blk = image[1].start_blk - MOVI_ENV_BLKCNT;
#endifimage[2].used_blk = MOVI_ENV_BLKCNT;image[2].size = CFG_ENV_SIZE;image[2].attribute = 0x10;strcpy(image[2].description, "environment");dbg("env: %d\n", image[2].start_blk);/* image 3 should be bl2 */
#if defined(CONFIG_EVT1)image[3].start_blk = image[2].start_blk + MOVI_ENV_BLKCNT;
#elseimage[3].start_blk = image[2].start_blk - MOVI_BL2_BLKCNT;
#endifimage[3].used_blk = MOVI_BL2_BLKCNT;image[3].size = PART_SIZE_BL;image[3].attribute = 0x2;strcpy(image[3].description, "u-boot");dbg("bl2: %d\n", image[3].start_blk);/* image 4 should be kernel */
#if defined(CONFIG_EVT1)image[4].start_blk = image[3].start_blk + MOVI_BL2_BLKCNT;
#elseimage[4].start_blk = image[3].start_blk - MOVI_ZIMAGE_BLKCNT;
#endifimage[4].used_blk = MOVI_ZIMAGE_BLKCNT;image[4].size = PART_SIZE_KERNEL;image[4].attribute = 0x4;strcpy(image[4].description, "kernel");dbg("knl: %d\n", image[4].start_blk);/* image 5 should be RFS */
#if defined(CONFIG_EVT1)image[5].start_blk = image[4].start_blk + MOVI_ZIMAGE_BLKCNT;
#elseimage[5].start_blk = image[4].start_blk - MOVI_ROOTFS_BLKCNT;
#endifimage[5].used_blk = MOVI_ROOTFS_BLKCNT;image[5].size = PART_SIZE_ROOTFS;image[5].attribute = 0x8;strcpy(image[5].description, "rfs");dbg("rfs: %d\n", image[5].start_blk);for (i=6; i<15; i++) {raw_area_control.image[i].start_blk = 0;raw_area_control.image[i].used_blk = 0;}}
}

五、uboot 内部获取环境变量

1、getenv

(1) 应该是不可重入的。

(2) 实现方式就是,去遍历 default_environment 数组,挨个拿出所有的环境变量比对 name,找到相等的直接返回这个环境变量的首地址即可。

在这里插入图片描述


2、getenv_r

(1) 可重入版本。(可自行搜索补充可重入函数的概念)

(2) getenv 函数是直接返回这个找到的环境变量在 DDR 中环境变量处的地址,而 getenv_r 函数的做法是,找到了 DDR 中环境变量地址后,将这个环境变量复制一份到提供的 buf 中,而不去动原来 DDR 中环境变量。

在这里插入图片描述

所以差别就是:getenv 中返回的地址,只能读,不能随便乱写,而 getenv_r 中返回的环境变量是在自己提供的 buf 中,是可以随便改写加工的。


3、总结

(1) 功能是一样的,但是可重入版本会比较安全一些,建议使用。

(2) 有关于环境变量的所有操作,主要理解了环境变量在 DDR 中的存储方法,理解了环境变量和 gd 全局变量的关联和优先级,理解了环境变量在存储介质中的存储方式(专用raw分区),整个环境变量相关的都清楚了。


源自朱有鹏老师.

相关文章:

ARM uboot 源码分析8 - uboot的环境变量

一、uboot 的环境变量基础 1、环境变量的作用 (1) 让我们可以不用修改 uboot 的源代码&#xff0c;而是通过修改环境变量&#xff0c;来影响 uboot 运行时的一些数据和特性。譬如说&#xff0c;通过修改 bootdelay 环境变量&#xff0c;就可以更改系统开机自动启动时倒数的秒…...

【蓝牙mesh】Network协议层介绍

【蓝牙mesh】Network协议层介绍 Network层简介 上一章节我们讲解了蓝牙Mesh中Lower层的功能和数据格式。 Lower层的数据往下传输就到了网络层&#xff08;Network Layer&#xff09;。网络层定义了收到Lower层的数据后&#xff0c;如何对其进行判断、封装、加密、认证&#xf…...

基于遗传算法的配电网故障定位(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5;&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密…...

Leetcode.1247 交换字符使得字符串相同

题目链接 Leetcode.1247 交换字符使得字符串相同 Rating &#xff1a; 1597 题目描述 有两个长度相同的字符串 s1和 s2&#xff0c;且它们其中 只含有 字符 "x"和 "y"&#xff0c;你需要通过「交换字符」的方式使这两个字符串相同。 每次「交换字符」的时…...

python语音识别whisper

一、背景 最近想提取一些视频的字幕&#xff0c;语音文案&#xff0c;研究了一波 二、whisper语音识别 Whisper 是一种通用的语音识别模型。它在不同音频的大型数据集上进行训练&#xff0c;也是一个多任务模型&#xff0c;可以执行多语言语音识别以及语音翻译和语言识别。 …...

Prometheus -- 浅谈Exporter

Prometheus系统 – Exporter原理 为什么我们需要Exporter&#xff1f; 广义上讲所有可以向Prometheus提供监控样本数据的程序都可以被称为一个Exporter。而Exporter的一个实例称为target&#xff0c;如下所示&#xff0c;Prometheus通过轮询的方式定期从这些target中获取样本…...

如何确定RocketMQ中消费者的线程大小

背景 随着物联网行业的发展、智能设备数量越来越多&#xff0c;随着设备活跃量过大&#xff0c;常常存在一些高并发的请求&#xff0c;形成了流量尖峰&#xff0c;过多的请求会压垮服务器&#xff0c;影响其他服务运行。因此&#xff0c;为了保护云端服务&#xff0c;需要对请求…...

OpenAPI SDK组件之Spring Aop源码拓展

Spring Aop 看这个分享的应该都用过Spring Aop&#xff0c;这里就不再过多介绍了它是什么了。 我抽取了Spring Aop的部分源码&#xff0c;通过它实现请求参数可变拦截&#xff0c;同时apisdk离开Spring框架&#xff0c;仍然可以正常运行。 讲拦截也好&#xff0c;通知也罢&a…...

蓝桥杯C/C++VIP试题每日一练之龟兔赛跑预测

&#x1f49b;作者主页&#xff1a;静Yu &#x1f9e1;简介&#xff1a;CSDN全栈优质创作者、华为云享专家、阿里云社区博客专家&#xff0c;前端知识交流社区创建者 &#x1f49b;社区地址&#xff1a;前端知识交流社区 &#x1f9e1;博主的个人博客&#xff1a;静Yu的个人博客…...

为你的Vue2.x老项目安装Vite发动机吧

天下苦webpack久矣&#xff0c;相信作为前端开发者一定经历过在项目迭代时间较长的时候经历漫长等待的这一过程&#xff0c;每一次保存都会浪费掉大量时间&#xff0c;这是webpack这种机制所带来的问题。 于是&#xff0c;尤大为我们带来了新一代前端构建工具&#xff1a;vite…...

ZCMU--5012: 铺设道路(差分思路)

Description 春春是一名道路工程师&#xff0c;负责铺设一条长度为 n 的道路。 铺设道路的主要工作是填平下陷的地表。 整段道路可以看作是 n 块首尾相连的区域&#xff0c;一开始&#xff0c;第 i 块区域下陷的深度为 di。  春春每天可以选择一段连续区间 [L,R]&…...

算法模板总结(自用)

算法模板总结滑动窗口双指针算法数组相关合并两个有序数组左右指针技巧快慢指针技巧字符串相关左右指针反转字符串问题快慢指针替换空格字符问题链表相关快慢双指针删除链表的倒数第N个节点链表相交环形链表链表操作几数之和两数之和四个数组的四数之和三数之和同一数组中四数之…...

【架构师】零基础到精通——架构发展

博客昵称&#xff1a;架构师Cool 最喜欢的座右铭&#xff1a;一以贯之的努力&#xff0c;不得懈怠的人生。 作者简介&#xff1a;一名Coder&#xff0c;软件设计师/鸿蒙高级工程师认证&#xff0c;在备战高级架构师/系统分析师&#xff0c;欢迎关注小弟&#xff01; 博主小留言…...

C++(20):三路比较运算符

C20增加了三路比较运算符<>&#xff08;戏称航天飞机运算符&#xff09;&#xff0c;用于对类的比较运算符进行统一的设计。有两种使用方式&#xff1a;默认比较对于某些类&#xff0c;如果按照其成员逐一比较即可决定比较运算符的值&#xff0c;那么可以使用默认的三路运…...

MySQL workbench 字符集、字符序的概念与联系

在数据的存储上&#xff0c;MySQL提供了不同的字符集支持。而在数据的对比操作上&#xff0c;则提供了不同的字符序支持。 MySQL提供了不同级别的设置&#xff0c;包括server级、database级、table级、column级&#xff0c;可以提供非常精准的设置。 什么是字符集、字符序&am…...

DBA之路---数据库启动与关闭过程

DBA之路—数据库启动与关闭过程 1、启动过程 oracle启动的四个状态 shutdown、就是数据库关闭状态。 nomount模式 #启动instance &#xff0c;读取参数文件、分配sga空间启动后台进程&#xff0c;打开alter日志和其他trace文件startup nomount #该模式下只会创建实例并不加…...

Shell文件包含

和其他语言一样&#xff0c;Shell 也可以包含外部脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件。 一、语法格式 Shell 文件包含的语法格式如下&#xff1a; . filename # 注意点号(.)和文件名中间有一空格 或 source filename 在当前bash环境下读取并执行file…...

计算机网络(六): HTTP,HTTPS,DNS,网页解析全过程

文章目录一、HTTP头部包含的信息通用头部请求头部响应头部实体头部二、Keep-Alive和非Keep-Alive的区别三、HTTP的方法四、HTTP和HTTPS建立连接的过程4.1 HTTP4.2 HTTPS五、HTTP和HTTPS的区别六、HTTPS的加密方式七、cookie和sessionsessioncookie八、HTTP状态码状态码200&…...

Android仿京东金融的数值滚动尺功能

自定义数值滚动尺,这个用的还是挺多的&#xff0c;例如京东金融的通过滚动尺选择金额等,而这次就是高仿京东金融的数值滚动尺。首先看看下效果图&#xff0c;如下&#xff1a;首先先给你们各个变量的含义&#xff0c;以免在后面的讲解中不知变量的意思&#xff0c;代码如下://最…...

Nginx 和 Tomcat 实现负载均衡

Nginx 和 tomcat 实现负载均衡 &#x1f3c6;荣誉认证&#xff1a;51CTO博客专家博主、TOP红人、明日之星&#xff1b;阿里云开发者社区专家博主、技术博主、星级博主。 &#x1f4bb;微信公众号&#xff1a;微笑的段嘉许 &#x1f4cc;本文由微笑的段嘉许原创&#xff01; &am…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...

Web 架构之 CDN 加速原理与落地实践

文章目录 一、思维导图二、正文内容&#xff08;一&#xff09;CDN 基础概念1. 定义2. 组成部分 &#xff08;二&#xff09;CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 &#xff08;三&#xff09;CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 &#xf…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测

uniapp 中配置 配置manifest 文档&#xff1a;manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号&#xff1a;4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...