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

计算机系统启动过程

计算机系统启动过程

阅读笔记:

《计算机体系结构基础(第三版)》-- 胡伟武

第7章:计算机系统启动过程分析

系统启动的整个过程中, 计算机系统在软件的控制下由无序到有序, 所有的组成部分都由程序管理, 按照程序的执行发挥各自的功能, 最终将系统的控制权安全交到操作系统手中, 完成整个启动过程。

无论采用何种指令系统的处理器, 复位后的第一条指令都会从一个预先定义的特定地址取回

处理器的启动过程, 实际上就是一个特定程序的执行过程。 这个程序我们称之为固件, 又称为 BIOS(Basic Input Output System, 基本输入输出系统)

对于 LoongArch, 处理器复位后的第一条指令将固定从地址 0x1C000000 的位置获取。 这个地址需要对应一个能够给处理器核提供指令的设备, 这个设备以前是各种 ROM, 现在通常是闪存(Flash)

第一条执行的指令是由硬件层面决定的,这个地址需要结合硬件相关的手册。

1、处理器核初始化

所谓初始化, 实际上是将计算机内部的各种寄存器状态从不确定设置为确定, 将一些模块状态从无序强制为有序的过程。 简单来说, 就是通过 load / store 指令或其他方法将指定寄存器或结构设置为特定数值。

在 MIPS 和 LoongArch 结构中, 都只将 0 号寄存器的值强制规定为 0, 而其他的通用寄存器值是没有要求的。 在处理器复位后开始运行的时候, 这些寄存器的值可能是任意值。如果需要用到寄存器内容, 就需要先对其进行赋值, 将这个寄存器的内容设置为软件期望的值。

在现代处理器支持的猜测执行、 预取等微结构特性中, 可能会利用某些通用寄存器的值或者高速缓存的内容进行猜测。 如果整个处理器的状态并没有完全可控, 或许会猜测出一些极不合理的值, 导致处理器微结构上执行出错而引发死机。 这样就需要对一些必要的单元结构进行初始化, 防止这种情况发生。

在处理器开始执行之后, 一方面需要先对相关的寄存器内容进行初始化, 设置为一个正常地址值, 另一方面则需要对地址空间进行处理, 防止出现一般情况下不可访问的地址空洞。 这样即使发生了这种猜测访问, 也可以得到响应, 避免系统出错或死机。

1.1、 处理器复位

处理器的第一条指令实际上是由复位信号控制的, 但受限于各种其他因素, 复位信号并没有对处理器内部的所有部分进行控制, 例如 TLB、 Cache 等复杂结构, 而是只保证从取指部件到 BIOS 取指令的通路畅通。

在 LoongArch 架构下, 处理器复位后工作在直接地址翻译模式下。 该模式下的地址为虚实地址直接对应的关系, 也就是不经 TLB 映射, 也不经窗口映射。 默认情况下, 无论是取指访问还是数据访问, 都是 Uncache 模式, 也即不经缓存。 这样即使硬件不对 TLB、 Cache 两个结构进行初始化, 处理器也能正常启动并通过软件在后续的执行中对这些结构进行初始化。

但现在大部分处理器在硬件上自动处理, 从而减轻软件负担, 缩短系统启动时间。硬件初始化的时机是在系统复位解除之后、 取指访问开始之前, 以此来缩短 BIOS 的启动时间。

LoongArch 处理器复位后的第一条指令将固定从地址 0x1C000000 的位置获取, 这个过程是由处理器的执行指针寄存器PC被硬件复位为 0x1C000000 而决定的。

需要指出的是,处理器复位后先是通过频率为几十兆赫兹 (MHz) 以下的低速设备取指令, 例如 SPI 或 LPC 等接口。 一拍只能取出 1 比特 (SPI) 或 4 比特 (LPC), 而一条指令一般需要 32 比特。

整个处理器由系统复位到操作系统启动的简要流程如图7.1所示。 其中第一列为处理器核初始化过程, 第二列为芯片核外部分初始化过程, 第三列为设备初始化过程, 第四列为内核加载过程, 第五列为多核芯片中的从核 (Slave Core) 独有的启动过程

由内之外的过程:核内初始化 -> 核心周围初始化 -> 设备初始化 -> 加载内核 -> 唤醒其他核心

在这里插入图片描述

1.2、 调试接口初始化

在启动过程中优先初始化的是什么呢? 首先是用于调试的接口部分。 比如开机时听到的蜂鸣器响声, 或者在一些主板上看到的数码管显示, 都是最基本的调试用接口。 对于龙芯3 号处理器来说, 最先初始化的结构是芯片内集成的串口控制器。

对串口的初始化操作实际上是处理器对串口执行一连串约定好的 IO 操作。在 X86 结构下, IO 地址空间与内存地址空间相互独立, IO 操作与访存操作是通过不同的指令实现的。 MIPS 和LoongArch 等结构并不显式区分 IO 和内存地址, 而是采用全局编址, 使用地址空间将 IO 和内存隐式分离, 并通过地址空间或 TLB 映射对访问方式进行定序及缓存等的控制。

内存空间对应的是存储器, 存储器不会发生存储内容的自行更新。 也就是说, 如果处理器核向存储单元 A 中写入了 0x5a5a 的数值, 除非有其他的主控设备 (例如其他的处理器核或是其他的设备 DMA) 对它也进行写入操作, 否则这个 0x5a5a 的数值是不会发生变化的。

IO 空间一般对应的是控制寄存器或状态寄存器, 是受 IO 设备的工作状态影响的。 此时,写入的数据与读出的数据可能会不一致, 而多次读出的数据也可能不一致, 其读出数据是受具体设备状态影响的。

串口的线路状态寄存器里的各个数据位都与当时的设备状态相关。

IO 寄存器的行为与具体的设备紧密相关, 每种 IO 设备都有各自不同的寄存器说明, 需要按照其规定的访问方式进行读写, 而不像内存可以进行随意的读写操作。

处理器上运行的指令使用虚拟地址, 虚拟地址通过地址映射规则与物理地址相关联。基本的虚拟地址属性首先区分为经缓存 (Cache) 与不经缓存 (Uncache)两种。 对于内存操作, 现代高性能通用处理器都采用 Cache 方式进行访问, 以提升访存性能。Cache 在离处理器更近的位置上利用访存局部性原理进行缓存, 以加速重复访存或者其他规则访存 (通过预取等手段)。 对于存储器来说, 在 Cache 中进行缓存是没有问题的, 因为存储器所存储的内容不会自行修改 (但可能会被其他核或设备所修改, 这个问题可以通过缓存一致性协议解决)。 但是对于 IO 设备来说, 因为其寄存器状态是随着工作状态的变化而变化的, 如果缓存在 Cache 中, 那么处理器核将无法得到状态的更新, 所以一般情况下不能对 IO 地址空间进行 Cache 访问, 需要使用 Uncache 访问。 使用 Uncache 访问对 IO 进行操作还有另一个作用,就是可以严格控制读写的访问顺序, 不会因为预取类的操作导致寄存器状态的丢失。

串口初始化程序仅仅是对串口的通信速率及一些控制方法进行设置, 以使其很方便地通过一个串口交互主机进行字符的读写交互。

1.3、 TLB初始化

TLB 作为一个地址映射的管理模块, 主要负责操作系统里用户进程地址空间的管理, 用以支持多用户多任务并发。然而在处理器启动的过程中, 处理器核处于特权态, 整个 BIOS 都工作在统一的地址空间里, 并不需要对用户地址空间进行过多干预。此时 TLB 的作用更多是地址转换, 以映射更大的地址空间供程序使用。

LoongArch 结构采用了分段和分页两种不同的地址映射机制。 分段机制将大段的地址空间与物理地址进行映射, 具体的映射方法在 BIOS 下使用窗口机制进行配置, 主要供系统软件使用。 而分页机制通过 TLB 起作用, 主要由操作系统管理, 供用户程序使用

BIOS 一般映射两段, 其中 0x90000000_00000000 开始的地址空间被映射为经缓存的地址, 0x80000000_00000000 开始的地址空间被映射为不经缓存的地址。 根据地址空间的转换规则, 这两段转换为物理地址时直接抹除地址的高位, 分别对应整个物理地址空间, 仅仅在是否经过Cache 缓存上有所区别。

这个地址也并不是绝对的,要看相关手册。

由于分段机制是通过不同的虚拟地址来映射全部的物理地址空间, 并不适合用作用户程序的空间隔离和保护, 也不适合需要更灵活地址空间映射方法的场合。 这些场景下就需要利用TLB 机制。

TLB 的初始化主要是将全部表项初始化为无效项。

**初始化为无效项就是将 TLB 的每项逐一清空, 以免程序中使用的地址被未初始化的 TLB 表项所错误映射。**在没有硬件复位 TLB 逻辑的处理器里, 启动时 TLB 里可能会包含一些残留的或者随机的内容, 这部分内容可能会导致 TLB 映射空间的错误命中。 因此在未实现硬件复位TLB 的处理器中, 需要对整个 TLB 进行初始化操作。

越来越多的处理器已经实现了在芯片复位时由硬件进行 TLB 表项的初始化, 这样在 BIOS 代码中可以不用再使用类似的软件初始化流程。

1.4、 Cache初始化

Cache 的引入能够减小处理器执行和访存延迟之间的性能差异, 即缓解存储墙的问题。 引入 Cache 结构, 能够大大提高处理器的整体运行效率。

在系统复位之后, Cache 同样也处于一个未经初始化的状态, 也就是说 Cache 里面可能包含残留的或随机的数据, 如果不经初始化, 对于 Cache 空间的访问也可能会导致错误的命中。

Cache 的组织结构主要包含标签 (Tag) 和数据 (Data) 两个部分, Tag 用于保存 Cache 块状态、 Cache 块地址等信息, Data 则保存数据内容。 大多数情况下对 Cache 的初始化就是对 Tag 的初始化, 只要将其中的 Cache 块状态设置为无效, 其他部分的随机数据就不会产生影响。

处理器在复位完成之后就处于最高特权态中, 完成各项初始化。

在完成所有 Cache 层次的初始化之后, 就可以跳转到 Cache 空间开始执行。 此后程序的运行效率将会有数十倍的提升。

在跳转到 Cache 空间执行后, 程序运行效率大大提升, 随之而来的是处理器内各种复杂猜测机制的使用。

得益于摩尔定律的持续生效, 片上 Cache 的容量越来越大, 由此却带来了初始化时间越来越长的问题。 但同时, 在拥有越来越多可用片上资源的情况下, TLB、 Cache 等结构的初始化也更多地开始使用硬件自动完成, 软件需要在这些初始化上耗费的时间也越来越少。

TLB初始化要在Cache初始化之前进行。

完成 Cache 空间的初始化并跳转至 Cache 空间运行也标志着处理器的核心部分, 或者说体系结构相关的初始化部分已经基本完成。 接下来将对计算机系统所使用的内存总线和 IO 总线等外围部分进行初始化。

2、 总线接口初始化

在使用同一款处理器的不同系统上, TLB、 Cache 这些体系结构紧密相关的芯片组成部分的初始化方法是基本一致的, 不需要进行特别的改动。 与此不同的是, 内存和 IO 设备的具体组成在计算机系统中则可以比较灵活地搭配, 不同系统间的差异可能会比较大。

这些不同的配置情况要求在计算机系统启动时能够进行有针对性的初始化。

2.1、 内存初始化

冯·诺依曼结构下, 计算机运行时的程序和数据都被保存在内存之中。 相对复位时用于取指的 ROM 或是 Flash 器件来说, 内存的读写性能大幅提高。

越来越多的处理器已经集成内存控制器。 因为内存的使用和设置与外接内存芯片的种类、配置相关, 所以在计算机系统启动的过程中需要先获取内存的配置信息, 再根据该信息对内存控制器进行配置。

获取这些信息是程序通过 I2C 总线对外部内存条的 SPD 芯片进行读操作来完成的。 SPD 芯片也相当于一个 Flash 芯片, 专门用于存储内存条的配置信息。

对内存的初始化实际上就是根据内存配置信息对内存控制器进行初始化。 与 Cache 初始化类似的是, 内存初始化并不涉及其存储的初始数据。 与 Cache 又有所不同的地方在于, Cache 有专门的硬件控制位来表示 Cache 块是否有效, 而内存却并不需要这样的硬件控制位。 内存的使用完全是由软件控制的, 软件知道其访问的每一个地址是否存在有效的数据。 而 Cache 是一个硬件加速部件, 大多数情况下软件并不需要真正知道而且也不希望知道其存在, Cache 的硬件控制位正是为了掩盖内存访问延迟, 保证访存操作的正确性。 因此内存初始化仅仅需要对内存控制器进行初始化, 并通过控制器对内存的状态进行初始化。 在初始化完成以后, 如果是休眠唤醒, 程序可以使用内存中已有的数据来恢复系统状态; 如果是普通开机, 则程序可以完全不关心内存数据而随意使用。

内存控制器的初始化包括两个相对独立的部分, 一是根据内存的行地址、 列地址等对内存地址映射进行配置, 二是根据协议对内存信号调整的支持方式对内存读写信号相关的寄存器进行训练, 以保证传输时的数据完整性。

在内存初始化完成后, 可能还需要根据内存的大小对系统可用的物理地址空间进行相应的调整和设置。

2.2、 IO总线初始化

受外围桥片搭配及可插拔设备变化的影响, 系统每次启动时需要对 IO 总线进行有针对性的初始化操作。

完成内存与 IO 总线初始化后, BIOS 中基本硬件初始化的功能目标已经达到。 但是为了加载操作系统, 还必须对系统中的一些设备进行配置和驱动。 操作系统所需要的存储空间比较大, 通常无法保存在 Flash 这样的存储设备中, 一般保存在硬盘中并在使用时加载, 或者也可以通过网口、 U 盘等设备进行加载。 为此就需要使用更复杂的软件协议来驱动系统中的各种设备, 以达到加载操作系统的最终目标。

在此之前的程序运行基本没有使用内存进行存数取数操作, 程序也是存放在 Flash 空间之中的, 只不过是经过了 Cache 的缓存加速。 在此之后的程序使用的复杂数据结构和算法才会对内存进行大量的读写操作。 为了进一步提高程序的运行效率, 先将程序复制到内存中, 再跳转到内存空间开始执行。

此后, 还需要对处理器的运行频率进行测量, 对 BIOS 中的计时函数进行校准, 以便在需要等待的位置进行精确的时间同步。 在经过对各种软件结构必要的初始化之后, BIOS 将开始一个比较通用的设备枚举和驱动加载的过程。

3、 设备的探测及驱动加载

PCI 总线于 20 世纪 90 年代初提出, 发展到现在已经逐渐被 PCIE 等高速接口所替代, 但其软件配置结构却基本没有发生变化, 包括 HyperTransport、 PCIE 等新一代高速总线都兼容 PCI协议的软件框架。

在 PCI 软件框架下, 系统可以灵活地支持设备的自动识别和驱动的自动加载。

在 PCI 协议下, IO 的系统空间分为三个部分: 配置空间、 IO 空间和 Memory 空间。 配置空间存储设备的基本信息, 主要用于设备的探测和发现; IO 空间比较小, 用于少量的设备寄存器访问; Memory 空间可映射的区域较大, 可以方便地映射设备所需要的大块物理地址空间。

配置空间的地址偏移由总线号、 设备号、 功能号和寄存器号的组合得到, 通过对这个组合的全部枚举, 可以很方便地检测到系统中存在的所有设备。

每一个提供 PCI 设备的厂商都应该拥有唯一的厂商识别号, 以在设备枚举时正确地找到其对应的驱动程序。设备识别号对于每一个设备提供商的设备来说应该是唯一的。 这两个识别号的组合就可以在系统中唯一地指明正确的驱动程序。

所谓驱动程序就是一组函数, 包含用于初始化设备、 关闭设备或是使用设备的各种相关操作。

基址寄存器 (Base Address Register, 简称 BAR)。其最低位表示该 BAR 是 IO 空间还是 Memory 空间。BAR 中间有一部分只读位为 0, 正是这些 0 的个数表示该 BAR 所映射空间的大小, 也就是说BAR 所映射的空间为 2 的幂次方大小。 BAR 的高位是可写位, 用来存储软件设置的基地址。

在这里插入图片描述

对 PCI 设备的探测和驱动加载是一个递归调用过程, 大致算法如下:

  1. 将初始总线号、 初始设备号、 初始功能号设为 0。

  2. 使用当前的总线号、 设备号、 功能号组成一个配置空间地址, 这个地址的构成如图 3 所示, 使用该地址, 访问其 0 号寄存器, 检查其设备号。

  3. 如果读出全 1 或全 0, 表示无设备。

  4. 如果该设备为有效设备, 检查每个 BAR 所需的空间大小, 并收集相关信息。

  5. 检测其是否为一个多功能设备, 如果是则将功能号加 1 再重复扫描, 执行第 2 步。

  6. 如果该设备为桥设备, 则给该桥配置一个新的总线号, 再使用该总线号, 从设备号 0、功能号 0 开始递归调用, 执行第 2 步。

  7. 如果设备号非 31, 则设备号加 1, 继续执行第 2 步; 如果设备号为 31, 且总线号为 0,表示扫描结束, 如果总线号非 0, 则退回上一层递归调用。

通过这个递归调用, 就可以得到整个 PCI 总线上的所有设备及其所需要的所有空间信息。有了这些信息, 就可以使用排序的方法对所有的空间从大到小进行分配。 最后, 利用分配的基地址和设备的 ID 信息, 加载相应的驱动就能够正常使用该设备。

4、 多核启动过程

实现不同处理器核之间相互同步与通信的一种手段是核间中断与通信信箱机制

实际上, 信箱寄存器完全可以通过在内存中分配一块地址空间实现, 这样 CPU 访问延迟更短。 而专门使用寄存器实现的信箱寄存器更多是为了在内存还没有初始化前就让不同的核间能够有效通信。

4.1、 初始化时的多核协同

在 BIOS 启动过程中, 为了简化处理流程, 实际上并没有用到中断寄存器, 对于各种外设也没有使用中断机制, 都是依靠处理器的轮询来实现与其他设备的协同工作。

为了简化多核计算机系统的启动过程, 我们将多核处理器中的一个核定为主核, 其他核定为从核主核除了对本处理器核进行初始化之外, 还要负责对各种总线及外设进行初始化; 而从核只需要对本处理器核的私有部件进行初始化, 之后在启动过程中就可以保持空闲状态, 直至进入操作系统再由主核进行调度。

从核需要初始化的工作包括哪些部分呢? 首先是从核私有的部分。 所谓私有, 就是其他处理器核无法直接操纵的部件, 例如核内的私有寄存器、 TLB、 私有 Cache 等, 这些器件只能由每个核自己进行初始化而无法由其他核代为进行。 其次还有为了加速整个启动过程,由主核分配给从核做的工作, 例如当共享 Cache 的初始化操作非常耗时的时候, 可以将整个共享 Cache 分为多个部分, 由不同的核负责某一块共享 Cache 的初始化, 通过并行处理的方式进行加速。

主核的启动过程与前三节介绍的内容基本是一致的。 但在一些重要的节点上则需要与从核进行同步与通信, 或者说通知从核系统已经到达了某种状态。 为了实现这种通知机制,可以将信箱寄存器中不同的位定义为不同的含义, 一旦主核完成了某些初始化阶段, 就可以给信箱寄存器写入相应的值。 例如将信箱寄存器的第 0 位定义为 “ 串口初始化完成” 标志, 第 1 位定义为 “ 共享 Cache 初始化完成” 标志, 第 2 位定义为 “ 内存初始化完成”标志。

在主核完成串口的初始化后, 可以向自己的信箱寄存器写入 0x1。 从核在第一次使用串口之前需要查询主核的信箱寄存器, 如果第 0 位为 0, 则等待并轮询, 如果非 0, 则表示串口已经初始化完成, 可以使用。

在主核完成了共享 Cache 的初始化后, 向自己的信箱寄存器写入 0x3。 而从核在初始化自己的私有 Cache 之后, 还不能直接跳转到 Cache 空间执行, 必须等待信号, 以确信主核已将全部的共享 Cache 初始化完成, 然后再开始 Cache 执行才是安全的。

在主核完成了内存初始化后, 其他核才能使用内存进行数据的读写操作。 那么从核在第一次用到内存之前就必须等待表示内存初始化完成的 0x7 标志。

4.2、 操作系统启动时的多核唤醒

当从核完成了自身的初始化之后, 如果没有其他工作需要进行, 就会跳转到一段等待唤醒的程序。 在这个等待程序里, 从核定时查询自己的信箱寄存器。 如果为 0, 则表示没有得到唤醒标志。 如果非 0, 则表示主核开始唤醒从核, 此时从核还需要从其他几个信箱寄存器里得到唤醒程序的目标地址, 以及执行时的参数。 然后从核将跳转到目标地址开始执行。

在操作系统中, 主核在各种数据结构准备好的情况下就可以开始依次唤醒每一个从核。 唤醒的过程也是串行的, 主核唤醒从核之后也会进入一个等待过程, 直到从核执行完毕再通知主核, 再唤醒一个新的从核, 如此往复, 直至系统中所有的处理器核都被唤醒并交由操作系统管理。

4.2、 核间同步和通信

操作系统启动之前, 利用信箱寄存器进行了大量的多核同步与通信操作, 但在操作系统启动之后, 除了休眠唤醒一类的操作, 却基本不会用到信箱寄存器。 Linux 内核中, 只需要使用核间中断就可以完成各种核间的同步与通信操作。

核间中断也是利用一组 IO 寄存器实现的。 通过将目标核的核间中断寄存器置 1 来产生一个中断信号, 使目标核进入中断处理。 **中断处理的具体内容则是通过内存进行交互的。 内核中为每个核维护一个队列 (内存中的一个数据结构), 当一个核想要中断其他核时, 它将需要处理的内容加入目标核的这个队列, 然后再向目标核发出核间中断 (设置其核间中断寄存器)。**当目标核被中断之后, 开始处理其核间通信队列, 如果其间还收到了更多的核间中断请求, 也会一并处理。

为什么 Linux 内核中的核间中断处理不通过信箱寄存器进行呢? 首先信箱寄存器只有一组, 也就是说如果通过信箱寄存器发送通信消息, 在这个消息没被处理之前, 是不能有其他核再向其发出新的核间中断的。 这样无疑会导致核间中断发送方的阻塞。 另外, 核间中断寄存器实际上是 IO 寄存器, 前面我们提到, 对于 IO 寄存器的访问是通过不经缓存这种严格访问序的方式进行的, 相比于 Cache 访问方式, 不经缓存读写效率极其低下, 本身延迟开销很大, 还可能会导致流水线的停顿。 因此在实际的内核中, 只有类似休眠唤醒这种特定的同步操作才会利用信箱寄存器进行, 其他的同步通信操作则是利用内存传递信息, 并利用核间中断寄存器产生中断的方式共同完成的。

相关文章:

计算机系统启动过程

计算机系统启动过程 阅读笔记: 《计算机体系结构基础(第三版)》-- 胡伟武 第7章:计算机系统启动过程分析 系统启动的整个过程中, 计算机系统在软件的控制下由无序到有序, 所有的组成部分都由程序管理, 按照程序的执行发挥各自的功…...

DedeCms后台文章列表文档id吗?或者快速定位id编辑文章

我们在建站时有的时候发现之前的文章有错误了,要进行修改,但又不知道文章名,只知道大概的文章id,那么可以搜索到DedeCms后台文章列表文档id吗?或者快速定位文章id方便修改? 第一种方法:复制下面…...

【开发问题解决方法记录】03.dian

登录提示 ERR-1002 在应用程序 "304" 中未找到项 "ROLE_ID" 的项 ID。 一开始找错方向了,以为是代码错误,但是后来在蒋老师的提醒下在共享组件-应用程序项 中发现设的项不是ROLE_ID而是ROLEID,怪不得找不到ORZ 解决方法…...

QT之QString

QT之QString 添加容器 点击栅格布局 添加容器,进行栅格布局 布局总结:每一个模块放在一个Group中,排放完之后,进行栅格布局。多个Group进行并排时,先将各个模块进行栅格布局,然后都选中进行垂直布…...

常见的几种计算机编码格式

前言: 计算机编码是指将字符、数字和符号等信息转换为计算机可识别的二进制数的过程,正因如此,计算机才能识别中英文等各类字符。计算机中有多种编码格式用于表示和存储文本、字符和数据,实际走到最后都是二进制,本质一…...

3D旋转tab图

上图 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>3D旋转tab图</title><style>* {margin: 0;padding: 0;}body {height: 100vh;background: linear-gradient(to top, #29323c, #…...

openGL 三:矩阵和向量

1.使用glm数学库进行矩阵和向量的计算 2.位置坐标可以看做一个向量 3.向量的移动&#xff0c;缩放&#xff0c;旋转&#xff0c;都是可以通过和矩阵的计算得出 4.向量的缩放乘一个44的矩阵 5.注意事项&#xff08;有些版本的glm::mat4 不是默认构建一个单位44的矩阵&#xff09…...

Socket和Http的通讯原理,遇到攻击会受到哪些影响以及如何解决攻击问题。

德迅云安全-领先云安全服务与解决方案提供商 Socket和HTTP通信原理&#xff1a; Socket通信原理&#xff1a; Socket是一种应用程序编程接口&#xff08;API&#xff09;&#xff0c;用于在单个进程或多个进程之间进行通信。它提供了一种灵活的、异步的通信方式&#xff0c;使…...

【springboot】整合redis

1.前提条件:docker安装好了redis确定redis可以访问 可选软件: 2.测试代码 (1)redis依赖 org.springframework.boot spring-boot-starter-data-redis (2)配置redis &#xff08;3&#xff09; 注入 Resource StringRedisTemplate stringRedisTemplate; 对键进行操作 –o…...

回溯和分支算法

状态空间图 “图”——状态空间图 例子&#xff1a;农夫过河问题——“图”状态操作例子&#xff1a;n后问题、0-1背包问题、货郎问题(TSP) 用向量表示解&#xff0c;“图”由解向量扩张得到的解空间树。 ——三种图&#xff1a;n叉树、子集树、排序树 ​ 剪枝 不满住条件的…...

深入理解:指针变量的解引用 与 加法运算

前言 指针变量的解引用和加法运算是非常高频的考点&#xff0c;也是难点&#xff0c;因为对初学者的不友好&#xff0c;这就导致了各大考试都很喜欢在这里出题&#xff0c;通常会伴随着强制类型转换、二维数组、数组指针等一起考查大家对指针的理解。但是不要怕&#xff0c;也许…...

Docker 镜像构建的最佳做法

一、镜像分层 使用docker image history命令&#xff0c;可以看到用于在镜像中创建每个层的命令。 1、 使用docker image history命令查看创建的入门镜像中的层。 docker image history getting-started 您应该得到如下所示的输出&#xff1a; IMAGE CREATED…...

工作上Redis安装及配置

下载redis软件 第一步&#xff1a;解压压缩包 tar -zxvf redis-7.0.14.tar.gz 第二步&#xff1a;移动redis存放目录&#xff08;结合个人需求而定&#xff01;&#xff09; redis-7.0.14&#xff1a;解压后的文件路径 /usr/local&#xff1a;移动后的文件路径 mv redis-7.0.…...

电商项目之Web实时消息推送(附源码)

文章目录 1 问题背景2 前言3 什么是消息推送4 短轮询5 长轮询5.1 demo代码 6 iframe流6.1 demo代码 7 SSE7.1 demo代码7.2 生产环境的应用 &#xff08;重要&#xff09; 8 MQTT 1 问题背景 扩宽自己的知识广度&#xff0c;研究一下web实时消息推送 2 前言 文章参考自Web 实时消…...

上机实验四 哈希表设计 西安石油大学数据结构

实验名称&#xff1a;哈希表设计 &#xff08;1&#xff09;实验目的&#xff1a;掌握哈希表的设计方法及其冲突解决方法。 &#xff08;2&#xff09;主要内容&#xff1a; 已知一个含有10个学生信息的数据表&#xff0c;关键字为学生“姓名”的拼音&#xff0c;给出此表的一…...

Ubuntu22.04 交叉编译mp4V2 for Rv1106

一、配置工具链环境 sudo vim ~/.bashrc在文件最后添加 export PATH$PATH:/opt/arm-rockchip830-linux-uclibcgnueabihf/bin 保存&#xff0c;重启机器 二、下载mp4v2 下载路径&#xff1a;MP4v2 | mp4v2 三、修改CMakeLists.txt 四、执行编译 mkdir build cd buildcmak…...

缓存穿透、击穿、雪崩

缓存穿透&#xff1a; 指的是恶意用户或攻击者通过请求不存在于缓存和后端存储中的数据来使得所有请求都落到后端存储上&#xff0c;导致系统瘫痪。 解决方案&#xff1a; 通常包括使用布隆过滤器或者黑白名单等方式来过滤掉无效请求&#xff0c;以及在应用程序中加入缓存预热…...

(1w字一篇理解透Unsafe类)Java魔法类:Unsafe详解

Java魔法类 Unsafe 文章导读&#xff1a;(约12015字&#xff0c;阅读时间大约1小时)1. Unsafe介绍2. Unsafe创建3. Unsafe功能3.1内存操作3.2 内存屏障3.3 对象操作3.4 数组操作3.5 CAS操作3.6 线程调度3.7 Class操作3.8 系统信息 4. 总结 JUC源码中的并发工具类出现过很多次 …...

Python的正则表达式使用

Python的正则表达式使用 定义使用场景查替换分割 常用的正则表达符号查原字符英文状态的句号点 .反斜杠 \英文的[]英文的()英文的?加号 星号 *英文状态的大括号 {} 案例 定义 正则表达式是指专门用于描述或刻画字符串内在规律的表达式。 使用场景 无法通过切片&#xff0c;…...

Elasticsearch:评估 RAG - 指标之旅

作者&#xff1a;Quentin Herreros&#xff0c;Thomas Veasey&#xff0c;Thanos Papaoikonomou 2020年&#xff0c;Meta发表了一篇题为 “知识密集型NLP任务的检索增强生成” 的论文。 本文介绍了一种通过利用外部数据库将语言模型 (LLM) 知识扩展到初始训练数据之外的方法。 …...

【2023.12.4练习】数据库知识点复习测试

概论 数据表&#xff1a;用于存储现实中数据的联系。 储存信息联系。 字段&#xff1a;又称列&#xff0c;如姓名、年龄、编号等。 记录&#xff1a;又称元组&#xff0c;为数据表中的一行&#xff0c;代表了一个实体的信息。 数据库&#xff08;DB&#xff09;&#xff1…...

【wvp】测试记录

ffmpeg 这是个莫名其妙的报错&#xff0c;通过排查&#xff0c;应该是zlm哪个进程引起的 会议室的性能 网络IO也就20M...

【若依框架实现上传文件组件】

若依框架中只有个人中心有上传图片组件&#xff0c;但是这个组件不适用于el-dialog中的el-form表单页面 于是通过elementui重新写了一个上传组件&#xff0c;如图是实现效果 vue代码 <el-dialog :title"title" v-model"find" width"600px"…...

玩转大数据5:构建可扩展的大数据架构

1. 引言 随着数字化时代的到来&#xff0c;大数据已经成为企业、组织和个人关注的焦点。大数据架构作为大数据应用的核心组成部分&#xff0c;对于企业的数字化转型和信息化建设至关重要。我们将探讨大数据架构的基本要素和原则&#xff0c;以及Java在大数据架构中的角色&…...

【华为数据之道学习笔记】非数字原生企业的特点

非数字原生企业的数字化转型挑战 软件和数据平台为核心的数字世界入口&#xff0c;便捷地获取和存储了大量的数据&#xff0c;并开始尝试通过机器学习等人工智能技术分析这些数据&#xff0c;以便更好地理解用户需求&#xff0c;增强数字化创新能力。部分数字原生企业引领着云计…...

Kubernetes学习笔记-Part.01 Kubernets与docker

目录 Part.01 Kubernets与docker Part.02 Docker版本 Part.03 Kubernetes原理 Part.04 资源规划 Part.05 基础环境准备 Part.06 Docker安装 Part.07 Harbor搭建 Part.08 K8s环境安装 Part.09 K8s集群构建 Part.10 容器回退 第一章 Kubernets与docker Docker是一种轻量级的容器…...

k8s学习

文章目录 前言一、k8s部署方式二、学习k8s的方式今天主要配置k8s环境的方式今天遇到的是一个在k8s进行初始化的方式&#xff0c;但是发现k8s不能正常初始化总是出现错误&#xff0c;或者在错误中有问题的方式&#xff0c;在网上查询挺多资料需要重新启动kub文件&#xff0c;删除…...

测试:JMeter和LoadRunner比较

比较 JMeter和LoadRunner是两款常用的软件性能测试工具&#xff0c;它们在功能和性能上有一定的相似性和差异。下面从几个方面对它们进行比较&#xff1a; 1. 架构和原理&#xff1a; JMeter和LoadRunner的架构和原理基本相同&#xff0c;都是通过中间代理监控和收集并发客户…...

(C语言)通过循环按行顺序为一个矩阵赋予1,3,5,7,9,等奇数,然后输出矩阵左下角的值。

#include<stdio.h> int main() {int a[5][5];int n 1;for(int i 0;i < 5;i ){for(int j 0;j < 5;j ){a[i][j] n;n 2;}}for(int i 0;i < 5;i ){for(int j 0;j < i;j )printf("%-5d",a[i][j]);printf("\n");}return 0; } 运行截图…...

GitHub项目推荐-Deoldify

有小伙伴推荐了一个老照片上色的GitHub项目&#xff0c;看了简介&#xff0c;还不错&#xff0c;推荐给大家。 项目地址 GitHub - SpenserCai/sd-webui-deoldify: DeOldify for Stable Diffusion WebUI&#xff1a;This is an extension for StableDiffusions AUTOMATIC1111 w…...

建设一个网站要多少钱/抚顺网络推广

网站微信登录&#xff0c;做起来挺简单的&#xff0c;我们做这个&#xff0c;首页是要去看微信文档&#xff0c;文档看懂了&#xff0c;然后理清楚逻辑&#xff0c;怎么进行绑定贵公司的账号&#xff0c;业务那块要理清楚&#xff01; 首先&#xff0c;微信官方告诉我们&#…...

网站建设容易吗/百度竞价seo排名

点击上方“程序员小灰”&#xff0c;选择“置顶公众号” 有趣有内涵的文章第一时间送达&#xff01; 上一次送书活动刚刚完结&#xff0c;有9位幸运的小伙伴得到了想要的图书&#xff0c;但是更多的读者们并没有获奖。 不过大家不必遗憾&#xff0c;慷慨的博文视点再次赞助了10…...

池州市网站建设优化/百度推广首次开户需要多少钱

谁发明了计算机&#xff1f;大多数人会说是美国人Mocheri和Ecatki. 他们两个人于1946年发明了名为“ ENIAC”的计算机.实际上&#xff0c;这是一种误解. 该计算机的真正发明者应该是德国人Conrad Zuse. 2010年6月22日是楚泽诞辰100周年.今天&#xff0c;在他出生的柏林市&#…...

视频托管平台/谷歌seo推广公司

前段时间一直翻译《WCF技术内幕》&#xff0c;所以这个系列停滞了下来&#xff0c;现在翻译工作完成。现在继续来写《WCF4.0新特性体验》这个系列。今天我们来学习一下Rest WCF服务&#xff0c;文章会先介绍一下Rest的基本概念&#xff0c;以及特点&#xff0c;其次会介绍WCF如…...

微网站独立域名/西安sem竞价托管

1 从file中选择 2...

微网站的图标怎么做/seo优化推广教程

一、介绍Oracle的同义词(synonyms)从字面上理解就是别名的意思&#xff0c;和视图的功能类似&#xff0c;就是一种映射关系。它可以节省大量的数据库空间&#xff0c;对不同用户的操作同一张表没有多少差别;它扩展了数据库的使用范围&#xff0c;能够在不同的数据库用户之间实现…...