Linux 中断机制(一)之中断和异常

news2024/11/25 2:47:54

目录

  • 一、什么是中断
    • 1、概述
    • 2、中断的分类
  • 二、中断和异常
    • 1、中断和异常
    • 2、中断的上下部
    • 3、异常
    • 4、APIC
    • 5、中断描述符表
  • 三、软件实现


一、什么是中断

1、概述

中断(interrupt)是指在 CPU 正常运行期间, 由外部或内部事件引起的一种机制。 当中断发生时,CPU 会停止当前正在执行的程序,并转而执行触发该中断的中断处理程序。处理完中断处理程序后,CPU 会返回到中断发生的地方, 继续执行被中断的程序。中断机制允许 CPU 在实时响应外部或内部事件的同时,保持对其他任务的处理能力。

中断的流程图如下:

2、中断的分类

中断通常被定义为一个事件,该事件改变处理器执行的指令顺序。这样的事件与 CPU 芯片内外部硬件电路产生的电信号相对应。

中断通常分为同步(synchronous)中断和异步(asynchronous)中断:

  • 同步中断是当指令执行时由 CPU 控制单元产生的,之所以称为同步,是因为只有在一条指令终止执行后CPU才会发出中断;
  • 异步中断是由其他硬件设备依照 CPU 时钟信号随机产生的。

在 Intel 微处理器手册中,把同步和异步中断分别称为异常(exception)和中断(interrupt)我们也采用这种分类,当然有时我们也用术语“中断信号”指这两种类型(同步及异步)。

二、中断和异常

1、中断和异常

Intel 文档把中断和异常分为以下几类:

  • 中断
    • 可屏蔽中断(maskabie interrupt
      I/O 设备发出的所有中断请求(IRQ)都产生可屏蔽中断。可屏蔽中断可以处于两种状态:屏蔽的(masked)或非屏蔽的(unmasked),一个屏蔽的中断只要还是屏蔽的,控制单元就忽略它。要根据中断允许标志的设置来判断 CPU 是否能响应中断请求
    • 非屏蔽中断(nonmakable interrupt
      只有几个危急事件(如硬件故障)才引起非屏蔽中断。非屏蔽中断总是由 CPU 辨认。不受中断允许标志的影响,不能用软件进行屏蔽
      可屏蔽的中断可以被阻塞,使用 x86_64 的指令 sticli。这两个指令修改了在中断寄存器中的 IF 标识位。 sti 指令设置 IF 标识,cli 指令清除这个标识。不可屏蔽的中断总是被报告。通常,任何硬件上的失败都映射为不可屏蔽中断。我们可以在 Linux 内核代码中找到这两个指令的使用:
static inline void native_irq_disable(void)
{
        asm volatile("cli": : :"memory");
}

static inline void native_irq_enable(void)
{
        asm volatile("sti": : :"memory");
}
  • 异常
    1. 处理器探测异常(processor-detected exception
      当 CPU 执行指令时探测到的一个反常条件所产生的异常。可以进一步分为三组,这取决于 CPU 控制单元产生异常时保存在内核态堆栈 eip 寄存器中的值。
      • 故障(fault
        通常可以纠正:一旦纠正,程序就可以在不失连贯性的情况下重新开始。保存在 eip 中的值是引起故障的指令地址。因此,当异常处理程序终止时,那条指令会被重新执行。
      • 陷阱(trap
        在陷阱指令执行后立即报告;内核把控制权返回给程序后就可以继续它的执行而不失连贯性。保存在 eip 中的值是一个随后要执行的指令地址。只有当没有必要重新执行已终止的指令时,才触发陷阱。陷阱的主要用途是为了调试程序。在这种情况下,中断信号的作用是通知调试程序一条特殊指令已被执行(例如到了一个程序内的断点)。一旦用户检查到调试程序所提供的数据,它就可能要求被调试程序从下一条指令重新开始执行。
      • 异常中止(abort
        发生一个严重的错误:控制单元出了问题,不能在 eip 寄存器中保存引起异常的指令所在的确切位置。异常中止用于报告严重的错误,如硬件故障或系统表中无效的值或不一致的值。由控制单元发送的这个中断信号是紧急信号,用来把控制权切换到相应的异常中止处理程序,这个异常中止处理程序除了强制受影响的进程终止外,没有别的选择。
    2. 编程异常(programmed exception
      在编程者发出请求时发生。是由 intint3 指令触发的,当 into(检查溢出)和 bound(检查地址出界)指令检查的条件不为真时,也引起编程异常。控制单元把编程异常作为陷阱来处理。编程异常通常也叫做软中断sofware interrupt)这样的异常有两种常用的用途:执行系统调用及给调试程序通报一个特定的事件。

每个中断和异常是由 0~255 之间的一个数来标识。因为一些未知的原因,Intel 把这个 8 位的无符号整数叫做一个向量(vector)。非屏蔽中断的向量和异常的向量是固定的,而可屏蔽中断的向量可以通过对中断控制器的编程来改变。

中断和异常的区别:中断是由硬件引起的;异常则发生在编程失误而导致错误指令,或者在执行期间出现特殊情况必须要靠内核来处理的时候(比如缺页)。

2、中断的上下部

中断的执行需要快速响应, 但并不是所有中断都能迅速完成。 此外, Linux 中的中断不支持嵌套, 意味着在正式处理中断之前会屏蔽其他中断, 直到中断处理完成后再重新允许接收中断,如果中断处理时间过长, 将会引发问题。

这里以炒菜的过程中接电话进行举例:当你正在炒菜的时候,菜正在锅里翻炒着。 突然, 你的手机响起,打破了你正常的炒菜流程,接电话的时间很短并不会对炒菜产生很大的影响, 而接电话的时候可能就有问题了,因为菜可能会因为没来得及翻面而炒糊了。

为了让系统可以更好地处理中断事件, 提高实时性和响应能力, 将中断服务程序划分为上下文两部分:

  • 上半部:上半部是中断处理函数的一部分,它主要处理一些紧急且需要快速响应的任务。 中断上文的特点是执行时间较短,旨在尽快完成对中断的处理。这些任务可能包括保存寄存器状态、更新计数器等, 以便在中断处理完成后能够正确地返回到中断前的执行位置。
    上半部的执行是在中断上下文中进行的,它运行在中断服务例程(ISR)所在的内核线程上下文中,而不是用户进程的上下文中。因此,上半部的执行是在中断被触发时立即执行的,不会被其他中断打断。

  • 下半部是中断处理函数的另一部分,它相对于上半部来说是延迟执行的。下半部的目的是在中断被触发后,尽快将一些不紧急或者耗时的处理工作延后执行,以减轻上半部的负担,从而使中断处理更加高效。
    下半部的执行是在非中断上下文中进行的,它不会被其他中断打断,并且可以访问用户空间的内存。下半部的执行可以在任意时刻进行,但是需要注意的是,下半部执行的时间越长,会导致中断延迟更长,从而影响系统的响应性能。下半部一般包括以下几种形式:

    • 内核线程:创建一个新的内核线程来执行一些独立于中断的任务。
    • 任务队列:将需要执行的任务放入任务队列中,由内核调度器来选择适当的时机执行。
    • 工作队列:类似于任务队列,但是工作队列可以绑定到某个 CPU,以提高处理效率。

3、异常

80x86 微处理器发布了大约 20 种不同的异常(依赖于体系结构)。内核必须为每种常提供一个专门的异常处理程序。对于某些异常,CPU 控制单元在开始执行异常处理程序前会产生一个硬件出错码(hardware error code),并且压入内核态堆栈。

下面的列表给出了在 80x86 处理器中可以找到的异常的向量、名字、类型及其简单描述。更多的信息可以在 Intel 的技术文挡中找到。

  • 0:“Divide error”(故障)
    当一个程序试图执行整数被 0 除操作时产生。
  • 1:“Debug”(陷阱或故障)
    产生于:
    • 设置 eflags 的 TF 标志时(对于实现调试程序的单步执行是相当有用的);
    • 一条指令或操作数的地址落在一个活动 debug 寄存器的范围之内。
  • 2:未用
    为非屏蔽中断保留(利用 NMI 引脚的那些中断)。
  • 3:“Breakpoint”(陷阱)
    int3(断点)指令(通常由 debugger 插入)引起。
  • 4:“Overflow”(陷阱)
    当 eflags 的 OF(overflow)标志被设置时,into(检查溢出)指令被执行。
  • 5:“Bounds check"(故障)
    对于有效地址范围之外的操作数,bound(检查地址边界)指令被执行。
  • 6:“Invalid opcode"(故障)
    CPU 执行单元检测到一个无效的操作码(决定执行操作的机器指令部分)
  • 7:“Device not available”(故障)
    随着 cr0 的 TS 标志被设置,ESCAPEMMXXMM 指令被执行。
  • 8:“Double fault”(异常中止)
    正常情况下,当 CPU 正试图为前一个异常调用处理程序时,同时又检测到一个异常,两个异常能被串行地处理。然而,在少数情况下,处理器不能串行地处理它们因而产生这种异常。
  • 9:“Coprocessor segment overrun”(异常中止)
    因外部的数学协处理器引起的问题(仅用于 80386 微处理器)。
  • 10:“Invalid TSS”(故障)
    CPU 试图让一个上下文切换到有无效的 TSS 的进程。
  • 11:“Segment not present”(故障)
    引用一个不存在的内存段(段描述符的 Segment-Present 标志被清0)。
  • 12:“Stack segment fault”(故障)
    试图超过栈段界限的指令,或者由 ss 标识的段不在内存
  • 13:“General protection”(故障)
    违反了 80x86 保护模式下的保护规则之一。
  • 14:“Page fault”(故障)
    寻址的页不在内存,相应的页表项为空,或者违反了一种分页保护机制。
  • 15:由 Intel 保留
  • 16:“Floating point error”(故障)
    集成到 CPU 芯片中的浮点单元用信号通知一个错误情形,如数字溢出,或被 0 除。
  • 17:“Alignment check”(故障)
    操作数的地址没有被正确地对齐(例如,一个长整数的地址不是 4 的倍数)。
  • 18:“Machine check”(异常中止)
    机器检查机制检测到一个 CPU 错误或总线错误。
  • 19:“SIMD floating point exception"(故障)
    集成到 CPU 芯片中的 SSE 或 SSE2 单元对浮点操作用信号通知一个错误情形。

20~31 这些值由 Intel 留作将来开发。如下表所示,每个异常都由专门的异常处理程序来处理,它们通常把一个 Unix 信号发送到引起异常的进程。

编号异常异常处理程序信号
0Divide errordivide error()SIGFPE
1Debugdebug()SIGTRAP
2NMInmi()None
3Breakpointint3()SIGTRAP
4Overflowoverflow()SIGSEGV
5Bounds checkbounds()SIGSEGV
6Invalid opcodeinvalid_op()SIGILL
7Device not availabledevice_not_available()None
8Double faultdoublefault_fn()None
9coprocessor segment overruncoprocessor_segment_overrun()SIGFPE
10Invalid TSSinvalid_tss()SIGSEGV
11Segment not presentsegment_not_present()SIGBUS
12Stack exceptionstack_segment()SIGBUS
13General protectiongeneral_protection()SIGSEGV
14Page faultpage_fault()SIGSEGV
15Intel reservedNoneNone
16Floating point errorcoprocessor_error()SIGFPE
17Alignment checkalignment_check()SIGSEGV
18Machine checkmachine_check()None
19SIMD floating pointsimd_coprocessor_error()SIGFPE

4、APIC

前面已经讲了什么是中断,那么中断信号是怎么处理的呢?比如,当我们在键盘上按下一个键的时候,我们下一步期望做什么?操作系统和电脑应该怎么做?做一个简单的假设,每一个物理硬件都有一根连接 CPU 的中断线,设备可以通过它对 CPU 发起中断信号。但是中断信号并不是直接发送给 CPU。在老机器上中断信号发送给 PIC ,它是一个顺序处理各种设备的各种中断请求的芯片。在新机器上,则是高级程序中断控制器(Advanced Programmable Interrupt ControllerAPIC)做这件事情。一个 APIC 包括两个独立的设备:

  • Local APIC:在于每个 CPU 核心中,Local APIC 负责处理特定于 CPU 的中断配置。Local APIC 常被用于管理来自 APIC 时钟(APIC-timer)、热敏元件和其他与 I/O 设备连接的设备的中断。
  • I/O APIC:提供了多核处理器的中断管理。它被用来在所有的 CPU 核心中分发外部中断。

下图显示了一个多 APIC 系统的结构。一条 APIC 总线把“前端” I/O APIC 连接到本地 APIC。来自设备的 IRQ 线连接到 I/O APIC,因此,相对于本地 APIC,I/O APIC 起路由器的作用。在 Pentium III 和早期处理器的母板上,APIC 总线是一个串行三线总线;从 Pentium 4 开始,APIC 总线通过系统总线来实现。不过,因为 APIC 总线及其信息对软件是不可见的,因此,我们不做进一步的详细讨论。

5、中断描述符表

中断可以在任何时间发生,当一个中断发生时,操作系统必须确保下面的步骤顺序:

  1. 内核必须暂停执行当前进程(取代当前的任务);
  2. 内核必须搜索中断处理程序并且转交控制权(执行中断处理程序);
  3. 中断处理程序结束之后,被中断的进程能够恢复执行。

每个中断处理程序的地址都保存在一个特殊的位置,这个位置被称为中断描述符表Interrupt Descriptor TableIDT)。处理器使用一个唯一的数字来识别中断和异常的类型,这个数字被称为中断标识码vector number)。一个中断标识码就是一个 IDT 的标识。中断标识码范围是有限的,从 0 到 255。你可以在 Linux 内核源码中找到下面的中断标识码范围检查代码:

BUG_ON((unsigned)n > 0xFF);

在 Linux 内存管理(二)之GDT与LDT 一文中,我们讲到了 GDT 和 LDT,IDT 的格式与这两种表的格式非常相似,表中的每一项对应一个中断或异常向量,每个向量由 8 个字节组成。因此,最多需要 256 ∗ 8 = 2048 256*8=2048 2568=2048 字节来存放 IDT。

idtr CPU寄存器使 IDT 可以位于内存的任何地方,它指定 IDT 的线性基地址及其限制(最大长度)。在允许中断之前,必须用 lidt 汇编指令初始化 idtr

IDT 包含三种类型的描述符,下图显示了每种描述符中的 64 位的含义。尤其值得注意的是,在 40~43 位的 Type 字段的值表示描述符的类型。

  • 任务门(task gate
    当中断信号发生时,必须取代当前进程的那个进程的 TSS 选择符存放在任务门中。
  • 中断门(interrupt gate
    包含段选择符和中断或异常处理程序的段内偏移量。当控制权转移到一个适当的段时,处理器清 IF 标志,从而关闭将来会发生的可屏蔽中断。
  • 陷阱门(Trap gate
    与中断门相似,只是控制权传递到一个适当的段时处理器不修改 IF 标志。

三、软件实现

中断描述符表 使用 gate_desc 的数组描述:

extern gate_desc idt_table[];

gate_desc 定义如下:

#ifdef CONFIG_X86_64
	...
	typedef struct gate_struct64 gate_desc;
	...
#endif

gate_struct64 定义如下:

struct gate_struct64 {
	u16 offset_low;
    u16 segment;
    unsigned ist : 3, zero0 : 5, type : 5, dpl : 2, p : 1;
    u16 offset_middle;
    u32 offset_high;
    u32 zero1;
} __attribute__((packed));

在 x86_64 架构中,每一个活动的线程在 Linux 内核中都有一个很大的栈。这个栈的大小由 THREAD_SIZE 定义,而且与下面的定义相等:

#define PAGE_SHIFT      12
#define PAGE_SIZE       (_AC(1,UL) << PAGE_SHIFT)
...
#define THREAD_SIZE_ORDER       (2 + KASAN_STACK_ORDER)
#define THREAD_SIZE  (PAGE_SIZE << THREAD_SIZE_ORDER)

其中,PAGE_SIZE 是 4096 字节,THREAD_SIZE_ORDER 的值依赖于 KASAN_STACK_ORDER。就像我们看到的,KASAN_STACK 依赖于 CONFIG_KASAN 内核配置参数,它定义如下:

#ifdef CONFIG_KASAN
    #define KASAN_STACK_ORDER 1
#else
    #define KASAN_STACK_ORDER 0
#endif

KASan 是一个运行时内存调试器。所以:

  • 如果 CONFIG_KASAN 被禁用,THREAD_SIZE 是 16384;
  • 如果内核配置选项打开,THREAD_SIZE 的值是 32768。

这块栈空间保存着有用的数据,只要线程是活动状态或者僵尸状态。但是当线程在用户空间的时候,这个内核栈是空的,除非 thread_info 结构在这个栈空间的底部。活动的或者僵尸线程并不是在他们栈中的唯一的线程,与每一个 CPU 关联的特殊栈也存在于这个空间。当内核在这个 CPU 上执行代码的时候,这些栈处于活动状态;当在这个 CPU 上执行用户空间代码时,这些栈不包含任何有用的信息。每一个 CPU 也有一个特殊的 per-cpu 栈。首先是给外部中断使用的 中断栈(interrupt stack)。它的大小定义如下:

#define IRQ_STACK_ORDER (2 + KASAN_STACK_ORDER)
#define IRQ_STACK_SIZE (PAGE_SIZE << IRQ_STACK_ORDER)

或者是 16384 字节。Per-cpu 的中断栈在 x86_64 架构中使用 irq_stack_union 联合描述:

union irq_stack_union {
    char irq_stack[IRQ_STACK_SIZE];
    struct {
        char gs_base[40];
        unsigned long stack_canary;
    };
};

第一个 irq_stack 域是一个 16KB 的数组。然后你可以看到 irq_stack_union 联合包含了一个结构体,这个结构体有两个域:

  • gs_base:总是指向 irqstack 联合底部的 gs 寄存器。在 x86_64 中, per-cpu 和 stack canary 共享 gs 寄存器。所有的 per-cpu 标志初始值为零,并且 gs 指向 per-cpu 区域的开始。
  • stack_canary:stack canary 对于中断栈来说是一个用来验证栈是否已经被修改的 栈保护者stack protector)。gs_base 是一个 40 字节的数组,GCC 要求 stack canary 在被修正过的偏移量上,并且 gs 的值在 x86_64 架构上必须是 40,在 x86 架构上必须是 20。

下面来看 irq_stack_union 的初始化过程。除了 irq_stack_union 的定义,我们可以在arch/x86/include/asm/processor.h 中查看下面的 per-cpu 变量:

DECLARE_PER_CPU(char *, irq_stack_ptr);
DECLARE_PER_CPU(unsigned int, irq_count);

第一个参数 irq_stack_ptr,它是一个指向这个栈顶的指针。第二个参数 irq_count 用来检查 CPU 是否已经在中断栈。irq_stack_ptr 的初始化在 arch/x86/kernel/setup_percpu.csetup_per_cpu_areas 函数中:

void __init setup_per_cpu_areas(void)
{
...
...
#ifdef CONFIG_X86_64
for_each_possible_cpu(cpu) {
    ...
    per_cpu(irq_stack_ptr, cpu) =
            per_cpu(irq_stack_union.irq_stack, cpu) +
            IRQ_STACK_SIZE - 64;
    ...
#endif
...
}

在这个函数里,我们一个一个查看所有 CPU,并且设置 irq_stack_ptr,它等于中断栈的顶减去 64。为什么是 64?见文件 arch/x86/kernel/cpu/common.c 代码如下:

void load_percpu_segment(int cpu)
{
	...
    loadsegment(gs, 0);
	wrmsrl(MSR_GS_BASE, (unsigned long)per_cpu(irq_stack_union.gs_base, cpu));
}

其中 gs 寄存器指向中断栈的栈底:

movl    $MSR_GS_BASE,%ecx
movl    initial_gs(%rip),%eax
movl    initial_gs+4(%rip),%edx
wrmsr

GLOBAL(initial_gs)
.quad    INIT_PER_CPU_VAR(irq_stack_union)

其中 wrmsr 指令从 edx:eax 加载数据到被 ecx 指向的 MSR 寄存器)。在这里 MSR 寄存器是 MSR_GS_BASE,它保存了被 gs 寄存器指向的内存段的基址。edx:eax 指向 initial_gs 的地址,它就是 irq_stack_union 的基址。

我们还知道,x86_64 有一个叫 中断栈表Interrupt Stack TableIST)的组件,当发生不可屏蔽中断、双重错误等等的时候,这个组件提供了切换到新栈的功能。这可以到达 7 个 IST per-cpu 入口。其中一些定义如下:

#define DOUBLEFAULT_STACK 1
#define NMI_STACK 2
#define DEBUG_STACK 3
#define MCE_STACK 4

所有被 IST 切换到新栈的中断门描述符都由 set_intr_gate_ist 函数初始化。例如:

set_intr_gate_ist(X86_TRAP_NMI, &nmi, NMI_STACK);
...
set_intr_gate_ist(X86_TRAP_DF, &double_fault, DOUBLEFAULT_STACK);

其中 &nmi&double_fault 定义在 arch/x86/kernel/entry_64.S 中,是中断函数的入口地址:

asmlinkage void nmi(void);
asmlinkage void double_fault(void);


// arch/x86/kernel/entry_64.S
idtentry double_fault do_double_fault has_error_code=1 paranoid=2
...
ENTRY(nmi)
...
END(nmi)

当一个中断或者异常发生时,新的 ss 选择器被强制置为 NULL,并且 ss 选择器的 rpl 域被设置为新的 cpl。旧的 ss、rsp、寄存器标志、cs、rip 被压入新栈。在 64 位模型下,中断栈帧大小固定为 8 字节,所以我们可以得到下面的栈:

+---------------+
|               |
|      SS       | 40
|      RSP      | 32
|     RFLAGS    | 24
|      CS       | 16
|      RIP      | 8
|   Error code  | 0
|               |
+---------------+
  • 如果在中断门中 IST 域不是 0,我们把 IST 读到 rsp 中。
    • 如果它关联了一个中断向量错误码,我们再把这个错误码压入栈。
    • 如果中断向量没有错误码,就继续并且把虚拟错误码压入栈。
  • 我们必须做以上的步骤以确保栈一致性。接下来我们从门描述符中加载段选择器域到 CS 寄存器中,并且通过验证第 21 位的值来验证目标代码是一个 64 位代码段,例如 L 位在 GDT。
  • 最后我们从门描述符中加载偏移域到 rip 中,rip 是中断处理函数的入口指针。然后中断函数开始执行,在中断函数执行结束后,它必须通过 iret 指令把控制权交还给被中断进程。iret 指令无条件地弹出栈指针(ss:rsp)来恢复被中断的进程,并且不会依赖于 cpl 改变。

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

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

相关文章

Miracast ——随时随地在Wi-Fi®设备上分享高清内容

Miracast 是一种无线显示技术&#xff0c;由 Wi-Fi 联盟开发&#xff0c;允许设备之间通过无线方式分享多媒体内容。 Wi-Fi CERTIFIED Miracast™支持在Miracast设备之间无缝显示多媒体内容。Miracast使用户能够通过无线连接在Wi-Fi设备之间分享多媒体内容&#xff0c;包括高分…

六西格玛绿带培训对企业有什么帮助?

六西格玛&#xff0c;这一源自摩托罗拉、风靡全球的管理哲学和方法论&#xff0c;以其严谨的数据分析、持续改进的流程优化理念&#xff0c;帮助无数企业实现了从“好”到“卓越”的跨越。而六西格玛绿带&#xff0c;作为这一体系中的中坚力量&#xff0c;是连接高层管理者与一…

Linux--C语言之分支结构

文章目录 一、分支结构&#xff08;一&#xff09;概念&#xff08;二&#xff09;条件构建1.关系表达式&#xff1a;2.逻辑表达式&#xff1a;3.常量/变量&#xff1a;值是否非0&#xff0c;取值&#xff08;0|1&#xff09; &#xff08;三&#xff09;选择结构的形式1.单分支…

QT容器组

目录 容器组 Group BoX&#xff08;组&#xff09; Scroll Area&#xff08;组滑动&#xff09; Tool Box&#xff08;分页显示&#xff09; Tab Widget&#xff08;也是分页显示&#xff09; Stacked widget&#xff08;也是分页&#xff09; Frame&#xff08;就一个框…

无字母数字webshell之命令执行

文章目录 无字母数字的webshell构造技巧php7下简单解决问题php5下解决问题glob开始操作 无字母数字的webshell构造技巧 <?php if(isset($_GET[code])){$code $_GET[code];if(strlen($code)>35){die("Long.");}if(preg_match("/[A-Za-z0-9_$]/",$c…

应对FingerprintJS反爬:Selenium的破解策略与技术详解

目录 引言 FingerprintJS技术概述 技术原理 应用场景 应对策略 高级解决方案 代码实现与案例分析 去除webdriver特征 使用Undetected_chromedriver 案例分析&#xff1a;爬取目标网站数据 结论 引言 在现代互联网环境中&#xff0c;网站反爬技术日益成熟&#xff0…

分布式知识总结(基本概念)

文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 基本概念 吞吐量 指系统在单位时间能够处理多少个请求 QPS 每秒…

【mars3d】加载pbf矢量瓦片的最佳方案介绍

矢量瓦片的目前最佳方案&#xff1a; 目前示例中提供了不同的矢量瓦片的加载方案 但是加载矢量瓦片pbf的最佳方案&#xff1a; 使用 TileServer GL 开源地图服务工具&#xff1a;https://github.com/maptiler/tileserver-gl &#xff0c; 它利用 MapLibre GL Native 进行服务…

day34——TCP和UDP的基础通信

一、网络通信之套接字 1.1 套接字通信原理 1.2 socket函数介绍 #include <sys/types.h> /* See NOTES */#include <sys/socket.h>int socket(int domain, int type, int protocol);功能&#xff1a;为通信创建一个端点&#xff0c;并返回该端点的文件描述…

Llama 3.1中文微调数据集已上线,超大模型一键部署

7 月的 AI 圈真是卷完小模型卷大模型&#xff0c;精彩不停&#xff01;大多数同学都能体验 GPT-4o、Mistral-Nemo 这样的小模型&#xff0c;但 Llama-3.1-405B 和 Mistral-Large-2 这样的超大模型让很多小伙伴犯了难。 别担心&#xff01;hyper.ai 官网在教程板块为大家提供了…

从AGV到立库,物流自动化的更迭与未来

AGV叉车 随着柔性制造系统的广泛应用&#xff0c;小批量、多批次的生产需求不断增强&#xff0c;“订单导向”生产已经成为趋势。这也让越来越多的企业认识到&#xff0c;产线的智能设备导入只是第一步&#xff0c;要想达到生产效率的最优解&#xff0c;物流系统的再优化必须提…

【redis的大key问题】

在使用 Redis 的过程中&#xff0c;如果未能及时发现并处理 Big keys&#xff08;下文称为“大Key”&#xff09;&#xff0c;可能会导致服务性能下降、用户体验变差&#xff0c;甚至引发大面积故障。 本文将介绍大Key产生的原因、其可能引发的问题及如何快速找出大Key并将其优…

Z 字形遍历二叉树

假设一个二叉树上各结点的权值互不相同。 我们就可以通过其后序遍历和中序遍历来确定唯一二叉树。 请你输出该二叉树的 ZZ 字形遍历序列----也就是说&#xff0c;从根结点开始&#xff0c;逐层遍历&#xff0c;第一层从右到左遍历&#xff0c;第二层从左到右遍历&#xff0c;…

Linux文本处理shell脚本

文本处理 在进行文本处理时&#xff0c;我们有一些常见的需求&#xff1a; 获取文本的行数、字数比较两段文本的不同之处查看文本的开头几行和最后几行在文本中查找字符串在文本中替换字符串 下面介绍如何在 shell 中做到这些事情。 文本统计&#xff1a;wc wc 是文本统计…

了解经典的 MPLS L3VPN 网络架构

1.多协议标签交换技术MPLS的概念 MPLS&#xff08;Multi-Protocol Label Switching&#xff0c;多协议标签交换技术&#xff09;,传统网络中就拥有了 3 种经典转发实现&#xff0c;它们分别是&#xff1a; L2 交换转发L2.5 标签转发L3 路由转发 MPLS 协议则作用于 L2.5 层&…

大数据-80 Spark 简要概述 系统架构 部署模式 与Hadoop MapReduce对比

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

布隆过滤器将应用Redis缓存使用量降低100倍

文章目录 背景布隆过滤器介绍定义工作原理数据结构&#xff1a; Redis布隆过滤器实战总结 背景 由于在业务中用到了Redis用于存储一些关系信息&#xff0c;且对应的请求量比较大&#xff0c;为了防止缓存击穿导致数据库压力过大&#xff0c;一般我们都会采用将不存在的内容存储…

头狼择校小程序

综述介绍 头狼择校&#xff0c;是头狼择™高校的简称&#xff0c;我们专注高校、大学的择校。倡导先嗅就业再择校&#xff0c;是预约工具和对话平台。帮您嗅招办、嗅教授、嗅学姐&#xff0c;预约择校有关的老师、顾问&#xff0c;助力考大学和考研的“双考”学生及家长了解就…

C# OnnxRuntime部署LivePortrait实现快速、高质量的人像驱动视频生成

目录 效果 说明 项目 模型信息 代码 下载 效果 LivePortrait实现快速、高质量的人像驱动视频生成 说明 官网地址&#xff1a;https://github.com/KwaiVGI/LivePortrait 代码实现参考&#xff1a;https://github.com/hpc203/liveportrait-onnxrun 模型下载&#xff1a;…

【健康革命】让AI成为你的私人健身教练!

本文由 ChatMoney团队出品 现在市面上有很多男生和女生为了保持身材都进行疯狂的减肥&#xff0c;有些是靠吃减肥药来保持身材&#xff0c;有些是晚上不吃饭要控制&#xff0c;还有些疯狂且漫无目的健身&#xff1b;但往往以上几种都很伤身体的&#xff0c;毕竟身体健康是革命的…