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

Linux内核Kernel启动过程

一、内核启动的基本流程

1. 启动加载程序 (Bootloader)

启动加载程序(如GRUB、LILO、syslinux等)负责将内核映像从存储设备加载到内存中,并准备好内核启动所需的环境。

  • 加载内核映像:启动加载程序将压缩的内核映像(如vmlinuz)从硬盘加载到内存中。内核映像通常是一个gzip或其他格式压缩的二进制文件。
  • 加载initrd/initramfs:如果使用initrd(初始RAM盘)或initramfs(初始RAM文件系统),启动加载程序也会将这些文件加载到内存中,以便内核在启动时使用。

2. 内核解压阶段

在内核映像的开头,有一个小的解压缩程序,它负责解压内核的主体部分。

  • 解压内核:内核映像被加载到内存后,解压缩程序会运行并将压缩的内核映像解压到适当的内存位置。
  • 跳转到解压后的内核:一旦解压完成,控制权会被移交给解压后的内核代码的入口点。

3. 内核启动(Kernel Startup)

解压后的内核代码会从一个固定的入口点开始执行,这个入口点是平台和架构相关的。对于x86架构,通常是startup_32或startup_64函数。

  • 架构特定的初始化:根据具体的硬件架构,内核会执行一些必要的初始化步骤,比如设置CPU的运行模式,初始化分页机制,建立基本的内存映射等。
  • 初始化内核堆栈:内核设置好自己的堆栈,以便后续的函数调用和操作。
  • 调用start_kernel函数:完成基础的硬件初始化后,内核会调用start_kernel函数,这是内核初始化的核心部分。

4. start_kernel函数

start_kernel函数位于init/main.c文件中,负责完成大部分内核的初始化工作。

  • 初始化控制台:设置内核的打印机制,以便后续的输出可以显示出来。
  • 初始化内存管理子系统:建立初始的内存管理结构,准备好内存分配机制。
  • 检测和初始化硬件设备:内核会检测并初始化系统中的各种硬件设备和驱动程序。
  • 启动中断处理机制:设置和启动中断处理机制,使得内核可以响应硬件中断。
  • 初始化内核调度器:初始化内核调度器,以便管理进程调度。
  • 加载初始进程:内核创建并启动第一个用户空间进程,通常是/sbin/init。

5. 启动初始进程

init进程是用户空间的第一个进程,负责进一步的系统初始化工作,包括启动系统服务和守护进程。

  • init进程的初始化:init进程执行系统初始化脚本,设置各种系统参数和启动服务。
  • 启动用户空间服务:最终,init进程启动配置的所有用户空间服务和守护进程,从而完成系统的启动过程。

二、内核文件加载及解压缩

1.为什么是压缩文件

Linux内核映像通常是一个压缩文件,主要有以下原因:

  • 减少存储空间: 压缩内核映像可以显著减少其在存储设备上的占用空间。这对嵌入式系统、存储资源有限的设备以及需要快速分发和更新内核的环境尤其重要
  • 加快加载速度: 压缩文件占用的空间更小,这意味着启动加载程序从磁盘读取文件到内存中的时间会更短。虽然解压缩内核映像需要一些时间,但现代处理器的解压缩速度非常快,通常解压缩的时间比从存储设备读取更多数据的时间要少。这会整体上加快启动过程。
  • 提高传输效率: 在网络上传输内核映像时,压缩文件可以显著减少带宽使用量。这对于需要远程更新内核的系统(OTA)非常有利。
  • 便于管理和分发: 压缩内核映像更便于在各种介质上分发,比如光盘、U盘等。一个较小的文件更容易管理、备份和分发。
  • 标准化处理: 使用压缩内核映像是一种标准做法,启动加载程序(如GRUB)已经能够很好地支持这种格式,能自动识别并处理压缩的内核映像。这使得系统启动过程更简单可靠。

2.文件类型vmlinuxz和bzImage

在连接压缩映像文件之前,我们先来了解一下未经压缩的编译文件vmlinux

2.1 什么是vmlinux?

vmlinux内核编译过程中生成的一个包含所有内核代码和数据的二进制文件。它是未经压缩和未经过处理的内核映像,通常位于内核源码目录的根目录下,特性如下:

  • 未压缩:vmlinux 是内核的未压缩映像。它包含所有内核代码、内核模块以及相关的数据结构。
  • ELF 格式:vmlinux 通常是一个 ELF(Executable and Linkable Format)文件,这是一个标准的可执行文件格式,用于存储可执行文件、目标代码和共享库等。
  • 符号信息:vmlinux 文件中包含调试符号和符号表信息,这些信息对内核调试和分析非常重要。
  • 没有文件后缀:虽然 vmlinux 通常没有文件后缀,但它是一个标准的 ELF 文件,可以通过文件头信息识别其格式。

2.2 vmlinux的生成过程

编译Linux内核时,vmlinux是在链接阶段生成的。以下是一个简化的生成过程:

  • 编译各个源文件:内核的各个源文件(.c 和 .S 文件)首先被编译为目标文件(.o 文件)。
  • 链接目标文件:所有目标文件通过链接器(如ld)链接在一起,生成一个完整的内核映像,这个映像就是 vmlinux。

链接命令举例:ld -o vmlinux [object files] [linker scripts]

2.3 vmlinuxz和bzImage的生成过程

在获得编译文件vmlinux后,通常使用压缩工具做进一步处理。

  • 压缩内核映像:将 vmlinux 压缩生成 vmlinuz。通常使用 gzip 或其他压缩工具。

压缩命令: gzip -c vmlinux > vmlinuz

  • 生成引导加载程序格式的内核映像:一些系统需要特定格式的内核映像,例如 bzImage(适用于 x86 架构)。

生成命令:make bzImage

2.4 其他压缩格式

vmlinuzbzImagezImage 和 uImage 都是不同的 Linux 内核映像文件格式,它们各自有不同的用途和特性。

  • vmlinuz:通用的压缩内核映像名称,主要用于各种 Linux 发行版。通常使用 gzip 压缩。
  • bzImage:大内核映像,解决了早期 zImage 的内存限制问题。用于 x86 架构,支持较大的内核映像。
  • zImage:较老的内核映像格式,适用于小内核映像,受限于低内存地址空间。
  • uImage:U-Boot 使用的内核映像格式,广泛用于嵌入式系统。包含 U-Boot 头部信息,支持多种压缩算法。

2.5 Android系统文件

在 Android 系统中,内核的压缩文件格式通常是zImageImage.gz,具体取决于所使用的启动加载程序和设备的要求。

  • zImage:在一些早期的 Android 设备上,内核映像可能采用 zImage 格式。这种格式的内核映像通常会被启动加载程序直接加载并解压,然后启动内核。
  • Image.gz:Image.gz 是指经过 gzip 压缩的内核映像。这种格式的内核映像通常是 Linux 内核编译过程中生成的 vmlinuz 文件,只是在 Android 系统中可能被重新命名为 Image.gz。启动加载程序会加载这个压缩的内核映像,并在加载到内存后解压缩,然后启动内核。

3.内核加载过程

3.1 内核映像加载到内存中

启动加载程序(Bootloader)负责将压缩的内核映像加载到内存中,并准备好启动内核的环境。

  • 加载内核和initrd:GRUB 会根据配置文件(通常是 grub.cfg)加载压缩的内核映像和可选的 initrd/initramfs 文件。
  • 设置内核参数:GRUB 会设置内核启动参数,这些参数可以通过命令行传递给内核。
  • 跳转到内核入口点:GRUB 将控制权转移到内核映像的入口点。对于 x86 架构,这个入口点通常在内核映像的开头。
3.1.1 启动BootLoader

以BIOS为例

  • CPU在重置后执行的第一条指令的内存地址0xfffffff0,它包含一个 jump 指令,这个指令通常指向BIOS入口点。
  • BIOS会进行一系列硬件初始化和自检,然后根据设置(例如启动顺序)选择一个启动设备(如硬盘、光盘、USB 等)
  • 将控制权转移到启动设备的启动扇区代码。
3.1.2 加载内核文件
  • 启动设备的启动扇区代码被执行,通常这段代码非常小,只占用一个扇区(512字节)。
  • 启动扇区代码负责完成一些基本的初始化操作,然后跳转到更复杂的引导加载程序,如 GRUB 的核心映像(core image)。
  • 核心映像开始执行,它负责进一步的初始化操作,如加载GRUB的模块和配置文件(grub.cfg)。
  • 根据grub.cfg文件中的配置,GRUB加载压缩的内核映像(vmlinuz)和可选的initrd/initramfs文件。
  • 内核映像加载完成后,GRUB 将控制权转移给内核的入口点代码,完成控制权从 BIOS 到内核的转移。

3.2 内核解压

以下以x86系统为例

3.2.1 关键文件和代码路径
  • arch/x86/boot/header.S:启动代码的汇编部分,定义了内核入口点。
  • arch/x86/boot/compressed/head_64.S 和 arch/x86/boot/compressed/misc.c:解压缩代码。
  • arch/x86/kernel/head_64.S 和 arch/x86/kernel/head.c:解压后的内核启动代码。
3.2.2 主要步骤
3.2.2.1 启动加载程序跳转到内核入口点:
  • BootLoader根据grub.cfg文件中的配置加载内核映像(vmlinuz)到内存,并跳转到内核映像的入口点,即内核代码的起始地址。
3.2.2.2 解压缩程序的初始化:
  • 内核入口点代码(在 header.S 中)会设置初始的 CPU 状态和内存环境,然后跳转到解压缩代码的入口。
  • 32位方法startup_32,64位方法startup_64。(长模式的32到64转换这里不做讨论,有兴趣的可以自行查阅资料)
 
ENTRY(startup_32)
// 设置 CPU 状态和内存环境
jmp decompress_kernel // 跳转到解压缩代码
3.2.2.3 解压缩代码执行:
  • 解压缩代码的入口点在 arch/x86/boot/compressed/head_64.S 中。
  • arch/x86/boot/compressed/head_64.S 会设置解压环境,如设置段寄存器、建立临时堆栈等。
 
ENTRY(decompress_kernel)
// 设置硬件环境
// 调用解压缩函数入口方法
jmp decompress_kernel_method
3.2.2.4 调用解压缩函数:
  • 在arch/x86/boot/compressed/misc.c 中,decompress_kernel 函数负责选择解压算法并解压内核映像。
 
void decompress_kernel(...) {
// 选择解压算法
// 调用相应的解压函数
decompress_method(); // 调用特定的解压算法,如 inflate()
}
3.2.2.5 解压缩完成后跳转到内核入口:
  • 解压完成后,解压缩代码会跳转到解压后的内核入口点。
  • arch/x86/boot/compressed/head_64.S中定义了一个跳转指令,内核入口点的地址加载到寄存器中(例如 %eax),通常是内核主函数(start_kernel),跳转后即将控制权转移到解压后的内核代码。
 
jmp *%eax
3.2.3 名词解释
3.2.3.1 跳转入口点和控制权转移
  • 在技术实现上跳抓入口点和控制权转移是一致的,都是通过改变程序计数器(Program Counter,PC)或指令指针(Instruction Pointer,IP)的值来实现的。

程序计数器或指令指针是一个特殊的寄存器,用于存储正在执行的指令的内存地址。当处理器执行一条指令时,程序计数器会自动递增到下一条指令的地址,从而控制执行流程。这样就实现了执行流程的转移,从而使得程序执行从一个代码段转移到另一个代码段。

  • "跳转到入口点"强调了执行流程从某个特定的位置(入口点)开始执行,而"控制权转移"则更加广泛地描述了执行流程从一个执行上下文到另一个执行上下文的转移过程。
  • 在内核加载和启动的上下文中,这两个术语通常可以互换使用,因为在设置了内核入口点后,执行流程的转移也意味着控制权的转移。
3.2.3.2 initrd/initramfs文件

加载 vmlinuz(Linux 内核映像)时,通常还会加载 initrd(initial ramdisk)或 initramfs(initial ram filesystem)文件。initrd 和 initramfs 文件的主要作用是在内核启动的早期阶段提供一个临时的根文件系统,帮助内核完成启动过程。

特性如下

  • 硬件驱动支持: 在系统启动时,内核可能需要加载某些硬件驱动程序(如文件系统驱动、磁盘驱动、网络驱动等)来访问根文件系统。这些驱动程序可能并未内置在内核映像中,而是作为模块存在。initrd/initramfs 提供了一个早期的文件系统,内核可以从中加载必要的模块。
  • 根文件系统挂载:在一些复杂的存储配置中,如 LVM(Logical Volume Manager)、RAID、加密文件系统等,内核需要在挂载实际根文件系统之前进行一些初始化操作。这些操作通常通过 initrd/initramfs 中的脚本完成。
  • 通用内核:发行版通常提供通用内核以支持多种硬件配置。使用 initrd/initramfs 可以在启动时动态加载适配不同硬件配置的模块,而无需为每种硬件配置编译一个特定的内核。

加载过程

  • 启动加载程序(BootLoader)将内核映像和initrd/initramfs文件加载到内存中,并将控制权交给内核。
  • 内核启动时会识别并加载initrd/initramfs文件,将其作为初始根文件系统挂载。
  • 内核从临时根文件系统中加载必要的模块并运行初始化脚本。
  • 初始化脚本完成必要的硬件初始化和配置后,会挂载实际的根文件系统(如 /dev/sda1)。
  • 初始化脚本切换到实际根文件系统,然后移除initrd/initramfs文件。

三、内核启动(start_kernel)

start_kernel是Linux内核中非常重要的一个函数,它是整个内核初始化的核心函数,负责初始化内核的各个子系统、驱动程序以及其他关键组件,并最终将控制权转移到用户空间。

1.start_kernel方法介绍

1.1 第一个C函数的位置

start_kernel方法的定义通常位于init/main.c文件中,也是Linux启动过程中执行的第一个C函数

1.2 主要功能

  • 初始化内核的基本设置:如内存管理、进程管理等。
  • 初始化各个子系统:如文件系统、网络子系统、设备驱动程序等。
  • 启动第一个用户进程:将控制权从内核转移到用户空间。

2.start_kernel源码解析

 
asmlinkage __visible void __init start_kernel(void)
{
char *command_line;
extern const struct kernel_param __start___param[], __stop___param[];
/* ... 其他初始化代码 ... */
/* 设置页表和内存管理 */
paging_init();
mem_init();
kmem_cache_init();
/* 设备和驱动程序初始化 */
driver_init();
init_irq_proc();
softirq_init();
time_init();
console_init();
/* 文件系统初始化 */
vfs_caches_init_early();
mnt_init();
init_rootfs();
init_mount_tree();
/* 初始化进程 */
pid_cache_init();
proc_caches_init();
/* 启动 init 进程 */
rest_init();
/* ... 其他初始化代码 ... */
/* 调用内核参数解析函数 */
kernel_param_init(karg_strings, num_args);
/* ... 其他初始化代码 ... */
/* 永远不会返回 */
cpu_idle();
}

四、启动初始进程(init process)

1.进程概念介绍

1.1 内核进程(Kernel Thread)和用户进程(User Process)

1.1.1 内核进程(Kernel Thread)

内核进程是由内核创建和调度的线程,运行在内核态,用于处理内核的各类任务。与用户进程不同,内核进程不直接与用户空间交互,主要用于执行内核内部的工作,如处理中断、管理设备、调度任务等。

  • 运行空间:内核进程运行在内核地址空间,而普通用户进程运行在用户地址空间。
  • 权限:内核进程可以直接访问内核数据结构,而用户进程通过系统调用与内核交互。
  • 交互:内核进程通常不与用户交互,其生命周期完全由内核管理。
  • 创建方式:内核进程的创建通常通过kernel_thread函数实现。

注意内核进程是独立的,与0、1、2号进程无关

1.1.2 用户进程(User Process)

用户进程是在用户空间中执行的进程,用户通过编写和执行应用程序来创建用户进程。用户进程通过系统调用与内核交互,进行资源分配、文件操作、网络通信等。

  • 运行空间:用户进程运行在用户态,受限于用户空间的权限,不能直接访问硬件和内核数据结构。
  • 权限:内核线程运行在内核态,具有更高的权限,能够直接操作内核资源。

1.2 0号进程、1号进程、2号进程

  • 0号进程:是内核进程,运行在内核态,负责在系统空闲时执行。
  • 1号进程:是用户进程,虽然最初由内核创建,但主要运行在用户态,负责系统初始化和管理用户空间的其他用户进程。
  • 2号进程:是内核进程,运行在内核态,负责创建和管理其他内核线程。这些内核线程通常用于执行内核中的异步任务,如磁盘I/O、网络操作等。
1.2.1 0号进程(swapper/idle/空闲进程)
  • 0号进程是Linux启动的第一个进程,它的task_struct的comm字段为"swapper",也称为swapper进程、idel进程、空闲进程。
  • 0号进程是在系统引导过程中由内核创建的第一个进程。它的任务是进入空闲循环,当系统中没有其他可运行的进程时,它会被调度执行,以避免CPU闲置。

0号进程(idle进程)是在系统引导过程中,由内核初始化代码创建的。在x86架构中,这个过程发生在汇编启动代码(通常在arch/x86/kernel/head.S中),该代码会设置基本的CPU和内存环境,然后跳转到C语言的start_kernel函数。

1.2.2 1号进程(init进程)和2号进程(kthreadd进程)
  • 1号进程和2号进程都是在rest_init函数中创建的
  • 1号进程通过kernel_init创建
  • 2号进程通过kthreadd创建

2.rest_init函数-初始化入口

rest_init函数负责创建初始进程并进行一些进一步的初始化工作。其代码实现如下:

 
static noinline void __ref rest_init(void)
{
// 通知RCU(Read-Copy Update)子系统,调度器即将开始。这是确保RCU在调度器开始运行前正确初始化的关键步骤。
rcu_scheduler_starting();
// 创建pid=1的1号进程
pid = kernel_thread(kernel_init, NULL, CLONE_FS);
/** 处理1号进程相关代码 **/
// 创建pid=2的2号进程
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
/** 处理2号进程相关代码 **/
/** 其他初始化代码 **/
}

2.1 kernel_thread函数

 
int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, (unsigned long)fn,
(unsigned long)arg, NULL, NULL, 0);
}
  • do_fork:这是内核中实现创建新进程(或线程)的核心函数。通过该函数,内核可以复制当前进程的上下文,生成一个新的进程(或线程)。

2.2 kernel_init函数 - 初始化1号进程(init)

kernel_init负责启动初始用户空间进程(/sbin/init或指定的init进程)。

 
static int __ref kernel_init(void *unused)
{
/** 其他初始化代码 **/
// 启动用户空间进程
if (ramdisk_execute_command) {
run_init_process(ramdisk_execute_command);
} else if (execute_command) {
run_init_process(execute_command);
} else {
run_init_process("/sbin/init");
}
return 0;
}

3. 系统启动

init进程启动后,通过后续工作完成了操作系统的加载和启动

3.1 系统初始化脚本

init进程读取系统的初始化脚本(如/etc/inittab、/etc/init.d/脚本)或systemd的单元文件(unit files),执行系统初始化任务。这包括设置系统环境、挂载文件系统、启动网络服务、启动守护进程等。

3.2 启动用户界面

  • 图形登录管理器:如果系统配置为使用图形界面,init进程会启动图形登录管理器(如GDM、LightDM、SDDM)。这些登录管理器负责提供图形化的登录界面,供用户输入用户名和密码。
  • 启动桌面环境:用户登录成功后,登录管理器会启动用户的桌面环境(如GNOME、KDE、Xfce)。桌面环境提供完整的图形用户界面,允许用户运行应用程序、管理文件、设置系统等。

3.3 图形界面启动流程(systemd示例)

  • systemd初始化:systemd作为init进程启动,读取其配置文件(通常在/lib/systemd/system/和/etc/systemd/system/)。
  • 启动目标(target):systemd根据配置文件启动系统目标(如graphical.target)。graphical.target包含了启动图形界面所需的所有服务。
  • 启动显示管理器:systemd启动图形显示管理器服务(如gdm.service、lightdm.service)。
  • 显示管理器运行:显示管理器提供图形登录界面,用户登录后启动用户会话。
  • 启动桌面环境:用户会话启动后,显示管理器启动桌面环境,用户进入图形用户界面。

五、流程图总结

前面使用了大量文字来说明,这里使用一张流程图来做概要总结 

                                                                 

  • 关注灵活就业新业态,关注公账号:贤才宝(贤才宝https://www.51xcbw.com)

相关文章:

Linux内核Kernel启动过程

一、内核启动的基本流程 1. 启动加载程序 (Bootloader) 启动加载程序(如GRUB、LILO、syslinux等)负责将内核映像从存储设备加载到内存中,并准备好内核启动所需的环境。 加载内核映像:启动加载程序将压缩的内核映像(如…...

苍穹外卖复习(持续更新)

文章目录 苍穹外卖复习Day01Day02(新增员工) 苍穹外卖复习 Day01 Day02(新增员工)...

子网划分实例

看到有人问这个问题: 想了一下,这是一个子网划分的问题: 处理方法如图: 这是一个子网划分的问题 设备1用三层交换机,端口设置为路由模式,设备2和设备3为傻瓜交换机模式 设备2和设备3下挂设备都是26为掩码&…...

二进制部署Prometheus+grafana+alertmanager+node_exporter

Prometheus 是一个开源的监控和告警工具包,旨在提供高可靠性和可扩展性。它最初由 SoundCloud 开发,现已成为云原生计算基金会(CNCF)的一部分。以下是 Prometheus 的一些关键特性和概念: 1. **时间序列数据库**&#…...

数据结构——图(遍历,最小生成树,最短路径)

目录 一.图的基本概念 二.图的存储结构 1.邻接矩阵 2.邻接表 三.图的遍历 1.图的广度优先遍历 2.图的深度优先遍历 四.最小生成树 1.Kruskal算法 2.Prim算法 五.最短路径 1.单源最短路径--Dijkstra算法 2.单源最短路径--Bellman-Ford算法 3.多源最短路径--Floyd-…...

Android APK打包流程

文章目录 前言1. 资源打包(通过 AAPT)2. 处理 AIDL 文件3. Java 源代码编译4. Dex 转换5. APK 打包6. APK 签名7. APK 对齐总结 前言 Android APK 打包过程包括多个关键步骤,每个环节都提供了不同的操作机会。开发者可以在这些步骤中进行自定…...

《Liunx系统》之基础操作命令

目录 简介 1. 文件和目录操作 1.1 查看当前目录 1.2 列出目录内容 1.3 切换目录 1.4 创建目录 1.5 删除目录 1.6 创建文件 1.7 删除文件 2. 文件内容操作 2.1 查看文件内容 2.2 搜索文件内容 2.3 复制文件 2.4 移动文件 3. 系统信息和进程管理 3.1 查看系统信息…...

linux 编译、交叉编译 opencv+ffmpeg 为动态库

文章目录 x86 编译先编译 ffmpeg再编译 opencv验证 opencv 的安装是否链接了 ffmpeg 交叉编译(目标系统 armv8 即 arrch64)准备交叉编译工具链(arm 版 gcc、g)先编译 ffmpeg再编译 opencv验证 opencv 的安装是否链接了 ffmpeg 参考…...

Robust Univariate Mean Estimation算法简介

Robust Univariate Mean Estimation 是一种统计算法,主要用于在单变量场景中估计样本的均值,同时对异常值(outliers)具有鲁棒性。传统的均值估计使用样本的算术平均值,但它对异常值高度敏感。为了缓解这个问题&#xf…...

区块链智能合约( solidity) 安全编程

引言:本文由天玄链开源开发者提供,欢迎报名公益天玄链训练营 https://blockchain.163.com/trainingCamp 一、重入和竞态 重入和竞态在solidity 编程安全中会多次提及,历史上也造成了重大的损失。 1.1 问题分析 竞态的描述不严格&#xf…...

断点续传【授权访问】

本文介绍如何使用STS以及签名URL临时授权访问OSS资源。 授权方式 OSS支持多种授权方式进行客户端授权,以下提供了三种不同授权方式的简要说明,并提供了使用相应授权方式实现简单上传的代码示例,您可以根据使用场景对认证和授权的要求&#…...

中华国际游艇会出席第六届地博会助世界酒中国菜地理标志走向全球

中华国际游艇会积极参与第六届知交会暨地博会,助力世界酒中国菜地理标志产品走向全球 第六届粤港澳大湾区知识产权交易博览会暨国际地理标志产品交易博览会于2024年12月9日至11日在中新广州知识城盛大举行。此次盛会汇聚了众多行业精英和企业代表,共同探…...

Python爬虫——HTML中Xpath定位

Xpath是一种路径查询语言。利用一个路径表达式从html文档中找到我们需要的数据位置,进而将其写入到本地或者数据库中。 学习Xpath爬虫,我们首先学习一下python中lxml库 关于库 lxml 终端下载Xpath需要用到的模块 pip install lxml 关于HTML 超文本标…...

Ubuntu防火墙管理(六)——ARP防火墙过滤防御自定义系统服务

起因 在ubuntu24.04中检查arp表,输出异常 arp -a? (10.162.242.142) 位于 74:3a:20:b9:e8:02 [ether] 在 wlp2s0 ? (10.162.0.1) 位于 在 wlp2s0 ubuntu环境中,这是否表示ARP攻击,本地网关为10.162.0.1,可用arptables防御吗&a…...

Halcon_数据类型_ROI_仿射变换_投影变换

文章目录 算子快捷键一、Halcon数据类型Iconic (图标)Control (控制)Tuple (数组) 二、ROI(区域)1.代码创建ROI2.手动创建ROI 三、图形预处理1.图像的变换与矫正平移 -hom_mat2d_translate旋转缩放-HomMat2D:输入的仿射…...

java+ssm+mysql高校学籍管理系统

项目介绍: 使用javassmmysql开发的高校学籍管理系统,系统包含超级管理员,系统管理员、教师、学生角色,功能如下: 超级管理员:管理员管理(可以新增管理员);专业管理&…...

多表设计-一对多一对多-外键

一.多表设计概述: 二.一对多: 1.需求: 根据 页面原型 及 需求文档,完成部门及员工模块的表结构设计 -->部门和员工就是一对多,因为一个部门下会有多个员工,但一个员工只归属一个部门 2.页面原型&…...

Scala的正则表达式(1)

package hfd //正则表达式的应用场景 //1.查找 findAllin //2.验证 matches //3.替换//验证用户名十分合法 //规则: //1.长度在6-12之间 //2.不能数字开头 //3.只能包含数字,大小写字母,下划线 object Test36 {def main(args: Array[String])…...

uniapp扭蛋机组件

做了一个uniapp的扭蛋机组件,可以前往下载地址下载 仅测试了vue2、3、h5页面微信小程序,理论支持全平台 使用方法简单,具有待机动效、抽奖中动效、掉落奖品动效,可以替换奖品图片,足以满足大部分抽奖页面需求。 示例图…...

并发背后的技术与原理

一个Java Web项目能够同时支持多个用户请求,而不会导致数据混乱,这主要得益于Java平台的多线程处理机制、Web容器的请求处理模型以及良好的编程实践。 Java的多线程处理: Java是一种支持多线程的编程语言。在Java Web应用中,每个用…...

HarmonyOS(63) ArkUI 自定义占位组件NodeContainer

NodeContainer 1、前言2、NodeContainer和NodeController3、示例代码3.1、创建@Builder3.2、 创建NodeController3.3、 使用NodeCtroller4、NodeContainer的作用5、FrameNode简介6、BuilderNode简介7、参考资料1、前言 在HarmonyOS(62) ArkUI @Reusable组件复用原理讲了组件复…...

机器学习实战学习笔记:前言与准备

个人学习介绍 该专栏作为《机器学习实战(原书第三版)》的读书笔记,涉及对书本内容的理解和书本内容原有的示例和部分原文。略懂一点点python语法编程环境选用python 3.12.1 ,IDE为 DataSpell (主要)&#…...

Linux应用开发————多线程的互斥与同步——同步

同步和互斥 互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。 同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。 条件变量机制…...

【人工智能】深度解剖利用人工智能MSA模型

目录 情感分析的应用一、概述二、研究背景三、主要贡献四、模型结构和代码五、数据集介绍六、性能展示七、复现过程 情感分析的应用 近年来社交媒体的空前发展以及配备高质量摄像头的智能手机的出现,我们见证了多模态数据的爆炸性增长,如电影、短视频等…...

Java 环境配置 + IntelliJ IDEA 使用指南

文章目录 一、Java 程序的运行必须经过3 个步骤:编写、编译、运行(1)Java 和 JavaScript 的区别(2)JDK、JRE、JVM 的关系(3)是否需要 Maven? 二、软件下载2.1、JDK下载与安装 —— 是…...

兴业周报|楼市新政效果显著~单周成交破5亿~

香山清琴山庄丙19号(独栋别墅) 稀缺房源:标的物为京城少有的独栋别墅,在连续的 “限墅令” 及相关容积L限制政策下,市场上独栋别墅的新增供应不断减少。 环境优美:香山清琴山庄依山而筑,错落有…...

学习笔记063——通过使用 aspose-words 将 Word 转 PDF 时,遇到的字体改变以及乱码问题

文章目录 1、问题描述:2、解决方法: 1、问题描述: Java项目中,有个需要将word转pdf的需求。本人通过使用aspose-words来转换的。在Windows中,转换是完全正常的。但是当部署到服务器时,会出现转换生成的pdf…...

人工智能导论学习笔记

目录 一、概要 二、人工智能基础知识 智能 人工智能 人工智能三要素 人工智能发展历程 人工智能的三次浪潮 人工智能行业发展现状 人工智能技术水平现状 人工智能技术层级 人工智能应用开发周期 机器学习的流程 一、概要 《人工智能导论(通识版)》张大斌 田恒义 许…...

FCOS: Fully Convolutional One-Stage Object Detection——全卷积一阶段目标检测

FCOS(Fully Convolutional One-Stage Object Detector)是一种全卷积的单阶段目标检测器,旨在通过消除锚点(anchor)的使用,简化目标检测的流程。以下是FCOS的主要特点和组成部分: 1. 无锚点设计…...

《Java核心技术I》映射条目的原子更新

映射条目的原子更新 ConcurrentHashMap只有部分原子更新。 JavaAPI提供了一些新方法,例如:compute方法可以提供一个键和一个计算新值的函数。 map.compute(word,(k,v)->v null ? 1 : v1) 注释:ConcurrentHashMap中不允许有null值。很…...

微信小程序介绍-以及写项目流程(重要)

前言:本篇文章介绍微信小程序以及项目介绍: 文章介绍:介绍了微信小程序常用的指令、组件、api。tips:最好按照官方文档来进行学习,大致可以我的目录来学习,对于写项目是没有问题的 微信小程序官方文档https…...

241207-通过Docker部署Wiki.JS并设置ElasticSearch进行中文搜索

A. 最终效果 B. 配置文件 version: "3" services:wiki:image: ghcr.io/requarks/wiki:2container_name: wikijsports:- "3000:3000"volumes:- /home/lgk/Projects/WikiJS/config:/configenvironment:- DB_TYPEpostgres- DB_HOSTdatabase- DB_PORT5432- DB…...

yum 离线软件安装

适用范围 支持YUM软件管理的操作系统: 银河麒麟 服务器操作系统V10统信服务器操作系统V20CentOS 系列 准备 准备一台可以连接互联网并且与离线安装的操作系统相同版本的操作系统,包括指令集类型相同。 安装下载工具 查询是否已经安装下载工具 yum…...

【jvm】垃圾回收的优点和原理

目录 1. 说明2. 优点3. 原理3.1 发现无用对象3.2 回收无用对象所占用的内存 4. 回收算法4.1 标记-清除算法4.2 复制算法4.3 标记-整理算法4.4 分代收集算法 1. 说明 1.JVM(Java虚拟机)垃圾回收是Java语言的一大特性,它自动管理内存&#xff…...

LeetCode322. 零钱兑换(2024冬季每日一题 28)

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。 你可以认为每种硬币的数量是无限的。 示…...

LearnOpenGL学习(高级OpenGL --> 帧缓冲,立方体贴图,高级数据)

完整代码见:zaizai77/Cherno-OpenGL: OpenGL 小白学习之路 帧缓冲 帧缓冲(FrameBuffer)是所有屏幕缓冲(包括颜色缓冲,深度缓冲,模板缓冲)的集合。它被存储在GPU内存中,我们可以定义…...

mysql笔记——索引

索引 InnoDB采用了B树索引结构。 相比于二叉树,层级更少,搜索效率高。 B树中叶子节点和非叶节点都会存储数据,导致段页式存储中一页存储的键值减少,指针也会减少,要同样保存大量数据,只能增加树的高度&a…...

React第十五节useReducer使用详解差异

useReducer() 的用法注意事项 1、 概述: useReducer() 常用于管理复杂的状态更新逻辑,特别是在状态更新依赖于多个条件或动作时,useReducer 提供了一种更加结构化和可维护的方式来处理状态。可以将更新函数写在组件外面 它与 useState() 相…...

高效备考 Oracle 19c OCM 的建议

满足报考条件 考生需要先获得 19c OCP(Oracle Certified Professional)认证,并完成 Oracle 官方认可的 OCP 培训课程 制定学习计划 规划学习时间:根据考试时间和自己的日常安排,制定详细的学习计划,合理分配…...

01-Chromedriver下载与配置(mac)

下载地址: 这里我用的最后一个,根据自己chrome浏览器选择相应的版本号即可 ChromeDriver官网下载地址:https://sites.google.com/chromium.org/driver/downloads ChromeDriver官网最新版下载地址:https://googlechromelabs.git…...

网站流量和用户行为深度分析

关于网站流量数据集的探索 import pandas as pd import plotly.express as px import plotly.graph_objects as go import plotly.subplots as sp import matplotlib.pyplot as plt import seaborn as sns file_path /home/mw/input/webs4651/website_wata.csv data pd.rea…...

centOS7如何配置阿里云或者腾讯云yum源

众所周知,CentOS很多版本目前已经不再维护了,原本的在线yum源已经无法使用,所以需要我们配置其他的yum源。目前腾讯云或者阿里云的yum源都可以正常使用,所以本文教大家如何配置阿里云/腾讯云在线yum源。 阿里云yum源配置&#xf…...

洗鞋小程序(源码+文档+部署+讲解)

本文将深入解析“洗鞋小程序”的项目,探究其架构、功能以及技术栈,并分享获取完整源码的途径。 系统概述 为洗鞋提供服务,包含小程序和管理端。 本项目名称为洗鞋小程序,是一个基于小程序的在线洗鞋平台。该系统提供下单、订单管…...

MySQL|通过JSON_UNQUOTE实现MySQL中JSON数据的干净提取

文章目录 语法使用示例注意事项 JSON_UNQUOTE() 是 MySQL 中用于处理 JSON 数据类型的一个函数。它的主要作用是从 JSON 字符串中移除最外层的引号,这对于从 JSON 对象或数组中提取字符串值特别有用。 语法 JSON_UNQUOTE(json_string)json_string: 这是你想要去掉引…...

动态规划part01

文章参考来源代码随想录 理论基础 适用范围: 如果某一问题有很多重叠子问题,使用动态规划是最有效的。 与贪心算法的区别: 动态规划中每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心,贪心没有状态推…...

生活大爆炸版石头剪刀布(洛谷P1328)

生活大爆炸版石头剪刀布(洛谷P1328) [NOIP2014 提高组] 前言: 由于洛谷发布题解有限制,所以在CSDN上发布洛谷题解。 所有题解均是Java语言, 但是思路是相同的 每篇都是刷题日常,尽量讲清楚算法逻辑。 希望有问题还请大佬们指导! …...

【GOOD】DeGEM

ICLR2025 under review 看到不错的想法,学习一下 Decoupled Graph Energy-based Model for Node Out-of-Distribution Detection on Heterophilic Graphs 🐱🐶图上的OOD检测的工作是比较少的,相比于图像数据,图结构数…...

jenkins邮件的配置详解

Jenkins邮件的配置涉及多个步骤和细节,以下是详细的配置指南: 一、前期准备 确定邮件服务:明确Jenkins将要使用的邮件服务,如QQ邮箱、163邮箱、公司邮箱(基于Microsoft 365或Exchange Server)等。获取SMTP配置信息:根据邮件服务类型,获取相应的SMTP服务器地址、端口号…...

flask-socketio相关总结

flask-socketio是一个为flask应用程序添加的实时双向通信功能的扩展库,有了这个库,就可以在flask应用中应用websocket协议,帮助flask实现低延迟、双向的客户端、服务端通信。客户端通过任何SocketIO官方库,都能与服务器建立长连接…...

每日一题 LCR 097. 不同的子序列

LCR 097. 不同的子序列 使用动态规划就可以解决&#xff0c;重点是知道 动态规划的状态是如何转移的 class Solution { public:int numDistinct(string s, string t) {int ns s.size();int nt t.size();vector<vector<long>> dp(ns1,vector<long>(nt1,0)…...