uboot启动流程简要版(基于armv7)

news2025/1/18 3:28:13

uboot启动流程(armv7)

uboot介绍

uboot就是一段引导程序,在加载系统内核之前,完成硬件初始化,内存映射,为后续内核的引导提供一个良好的环境。
uboot是bootloader的一种,全称为universal boot loader。

第一阶段前(Boot rom)

Boot Rom是芯片内部ROM固化程序,是uboot 的引导代码。Boot room读硬件的启动信息(拨码开关设置),从指定的启动介质(SD、MMC等)中读取uboot-spl代码。

  • uboot已经是一个bootloader了,那么为什么还多一个uboot spl呢?
    • 这个主要原因是对于一些SOC来说,它的内部SRAM可能会比较小,小到无法装载下一个完整的uboot镜像,那么就需要spl,它主要负责初始化外部RAM和环境,并加载真正的uboot镜像到外部RAM(DDR)中来执行。

    • 所以由此来看,SPL应该是一个非常小的loader程序,可以运行于SOC的内部SRAM中,它的主要功能就是加载真正的uboot并运行之。

第一阶段(uboot-spl)

在这里插入图片描述
在arch级初始化(架构体系级)
_start———–>reset————–>关闭中断,设置SVC模式
………………………………|
………………………………———->cpu_init_cp15———–>关闭MMU,TLB
………………………………|
………………………………———->cpu_init_crit————->lowlevel_init————->关键寄存器的配置和初始化
………………………………|
………………………………———->_main————–>进入板级初始化,具体看下面
板级初始化
_main————–>board_init_f_alloc_reserve —————>堆栈、GD、early malloc空间的分配
…………|
…………————->board_init_f_init_reserve —————>堆栈、GD、early malloc空间的初始化
…………|
…………————->board_init_f —————>uboot relocate前的板级初始化以及relocate的区域规划
…………|
…………————->relocate_code、relocate_vectors —————>进行uboot和异常中断向量表的重定向
…………|
…………————->旧堆栈的清空
…………|
…………————->board_init_r —————>uboot relocate后的板级初始化
…………|
…………————->run_main_loop —————>进入命令行状态,等待终端输入命令以及对命令进行处理

  1. uboot启动入口为 _start (在u-boot.lds链接文件中可找到),_start函数跳转到reset函数(reset函数为spl的核心,结束进入第二阶段)
  2. reset函数会设置CPU为SVC32模式,关闭FIQ和IRQ中断
  3. resset函数跳转cpu_init_cp15 , 控制cp15协处理器,启动ICACHE,关闭DCACHE,关闭MMU和TLB
  4. resset函数跳转cpu_init_crit,再进入lowlevel_init函数。内部RAM初始化,设置临时堆栈(芯片级初始化)
  5. resset函数跳转_main函数,进入板级初始化 ,设置c语言运行环境。
  6. _main 函数首先设置堆栈基地址,为GD(全局变量)结构体分配内存
  7. _main函数调用board_init_f (c语言开始函数) ,重定位前板级初始化。设置GD各成员内存地址、初始化部分外设接口(串口)、打印板块信息。
  8. _main函数调用relocate_code、relocate_vectors函数,对uboot和中断向量表进行重定位
  9. _main函数清除.BSS段
  10. _main函数调用board_init_r ,重定位后板级初始化。设置外设接口、环境变量、中断等初始化。
  11. board_init_r函数调用run_main_loop 函数,倒计时等待中断输入命令,超时执行bootcmd启动内核

第二阶段(uboot)

uboot相关问题

  • 为什么要设置成svc模式?

    • svc模式属于特权模式,可以访问所有硬件受控资源。相对于其他的模式,SVC模式可以访问的资源更多。
  • 如何设置SVC模式

    • 设置CPSR(当前程序状态寄存器)寄存器
  • 为什么关闭中断?

    • 在启动过程中,中断环境并没有完全准备好,也就是中断向量表和中断处理函数并没有完成设置,一旦有中断产生,可能会导致预想不到的问题,或者是程序跑飞。
  • 如何关闭中断?

    • 设置CPSR(当前程序状态寄存器)寄存器
  • CPSR寄存器

    • 条件标志位
      在这里插入图片描述
    • 控制位
      在这里插入图片描述
      在这里插入图片描述
  • 为什么关闭MMU

    • MMU是用于虚拟地址向物理地址进行映射的一个结构。在 uboot阶段操作的就直接是 物理地址,所以不需要转换。
  • 为什么启动ICACHE(指令),关闭DCACHE(数据)

    • 启动指令CACHE课可以加快指令读取的速度,但是数据CACHE 必须 要关闭,因为它本身是一个CPU的二级缓存,在运行程序的时候可能会往里面去取数据,但是此时ram里面的数据可能并没有存入到里面,这就可能导致读取到错误的数据。
  • 为什么清理.BBS段

    • bss段都是未初始化的全局变量或者已经初始化为零的变量,本来就是零,直接清零就好。不清零的话未初始化的变量可能会存在未知的数值。
  • 为什么要进行uboot重定位

    • uboot 会将自己重定位到 DRAM(DDR) 最后面的地址区域,也就是将自己拷贝到 DRAM 最后面的内存区域中。这么做的目的是给 Linux 腾出空间,防止 Linuxkernel 覆盖掉 uboot,将 DRAM 前面的区域完整的空出来。
  • 程序编译时候我们会指定一个链接地址(也就是程序定位的运行地址),编译后,cpu的pc指针指向这个地址开始执行。但是如何解决uboot重定位后运行地址和链接地址不同。

    • uboot 对于重定位后链接地址和运行地址不一致的解决方法就是采用位置无关码,在使用 ld 进行链接的时候使用选项“-pie”生成位置无关的可执行文件。
    • 位置无关码的原理为:跳转目的函数地址存储在Label中,函数之间的地址相互是不变的,是一个确定相对关系。跳转时候使用地址无关跳转(固定的偏移量跳转)到Label中,然后Label中存储是真正的地址。
    • 如果发生uboot重定义,Label中存储真实地址都加上uboot的整体偏移量即可。

uboot 启动函数解析

uboot.lds(u-boot/arch/arm/cpu/u-boot.lds)
|---->vectors.S (arch/arm/lib/vectors.S)
|---->start.S (arch/arm/cpu/armv7/start.S)
|---|---->reset 
|---|---->save_boot_params 
|---|---->save_boot_params_ret 
        /*将处理器设置为svc模式,并关闭FIQ和IRQ :
            1) sys模式和usr模式相比,所用的寄存器组,都是一样的,但是增加了一些访问一些在usr模式下不能访问的资源.
            而svc模式本身就输入特权模式,本身就可以访问那些受控资源,而且比sys模式还多了写自己模式下的影子寄存器,所以,相对于sys模式来说,可以访问资源的能力相同,但是拥有更多的硬件资源.所以从理论上说,虽然可以设置为sys和svc模式的任一种,但是从uboot方面考虑,其要做的事情是初始化系统相关硬件资源,需要获取尽量多的权限,以方便操作硬件,初始化硬件.从uboot的目的是初始化硬件的角度来说,设置为svc模式,更有利于工作. 
            2) uboot作为一个bootloader来说,最终目的是为了启动linux的kernel,在做好准备工作(即初始化硬件,准备好kernel和rootfs等)跳转到kernel之前,
            本身就要满足一些条件,其中一个条件,就是要求cpu处于svc模式的,所以,uboot在最初的初始化阶段,就将cpu设置为svc模式,也是最合适的.*/
|---|---->cpu_init_crit  /* 仅调用函数lowlevel_init */
|---|---|---->lowlevel_init  /* 主要完成Ram的初始化,对寄存器的存取方式进行控制 ,我们将的第一个函数*/
|---|---->main(arch/arm/lib/crt0.S)
|---|---|---->board_init_f_alloc_reserve(common/init/board_init.c) 
            /* 留出早期的malloc内存区域和gd_t内存区域,这个的返回值就是gd_t的结构体的指针gd,
                这个指针保存在一个寄存器中,后续的传递,都是通过保存在寄存器的指针实现  */
|---|---|---->board_init_f_init_reserve(common/init/board_init.c)
            /*  初始化gd,指向gd_t结构体的指针(gd_t就是global_data的结构体,成员主要是一些全局的系统初始化参数),初始化就是清零.另外,还设置了early malloc的起始地址.在gd_t结构体中,还有一个重要的结构体成员,bd_t.主要是保存开发板的相关参数.uboot启动内存kernel是要传递参数给它,这个时候就要使用到gd_t,bd_t的信息来设置标记列表.所以这2个结构体非常重要  */
|---|---|---->board_init_f(common/board_f.c) /* 我们将的第二个函数 */
                /*1) 初始化DDR,串口,定时器,i2c,打印开发板cpu等信息.
                  2) 初始化gd的成员变量,uboot会将自己重定位到dram最后面的地址区域,也就是将自己拷贝到dram最后面的内存区域中,也就是将自己拷贝到dram最后面的内存区域中.这么做的目的是给linux腾空间,防止kernel覆盖掉uboot,将dram前面的区域完整的空出来.在拷贝之前肯定要给uboot各部分分配好内存位置和大小,比如gd应该存放到那个位置,malloc内存放到哪个位置(这些都放到gd中,指针保存到寄存器R9)。因此要对gd的成员变量初始化。在这里就将sp和gd存放到ddr,而不是内部的ram中,形成一个完整的内存图,后面重定位的时候要用到这个分配图。*/
|---|---|---|---->initcall_run_list(init_sequence_f) (lib/initcall.c)
                /* 运行初始化序列init_swquence_f里面的一系列的函数,函数序列在init_sequence_f中 */
|---|---|---|---|---->init_sequence_f[] (common/board_f.c)
                        /* 初始化gd结构相关的参数 */
|---|---|---|---|---|---->setup_ram_buf
|---|---|---|---|---|---->setup_mon_len /* 设置gd的mon,表示uboot代码的长度 */
|---|---|---|---|---|---->initf_malloc /* 初始化gd中malloc成员,设置malloc=0x400 */
                        /*初始化arm架构相关的东西 */
|---|---|---|---|---|---->arch_cpu_init /* basic arch cpu dependent setup */
|---|---|---|---|---|---->initf_dm /*初始化驱动模式 */
|---|---|---|---|---|---->mark_bootstage /*定义某些标志 */
|---|---|---|---|---|---->board_early_init_f /*与板子的早期初始化匹配,imx6ull用来初始化debug串口,主要完成相关引脚的复用配置和电气属性配置 */
|---|---|---|---|---|---->get_clocks /* 获取芯片内某些外设的时钟 */
|---|---|---|---|---|---->timer->init /* 初始化cortex-a7中的定时器 */
                        /* 和环境变量相关,设置gd的env_addr成员 */
|---|---|---|---|---|---->env_init /* 初始化gd结构中的env相关的成员,设置gd->env_addr,环境变量的保存地址 */
|---|---|---|---|---|---->init_baud_rate /*初始化串口波特率,设置gd->baudrate */
|---|---|---|---|---|---->serial_init /*初始化debug串口,会调用串口驱动设备中注册的start函数完成初始化 */
|---|---|---|---|---|---->console_init_f /*设置console标志位=1,标明前面定义的debug串口可以使用了 */
|---|---|---|---|---|---->display_options /*第一次开始在debug串口写(显示)信息,显示uboot版本和编译时间,时区等 ,uboot启动显示在终端的第一句*/
|---|---|---|---|---|---->display_text_info /*显示一些debug调试信息,前提是定义宏DEBUG,在这里我们可以在出现问题的时候开启调试功能 */
|---|---|---|---|---|---->print_cpuinfo /* 显示cpu信息,速度 */
|---|---|---|---|---|---->show_board_info /*显示板子的信息 包含evk信息 */
|---|---|---|---|---|---->init_func_i2c /* 初始化i2c接口,实际上初始化的是spd_bus */
|---|---|---|---|---|---->announce_dram_init /* 显示 DRAM: 字符串 */
|---|---|---|---|---|---->dram_init /*获取ddr大小,实际上没有初始化dram,仅仅设置gd->ram_size */
|---|---|---|---|---|---->setup_dest_addr /* 设置目的地址,gd_ram_top,gd->relocaddr */
|---|---|---|---|---|---->reserve_uboot
|---|---|---|---|---|---->reserve_round_4k /* gd->relocaddr 内存做4K对齐 */
|---|---|---|---|---|---->reserve_mmu /* 留出mmu的TLB表位置 */
|---|---|---|---|---|---->reserve_video 
|---|---|---|---|---|---->reserve_lcd
|---|---|---|---|---|---->reserve_trace /* 留出ddr调试追踪的内存位置 */
|---|---|---|---|---|---->reserve_uboot /* 留出重定位uboot占用的位置 */
|---|---|---|---|---|---->reserve_malloc /* 留出malloc的内存位置和env的内存大小 */
|---|---|---|---|---|---->reserve_board /* 留出bd所占用的内存大小(80字节) */
|---|---|---|---|---|---->setup_machine /*对于6ull 无效 */
|---|---|---|---|---|---->reserve_global_data /* 留出gd_t结构的内存大小 248字节 */
|---|---|---|---|---|---->reserve_fdt /* 留出设备树的内存大小,6ul无效 */
|---|---|---|---|---|---->reserve_arch /* */
|---|---|---|---|---|---->reserve_stacks /* 留出栈空间(16字节)并做16字节对齐 */
|---|---|---|---|---|---->setup_dram_config /* 设置dram信息,显示dram位置,运行到这里就知道预留多大的内存,同时显示内存数据,
                            和上面的announce_dram_init函数共同显示: DRAM : 256 MiB*/
|---|---|---|---|---|---->display_new_sp /* 显示新的sp位置,一般为 New Stack Pointer is: 0x8ef1ae90 */
|---|---|---|---|---|---->reloc_fdt /* 重定位fdt ,无效 */
|---|---|---|---|---|---->setup->reloc /* 设置gd结构体的一些其他成员,到这里内存分配就完成了 */
|---|---|---|---|---|---->copy_uboot_to_ram /* 拷贝uboot代码到内存 */
|---|---|---|---|---|---->clear_bss /* 做一些清除收尾工作 */
|---|---|---|---|---|---->jump_to_copy /* 跳转到下面的relocate->code函数 */
|---|---|---->relocate_code(arch/arm/lib/relocate.S)
            /* 代码重定位,首先获取镜像的起始地址R0,然后根据计算,得到镜像的结束地址R2 */
|---|---|---|---->copy_loop
                /* 完成代码拷贝工作,将uboot拷贝的ddr中 */
|---|---|---->relocate_vectors 
            /*  中断向量表做重定位,将中断向量表的地址地址放到寄存器里面,重定位后,首地址就是uboot的首地址 */    
            /*  到这里,真正的C环境就建立起来了,接下来的所有工作都是ddr中了. */
|---|---|---->board_init_r(common/board_r.c) /* 第三个函数开始 */
            /*  调用一系列的函数来完成board_f. c没有完成的初始化工作 ,顺序放到init_sequencd_f[]中*/
|---|---|---|---->initcall_run_list(init_sequence_f) (lib/initcall.c)
|---|---|---|---|---->initr_trace /* 初始化buffer和跟踪相关,如果定义了config_trace宏的话,后面会执行trace_init函数,初始化与调试跟踪相关参数 */
|---|---|---|---|---->initr_reloc(common/board_r.c)/* 设置gd->flag表示relocation重定位完成 */
|---|---|---|---|---->initr_caches /* 调用initr_caches 使能芯片caches */
|---|---|---|---|---->initr_reloc_global_data /* 初始化重定位后gd的一些成员变量 */
|---|---|---|---|---->initr_malloc /* 初始化malloc内存区域 */
|---|---|---|---|---->initr_console_record /* 初始化控制台相关参数,对于6ul为空 */
|---|---|---|---|---->bootstage_relocate /* 启动状态重定位,用于重定位bootstage相关的东西 */
|---|---|---|---|---->initr_bootstage /* 初始化bootstage相关的东西 */
|---|---|---|---|---->board_init(board/freescale/mx6ul_14X14_evk/mx6ul_14x14_evk.c) () 
                        /* Setup chipselects,判定宏CONFIG_ARM是否定义,6ull有定义,调用board_init函数,该函数与板级相关,                        定义文件为uboot/board/freescale/mx6ul_comp6ul_nand/mx6ul_comp6ul_nand.c,通过函数可以看到包含函数,setup_i2c,setup_fec,setup_usb,board_qspi_init,setup_gpmi_nand主要是用来初始化板子上的一些外设,如gpio,i2c,网络等相关接口 */
|---|---|---|---|---->set_cpu_clk_info /* 设置与始终相关的信息 */
|---|---|---|---|---->stdio_init_tables /* stdio相关的初始化 */
|---|---|---|---|---->initr_serial /* serial接口初始化 */
|---|---|---|---|---->initr_announce /* 用于调试相关的内容,通知已经在dram中运行 */
|---|---|---|---|---->initr_nand /* 初始化nand flash 并在串口以字符串型式输出nand flash大小,根据宏定义,来判断是否初始化 -> initr_nand()*/
|---|---|---|---|---->initr_mmc /* 初始化sd/mmc相关的接口,宏定义键/include/imx6_common.h */
|---|---|---|---|---->initr_env /* 初始化环境变量 */
|---|---|---|---|---->initr_secondary_cpu /* 初始化其他的cpu,6ul只有一个cpu无效 */
|---|---|---|---|---->stdio_add_devices /* 初始化各种输入输出设备,例如LCD */
|---|---|---|---|---->initr_jumptable /* 初始化跳转表相关的内容 */
|---|---|---|---|---->console_init_r /* 完成控制台的初始化,调用后,uboot输出
                    例如: In:    serial
                        Out:   serial
                        Err:   serial
                        */
|---|---|---|---|---->show_board_info /* 显示开发板相关信息 */
|---|---|---|---|---->interrupt_init (arch/arm/lib/interrupts.c) /* 初始化中断相关 */
|---|---|---|---|---->initr_enable_interrupts /* 使能中断 */
|---|---|---|---|---->timer_init /* 初始化定时器 */
|---|---|---|---|---->initr_status_led /*初始化运行状态灯 */
|---|---|---|---|---->initr_ethaddr /* 初始化网络,获得mac地址 */
|---|---|---|---|---->board_late_init /* 后期外设初始化,紧跟着调用initr_net 初始化开发板的网络设备,并在串口输出信息 */
|---|---|---|---|---->initr_fastboot_setup /* 初始化快速启动信息 */
|---|---|---|---|---->initr_check_fastboot /*检查快速启动配置 */
|---|---|---|---|---->lt8618_init /* 初始化8618芯片驱动 */
|---|---|---|---|---->run_main_loop(common/board_r.c) /* 执行一个死循环,循环执行main_loop */
|---|---|---|---|---|---->main_loop(common/main.c)
|---|---|---|---|---|---|---->setenv /* 设置环境变量,后面会用到 */
|---|---|---|---|---|---|---|---->version_string[] (cmd/version.c)  /* 版本号,编译日期和时间,时区 */
|---|---|---|---|---|---|---|---|---->include/version.h
|---|---|---|---|---|---|---->run_preboot_environment_command(common/main.c) /* 从环境变量中获得preboot的定义,一般不用 */
|---|---|---|---|---|---|---->bootstage_mark_name /* 用于打印启动进度 */
|---|---|---|---|---|---|---->cli_init /* 命令初始化 ,初始化shell相关的变量,配置以后就可以像操作终端一样,操作uboot */
|---|---|---|---|---|---|---->bootdelay_process(common/autoboot.c)
                            /* 读取环境变量bootdelay 和bootcmd内容,将bootdelay赋值给全局变量stored_bootdelay,将环境变量bootcmd的值作为返回值 */
|---|---|---|---|---|---|---|---->getenv('bootcmd') (common/autoboot.c)
|---|---|---|---|---|---|---|---|---->bootcmd=CONFIG_BOOTCOMMAND
|---|---|---|---|---|---|---->autoboot_command(common/autoboot.c)
                                /* 倒计时按下执行,没有操作执行bootcmd的参数 */
|---|---|---|---|---|---|---|----> abortboot (common/autoboot.c line 385)
|---|---|---|---|---|---|---|---|---->abortboot_normal 
                                    /* 倒计时处理函数,没有键盘输入,倒计时返回0,开始启动内核,如果有键盘输入返回1 ,不进入内核,走cli_loop函数 */
|---|---|---|---|---|---|---|----> run_command_list(common/cli.c line 72)
                                    /*根据abortboot返回的值,判断是否执行函数run_command_list,执行的command指令从上面的bootdelay_process函数获得,实际上就是uboot的env的值,从这里就启动内核了,如6ull-S2的env如下:
                                    bootcmd=run nandargs;run bootnand; 
                                    nandargs=setenv bootargs console=ttymxc0,115200  root=/dev/mtdblock5 rw rootfstype=yaffs2
                                    bootnand=nand read ${loadaddr} 0xa00000 0x800000;nand read ${fdt_addr} ${nand_addr} 0x40000;bootz ${loadaddr} - ${fdt_addr};
                                    从这里就看到,设定串口属性,设定地址,然后使用bootz启动内核*/                                  
|---|---|---|---|---|---|---->cli_loop(common/cli.c)  
                                /* 倒计时按下space键,执行用户输入命令 */
|---|---|---|---|---|---|---|---->parse_file_outer 
                                /* 如果倒计时结束前有按键输入,执行cli_loop函数 */
|---|---|---|---|---|---|---|---|---->setup_file_in_str /* 初始化input的成员变量  */
|---|---|---|---|---|---|---|---|---->parse_stream_outer /* hush shell的命令解释器 ,负责接收命令行输入,并解析执行命令 */
|---|---|---|---|---|---|---|---|---|---->parse_stream /* 解析并得到命令 */
|---|---|---|---|---|---|---|---|---|---->run_list /* 运行命令 */
|---|---|---|---|---|---|---|---|---|---|---->cmd_process /* 处理命令 */
|---|---|---|---|---|---|---|---|---|---|---|---->find_cmd /* 在命令列表中找到指定的指令(从u_boot_list里面查找,找到后,返回对应的结构体变量,cmd_tbl_t) */
|---|---|---|---|---|---|---|---|---|---|---|---->cmd_call  
                                                /* 执行具体的指令,通过直接引用命令的结构体变量cmd_tbl_t中的成员变量cmd,
                                                    cmd的成员变量就是命令出来函数do_XXX,
                                                    在命令行中,通过bootz命令启动内核 */
                                                    /* 内核其中后,控制权移交给内核 */

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

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

相关文章

二叉树基础oj练习

1.单值二叉树 题目: 力扣https://leetcode.cn/problems/univalued-binary-tree/ 思路: 单值二叉树 root和左右孩子的值相等 左子树是单值二叉树 右子树是单值二叉树 代码: /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeN…

CSS 艺术之心形-彩虹-加载动画

CSS 艺术之心形-彩虹-加载动画(居中抖动问题)参考描述效果HTMLCSS重置元素的部分默认样式bodyli动画定义指定animationul居中抖动问题代码总汇参考 项目描述搜索引擎BingMDNMDN Web Docs 描述 项目描述Edge109.0.1518.61 (正式版本) (64 位) 效果 HTML…

【Vue笔记】Vue中默认插槽slot、具名插槽slot、自定义组件的v-model属性的使用

这篇文章,主要介绍Vue中默认插槽slot、具名插槽slot、自定义组件的v-model属性的使用。 目录 一、插槽slot介绍 1.1、默认插槽 1.2、具名插槽 (1)如何定义具名插槽??? (2)如何使…

3DMAX纹理插件BerconMaps使用教程

BerconMaps是3dMax的第三方纹理插件。它添加了五个新的3dMax纹理贴图:噪波(Noise)、木纹(Wood)、瓷砖(Tile)、失真(Distortion)和渐变(Gradient)。…

linux配置samba服务

文章目录samba服务第一步:下载samba服务第二步:开启服务,查看服务状态第三步:在文件夹系统查看第四步:设置samba的配置文件第五步:再次去尝试连接linux服务共享修改smab服务的配置设置samba服务的用户名和密…

verilog实现计算器设计

该实验为用verilog编写的一个运算系统,其功能是实现4位整数的加、减、乘、除运算。运算时通过矩阵键盘输入运算类型和运算所需要的数据,然后通过内部电路处理,将计算的结果送于数码管或LCD1602显示。 工程截图如下: 本设计分为两个子模块,按键输入和数码管输出。 还有LC…

python字符串

python字符串 文章目录python字符串一、实验目的二、实验原理三、实验环境四、实验内容五、实验步骤1.字符串创建2.转义字符3.运算符4.格式化5.三引号6.内建函数总结一、实验目的 掌握字符串的用法 二、实验原理 字符串 字符串是一种序列,用于表示和存储文本。py…

Ubuntu16.04安装Chrome出错

安装了Ubuntu16.04之后想要装个Chrome,本来觉得是很简单的事情,下载->安装就结束了,结果没想到搞了好久,最后发现是因为自己偷懒所造成的!因此,写下这篇博文做警示,如果其他人也碰到这样的问…

20230124使AIO-3568J开发板在Android12下横屏

20230124使AIO-3568J开发板在Android12下横屏 2023/1/24 14:05 百度搜索:RK3568 强制横屏 http://www.360doc.com/content/12/0121/07/29321110_1049371522.shtml RK3568 Android-HDMI旋转屏幕显示 硬件开发板:OK3568-C开发板(基于国产瑞芯微…

【Leetcode每日一题】34.在排序数组中查找元素的第一个和最后一个位置|二分求下标

🌱博主简介:大一计科生,努力学习Java中!热爱写博客~预备程序媛 📜所属专栏:LeetCode每日一题–进击大厂 ✈往期博文回顾: 【Leetcode每日一题】35.搜素插入位置|二分查找数组下标 🕵️‍♂️近期目标&#…

恶意代码分析实战 5 分析恶意Windows程序

1 Lab07-01 本次实验分析lab07-01.exe,lab07-02.exe,先来看lab07-01.exe的问题 问题 计算机重启后,这个程序如何确保它继续运行(达到持久化驻留) 首先,查看导入函数。 OpenSCManagerA和CreateServiceA函数暗示着这个恶意代码…

蓝队-HTTP协议的分析

文章目录应用层的协议→HTTP协议访问地址web网站的正确访问姿势请求协议https访问的密钥文件网站的部署通过https协议访问请求报文响应报文请求方法http头协议cookiesRefererUser-AgentX-Forwarded-ForHTTP 状态码HTTP content-type抓包分析HTTP抓包代理插件的下载代理第一种方…

电商项目之同一笔单多次收款成功

1 问题背景 有个收单系统(简称S系统),作用是收单,相当于支付渠道,能够作为第三方服务对接其他支付平台。电商系统(简称A系统)与S系统是隔离的。A系统发起支付请求给S系统,S系统包装一…

Java高效率复习-坦克大战[Java基础合集]

目录 前言 该文章会从零开始制作一个坦克大战的游戏,该游戏使用Java的AWT和Swing作为窗体和容器,使用面向对象、线程等技术来丰富游戏,本文章相等于Java前期和中期技术的一个合集(对于IO、网络、反射并没有涉及到)。 …

高级Spring之Bean 生命周期

老样子,话不多说,直接上代码: 准备容器: SpringBootApplication public class A03 {public static void main(String[] args) {ConfigurableApplicationContext context SpringApplication.run(A03.class, args);context.close(…

【价格型需求响应】基于Logistic函数的负荷转移率模型需求响应研究(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

前端学习——CSS

文章目录1.CSS1.1什么是CSS1.2快速入门1.3.三种CSS导入方式2.选择器2.1基本选择器2.1.1标签选择器2.1.2类选择器2.1.3id选择器2.2层次选择器2.2.1后代选择器2.2.2子选择器2.2.3相邻兄弟选择器2.2.4通用选择器2.3结构伪类选择器2.4属性选择器3.美化网页元素3.1span标签3.2字体样…

swagger测试Restful API

swagger出现背景 我们构建Restful API的目的通常都是由于多终端的原因,这些终端会共用很多底层业务逻辑,因此我们会抽象出这样一层来同时服务于多个移动端或者Web前端,不同端由不同的团队或者小组负责,为了减少与其他团队平时开发…

LabVIEW将台式电脑转换为 Phar Lap ETS 实时目标

LabVIEW将台式电脑转换为 Phar Lap ETS 实时目标注意:NI将在NI2020软件版本中删除对cRIO的Pharlap和NI2022软件版本中对PXI的支持。更多信息,请参阅PharLapRTOSEOL路线图。您可能已经拥有一台台式PC,可以作为下一个可靠的确定性测试或控制系统…

初识 Express(基于 NodeJS http 模块封装的 Web 框架)

初识 Express(基于 NodeJS http 模块封装的 Web 框架)参考描述Express获取nodemon获取使用使用创建一个基本的服务器基本路由对客户端的 GET 请求进行响应获取通过 GET 方式提交的 URL 参数静态 URL 参数动态 URL 参数静态资源托管服务器public前缀参考 …