Linux 内核启动分析

news2024/11/19 17:22:30

Linux 内核启动分析-BugMan-ChinaUnix博客

通过《Linux应用程序elf描述》,我们了解到一个应用程序编译后,最终会按照指定方式进行链接,而我们通过ld --verbose可以查看对应应用的默认链接方式。那么对于Linux内核呢?毫无疑问, Linux内核也是按照指定格式进行链接的,只是Linux的链接方式是由arch/arm64/kernel/vmlinux.lds.S指定的(gcc可以在链接的时候指定自定义链接脚本-T)。本章基于Linux内核Linux-4.19.73来作为例子说明的。

首先,我们看看vxlinux.lds.S为何物,如下图:

上图是一个删减了很多其他暂时不关心段的vmlinux.ld.s脚本文件,vmlinux.ld.s脚本文件的语法,这里不作出介绍, 关注的可以去查看相关文档。通过vmlinux.ld.s我们可以看到,bin文件的入口被设置为ENTRY(_text),  因此,_text即为入口函数地址,那么_text又在那里呢?那么接着看这个脚本文件,我们发现在灰色框里面有一个_text = .; 这说明_text的地址就是.head.text的首地址。那么哪些数据被链接到.head.text段了呢?通过搜索发现

点击(此处)折叠或打开

  1. #define HEAD_TEXT  KEEP(*(.head.text))
  2. #define __HEAD .section ".head.text","ax"
  3. $ grep __HEAD arch/arm64/ -r
  4. include/linux/init.h:#define __HEAD             .section        ".head.text","ax"
  5. $grep __HEAD arch/arm64/ -r
  6. arch/arm64/kernel/head.S:       __HEAD

因此, 目前只有kernel/head.S有代码被放置在了.head.text段,我们进一步看看arch/arm64/kernel/head.S文件,如下:
点击(此处)折叠或打开

  1. #define __PHYS_OFFSET   (KERNEL_START - TEXT_OFFSET) // 内核物理地址起始位置
  2. __HEAD
  3. _head:
  4.     b stext // branch to kernel start, magic
  5.     .long 0 // reserved
  6.     le64sym _kernel_offset_le // Image load offset from start of RAM, little-endian
  7.     le64sym _kernel_size_le // Effective size of kernel image, little-endian
  8.     le64sym _kernel_flags_le // Informative flags, little-endian
  9.     .quad 0 // reserved
  10.     .quad 0 // reserved
  11.     .quad 0 // reserved
  12.     .ascii "ARM\x64" // Magic number
  13.     .long 0 // reserved
  14. __INIT
  15. ENTRY(stext)
  16.     bl  preserve_boot_args
  17.     bl  el2_setup           // Drop to EL1, w0=cpu_boot_mode
  18.     adrp    x23, __PHYS_OFFSET // 物理地址偏移
  19.     and x23, x23, MIN_KIMG_ALIGN - 1    // KASLR offset, defaults to 0,一种内核安全机制,通过物理地址起始位置计算出偏移大小,偏移大小保存在X23寄存器
  20.     bl  set_cpu_boot_mode_flag
  21.     bl  __create_page_tables
  22.     bl  __cpu_setup         // initialise processor
  23.     b   __primary_switch
  24. ENDPROC(stext)
  25. $ grep __INIT include/ -r
  26. include/linux/init.h:#define __INIT             .section        ".init.text","ax"
  27. $

从上面代码可以看出,只有_head被放在了.head.text段,而下面的stext是放在.init.text段的。因此,当前版本的Linux kernel的入口函数就是_head函数, 而_head函数就只有一条跳转指令:b stext;因此内核启动后, 最终去stext函数运行。而stext主要调用了几个函数,他们的作用如下:

1、preserve_boot_args: 将uboot传入的参数 保存到bootargs[4] 全局变量里面。

2、el2_setup :判断启动的模式是el2还是el1并进行相关级别的系统配置(armv8中el2是hypervisor模式,el1是标准的内核模式,具体的参考手册),  然后返回启动模式

3、set_cpu_boot_mode_flag: 将启动模式保存到全局变量

4、__create_page_tables: 创建内存映射表,一共两张,一张存放在swapper_pg_dir(线性映射),一张存放在idmap_pg_dir(一对一映射)。

5、__cpu_setup : 初始化处理器相关的代码,配置访问权限,内存地址划分等。

6、__primary_switch :开启MMU, 准备0号进程和内核栈,然后跳转到start_kernel运行

首先,我们说说preserve_boot_args函数, 它的实现如下:

点击(此处)折叠或打开

  1. preserve_boot_args:
  2.     mov x21, x0 // 默认x0是uboot传入的第一个参数,通常是fdt的基地址,这里给x21寄存器保存
  3.     adr_l x0, boot_args //adr指令读取boot_args变量的当前地址,而不是链接地址(因为此时还没没有创建映射表,链接地址占时还不能用),boot_args是一个全局变量,默认地址是链接地址。
  4.     stp x21, x1, [x0] // 将uboot传入的第一个参数和第二个参数保存到boot_args的[0],[1]里面,表示地址和大小
  5.     stp x2, x3, [x0, #16] // 将uboot传入的第三个核第四个参数保存到boot_args的[2],[3]变量里面
  6.     dmb sy // 数据存储器栅栏,具体作用参考汇编手册
  7.     mov x1, #0x20 // boot_args有四个变量,每个变量8字节大小,因此 x1存入boot_args的长度(x0是boot_args的地址),然后调用_inval_dcache_area无效这段地址的缓存
  8.     b __inval_dcache_area // 无效x0和x1指定区域的缓存
  9. ENDPROC(preserve_boot_args)

其次是el2_setup 函数, 它的实现如下:

点击(此处)折叠或打开

  1. ENTRY(el2_setup)
  2.     msr SPsel, #1 // 设置SP的使用方式,是各用各的 还是共用一个,这里设置的是各用各的(armv8的栈使用)
  3.     mrs x0, CurrentEL // 读取当前的EL模式
  4.     cmp x0, #CurrentEL_EL2 // 判断当前的模式是不是el2,是 就跳转到el2的处理代码
  5.     b.eq 1f
  6.     mov_q x0, (SCTLR_EL1_RES1 | ENDIAN_SET_EL1) // 配置el1模式
  7.     msr sctlr_el1, x0
  8.     mov w0, #BOOT_CPU_MODE_EL1 // 返回值设置成 el1模式启动,注:w0是32位寄存器,通常x0/w0作为函数返回值使用
  9.     isb
  10.     ret
  11. // 下面是el2,即hypervisor模式的处理代码,这里不介绍,在hypervisor会有介绍
  12. 1: mov_q x0, (SCTLR_EL2_RES1 | ENDIAN_SET_EL2)
  13.     msr sctlr_el2, x0
  14.     .............
  15.     eret
  16. ENDPROC(el2_setup)

然后set_cpu_boot_mode_flag函数用于保存启动模式,该函数实现如下:

点击(此处)折叠或打开

  1. set_cpu_boot_mode_flag:
  2.     adr_l x1, __boot_cpu_mode //将_boot_cpu_mode的物理地址读取到x1寄存器
  3.     cmp w0, #BOOT_CPU_MODE_EL2 // w0是el2_setup返回的值,即模式
  4.     b.ne 1f
  5.     add x1, x1, #4
  6. 1: str w0, [x1] // This CPU has booted in EL1,将模式保存到_boot_cpu_mode变量
  7.     dmb sy
  8.     dc ivac, x1 // Invalidate potentially stale cache line
  9.     ret
  10. ENDPROC(set_cpu_boot_mode_flag)

对于__create_page_tables,则主要是创建内存映射表(这里只是简单的映射,只把内核代码段映射进来,用于开启MMU),后期还会做出二次映射。在映射函数中,有两种映射,一个是直接映射(即va=pa, 用于处理开启mmu那一瞬间不会出现异常),一个是线性映射(va = pa + offset)。具体函数如下:

点击(此处)折叠或打开

  1. __create_page_tables:
  2.     mov x28, lr
  3.     // 无效 idmap_pg_dir和swpper_pg_end直接的数据缓存
  4.     adrp x0, idmap_pg_dir
  5.     adrp x1, swapper_pg_end
  6.     sub x1, x1, x0
  7.     bl __inval_dcache_area
  8.     // 清楚idmap和swapper映射表里的脏数据
  9.     adrp x0, idmap_pg_dir
  10.     adrp x1, swapper_pg_end
  11.     sub x1, x1, x0
  12. 1: stp xzr, xzr, [x0], #16
  13.     stp xzr, xzr, [x0], #16
  14.     stp xzr, xzr, [x0], #16
  15.     stp xzr, xzr, [x0], #16
  16.     subs x1, x1, #64
  17.     b.ne 1b
  18.     // mmu也属性标记
  19.     mov x7, SWAPPER_MM_MMUFLAGS
  20.     //创建直接映射 idmap,从idmap_text_start到idmap_text_end
  21.     adrp x0, idmap_pg_dir
  22.     adrp x3, __idmap_text_start // __pa(__idmap_text_start)
  23.     adrp x5, __idmap_text_end
  24.     clz x5, x5
  25.     cmp x5, TCR_T0SZ(VA_BITS) // default T0SZ small enough?
  26.     b.ge 1f // .. then skip VA range extension
  27.     adr_l x6, idmap_t0sz
  28.     str x5, [x6]
  29.     dmb sy
  30.     dc ivac, x6 // Invalidate potentially stale cache line
  31.     mov x4, #1 << (PHYS_MASK_SHIFT - PGDIR_SHIFT)
  32.     // VA_BITS = 48bit
  33.     str_l   x4, idmap_ptrs_per_pgd, x5
  34.     ldr_l   x4, idmap_ptrs_per_pgd
  35.     mov x5, x3              // __pa(__idmap_text_start)
  36.     adr_l   x6, __idmap_text_end        // __pa(__idmap_text_end)
  37.     // map_memory用于映射, 具体怎么写映射表, 参考armv8体系结构
  38.     map_memory x0, x1, x3, x6, x7, x3, x4, x10, x11, x12, x13, x14
  39.     // 线性映射内核代码段, 存放到swapper_pg_dir, 从_text段开始到_end之间的数据
  40.     adrp    x0, swapper_pg_dir
  41.     mov_q   x5, KIMAGE_VADDR + TEXT_OFFSET  // compile time __va(_text)
  42.     add x5, x5, x23         // add KASLR displacement
  43.     mov x4, PTRS_PER_PGD
  44.     adrp    x6, _end            // runtime __pa(_end)
  45.     adrp    x3, _text           // runtime __pa(_text)
  46.     sub x6, x6, x3          // _end - _text
  47.     add x6, x6, x5          // runtime __va(_end)
  48.     // map_memory用于映射, 具体怎么写映射表, 参考armv8体系结构
  49.     map_memory x0, x1, x5, x6, x7, x3, x4, x10, x11, x12, x13, x14
  50.     // 无效映射表对应的缓存
  51.     adrp    x0, idmap_pg_dir
  52.     adrp    x1, swapper_pg_end
  53.     sub x1, x1, x0
  54.     dmb sy
  55.     bl  __inval_dcache_area
  56.     ret x28
  57. ENDPROC(__create_page_tables)

注:__idmap_text_start到__idmap_text_end的数据,其实就是启用mmu前后,需调用的那几个函数(因为CPU有加速指令处理的关系, 有些指令是乱序执行,防止开启mmu后,因为地址空间切换,导致的代码混乱的问题),因为有一段是va=pa因此, 之后即使还有code在用老的物理地址,也是不会出问题的。

__cpu_setup主要设置一些访问属性和内存划分等 ,具体函数如下:

点击(此处)折叠或打开

  1. ENTRY(__cpu_setup)
  2.     tlbi vmalle1   // 无效TLB
  3.     dsb nsh
  4.     mov x0, #3 << 20
  5.     msr cpacr_el1, x0 // 使能FP/ASIMD
  6.     mov x0, #1 << 12
  7.     msr mdscr_el1, x0 // 允许EL0访问DCC
  8.     isb 
  9.     reset_pmuserenr_el0 x0 // 设置EL0禁止PMU访问
  10.     /*
  11.      * LPAE内存属性:
  12.      *
  13.      * n = AttrIndx[2:0]
  14.      * n MAIR
  15.      * DEVICE_nGnRnE 000 00000000
  16.      * DEVICE_nGnRE 001 00000100
  17.      * DEVICE_GRE 010 00001100
  18.      * NORMAL_NC 011 01000100
  19.      * NORMAL 100 11111111
  20.      * NORMAL_WT 101 10111011
  21.      */
  22.     ldr x5, =MAIR(0x00, MT_DEVICE_nGnRnE) | \
  23.              MAIR(0x04, MT_DEVICE_nGnRE) | \
  24.              MAIR(0x0c, MT_DEVICE_GRE) | \
  25.              MAIR(0x44, MT_NORMAL_NC) | \
  26.              MAIR(0xff, MT_NORMAL) | \
  27.              MAIR(0xbb, MT_NORMAL_WT)
  28.     msr mair_el1, x5
  29.     mov_q x0, SCTLR_EL1_SET
  30.     /*
  31.      * 设置 TCR and TTBR. 用户和内核采用512GB (39-bit) 地址
  32.      */
  33.     ldr x10, =TCR_TxSZ(VA_BITS) | TCR_CACHE_FLAGS | TCR_SMP_FLAGS | \
  34.             TCR_TG_FLAGS | TCR_KASLR_FLAGS | TCR_ASID16 | \
  35.             TCR_TBI0 | TCR_A1
  36.     tcr_set_idmap_t0sz x10, x9
  37.     /*
  38.      * Set the IPS bits in TCR_EL1.
  39.      */
  40.     tcr_compute_pa_size x10, #TCR_IPS_SHIFT, x5, x6
  41.     msr tcr_el1, x10
  42.     ret
  43. ENDPROC(__cpu_setup)

最后__primary_switch准备好0号进程栈,然后切换到start_kernel运行,具体代码实现如下:

点击(此处)折叠或打开

  1. __primary_switch:
  2. #ifdef CONFIG_RANDOMIZE_BASE
  3.     mov x19, x0 // preserve new SCTLR_EL1 value
  4.     mrs x20, sctlr_el1 // preserve old SCTLR_EL1 value
  5. #endif
  6.     bl __enable_mmu   // 开启mmu ,就是只是配置一些MMU寄存器
  7. #ifdef CONFIG_RELOCATABLE
  8.     ......  // 这里省略掉 内核代码重定位代码,这个主要用于gdb调试驱动
  9. #endif
  10.     ldr x8, =__primary_switched  // 将内核的物理地址起始地址作为参数1,调用_primary_switched函数
  11.     adrp x0, __PHYS_OFFSET
  12.     br x8
  13. ENDPROC(__primary_switch)

  1. union thread_union {
  2.      unsigned long stack[THREAD_SIZE/sizeof(long)];
  3. } init_thread_union;

  1. __primary_switched:
  2.     adrp    x4, init_thread_union  // 读取0号进程的thread_union地址
  3.     add sp, x4, #THREAD_SIZE       // 将init_thread_union  +THREAD_SIZE作为内核线程的栈顶地址
  4.     adr_l   x5, init_task          // 读取0号进程的task_struct结构
  5.     msr sp_el0, x5                 // 在内核空间中,将当前task_sturct给sp_el0保存
  6.     adr_l   x8, vectors         // 设置中断向量表,vector在中断章节说明
  7.     msr vbar_el1, x8            // 系统寄存器vector table address
  8.     isb
  9.     str_l   x21, __fdt_pointer, x5      // X21存放的是fdt指针, 这里将fdt保存到__fdt_pointer
  10.     // 下面是保存虚拟地址和物理地址之差到kimg_voffset变量
  11.     ldr_l   x4, kimage_vaddr  // 获取到内核虚拟起始地址
  12.     sub x4, x4, x0    // x0是传参传入的 内核物理起始地址
  13.     str_l   x4, kimage_voffset, x5
  14.     // 将内核BSS段 请0
  15.     adr_l   x0, __bss_start
  16.     mov x1, xzr
  17.     adr_l   x2, __bss_stop
  18.     sub x2, x2, x0
  19.     bl  __pi_memset
  20.     dsb ishst               // Make zero page visible to PTW
  21. #ifdef CONFIG_KASAN
  22.     bl  kasan_early_init  // 一种内存调试手段初始化
  23. #endif
  24.     mov x30, #0  // x30是Lr寄存器, 这里赋值成NULL,不需要返回,返回即异常
  25.     b   start_kernel // 跳转到start_kernel运行
  26. ENDPROC(__primary_switched)

最后Linux内核进入C代码空间,start_kernel。

注: PAGE_OFFSET是内核虚拟地址起始地址, PAGE_SHIFT是页大小位数, TEXT_OFFSET是内核代码起始位置到内核起始地址的偏移。
注:在32位CPU中, 内核通常会保留开始的32k(0x8000)地址,前16k(0x4000)保存bootargs参数,后16k用于保存pgd,因此可以看到内核的代码地址基本都是0x8000开始,如0xC0008000.
注:vectors向量表位于:"arch/arm64/kernel/entry.S"文件中,实现如下:
点击(此处)折叠或打开

  1. ENTRY(vectors)
  2.     kernel_ventry 1, sync_invalid // Synchronous EL1t
  3.     kernel_ventry 1, irq_invalid // IRQ EL1t
  4.     kernel_ventry 1, fiq_invalid // FIQ EL1t
  5.     kernel_ventry 1, error_invalid // Error EL1t
  6.     kernel_ventry 1, sync // Synchronous EL1h
  7.     kernel_ventry 1, irq // IRQ EL1h
  8.     kernel_ventry 1, fiq_invalid // FIQ EL1h
  9.     kernel_ventry 1, error // Error EL1h
  10.     kernel_ventry 0, sync // Synchronous 64-bit EL0
  11.     kernel_ventry 0, irq // IRQ 64-bit EL0
  12.     kernel_ventry 0, fiq_invalid // FIQ 64-bit EL0
  13.     kernel_ventry 0, error // Error 64-bit EL0
  14. #ifdef CONFIG_COMPAT
  15.     kernel_ventry 0, sync_compat, 32 // Synchronous 32-bit EL0
  16.     kernel_ventry 0, irq_compat, 32 // IRQ 32-bit EL0
  17.     kernel_ventry 0, fiq_invalid_compat, 32 // FIQ 32-bit EL0
  18.     kernel_ventry 0, error_compat, 32 // Error 32-bit EL0
  19. #else
  20.     kernel_ventry 0, sync_invalid, 32 // Synchronous 32-bit EL0
  21.     kernel_ventry 0, irq_invalid, 32 // IRQ 32-bit EL0
  22.     kernel_ventry 0, fiq_invalid, 32 // FIQ 32-bit EL0
  23.     kernel_ventry 0, error_invalid, 32 // Error 32-bit EL0
  24. #endif
  25. END(vectors)

注:armv8中,每个异常的 向量地址不再是4字节,而是0x80字节,可以放更多的代码在向量表里面。

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

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

相关文章

大模型背景下软件工程的机遇与挑战

点击链接了解详情 本文作者&#xff1a;汪晟杰 导语:AISE&#xff08;AI Software Engineering&#xff09;有人说是软件工程 3.0&#xff0c;即基于大模型&#xff08;LLM - Large Language Model&#xff09;时代下的软件工程。那么究竟什么是 AISE&#xff0c;他的发展历程对…

Java学习入门偏(2)

⭐简单说两句⭐ 作者&#xff1a;后端小知识 CSDN个人主页&#xff1a;后端小知识 &#x1f50e;GZH&#xff1a;后端小知识 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; Hello&#xff0c;亲爱的各位友友们&#xff0c;好久不见&#xff0…

官媒代运营:内容营销是什么?为什么要做内容营销?

在当今数字化时代&#xff0c;信息传播的速度和途径前所未有地广泛&#xff0c;企业们正在寻找新的方法来吸引潜在客户并保持现有客户的忠诚度。在这个背景下&#xff0c;内容营销崭露头角&#xff0c;成为了企业推广和营销的一项重要策略。 今天媒介易来跟大家分享干货知识&am…

Mini小主机All-in-one搭建教程6-安装苹果MacOS系统

笔者使用的ESXI7.0 Update 3 抱着试试的态度想安装一下苹果的MacOS系统 主要步骤有2个 1.解锁unlocker虚拟机系统 2.安装苹果MacOS系统 需要下载的文件 unlocker 这一步是最耗时间的&#xff0c;要找到匹配自己系统的unlocker文件。 https://github.com/THDCOM/ESXiUnloc…

VMware vCenter Server 6.7安装过程记录

0、前言 最近由于一些原因需要安装测试VMware ESXi&#xff0c;无奈所有服务器都是十几年前的&#xff0c;配置低也不支持。后来通过VMware兼容性列表查询&#xff0c;快要放弃的时候发现唯一一台Dell R420&#xff0c;如获至宝。通过查询得知最高支持到6.5 U3&#xff0c;好在…

214. Devu和鲜花

214. Devu和鲜花 - AcWing题库 如果每个盒子里的花的数量是无限的&#xff0c;用隔板法可以得出答案是 现在每个盒子中区的花数要满足n个条件 我们可以求答案的补集&#xff0c;用全部方案数减去补集方案数 每一个不符合条件的要求为&#xff0c;设为Bi 补集方案数为就成了…

内存卡怎么格式化?2个方法就足够!

“我是一名摄影业余爱好者&#xff0c;之前的内存卡里存了很多的文件&#xff0c;导致容量满了。我想将内存卡格式化后继续使用&#xff0c;但是不知道应该如何操作&#xff0c;谁能给些建议吗&#xff1f;” 内存卡格式化是维护存储设备性能和数据安全的关键操作之一。当我们发…

CRM系统在销售目标管理中的作用是什么?

销售管理者为了激励销售人员、评估业绩、进行奖励&#xff0c;往往需要建立一个基本标准&#xff0c;就是人们常说的销售目标。设定销售目标时&#xff0c;既要激励到员工&#xff0c;又不能把标准定得过高。CRM系统在销售目标管理中起到什么作用&#xff1f;该如何通过CRM销售…

yolov作者简介

作者简介 作者叫Joseph Redmon&#xff0c;在谷歌学术上搜索作者的简介。 地址&#xff1a;‪Joseph Redmon‬ -巨人学术搜索‬‬ (cljtscd.com) 他提出了最著名的YOLO算法。其中YOLOV1的引用量达到了40287次。 gitihub地址&#xff1a;github地址 主页&#xff1a;个人主页

复杂业务逻辑的判断与优化

作者 刘希忱 在日常开发工作当中&#xff0c;优秀的用户界面数据库、构建工具、样式预处理器是前端现工作阶段必不可少的三大利器&#xff0c;很多优秀的团队已经为我们提供了很多便利的解决方案&#xff0c;但仍然有很多开发场景需要提升优化&#xff0c;比如声明、输出、判…

Cobalt Strike 钓鱼工具使用

免杀 安装 需要js环境 介绍 obalt Strike是一款基于java的渗透测试神器&#xff0c;常被业界人称为CS神器。自3.0以后已经不在使用Metasploit框架而作为一个独立的平台使用&#xff0c;分为客户端与服务端&#xff0c;服务端是一个&#xff0c;客户端可以有多个&#xff…

mysql varchar int

年龄是数字类型int SELECT * FROM test ORDER BY age; 年龄是字符类型varchar SELECT * FROM test ORDER BY code; 第1种 补前导0可以和数字一样排序 MySQL会比较字符的ASCII值&#xff0c;并根据这些值来确定字符的排列顺序。 印象中oracle好像也是吧。 ASCII (American …

【yolov8目标检测】使用yolov8训练自己的数据集

目录 准备数据集 python安装yolov8 配置yaml 从0开始训练 从预训练模型开始训练 准备数据集 首先得准备好数据集&#xff0c;你的数据集至少包含images和labels&#xff0c;严格来说你的images应该包含训练集train、验证集val和测试集test&#xff0c;不过为了简单说…

2023年10月中国数据库排行榜:墨天轮榜单前五开新局,金仓、亚信热度攀升

怀鸿鹄之志&#xff0c;展骐骥之跃。 2023年10月的 墨天轮中国数据库流行度排行 火热出炉&#xff0c;本月共有286个数据库参与排名。本月排行榜前十名变动较大&#xff0c;**华为 openGauss 重归探花之位&#xff0c;人大金仓 KingBase 热度上升&#xff0c;亚信 AntDB 进军10…

开源软件-禅道Zentao

禅道Zentao 简介漏洞复现SQL注入漏洞**16.5****router.class.php SQL注入** **v18.0-v18.3****后台命令执行** 远程命令执行漏洞&#xff08;RCE&#xff09;后台命令执行 简介 是一款开源的项目管理软件&#xff0c;旨在帮助团队组织和管理他们的项目。Zentao提供了丰富的功能…

Spring Security—Servlet 应用架构

目录 一、Filter&#xff08;过滤器&#xff09;回顾 二、DelegatingFilterProxy 三、FilterChainProxy 四、SecurityFilterChain 五、Security Filter 六、打印出 Security Filter 七、添加自定义 Filter 到 Filter Chain 八、处理 Security 异常 九、保存认证之间的…

关于统信UOS不能使用“modprobe brd”创建内存盘的问题

前言 我自用的电脑内存都比较大&#xff0c;因此很早就养成了使用内存做临时盘的习惯 内存盘的好处很多&#xff0c;比如将系统临时文件夹、浏览器缓存文件等设置到内存盘&#xff0c;不仅可以提升速度&#xff0c;还可以减少对固态硬盘的写入&#xff0c;提升固态盘的使用寿…

金融机器学习方法:回归分析

回归分析是统计学中的一个重要分支&#xff0c;它用于建立一个或多个自变量和一个因变量之间的关联模型。在本博客中&#xff0c;我们将深入探讨线性回归和逻辑回归这两种常见的回归分析方法&#xff0c;并通过Python示例进行分析。 目录 1.线性回归1.1 模型介绍1.2 示例分析 …

使用STM32怎么喂狗 (IWDG)

STM32F1 的独立看门狗&#xff08;以下简称 IWDG&#xff09;。 STM32F1内部自带了两个看门狗&#xff0c;一个是独立看门狗 IWDG&#xff0c;另一个是窗口看门狗 WWDG&#xff0c; 本章只介绍独立看门狗 IWDG&#xff0c;窗口看门狗 WWDG 会在后面章节介绍。 本章要实现的功能…

吉利银河L6征战2023混合动力汽车极限挑战赛获双冠,同级“优等生”不负众望

9月22-9月27日&#xff0c;由中汽信科携手昆明检验中心联合发起的国内首个混动汽车专属赛事2023混合动力汽车极限挑战赛在云南圆满结束。比赛项目涉及纯电续航里程、亏电油耗、高速真实能耗、高原山地极限能耗等多项衡量混动车买点的关键指标。在为期六天的挑战中&#xff0c;这…