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

至强服务器BIOS/UEFI驱动开发笔记

至强服务器BIOS/UEFI驱动开发笔记

  • 驱动开发基础
    • Hello UEFI Driver 项目
      • 选择项目位置
      • 初始化驱动代码文件结构
      • 驱动程序入口和基本功能
      • 导入AMI工程
      • AMI平台Hello UEFI Driver 编译问题
      • 测试结果
    • 打印设备列表
      • 继续开发`HelloWorldSupported`函数
      • 依赖配置
      • 使用脚本编译
      • 编译测试此DXE驱动模块
      • 改进`HelloWorldSupported`函数
      • 问题
    • 继续实验
  • AMI实战
    • SDL和CIF
      • 以界面方式增加工程
      • 以代码方式增加工程
      • RoboVeb
      • 踩过的坑
    • AMI VEB构建技巧
      • AMI VEB命令行构建
      • AMI构建单个工程
    • AMI App开发
      • 关键函数和协议
      • 主要步骤
      • HellWorld.c
      • 测试结果
    • PCI Driver开发
      • 基于EDKII工程的HelloWorldDxe
      • 测试结果
      • 继续开发`Supported`函数
      • 测试结果
    • AMI PCI 驱动开发
      • 新增加的DXE驱动放到和BIOS固件的哪里?
      • U盘和键盘全消失等问题
      • 矛盾的根源
  • UDK2015
    • 编译环境
      • Windows编译环境
      • OvmfPkg
      • VS2008安装问题
      • UDK2017
      • OVMF 2015
    • 经典的DXE驱动案例
      • VGA驱动
      • 仿照VGA驱动修改MyPciDxe
      • 以上代码迁移到服务器
    • 驱动的其它属性
      • 工具类函数
      • 简化的Supported函数与初步的Start函数
      • 驱动的名字

  1. 实验使用的CPU架构Broardwell。
  2. EFI App的构建过程实际上先构建可以在OS上运行的动态库/可执行文件,然后利用PE32+工具改为UEFI运行。
  3. UEFI基于GObject(https://docs.gtk.org/gobject/)用C模拟OOP或C++。
  4. JRE 1.7安装目录整个拷贝到VisualeBios.exe所在目录,并改名为jre。则Visual Bios的启动不需要安装JRE。卸载JRE 1.7验证。
  5. UEFI编译系统强制要求函数的局部变量统一声明在函数体的头部,否则报错。
  6. GRUB运行在BDS阶段,因为GRUB运行期间未调用ExitBootServices方法,实际调用此方法的是OS Loader。
  7. UEFI环境特点
    1. 支持X86、X64、ARM等平台
    2. 单核CPU,没有线程,没有进程
    3. 没有抢断/优先级
    4. 没有中断,唯一的路径是定时器
    5. 模块内部通讯通过Protocols(协议)和Events(事件)
    6. C语言编程(原文:Programming is done through C language,实际上有汇编)
  8. 包的声明用dsc,模块的声明用inf,模块的依赖用dec

驱动开发基础

Hello UEFI Driver 项目

用UEFI Shell装载驱动进行测试。受载板平台限制,测试工程放在AMI项目里。

选择项目位置

与UEFI App开发不同,驱动代码所处项目架构应当与硬件构成映射关系。如果驱动代码与驱动硬件不在相同架构,则开发者需要手动处理固件布局才成封装成为正确的固件。我亲自踩坑证明这个说法:
在这里插入图片描述
关键错误消息:Build\GetPpiName.c(1) : fatal error C1083: Cannot open include file: '/RELEASE_MYTOOLS/PpiTableIA32.c': No such file or directory。不知道驱动项目存放须按规定的开发者会认为这个问题是玄学问题,怎么生成的代码找不到生成的代码呢?建议参照下图和项目结构选择合适的UEFI驱动存储放置:

在这里插入图片描述
根据驱动目标选择合适的项目位置。本示例的目标为USB键盘驱动,因此选择MdeModulePkg/Bus。当然,如果你已对BIOS固件布局已非常清楚,你可以随意。

初始化驱动代码文件结构

新建目录HelloWorldDxe,新建HelloWorldDxe.inf,代码如下:

[Defines]INF_VERSION = 0x00010005BASE_NAME=HelloWorldDxeFILE_GUID = de296c9d-8bac-08bc-ac6d-db2998aff781MODULE_TYPE = UEFI_DRIVERVERSION_STRING = 0.1ENTRY_POINT = DriverMain[Sources]HelloWorldDxe.c[Packages]MdePkg/MdePkg.decMdeModulePkg/MdeModulePkg.dec[LibraryClasses]BaseLibBaseMemoryLibDebugLibMemoryAllocationLibPrintLibUefiDriverEntryPointUefiLib

驱动程序入口和基本功能

新建HelloWorldDxe.c,代码如下:

#include <Uefi.h>
#include <Protocol/DriverBinding.h>
#include <Protocol/ComponentName2.h>
#include <Protocol/ComponentName.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Library/UefiLib.h>
#include <Library/DebugLib.h>#define HELLOWORLD_VERSION 0x10EFI_STATUS EFIAPI HelloWorldStart(IN EFI_DRIVER_BINDING_PROTOCOL* This,IN EFI_HANDLE Controller,IN EFI_DEVICE_PATH_PROTOCOL* RemainingDevicePath
)
{EFI_STATUS status = EFI_SUCCESS;Print(L"[HelloWorldStart] HelloWorld driver started.\n");return status;
}EFI_STATUS EFIAPI HelloWorldSupported(IN EFI_DRIVER_BINDING_PROTOCOL* This,IN EFI_HANDLE Controller,IN EFI_DEVICE_PATH_PROTOCOL* RemainingDevicePath
)
{EFI_STATUS status = EFI_SUCCESS;Print(L"[HelloWorldSupported] HelloWorld driver supported.\n");return status;
}EFI_STATUS EFIAPI HelloWorldStop(IN EFI_DRIVER_BINDING_PROTOCOL* This,IN EFI_HANDLE Controller,IN UINTN NumberOfChildren,IN EFI_HANDLE* ChildHandleBuffer
)
{EFI_STATUS status = EFI_SUCCESS;Print(L"[HelloWorldStop] HelloWorld driver stopped.\n");return status;
}EFI_DRIVER_BINDING_PROTOCOL g_helloworld_driver_binding = {HelloWorldSupported,HelloWorldStart,HelloWorldStop,HELLOWORLD_VERSION,NULL,NULL
};EFI_STATUS EFIAPI DriverMain(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE* SystemTable
)
{EFI_STATUS status = EFI_SUCCESS;status = EfiLibInstallDriverBindingComponentName2(ImageHandle,SystemTable,&g_helloworld_driver_binding,ImageHandle,NULL,NULL);ASSERT_EFI_ERROR(status);return status;
}

导入AMI工程

  1. 在ModuleExplorer视图中依次展开:ComponentsCoreMdeModulePkgLibraryInstances,右键单击,选择弹出菜单Add INF Module。关于Select EDK Project Root选项,选择结果无论是否正确,导入结果都存在错误。下文第2步和第3步就是纠错。
    在这里插入图片描述
  2. 找到导入的工程,移动生成的sdl文件到inf文件所在目录,并更名。
    在这里插入图片描述
  3. 按下图所示修改MdeModulePkg\Library\LibraryInstances.cif
    在这里插入图片描述
    在这里插入图片描述
  4. 关掉VeB软件,重新打开。编译报错:<work root>\Build\GrangevillePkg\RELEASE_MYTOOLS\X64\MdeModulePkg\Bus\HelloWorldDxe\HelloWorldDxe\DEBUG\AutoGen.h(16) : fatal error C1083: Cannot open include file: 'Uefi.h': No such file or directory。原因是HelloWorldDxe.inf有错。具体错误是Package依赖错误地写成dsc,正确的做法是写成dec

AMI平台Hello UEFI Driver 编译问题

  1. AMI的工程不能运行EDKII提供的命令
    在这里插入图片描述

  2. VeB不允许编译单个驱动

    这是假象。实际原因是VeB没有把导入INF生成的sdl文件放到inf所在目录。解决办法是移动sdl文件到inf文件所在目录并修正上级cif文件中的错误。

在这里插入图片描述

测试结果

  1. UEFI Shell运行运行load指令可见大量的HelloWorld输出,说明UEFI驱动管理支持一个设备绑定多个驱动,不会因为某个设备已存在绑定的驱动而停止匹配新驱动。UEFI如何选择调用哪个驱动呢?驱动的版本的如何在驱动选择中发挥作用的?
  2. 服务器启动慢问题存在新证据,证据指向问题发生在SEC或者PEI阶段。下图右边神秘的数字,在HelloWorldDxe集成进BIOS固件之前不确定它显示时CPU执行阶段。现在可以证明处于DXE阶段。那么,从通电到HelloWorldDxe产生输出的大约10秒钟时间,很可能都处于SEC和PEI阶段。现在没有确定串口设备未初始化造成的HelloWorldDxe无输出的时间。

在这里插入图片描述

  1. EFI Shell启动时会再执行一次设备驱动管理过程。
    在这里插入图片描述

  2. UEFI驱动管理在得到Supported函数的返回结果为EFI_SUCCESS后立即调用Start函数。上图Supported输出与Start输出成对出现无间断说明这一点。

  3. 任何驱动应在EFI Shell中先load试运行。否则驱动出错造成很大的麻烦。DXE出错的结果是载板变砖头,救砖的办法可能只有把FLASH从电路板上焊下来,烧好程序后再焊上去。

  4. 固件烧录与固件运行时不同。以下截图的实验:

    1. A版本包含HelloWorldDxe驱动,B版本与2023XXXX版本相同,唯一的区别是重新编译。编译环境、工具完全相同。
    2. 固件升级到A版本,重启后驱动绑定过程出现大量的HelloWorld打印
    3. 固件升级到B版本,重启时控制台出现大量的HelloWorld打印
      在这里插入图片描述

打印设备列表

继续开发HelloWorldSupported函数

目标:以字符串形式输出所有Device关键字,尝试寻找设备特征,在特征中搜寻键盘设备。代码如下:

EFI_STATUS EFIAPI HelloWorldSupported(IN EFI_DRIVER_BINDING_PROTOCOL* This,IN EFI_HANDLE Controller,IN EFI_DEVICE_PATH_PROTOCOL* RemainingDevicePath
)
{EFI_STATUS status = EFI_SUCCESS;EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* device2txt = NULL;CHAR16* device_path = NULL;status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (void**)&device2txt);if (EFI_ERROR(status)){Print(L"[HelloWorld Driver] LocateProtocol result: %d\n", status);return status;}device_path = device2txt->ConvertDeviceNodeToText(RemainingDevicePath, TRUE, TRUE);Print(L"[HelloWorld Driver] device: %s\n", device_path);return EFI_UNSUPPORTED;
}

依赖配置

这里采用VeB配置。

  1. 新增外部依赖DevicePathLib
    在这里插入图片描述
  2. 新增Protocols依赖gEfiDevicePathProtocolGuidgEfiDevicePathToTextProtocolGuid
    在这里插入图片描述

使用脚本编译

脚本编译的目的是为CI做准备。

@echo off
chcp 65001
title AMI UEFI Build Tool
echo Any question can be sent to zhtqs8@163.com
set CCX86DIR=<work root>\software\Aptio_5.x\x86\x86
set CCX64DIR=<work root>\software\Aptio_5.x\x86\amd64
set TOOLS_DIR=<work root>\software\Aptio_5.x\BuildTools
set PATH=%PATH%;<work root>\software\Aptio_5.x\x86\x86
set PATH=%PATH%;<work root>\software\Aptio_5.x\amd64
set PATH=%PATH%;<work root>\software\Aptio_5.x\x86
set PATH=%PATH%;<work root>\software\Aptio_5.x\BuildTools
set PATH=%PATH%;<work root>\software\Aptio_5.x\BuildTools\Bin\Win32
set PATH=%PATH%;<work root>\software\Aptio_5.x\VisualeBios\jre\bin\
cd <work root>
cmd /k

运行指令make rebuild,结果如下:
在这里插入图片描述

编译测试此DXE驱动模块

  1. 编译,确认编译输出

    - Done -
    Build end time: 17:07:12, Sep.15 2023
    Build total time: 00:00:07
    
  2. 运行load HelloWorldDxe.efi,确认结果

    RemainingDevicePath的值始终为:F3 EE 00 F0。期望的结果为不同的设备不同的值。UEFI Driver Writer’s Guide大部分示例显示,Supported适用的流程是开发者用期望的Protocol尝试打开。结果成功就是支持,结果失败就是不支持。很多示例㫫示Start还会把这个逻辑再运行一次。

    RemainingDevicePath不一定指设备,它还兼顾方便开发者在当前设备下挂载子设备。另外:

相关文章:

至强服务器BIOS/UEFI驱动开发笔记

至强服务器BIOS/UEFI驱动开发笔记 驱动开发基础Hello UEFI Driver 项目选择项目位置初始化驱动代码文件结构驱动程序入口和基本功能导入AMI工程AMI平台Hello UEFI Driver 编译问题测试结果打印设备列表继续开发`HelloWorldSupported`函数依赖配置使用脚本编译编译测试此DXE驱动…...

Linux:Termius连接本地虚拟机与虚拟机快照

Termius连接本地虚拟机与虚拟机快照 1. Termius连接本地虚拟机2. 虚拟机快照与还原2.1 设置快照以及恢复 附录 1. Termius连接本地虚拟机 ifconfig -a 查看配置 连接成功 2. 虚拟机快照与还原 在学习阶段我们无法避免的可能损坏Linux操作系统。 如果损坏的话&#xff0c;重新…...

高校教务系统登录页面JS分析——四川大学

高校教务系统密码加密逻辑及JS逆向 本文将介绍高校教务系统的密码加密逻辑以及使用JavaScript进行逆向分析的过程。通过本文&#xff0c;你将了解到密码加密的基本概念、常用加密算法以及如何通过逆向分析来破解密码。 本文仅供交流学习&#xff0c;勿用于非法用途。 一、密码加…...

Kafka SASL认证授权(四)认证源码解析

Kafka SASL认证授权(四)认证源码解析。 官网地址:https://kafka.apache.org/ 一、认证流程 在了解kafka网络模型的基础上,了解它的认证流程: ApiVersionsRequest->SaslHandshakeRequest->a series of SASL client and server tokens corresponding to the mechani…...

软件测试学习(一)基础概念、实质、说明书测试、分类、动态黑盒测试

目录 软件测试概念、背景 软件测试员究竟做些什么 大多数软件测试员应该具备的素质 软件测试的实质 完全测试程序是不可能的 测试无法显示潜伏的软件缺陷 并非所有软件缺陷都要修复 软件测试员在产品小组中不受欢迎 术语&#xff1a;精准和准确 产品说明书的测试技术…...

在fastapi中实现异步

在FastAPI应用中使用异步特性可以提高并发性能&#xff0c;但如果您要调用的模型是同步的&#xff0c;可能会导致阻塞。为了实现异步处理&#xff0c;您可以将阻塞的操作委托给线程池或进程池&#xff0c;以便异步执行。 以下是一种基本方法来实现异步处理图片识别任务&#x…...

js数组去重

在JavaScript中&#xff0c;有很多方法可以用来去除数组中的重复项。以下是一些常见的方法&#xff1a; 方法一&#xff1a;使用Set Set是ES6中的新数据类型&#xff0c;它只存储唯一值。因此&#xff0c;我们可以利用这一特性来去重。 let array [1, 2, 3, 2, 1, 4, 3, 5,…...

【前端】根据后端返回的url进行下载并设置文件下载名称

在我们项目当中存储文件是存储到厂商的服务器上的&#xff0c;然后厂商返回一个可以直接下载url地址&#xff0c;但是前端使用这个url下载的时候永远都是保存一个名字&#xff0c;这时候我们就需要设置文件保存的名称&#xff0c; 那么如何实现呢&#xff1f;使用了fet…...

《视觉SLAM十四讲》公式推导(一)

文章目录 CH3 三维空间刚体运动CH3-1 旋转矩阵的推导CH3-2 旋转矩阵是正交矩阵的证明CH3-3 变换矩阵的逆的推导CH3-4 罗德里格斯公式推导 CH3 三维空间刚体运动 CH3-1 旋转矩阵的推导 &#xff08;1&#xff09;二维空间中的旋转矩阵 易得 { x ′ ∣ O P ′ ∣ c o s ( θ …...

简单好用的解压缩软件:keka 中文 for mac

Keka是一款功能全面、易于使用的文件压缩和解压缩软件&#xff0c;为Mac用户提供了便捷的文件管理工具。它支持多种压缩格式&#xff0c;具有快速解压和强大的压缩功能&#xff0c;让您能够轻松地处理各种文件压缩需求。 隐私非常重要 安全共享只需设置密码并创建高度加密的文…...

【UE 插件】UE4 虚幻引擎 插件开发(带源码插件打包、无源码插件打包) 有这一篇文章就够了!!!

目录 0 引言1 快速入门1.1 新建插件的前提1.2 创建插件步骤1.3 打包插件 2 无源代码的插件制作3 插件详细介绍3.1 插件的使用方法3.1 UE 预置插件模版3.1.1 空白3.1.2 纯内容3.1.3 编辑器独立窗口3.1.4 编辑器工具栏按钮3.1.5 编辑器模式3.1.6 第三方库3.1.7 蓝图库 3.2 插件中…...

C# CodeFormer 图像修复

效果 项目 代码 using Microsoft.ML.OnnxRuntime; using Microsoft.ML.OnnxRuntime.Tensors; using OpenCvSharp; using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.Windows.Forms;namespace 图像修复 {p…...

Android Studio的笔记--HttpURLConnection使用GET下载zip文件

HttpURLConnection使用GET下载zip文件 http get下载zip文件MainActivity.javaAndroidMainfest.xmlactivity_main.xmllog http get下载zip文件 MainActivity.java 用HttpURLConnection GET方法进行需注意&#xff1a; 1、Android 9及以上版本需要设置这个&#xff0c;否则会有…...

phantom3D模体

phantom是人头模型&#xff0c;分为2D和3D两种&#xff0c;matlab中可直接调用phantom(size)生成2D数据&#xff0c;如图1&#xff0c;而三维需要对应函数文件&#xff0c;下载&#xff1a;3D 图1 2D phantom 3D模体为一个椭球体&#xff0c;只能生成xyz三个方向相同维度的模…...

贪心算法解决批量开票限额的问题

具体问题&#xff1a;批量订单开票 限制&#xff1a;1.开最少的张数 2.每张限额10w # 贪心算法 def split_invoice_by_item(items):items_sorted sorted(items, keylambda x: x.price, reverseTrue)invoices []for item in items_sorted:# 尝试将商品加入已有的发票中added …...

Unity后台登录/获取数据——BestHTTP的使用Get/Post

一、使用BestHTTP实现登录功能&#xff08;Post&#xff09; 登录具体的步骤如下&#xff1a; 1&#xff1a;传入你的用户名和密码&#xff0c;这是一条包括链接和用户名密码的链接 2&#xff1a;使用BestHTTP的Post功能将链接传到服务器后台 3&#xff1a;后台拿到了你传送…...

【Windows日志】记录系统事件的日志

文章目录 一、概要二、Windows日志介绍 2.1 应用程序日志2.2 系统日志2.3 安全日志 三、查看与分析日志四、常见事件ID 4.1 登录事件 4.1.1 4624登陆成功4.1.2 4625登陆失败 4.2 特权使用4.3 账户管理事件4.4 账户登录事件5.2 事件ID汇总 一、概要 Windows主要有以下三类日…...

物联网开发学习笔记——目录索引

什么是物联网&#xff1f; 物联网的英文名称是Internet of Things。IoT则是Internet of Things的缩写。 通俗地说&#xff0c;就是把设备与互联网连接起来&#xff0c;进行信息交互。 目录 一、开发环境配置 工欲善其事必先利其器&#xff0c;首先是开发环境配置。 开发环…...

Prometheus:优秀和强大的监控报警工具

文章目录 概述Prometheus的底层技术和原理数据模型数据采集数据存储查询语言数据可视化 Prometheus的部署Prometheus的使用配置数据采集目标查询监控数据设置警报规则 查看数据可视化总结 概述 Prometheus是一款开源的监控和警报工具&#xff0c;用于收集和存储系统和应用程序…...

Appium

# 获取元素和屏幕截图 echo on adb shell uiautomator dump /sdcard/app.uix adb pull /sdcard/app.uix F:\APP\app.uixadb shell screencap -p /sdcard/app.png adb pull /sdcard/app.png F:\APP\app.png卸载appium npm uninstall appium -g 重新安装appium npm install -g a…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

Leetcode 3577. Count the Number of Computer Unlocking Permutations

Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接&#xff1a;3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯&#xff0c;要想要能够将所有的电脑解锁&#x…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题&#xff1a;docker pull 失败 网络不同&#xff0c;需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

CSS设置元素的宽度根据其内容自动调整

width: fit-content 是 CSS 中的一个属性值&#xff0c;用于设置元素的宽度根据其内容自动调整&#xff0c;确保宽度刚好容纳内容而不会超出。 效果对比 默认情况&#xff08;width: auto&#xff09;&#xff1a; 块级元素&#xff08;如 <div>&#xff09;会占满父容器…...

Spring是如何解决Bean的循环依赖:三级缓存机制

1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间‌互相持有对方引用‌,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

Linux nano命令的基本使用

参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时&#xff0c;显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...

向量几何的二元性:叉乘模长与内积投影的深层联系

在数学与物理的空间世界中&#xff0c;向量运算构成了理解几何结构的基石。叉乘&#xff08;外积&#xff09;与点积&#xff08;内积&#xff09;作为向量代数的两大支柱&#xff0c;表面上呈现出截然不同的几何意义与代数形式&#xff0c;却在深层次上揭示了向量间相互作用的…...