[Linux] Linux 系统从启动到驱动加载
Linux 系统从启动到驱动加载
文章目录
- Linux 系统从启动到驱动加载
- 一、硬件上电与 BIOS/UEFI 阶段
- 1. 1 硬件上电初始化
- 1.2 BIOS/UEFI执行过程
- 1.3 Bootloader加载细节
- 二、Bootloader 阶段
- 三、Linux 内核初始化
- 3.1 架构相关初始化(setup_arch)
- 3.2 核心子系统初始化
- 3.3 虚拟文件系统(VFS)初始化
- 3.4 内核线程创建
- 四、驱动子系统初始化
- 4.1 设备模型初始化(driver_init)
- 4.2 总线子系统注册
- 4.3 早期驱动初始化(early platform drivers)
- 4.4 设备描述信息解析
- 五、驱动加载与设备枚举
- 5.1 总线驱动扫描硬件设备
- 5.2 设备与驱动匹配
- 5.3 驱动probe初始化
- 5.4 创建设备节点
- 六、用户空间初始化
- 6.1 init 进程启动
- 6.2 udev 设备管理
- 6.3 驱动模块加载
- 6.4 系统启动完成
- 七、关键数据结构与函数
- 八、典型驱动加载流程示例
- 九、调试与故障排查
- 9.1 查看内核启动日志(dmesg)
- 9.2 检查 sysfs 设备信息(/sys/devices)
- 9.3 使用 udevadm 监控设备事件
- 9.4 动态加载/卸载驱动模块(insmod/rmmod)
一、硬件上电与 BIOS/UEFI 阶段
1. 1 硬件上电初始化
当计算机电源接通后:
- 电源供应器完成自检,向主板发送Power Good信号
- CPU从复位状态解除,寄存器被初始化为默认值(如x86架构的CS=0xF000,EIP=0xFFF0)
- 主板时钟发生器开始工作,提供稳定的时钟信号
1.2 BIOS/UEFI执行过程
BIOS/UEFI固件执行的主要任务:
-
POST(Power-On Self Test):
- 检测关键硬件:CPU、内存、显卡等
- 声卡发出"滴"声表示检测通过(不同BIOS厂商的提示音编码不同)
- 对检测到的硬件生成设备列表(如通过ACPI表)
-
硬件初始化:
- 配置内存控制器和初始化RAM
- 初始化基本输入输出设备(键盘、显示器)
- 设置中断向量表(IVT)或高级中断控制器(APIC)
-
引导设备选择:
- 按照CMOS中存储的启动顺序(如:硬盘→USB→网络)
- 读取每个设备的引导扇区(MBR或GPT分区表的引导记录)
- 对于UEFI系统,会查找ESP分区中的/EFI/BOOT/BOOTx64.EFI文件
1.3 Bootloader加载细节
当BIOS/UEFI找到有效启动设备后:
- 传统BIOS会将MBR中的第一阶段引导程序(512字节)加载到内存0x7C00处
- UEFI则直接加载EFI应用程序到内存
- 对于GRUB2:
- 第一阶段(boot.img)仅负责加载core.img
- 第二阶段(core.img)包含基本文件系统驱动,用于定位/boot/grub
- 最终加载grub.cfg配置文件并显示启动菜单
示例启动顺序:
- 检测到SATA硬盘
- 读取MBR中的引导代码
- 加载GRUB的stage1 → stage1.5 → stage2
- 显示GRUB菜单等待用户选择
二、Bootloader 阶段
-
引导加载器启动:
- 系统 BIOS/UEFI 完成硬件初始化后,将控制权交给存储在 MBR 或 EFI 分区中的引导加载器(如 GRUB2)
- 常见的引导加载器包括:
- GRUB (Grand Unified Bootloader)
- Syslinux
- LILO (Linux Loader)
- systemd-boot (适用于 UEFI 系统)
-
内核加载过程:
- 引导加载器读取其配置文件(如 GRUB 的 grub.cfg)
- 加载压缩的内核镜像(通常在 /boot 目录下,命名为 vmlinuz-<版本号>)到内存
- 同时加载 initramfs(Initial RAM Filesystem)镜像
- 典型文件示例:
/boot/vmlinuz-5.4.0-91-generic /boot/initrd.img-5.4.0-91-generic
-
参数传递与内核启动:
- 引导加载器向内核传递启动参数,常见参数包括:
root=
:指定根文件系统设备(如 root=/dev/sda1)init=
:指定初始化程序路径(替代默认的 /sbin/init)quiet
:减少启动时内核信息输出splash
:显示启动画面
- 内核解压过程:
- 首先解压缩 zImage 或 bzImage 格式的内核
- 然后加载必要的驱动和初始化临时根文件系统
- 最终控制权转移到内核的入口点 start_kernel() 函数(位于 init/main.c)
- 引导加载器向内核传递启动参数,常见参数包括:
-
initramfs 的作用:
- 提供早期用户空间环境
- 包含必要的驱动程序(如磁盘控制器、文件系统驱动)
- 挂载真正的根文件系统
- 在嵌入式系统中可能直接作为最终根文件系统使用
-
特殊场景处理:
- 加密根分区:需要 initramfs 包含解密工具
- RAID/LVM:需要加载相应的模块
- 网络启动:需要包含网络驱动程序
三、Linux 内核初始化
3.1 架构相关初始化(setup_arch)
setup_arch() 是架构特定的初始化函数,主要负责:
- 解析硬件信息(如通过设备树获取CPU类型、内存布局等)
- 初始化物理内存管理(如建立memblock内存分配器)
- 设置处理器特殊功能(如开启MMU、缓存等)
- 架构相关的早期设备初始化(如SMP处理器初始化)
示例:在ARM架构中会初始化处理器异常向量表,x86架构则会检测和初始化ACPI。
3.2 核心子系统初始化
包括以下关键系统的初始化:
- 页表初始化:建立内核地址空间映射,包括:
- 内核代码段映射
- 设备I/O空间映射
- 早期内存分配区域映射
- 中断控制器初始化:
- 探测并初始化本地APIC/IOAPIC(x86)
- GIC初始化(ARM)
- 设置中断描述符表(IDT)
- 定时器子系统:
- 初始化高精度定时器(hrtimer)
- 设置系统时钟源(如HPET、TSC)
- 校准CPU频率(loops_per_jiffy)
3.3 虚拟文件系统(VFS)初始化
VFS初始化流程:
- 注册基础文件系统类型(如proc、sysfs、tmpfs)
- 初始化dentry缓存和inode缓存
- 挂载rootfs作为初始文件系统
- 创建标准文件描述符(stdin/stdout/stderr)
- 挂载实际根文件系统(通过root=内核参数指定)
3.4 内核线程创建
初始化的关键内核线程包括:
- kthreadd(内核线程守护进程,pid=2)
- 负责创建和管理其他内核线程
- 通过kthread_create()请求创建新线程
- 其他早期线程:
- ksoftirqd(软中断处理线程)
- kworker(工作队列线程)
- migration(CPU迁移线程)
- watchdog(看门狗监控线程)
这些初始化步骤完成后,内核将进入用户空间初始化阶段,启动第一个用户进程(通常是init或systemd)。
四、驱动子系统初始化
4.1 设备模型初始化(driver_init)
该阶段主要完成内核设备模型的核心数据结构初始化,包括:
- kobject 子系统初始化,建立设备层次结构基础
- 设备类和属性文件系统的创建(/sys/class/)
- 内核对象引用计数机制的建立
- 设备号分配器的初始化(devtmpfs)
4.2 总线子系统注册
分平台总线类型进行初始化:
-
platform_bus_init:
- 注册平台总线类型(platform_bus_type)
- 初始化 platform_device 和 platform_driver 的匹配机制
- 创建/sys/bus/platform/目录结构
- 典型应用场景:SoC内置设备(如GPIO控制器、时钟模块)
-
pci_init:
- PCI总线探测和枚举
- PCI设备资源分配(IO空间、内存空间)
- 建立PCI设备树结构
- PCI设备驱动匹配机制初始化
4.3 早期驱动初始化(early platform drivers)
在基本设备模型建立后立即加载的关键驱动:
- 内存控制器驱动
- 串口调试驱动(earlycon)
- 时钟源驱动
- 中断控制器驱动
这些驱动通过early_platform_init
机制注册,具有以下特性: - 在常规驱动加载前完成初始化
- 使用简化版的资源获取接口
- 通常通过命令行参数指定(如
earlycon=uart8250,mmio,0xfe001000
)
4.4 设备描述信息解析
根据系统类型采用不同配置机制:
设备树(DT)系统:
- 解析/boot/dtb文件或U-Boot传递的设备树
- 展开设备树节点为device_node结构
- 将设备树节点转换为platform_device
- 处理设备树中的中断映射、DMA范围等特殊属性
ACPI系统:
- 解析ACPI表(DSDT、SSDT等)
- 转换ACPI设备为平台设备
- 处理_PRT(中断路由)、_CRS(资源分配)等方法
- 支持ACPI电源管理扩展功能
两种系统最终都会生成统一的设备资源描述,包括:
- 内存映射区域
- 中断号
- DMA通道
- 时钟源
- 电源管理参数
五、驱动加载与设备枚举
5.1 总线驱动扫描硬件设备
总线驱动负责识别和扫描连接的硬件设备。常见方式包括:
- PCI设备枚举:通过读取PCI配置空间(Configuration Space)获取设备Vendor ID、Device ID等信息
- 设备树解析(DT node parsing):在ARM架构中解析设备树(Device Tree)的节点信息
- ACPI枚举:x86架构通过ACPI表获取设备信息
- USB总线枚举:通过USB协议发现连接的设备
示例:PCI枚举过程中会遍历所有总线号码(bus number),对每个设备/功能组合读取其配置寄存器。
5.2 设备与驱动匹配
内核通过driver_match_device()
函数进行匹配,主要依据:
- 设备树中的
compatible
属性 - ACPI设备ID(HID/UID/CID)
- PCI设备的Vendor/Device ID
- 平台设备的名称匹配
匹配优先级通常为:设备树匹配 > ACPI匹配 > ID表格匹配 > 名称匹配。
5.3 驱动probe初始化
匹配成功后调用驱动的probe()
函数进行:
- 资源分配(内存、IRQ、DMA等)
- 硬件初始化(寄存器配置、固件加载)
- 设备特定设置(时钟、电源管理)
- 子系统注册(如输入设备、网络设备等)
典型错误处理包括:检查资源可用性、逐步回滚失败操作。
5.4 创建设备节点
通过devtmpfs自动创建设备文件:
- 内核调用
device_add()
将设备注册到系统 - 根据设备类型(字符/块设备)创建
/dev
节点 - 设置正确的设备号(major/minor)
- 应用层可通过udev/mdev规则进一步配置节点属性
特殊设备可能还需要:
- sysfs属性文件(
/sys/class/...
) - debugfs接口
- 用户空间通知机制(uevent)
六、用户空间初始化
6.1 init 进程启动
内核最后阶段会启动第一个用户空间进程 init(PID=1),根据系统配置选用不同的 init 系统:
- systemd(现代主流发行版):采用并行化的服务启动方式,提供单元(unit)管理
- 读取
/etc/systemd/system/
和/usr/lib/systemd/system/
下的单元文件 - 启动基础目标(target)如
multi-user.target
或graphical.target
- 读取
- sysvinit(传统系统):串行执行
/etc/init.d/
中的启动脚本- 通过运行级别(runlevel)控制启动阶段
- 典型流程:先挂载文件系统,再启动基础服务
6.2 udev 设备管理
udev 守护进程负责动态设备管理:
- 监听内核通过 netlink 发送的 uevent
- 处理设备热插拔事件(如 USB 插入)
- 根据
/etc/udev/rules.d/
规则文件:- 创建设备节点(如
/dev/sda1
) - 设置设备权限和所有者
- 触发关联的硬件初始化脚本
- 创建设备节点(如
6.3 驱动模块加载
通过 modprobe
机制加载用户态驱动模块:
- 读取
/etc/modprobe.d/
配置文件 - 自动解决模块依赖关系
- 典型应用场景:
- 加载文件系统驱动(如 ext4、ntfs)
- 加载特殊硬件驱动(如 NVIDIA 显卡)
- 加载网络协议栈模块
6.4 系统启动完成
完成所有初始化步骤后:
- 启动登录管理器(如 gdm、lightdm)进入图形界面
- 或显示文本登录提示符(tty1-6)
- 记录启动日志到
/var/log/boot.log
- 系统进入多用户模式,所有服务正常运行
七、关键数据结构与函数
Linux设备驱动模型中的两个核心数据结构及其关系:
/*** struct device_driver - 驱动对象的基本表示* @name: 驱动名称,用于sysfs显示和模块匹配* @bus: 指向驱动所属的总线类型,如platform_bus_type* @probe: 驱动探测回调函数,当设备与驱动匹配时调用* * 示例:当注册i2c_driver时,需要设置这些字段*/
struct device_driver {const char *name;struct bus_type *bus;int (*probe)(struct device *dev);
};/*** struct bus_type - 总线类型的抽象表示* @name: 总线名称,如"platform"、"pci"等* @match: 总线匹配函数,用于判断设备和驱动是否兼容* * 总线负责管理其下的设备和驱动,典型的匹配过程:* 1. 设备注册时,总线遍历所有驱动,调用match函数* 2. 驱动注册时,总线遍历所有设备,调用match函数* 3. 匹配成功则调用驱动的probe函数*/
struct bus_type {char *name;int (*match)(struct device *dev, struct device_driver *drv);
};
工作流程说明:
- 系统初始化时会注册各种总线类型(platform/pci/i2c等)
- 驱动开发者编写驱动时:
- 定义device_driver结构体实例
- 实现probe函数进行设备初始化
- 向总线注册该驱动
- 当匹配发生时,总线层会:
- 通过match函数验证设备和驱动的兼容性
- 调用驱动的probe函数初始化设备
- 建立sysfs中的设备-驱动关联
八、典型驱动加载流程示例
以下是一个完整的 PCI 设备驱动加载流程示例,展示了从驱动注册到设备初始化的关键步骤:
// 1. 定义设备ID表,用于匹配支持的PCI设备
static const struct pci_device_id my_pci_ids[] = {{ PCI_DEVICE(0x10AB, 0x1100), .driver_data = 0 }, // 厂商ID 0x10AB,设备ID 0x1100{ PCI_DEVICE(0x10AB, 0x1200), .driver_data = 0 }, // 另一个兼容设备{ 0, } // 结束标记
};// 2. 定义驱动操作函数
static int my_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{int retval;// 使能PCI设备retval = pci_enable_device(pdev);if (retval) {dev_err(&pdev->dev, "Failed to enable PCI device\n");return retval;}// 获取设备资源(如内存区域、中断号等)// ...具体设备初始化代码...dev_info(&pdev->dev, "Device successfully initialized\n");return 0;
}static void my_remove(struct pci_dev *pdev)
{// 释放资源,关闭设备// ...清理代码...pci_disable_device(pdev);dev_info(&pdev->dev, "Device removed\n");
}// 3. 定义PCI驱动结构体
static struct pci_driver my_driver = {.name = "my_device", // 驱动名称.id_table = my_pci_ids, // 设备匹配表.probe = my_probe, // 设备发现时的回调.remove = my_remove, // 设备移除时的回调// 可选:.suspend/.resume 等电源管理回调
};// 4. 注册PCI驱动
module_pci_driver(my_driver);// 5. 模块信息
MODULE_DEVICE_TABLE(pci, my_pci_ids);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Sample PCI Device Driver");
典型工作流程:
- 内核加载驱动模块时调用
module_pci_driver
宏注册驱动 - 内核遍历PCI总线,将每个设备与
id_table
进行匹配 - 找到匹配设备后,调用
probe
函数初始化设备 - 设备使用期间可能触发中断/处理请求
- 设备移除或模块卸载时调用
remove
函数清理资源
注意事项:
- 必须实现基本的probe/remove函数对
- 需要正确处理设备使能/禁用流程
- 建议添加适当的错误处理和资源管理
- 现代驱动通常还需要实现电源管理回调
九、调试与故障排查
9.1 查看内核启动日志(dmesg)
dmesg
命令是排查驱动程序问题的首选工具,它显示内核环形缓冲区中的消息。典型使用场景包括:
- 查看驱动加载时的初始化信息:
dmesg | grep <驱动模块名>
- 跟踪设备插拔事件:
dmesg -w
(实时监控) - 检查错误信息:
dmesg --level=err,warn
- 清空缓冲区:
dmesg -c
(需要root权限)
9.2 检查 sysfs 设备信息(/sys/devices)
sysfs文件系统提供了设备树的详细信息:
/sys/devices/
├── platform
│ └── <设备名>/
│ ├── driver -> ../../../bus/platform/drivers/<驱动名>
│ ├── modalias
│ └── power/
重要检查项:
- 设备是否被正确识别:
ls /sys/bus/<总线类型>/devices
- 驱动绑定状态:查看设备目录下的
driver
符号链接 - 设备参数:如
/sys/class/gpio/gpio<N>/
下的各种属性文件
9.3 使用 udevadm 监控设备事件
udev工具链的典型应用:
# 监控所有设备事件
udevadm monitor# 查看特定设备属性
udevadm info -a -p /sys/class/net/eth0# 触发设备重扫描
udevadm trigger
关键调试场景:
- 检查规则匹配:
udevadm test /sys/class/<设备类>/<设备名>
- 验证热插拔事件处理
- 调试udev规则执行过程
9.4 动态加载/卸载驱动模块(insmod/rmmod)
驱动模块管理操作流程:
# 加载模块(需.ko文件路径)
sudo insmod module.ko [参数名=参数值]# 查看已加载模块
lsmod | grep <模块名># 卸载模块(需确保无设备在使用)
sudo rmmod module# 更常用的modprobe工具
sudo modprobe module_name
sudo modprobe -r module_name
注意事项:
- 模块参数可以通过
/sys/module/<模块名>/parameters/
查看或修改 - 依赖关系处理:
modprobe
会自动解决依赖 - 查看模块信息:
modinfo <模块名>
- 常见错误:模块版本不匹配、符号未导出、资源冲突等
研究学习不易,点赞易。
工作生活不易,收藏易,点收藏不迷茫 :)
相关文章:
[Linux] Linux 系统从启动到驱动加载
Linux 系统从启动到驱动加载 文章目录 Linux 系统从启动到驱动加载一、硬件上电与 BIOS/UEFI 阶段1. 1 硬件上电初始化1.2 BIOS/UEFI执行过程1.3 Bootloader加载细节 二、Bootloader 阶段三、Linux 内核初始化3.1 架构相关初始化(setup_arch)3.2 核心子系…...

基于微信小程序的云校园信息服务平台设计与实现(源码+定制+开发)云端校园服务系统开发 面向师生的校园事务小程序设计与实现 融合微信生态的智慧校园管理系统开发
博主介绍: ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台…...
大语言模型的技术原理与应用前景:从Transformer到ChatGPT
目录 摘要 1. 引言 2. Transformer架构核心原理 2.1 自注意力机制 2.2 位置编码 2.3 前馈神经网络 3. 从GPT到ChatGPT的演进 3.1 GPT系列模型架构 3.2 训练流程优化 4. 应用场景与案例分析 4.1 代码生成 4.2 文本摘要 4.3 问答系统 5. 挑战与未来方向 5.1 当前技…...
如何编写GitLab-CI配置文件
创建文件 在根目录新建.gitlab-ci.yml文件. 该文件与项目其他文件一样, 同样受到版本控制, 所以可以在不同的分支下, 配置不同的持续集成脚本 YAML语法 配置文件遵循YAML语法, 关于该语法的内容, 自行搜索 参考 YAML 语言教程 关键词 根主要关键词一览 关键词含义可选备…...

生成式人工智能:重构软件开发的范式革命与未来生态
引言 生成式人工智能(GenAI)正以颠覆性力量重塑软件开发的底层逻辑。从代码生成到业务逻辑设计,从数据分析到用户交互,GenAI通过其强大的推理能力与场景适应性,将传统开发流程的“复杂工程”转化为“敏捷实验”&#…...
关于 java:4. 异常处理与调试
一、异常核心语法 1.1 try-catch-finally:异常捕获与处理结构 1)作用 用于捕获和处理程序运行过程中可能发生的异常 防止程序因异常中断,提高代码的鲁棒性(健壮性) 2)基本语法结构: try {…...
Java基础 Day26
一、网络编程简介 1、概念 网络编程指在网络通信协议下,不同计算机上运行的程序,进行数据传输 2、软件架构 (1)CS架构(客户端和服务端) 在用户本地有一个客户端程序,在远程有一个服务器端程…...
android lifeCycleOwner生命周期
一 Fragment中 viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) 什么时候执行? 让我分析一下相关问题: 关于 onPause 时的数据更新: viewLifecycleOwner.lifecycleScope.launch {viewLifecycleOwner.repeatOnLifecycle(Lifecycle.Sta…...
高防IP能抗住500G攻击吗?
在当今互联网环境中,网络安全问题日益严峻,尤其是针对网站的DDoS攻击,更是让众多站长头疼不已。而高防IP作为应对此类攻击的有效手段,其性能与稳定性成为了大家关注的焦点。那么,高防IP真的能抗住500G的超大流量攻击吗…...
工作流引擎-10-什么是 BPM?
工作流引擎系列 工作流引擎-00-流程引擎概览 工作流引擎-01-Activiti 是领先的轻量级、以 Java 为中心的开源 BPMN 引擎,支持现实世界的流程自动化需求 工作流引擎-02-BPM OA ERP 区别和联系 工作流引擎-03-聊一聊流程引擎 工作流引擎-04-流程引擎 activiti 优…...

day1-小白学习JAVA---JDK安装和环境变量配置(mac版)
JDK安装和环境变量配置 我的电脑系统一、下载JDK1、oracle官网下载适合的JDK安装包,选择Mac OS对应的版本。 二、安装三、配置环境变量1、终端输入/usr/libexec/java_home -V查询所在的路径,复制备用2、输入ls -a3、检查文件目录中是否有.bash_profile文…...
每日温度(力扣-739)
【题目描述】 给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。 【输出输出样…...
QT中子线程触发主线程弹窗并阻塞等待用户响应-传统信号槽实现
目录 QT中子线程触发主线程弹窗并阻塞等待用户响应传统信号槽实现实现思路具体步骤1. 定义信号与槽2. 异步任务中触发弹窗3. 主线程处理弹窗4. 连接信号与槽关键点总结 更简单实现 QT中子线程触发主线程弹窗并阻塞等待用户响应 传统信号槽实现 场景需求:在子线程执…...
HarmonyOS鸿蒙系统深度运维指南
一、开发与调试环境全链路配置 工具链部署标准流程 HDC 3.0调试套件:支持分布式设备的跨端调试与性能分析,需配置端口转发规则(默认调试端口:8080)KaihongOS桌面开发环境:集成DevEco Studi…...
SpringBoot多租户系统的5种架构设计方案
多租户(Multi-tenancy)是一种软件架构模式,允许单个应用实例服务于多个客户(租户),同时保持租户数据的隔离性和安全性。 通过合理的多租户设计,企业可以显著降低运维成本、提升资源利用率,并实现更高效的服务交付。 本文将分享S…...

数据分析实战1(Excel制作报表)
Excel数据链接:【课程4.0】第2章_Excel.zip - 飞书云文档 1、拿到数据第一步 备份数据 ctrlshiftL:筛选 相关快捷键:(alt:自动求和、ctrlshift5:转换为%) 2、环比、同比 环比(本…...

本地部署大模型llm+RAG向量检索问答系统 deepseek chatgpt
项目视频讲解: 本地部署大模型llm+RAG向量检索问答系统 deepseek chatgpt_哔哩哔哩_bilibili 运行结果:...
设备健康管理的战略升维:用预测性维护重构企业竞争力
第一章 传统维护的沉默成本:被低估的利润黑洞 当轴承振动值突破安全阈值时,制造企业损失的远非维修费用。某重型装备制造厂的案例揭示了典型多米诺效应:传动系统突发故障导致36小时停产,触发订单违约金(合约金额的9%&…...
Redis事务详解:原理、使用与注意事项
文章目录 Redis事务详解:原理、使用与注意事项什么是Redis事务Redis事务的基本使用基本事务示例事务执行过程 Redis事务的错误处理1. 入队错误2. 执行错误 WATCH命令:乐观锁实现Redis事务的局限性事务的最佳实践Lua脚本总结 Redis事务详解:原…...
提升 GitHub Stats 的 6 个关键策略
哈哈,GitHub 的 “B-” 评级 其实是个玄学问题,但确实有一些 快速提升的技巧!你的数据看起来 提交数(147)和 PR(9)不算少,但 Stars(21)和贡献项目数ÿ…...
CSS Animation 详解
CSS Animation 允许元素平滑地从一个样式状态过渡到另一个样式状态。通过设置关键帧(keyframes),可以控制动画序列中的中间步骤。 一、核心概念 1.关键帧(Keyframes) 使用 keyframes 规则定义动画序列通过百分比或 …...

LabVIEW 中内存释放相关问题
在LabVIEW 编程领域,内存管理是一个关键且复杂的议题。我们常常关注 LabVIEW 如何将内存释放回操作系统(OS),以及是否有方法确保在特定数据结构(如队列、变体属性、动态数据引用 DVR 等)销毁、删除或清空后…...
【HarmonyOS 5】鸿蒙中的UIAbility详解(三)
【HarmonyOS 5】鸿蒙中的UIAbility详解(三) 一、前言 本文是鸿蒙中的UIAbility详解系列的最终章。主要针对UIAbility的冷启动和热启动,对于want数据的处理。UIAbility的备份恢复,UIAbility的接续等高级功能的概念和使用讲解。 …...

基于内存高效算法的 LLM Token 优化:一个有效降低 API 成本的技术方案
在使用 OpenAI、Claude、Gemini 等大语言模型 API 构建对话系统时,开发者普遍面临成本不断上升的挑战。无论是基于检索增强生成(RAG)的应用还是独立的对话系统,这些系统都需要维护对话历史以确保上下文的连贯性,类似于…...
vue-11(命名路由和命名视图)
命名路由和命名视图 命名路由和命名视图提供了组织和导航 Vue.js 应用程序的强大方法,尤其是在它们的复杂性增加时。它们提供了一种语义更合理、可维护的路由方法,使您的代码更易于理解和修改。命名路由允许您按名称引用路由,而不是依赖 URL…...
(附代码)自定义 LangChain 文档分割器,深入探索 LangChain 文档分割策略与应用
自定义文档分割器 在 LangChain 中,如果内置的文档分割器均没办法完成需求,还可以根据特定的需求实现自定义文档分割器(一般极少),实现的方法也非常简单,继承文本分割器基类 TextSplitter,在构造…...

Python打卡训练营Day42
DAY 42 Grad-CAM与Hook函数 知识点回顾 回调函数lambda函数hook函数的模块钩子和张量钩子Grad-CAM的示例 作业:理解下今天的代码即可 import torch import torch.nn as nn import torch.nn.functional as F import torchvision import torchvision.transforms as tr…...

基于微信小程序的scratch学习系统
博主介绍:java高级开发,从事互联网行业六年,熟悉各种主流语言,精通java、python、php、爬虫、web开发,已经做了六年的毕业设计程序开发,开发过上千套毕业设计程序,没有什么华丽的语言࿰…...
MATLAB实战:机器学习分类回归示例
以下是一个使用MATLAB的Statistics and Machine Learning Toolbox实现分类和回归任务的完整示例代码。代码包含鸢尾花分类、手写数字分类和汽车数据回归任务,并评估模型性能。 %% 加载内置数据集 % 鸢尾花数据集(分类) load fisheriris; X_i…...
动态库导出符号与extern “C“
1. windows下动态库导出符号 根据C/C语法规则,函数声明中的修饰符(如__declspec(dllexport))可以放在返回类型之前或返回类型之后、函数名之前。这两种方式在功能上是等价的,编译器会以相同的方式处理。 __declspec(dllexport) …...