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

linux-进程信号捕捉

1. 信号捕捉流程

操作系统会在合适的时候处理信号,那这个合适的时候是什么时候呢?进程从内核态返回到用户态的时候。

假如用户程序注册了 SIGQUIT 信号的处理函数 sighandler。当程序正在执行 main 函数时,如果发生中断、异常或系统调用,程序会进入 内核态(如图 1 所示)。

在内核完成中断或异常的处理后,准备返回用户态的 main 函数执行。但在这个过程中,内核会先检查当前进程是否有待递达的信号(如图 2 所示)。如果 SIGQUIT 信号已递达,内核会调用 do_signal() 进行信号处理(如图 3 所示)。

如果 SIGQUIT 信号的处理方式是用户自定义,那么内核不会恢复 main 的上下文,而是直接让程序跳转执行 sighandler 处理信号(如果信号处理函数正常返回,则会将pending表中的1置为0再返回应用层)。此时,sighandlermain 之间并没有调用和被调用的关系,它们是两个独立的控制流程,并且使用不同的堆栈空间(如图 3 所示)。

sighandler 处理完成后,程序不会直接返回 main,而是会执行一个特殊的 sigreturn 系统调用,使程序再次进入内核态(如图 4 所示)。

内核在 sys_sigreturn() 处理完成后,如果没有新的信号需要递达,就会恢复 main 函数的上下文,让程序从被中断的位置继续执行(如图 5 所示)。

2. 操作系统是如何运行的

2.1 硬件中断

硬件中断(Hardware Interrupt) 是操作系统运行的核心机制之一,它允许 CPU 在执行用户程序的过程中,被外部设备或特定事件打断,转而执行内核提供的中断处理程序。

1. 什么是硬件中断?

硬件中断是指 CPU 在执行指令时,外部设备(如键盘、网卡、硬盘)或内部组件(如定时器)向 CPU 发送信号,请求操作系统的处理

当 CPU 收到中断信号时,会暂停当前正在执行的任务,切换到内核态,执行对应的中断处理程序。

2. 硬件中断的来源

外部设备中断:键盘输入、鼠标点击、硬盘 I/O 完成、网络数据到达等。
定时器中断:操作系统定期使用定时器中断(如时钟中断)进行任务调度和时间管理。
处理器异常(Faults):如除零错误、缺页异常、非法指令等,也属于中断的一种,但由 CPU 内部产生。

3. 硬件中断的执行流程

(1)设备触发中断信号

例如,当用户按下键盘,键盘控制器会向 CPU 发送中断请求(IRQ,Interrupt Request)。

    (2)CPU 响应中断

    当前正在运行的用户程序暂停,CPU 保存当前执行状态(如寄存器内容、程序计数器),切换到内核态,然后跳转到中断处理程序(ISR,Interrupt Service Routine)。

    (3)执行中断处理程序

    内核根据中断类型,执行相应的处理逻辑。例如,处理键盘输入、读取磁盘数据、调度进程等。

    (4)中断处理完成,恢复执行

    操作系统处理完中断后,使用 iret 指令(x86 架构)或 ERET 指令(ARM 架构)恢复 CPU 的执行状态,切换回用户态,继续执行被中断的程序。

    4. OS如何知道键盘上有数据:通过中断机制和中断向量表

    中断向量表是操作系统的一部分,启动就加载到内存中了。

    通过外部硬件中断,操作系统就不需要对外设进行任何周期性的检测或者轮询。

    (1)用户按下按键

    当你按下键盘上的一个键(例如字母 "A"),键盘的电路会检测到按键事件,并将其转换为一个 扫描码(Scan Code)。

    (2)键盘控制器发送中断信号

    计算机的键盘与 CPU 之间通常通过 键盘控制器(Keyboard Controller, 例如 8042 芯片,这是一个中断控制器) 进行通信。

    该控制器检测到新的按键输入后,会通过 中断请求线(IRQ 1,键盘的默认 IRQ 号) 向 CPU 发送 中断请求(Interrupt Request, IRQ)。

    (3)CPU 响应键盘中断

    CPU 立即暂停当前执行的任务,保存寄存器状态,并跳转到中断向量表(Interrupt Vector Table, IVT)中查找对应的中断处理程序。

    对于键盘中断,通常是 IRQ 1(中断号 0x21),它指向键盘中断处理程序(ISR, Interrupt Service Routine)。

     总结

    硬件中断是用户态与内核态切换的重要触发机制之一。当外部设备或 CPU 内部事件需要操作系统的处理时,会触发中断,导致 CPU 进入内核态,执行中断处理程序。处理完成后,CPU 再次返回用户态,继续执行原来的任务。硬件中断使得计算机系统能够高效地管理设备和任务调度,从而实现多任务并发和实时响应。

    操作系统就是在硬件时钟中断的调度下进行调度的,操作系统就是基于中断进行工作的软件。

    2.2 时钟源与进程调度

    操作系统通过定时器中断(Timer Interrupt)来进行进程调度,决定哪个进程可以使用 CPU。定时器中断由时钟源提供,在一定时间间隔触发,触发后执行中断服务程序(ISR, Interrupt Service Routine),然后调用**调度器(Scheduler)**进行进程切换。

    (1)进程调度的工作流程

    1. CPU 运行某个进程

    2. 定时器触发中断(例如每 10 毫秒一次)

    3. CPU 进入内核态,执行定时器中断的 ISR

    4. 操作系统判断进程的时间片是否用完

      • 时间片未用完:继续执行当前进程。

      • 时间片用完:调用调度器选择新进程。

    5. 执行进程切换(Context Switch)

    6. 恢复新进程的上下文,返回用户态

    7. CPU 继续执行新进程

    (2) 时间片(Time Slice)

    时间片是指CPU 分配给进程的最大连续运行时间,单位通常是毫秒级(如 10ms、50ms)。

    时间片的作用

    • 控制进程的公平性:确保所有进程都能获得 CPU 运行时间。

    • 提高系统响应速度:避免某个进程长时间占用 CPU,导致其他任务等待。

    • 实现抢占式调度:当时间片用完时,操作系统强制切换进程。

    时间片与主频的关系

    假设 CPU 运行在 3 GHz,即 3×10⁹ 次时钟周期/秒

    • 时间片 = 10ms

    • 总时钟周期数 = 3×10⁹ × 10×10⁻³ = 3×10⁷(3000万次时钟周期)

    • CPU 在这 10ms 内可以执行 3000万条指令(假设 IPC = 1)。

    (3) 结合示例分析

    时钟源、主频、中断、时间片的协同工作

    假设 Linux 运行一个任务,并采用 时间片轮转调度(Round Robin)

    1. CPU 运行进程 A,时钟源提供 1ms 级定时信号。

    2. 10ms 后,时钟中断触发,CPU 进入内核态,执行进程调度

    3. 操作系统检查 A 的时间片

      • 若未用完,则继续执行。

      • 若用完,则调度 进程 B 运行,切换 CPU 上下文。

    4. 进程 B 开始执行,重复上述过程。

    ➡ 结论:

    • 时钟源提供中断信号,触发 CPU 进行进程调度。

    • 主频影响 CPU 处理指令的速度,从而影响时间片内可执行的任务量。

    • 中断服务负责处理定时器中断,调用调度器决定哪个进程运行。

    • 时间片决定每个进程能运行多长时间,从而实现多任务并发。

    2.3 软件中断

    1. 什么是软件中断?

    软件中断是由指令触发的中断,而不是由外部设备(如键盘、时钟)引发的硬件中断。它允许程序主动向操作系统请求服务,例如系统调用(syscall),从而完成文件操作、进程管理、内存管理等任务。

    2. 软件中断 vs. 硬件中断

    特性软件中断硬件中断
    触发方式由指令(如 int 指令)触发由外部设备或定时器触发
    控制权由程序主动请求由外部事件异步触发
    作用实现系统调用、调试等处理 I/O 事件、时钟中断等
    示例int 0x80(Linux 系统调用)键盘中断(IRQ 1)、时钟中断(IRQ 0)

    3. 软件中断的实现方式

    软件中断通常有以下几种实现方式:

    1. int 指令(x86 架构)
    2. syscall 指令(现代 Linux)
    3. 异常机制(如除零异常、缺页异常)
    4. trap 指令(RISC 体系结构)

    4. 软件中断在系统调用中的作用

    在 x86 架构的早期 Linux 版本(如 Linux 2.4 及更早),系统调用通常通过 int 0x80 指令来触发软件中断,使 CPU 切换到内核态并执行系统调用。例如:

    mov eax, 1     ; SYS_exit 
    mov ebx, 0     ; exit status = 0 
    int 0x80     ; 触发系统调用

    其中,eax 寄存器存放系统调用号(SYS_exit),ebx 传递参数,int 0x80 触发软件中断,进入内核处理。

    2. 软件中断 vs. syscall 指令

    随着 CPU 发展,为了提高性能,现代 x86-64 处理器引入了 syscall 指令,它比 int 0x80 更快,因为:

     - syscall 指令直接切换到内核态,而 int 0x80 需要查找中断向量表,额外的开销更大。

     - syscall 使用 rax 作为系统调用号,rdirsirdx 等寄存器传递参数,避免了 int 0x80 依赖栈的开销。

    CPU内部的软中断,比如int 0x80或者syscall,我们叫做 陷阱

    CPU内部的软中断,比如除零/野指针等,我们叫做 异常。

    2.4 系统调用表

    操作系统提供系统调用号以及系统调用的具体实现,而 glibc 等标准库对这些系统调用进行封装,提供更易用的接口。

    1. 用户调用系统调用:
    系统调用由操作系统内核提供,但不会以普通函数的形式直接暴露给用户程序。相反,操作系统通过 系统调用号 标识不同的系统调用。在用户空间,程序通常通过库函数(如 glibc)间接调用系统调用,这些库函数会生成相应的系统调用号并传递给操作系统,从而触发对应的系统调用。

    系统调用的实际处理过程:

    1. 系统调用号传递: 用户空间的程序(通过 glibc)会调用一些库函数(例如 write()),这些库函数会在内部使用 syscall 或类似机制,将系统调用号和参数传递给内核。

    2. 内核接收请求: 内核接收到系统调用号后,会通过系统调用号查询系统调用表,找到对应的内核处理函数。举个例子,write() 系统调用号会对应到内核中的 sys_write 函数。

    3. 系统调用表: 在 Linux 内核中,系统调用表是一个内核维护的函数指针表,其中每个条目对应一个系统调用的处理函数。例如,sys_write 函数用于处理 write() 系统调用。

    4. 系统调用函数实现

      • 内核中维护的 sys_write 函数实现了如何将数据写入文件描述符。

      • 这个函数会通过文件系统和设备驱动程序将数据写入磁盘或其他设备。

    总结:

     - 操作系统内核提供系统调用号和系统调用的处理机制,处理系统调用请求。

     - glibc等用户库封装了对系统调用的调用,提供了易用的接口给用户程序。

     - 内核通过系统调用号在系统调用表中找到对应的内核函数,并执行相应操作。

    综上所述,操作系统就是躺在中断处理例程上的代码块

    3. 用户态与内核态

    3.1 基本概念

    系统调用的过程也是在进程地址空间中进行的,所有的函数调用都是地址空间之间的跳转

    操作系统的运行分为用户态(User Mode)和内核态(Kernel Mode):

     - 用户态:普通应用程序在用户态运行,受限于权限,不能直接访问硬件资源。

     - 内核态:操作系统内核在内核态运行,拥有最高权限,可以直接访问硬件资源,如内存、CPU、设备等。

     - CPL(Current Privilege Level)用于指示当前 CPU 运行的权限级别,其中 0 代表内核态,3 代表用户态

     - 进程运行时,通常在用户态执行普通代码,而当需要访问系统资源(如文件、网络、设备)时,需要通过系统调用(syscall)切换到内核态执行。

    3.2 进程地址空间

     - 进程的地址空间划分为 [0,3]GB 作为用户区,[3,4]GB 作为内核区。

     - 每个进程都拥有各自独立的用户区,但内核区只有一份,并被所有进程共享。

    这意味着无论进程如何调度,我们总能找到操作系统。

    关键问题:用户和内核都在同一个地址空间上,用户是否可以直接访问内核?

     - 如果用户进程能随意访问[3,4GB]的内核地址,就会导致系统崩溃或安全问题。

     - 操作系统通过权限控制,限制用户进程访问内核地址。

     - 只有系统调用才能让用户进程间接访问内核功能。

    总结:

    用户态:只能访问自己的 [0,3GB]。

    内核态:拥有最高权限,可以访问 [3,4GB]。

    OS 通过 CS 寄存器(Code Segment Register)来标识当前运行态(CPL 0: 内核态, CPL 3: 用户态)。

    3.3 系统调用的触发

    软件中断(陷阱):用户程序无法直接执行内核代码,而是通过软件中断(如 int 0x80syscall 指令)请求内核服务。

    调用流程:

    1. 用户态程序调用库函数(如 printf()),该函数最终会调用底层的 write()。

    2. write() 函数内部使用 syscall 触发软件中断,让 CPU 切换到内核态。

    3. 操作系统内核查找对应的系统调用处理函数,执行系统级操作(如写入文件)。

    4. 执行完毕后,内核返回到用户态,继续执行用户程序。

    重要机制:

    进程在用户态时,不能访问内核空间,以防止非法操作和安全漏洞。

    进程只能通过 syscall 进入内核态,然后返回用户态。

    3.4 段描述符表(GDT)和段权限

    在x86 保护模式下,段描述符表(GDT/LDT)中的 RPL(请求特权级,Requested Privilege Level)和 DPL(描述符特权级,Descriptor Privilege Level)与系统的特权级机制紧密相关。它们用于控制进程或任务对不同内存段的访问权限,以增强安全性。

    1. CPL(Current Privilege Level)

     - CPL 是 当前运行的代码的特权级,存储在 CS 段寄存器的低 2 位。

     - 通常,内核代码运行在 CPL = 0,用户代码运行在 CPL = 3。

    2. DPL(Descriptor Privilege Level)

     - DPL 是 段描述符(如代码段、数据段、栈段)中的特权级字段,占 2 位(0~3)。

     - 其值决定了访问该段所需的最低特权级。

     - 只有 CPL(当前特权级,Current Privilege Level)≤ DPL 的进程才能访问该段(数据段)。

     - 代码段的访问规则更复杂,受 CPL == DPL 或 DPL < CPL(调用门)约束。

    3. RPL(Requested Privilege Level)

     - RPL 是 段选择子(Segment Selector)中的 2 位字段(最低 2 位)。

     - RPL 代表请求访问某个段时的优先级,通常用于 软件层级上的额外访问限制。

     - 访问时,CPU 取 最低特权级 max(CPL, RPL) 来与 DPL 进行权限判断。

    4. 权限检查规则

    当 CPU 访问某个段时,遵循以下规则:

    (1) 数据段访问:max(CPL, RPL) ≤ DPL,否则拒绝访问。

    (2) 代码段访问:

            CPL == DPL:可直接跳转(如同级调用)。

            CPL > DPL:需要通过调用门(call gate)访问。

            CPL < DPL:不允许访问(低权限代码不能直接跳到高权限代码)。

    5. 示例

    假设:

    CPL = 3(用户模式),段描述符 DPL = 0(内核段),RPL = 3(用户态请求)。

    则:

    由于 max(3,3) = 3,但 DPL = 0,不满足 max(CPL, RPL) ≤ DPL,访问被拒绝。

    如果 DPL = 3(用户模式段),则 max(3,3) ≤ 3,允许访问。

    总结

     - ​​​用户态程序不能直接访问内核资源,必须通过系统调用切换到内核态。

     - 软件中断(syscall, int 0x80)是用户态到内核态切换的主要方式。

     - 进程的地址空间是隔离的,但内核地址空间是所有进程共享的。

     - GDT 段描述符表用于定义不同的权限级别,确保用户进程不能非法访问内核数据。

    4. sigaction

    `sigaction` 是 Linux/Unix 系统中用于管理信号处理的系统调用和结构体,它提供了比传统 `signal()` 函数更灵活、更可靠的信号处理机制。

    3.1 函数原型

    #include <signal.h>
    int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
    - 参数:- `signum`:信号编号(如 `SIGINT`, `SIGTERM`)。- `act`:新的信号处理配置(若为 `NULL` 则不修改)。- `oldact`:保存旧的信号配置(若为 `NULL` 则不保存)。
    - 返回值:成功返回 `0`,失败返回 `-1`。

    3.2 `struct sigaction` 结构体

    struct sigaction {void     (*sa_handler)(int);         // 简单信号处理函数void     (*sa_sigaction)(int, siginfo_t *, void *); // 高级信号处理函数sigset_t   sa_mask;                  // 阻塞信号集int        sa_flags;                 // 控制行为标志void     (*sa_restorer)(void);       // 已废弃
    };

    - 关键字段:
      - `sa_handler`:类似 `signal()` 的简单处理函数。
      - `sa_sigaction`:支持附加信息的复杂处理函数(需 `SA_SIGINFO` 标志)。
      - `sa_mask`:在执行信号处理函数时,自动阻塞的信号集
      - `sa_flags`:控制信号行为的标志(如 `SA_RESTART` 自动重启被中断的系统调用)。

    3.3 关键特性
    (1)信号阻塞(`sa_mask`)
    在执行信号处理函数时,自动阻塞 `sa_mask` 中指定的信号,防止重入:

    sigaddset(&sa.sa_mask, SIGQUIT);  // 处理 SIGINT 时阻塞 SIGQUIT
    当某个信号的处理函数被调用时,内核自动将当前信号加⼊进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字。
    这样就保证了在处理某个信号时,如果这种信号再次产生,那么它会被阻塞到当前处理结束为止。
    如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。
    #include <iostream>
    #include <signal.h>
    #include <unistd.h>
    #include <cstdlib>void handler(int signum)
    {std::cout << "捕捉到信号:" << signum << std::endl;while(true){sigset_t pending;sigpending(&pending);for(int i = 31; i >= 1; i--){if(sigismember(&pending, i)) std::cout << "1";else std::cout << "0";}std::cout << std::endl;sleep(1);}exit(0);
    }
    int main()
    {struct sigaction act, oact;act.sa_handler = handler;sigemptyset(&act.sa_mask);sigaddset(&act.sa_mask, 3);sigaddset(&act.sa_mask, 4);act.sa_flags = 0;sigaction(SIGINT, &act, &oact); //对二号信号进行捕捉while(true){std::cout << "hello, world: " << getpid() << std::endl;sleep(1);}return 0;
    }

    (2)标志(`sa_flags`)
    - `SA_RESTART`:自动重启被信号中断的系统调用(如 `read()`)。
    - `SA_NODEFER`:不阻塞当前信号类型(默认阻塞)。
    - `SA_RESETHAND`:信号处理后恢复为默认行为(类似 `signal()`)。

    5. 可重入函数

    可重入函数(Reentrant Function) 是指在多个执行线程(如多线程或信号处理程序)可以同时安全调用的函数。它不会因为共享状态而引发数据竞争未定义行为

    (1) 可重入函数的特点

    1. 不使用全局或静态变量,只能使用局部变量(栈上的变量)。

    2. 不调用非可重入函数,不能调用可能修改全局状态的函数,例如 mallocprintfrand

    3. 不使用不可重入的系统调用,不能使用 fork() 之后只在子进程执行 malloc(),因为 malloc() 可能会导致死锁。

    4. 不依赖共享资源,不能访问文件描述符、全局变量、共享内存等。

    (2)常见的不可重入函数

    涉及全局状态的函数:

    rand() / srand()(使用全局种子)
    getenv()(返回指向全局缓冲区的指针)
    strtok()(内部使用静态变量)
    ctime() / asctime()(返回指向静态存储区的指针)

    动态内存管理:

    malloc() / free()(使用全局堆)

    标准 I/O:

    printf() / scanf() / sprintf()(使用全局缓冲区)

    (3)可重入函数示例

    #include <iostream>
    #include <signal.h>
    #include <unistd.h>
    #include <stdlib.h>void handler(int signum) {write(STDOUT_FILENO, "Signal received\n", 16); // 使用 write 而不是 printf_exit(0);
    }int main() {struct sigaction act;act.sa_handler = handler;sigemptyset(&act.sa_mask);act.sa_flags = 0;sigaction(SIGINT, &act, nullptr);while (true) {std::cout << "Running..." << std::endl;sleep(1);}return 0;
    }
    

    为什么 write() 可重入,而 printf() 不可重入?

    write() 是一个低级系统调用,直接操作文件描述符。

    printf() 需要管理标准 I/O 缓冲区,可能导致竞态条件。

    6. SIGCHLD信号

    SIGCHLD 信号是 Linux 进程间通信(IPC)中的一个重要信号,用于通知父进程其子进程的状态发生了变化,例如子进程终止、暂停或继续运行。它的主要作用是让父进程能够及时回收子进程的资源,避免僵尸进程的产生。

    1. SIGCHLD 信号的特点

     - 由子进程的状态变化(终止、暂停、恢复)触发,发送给其父进程。

     - 默认情况下,SIGCHLD 信号会被忽略,但可以通过 signal() 或 sigaction() 进行捕捉。

     - SIGCHLD 信号不会导致父进程退出,因此可以安全地用于子进程管理。

    2. SIGCHLD 信号的典型应用

    避免僵尸进程:父进程可以在信号处理函数中调用 waitpid() 以回收子进程资源。

    并发服务器:服务器程序可以利用 SIGCHLD 处理子进程结束事件,管理并发连接。

    3. 使用 SIGCHLD 处理子进程退出的示例

    int main()
    {signal(SIGCHLD, SIG_IGN);for(int i = 0; i < 10; i++){pid_t id = fork();if(id == 0){sleep(3);std::cout << "i am a child, exit" << std::endl;exit(3);}}while(true){sleep(1);std::cout << "i am father, exit" << std::endl;}return 0;
    }
    

    相关文章:

    linux-进程信号捕捉

    1. 信号捕捉流程 操作系统会在合适的时候处理信号&#xff0c;那这个合适的时候是什么时候呢&#xff1f;进程从内核态返回到用户态的时候。 假如用户程序注册了 SIGQUIT 信号的处理函数 sighandler。当程序正在执行 main 函数时&#xff0c;如果发生中断、异常或系统调用&…...

    【免杀】C2免杀技术(三)shellcode加密

    前言 shellcode加密是shellcode混淆的一种手段。shellcode混淆手段有多种&#xff1a;加密&#xff08;编码&#xff09;、偏移量混淆、UUID混淆、IPv4混淆、MAC混淆等。 随着杀毒软件的不断进化&#xff0c;其检测方式早已超越传统的静态特征分析。现代杀软往往会在受控的虚…...

    人工智能驱动的临床路径体系化解决方案与实施路径

    引言 临床路径管理作为现代医疗质量管理的重要工具,其核心在于通过标准化诊疗流程来提升医疗服务的规范性、一致性和效率。然而,传统临床路径管理面临路径设计僵化、执行依从性低、变异管理滞后等诸多挑战,亟需借助人工智能技术实现转型升级。本研究旨在探讨如何通过构建系…...

    旋变信号数据转换卡 旋变解码模块 汽车永磁同步电机维修工具

    旋变信号数据转换卡&#xff0c;是一款专门针对与永磁同步电机的旋变编码器和 BRX 型旋转变压器编码器进行旋变信号解码转换串行总线协议的专用转换卡。此款转换卡结合了专用的旋变信号解码芯片解码逻辑处理&#xff0c;解码信号分辨率高、线性度高、响应速度快。板卡采用工业级…...

    RPM 包制作备查 SRPM 包编译

    &#x1f308; 个人主页&#xff1a;Zfox_ 目录 &#x1f525; 前言 一&#xff1a;&#x1f525; 准备 二&#xff1a;&#x1f525; 制作 rpm 1.设置目录结构&#xff08;制作车间&#xff09;2. 源码放置到规划好的目录当中3. 创建一个spec文件&#xff0c;指挥如何使用这些…...

    [学习] RTKLib详解:rtcm2.c、rtcm3.c、rtcm3e与rtcmn.c

    RTKLib详解&#xff1a;rtcm2.c、rtcm3.c、rtcm3e与rtcmn.c 本文是 RTKLlib详解 系列文章的一篇&#xff0c;目前该系列文章还在持续总结写作中&#xff0c;以发表的如下&#xff0c;有兴趣的可以翻阅。 [学习] RTKlib详解&#xff1a;功能、工具与源码结构解析 [学习]RTKLib详…...

    MCU ESP32-S3+SD NAND(贴片式T卡):智能皮电手环(GSR智能手环)性能与存储的深度评测

    在智能皮电手环与数据存储领域&#xff0c;主控MCU ESP32-S3FH4R2 与 存储SD NAND MKDV2GIL-AST 的搭档堪称行业新典范。二者深度融合低功耗、高速读写、SMART 卓越稳定性等核心优势&#xff0c;以高容量、低成本的突出特性&#xff0c;为大规模生产场景带来理想的数据存储方案…...

    股指期货套期保值怎么操作?

    股指期货套期保值就是企业或投资者通过持有与其现货市场头寸相反的期货合约&#xff0c;来对冲价格风险的一种方式。换句话说&#xff0c;就是你在股票市场上买了股票&#xff08;现货&#xff09;&#xff0c;担心股价下跌会亏钱&#xff0c;于是就在期货市场上卖出相应的股指…...

    Pytorch的Dataloader使用详解

    PyTorch 的 DataLoader 是数据加载的核心组件&#xff0c;它能高效地批量加载数据并进行预处理。 Pytorch DataLoader基础概念 DataLoader基础概念 DataLoader是PyTorch基础概念 DataLoader是PyTorch中用于加载数据的工具&#xff0c;它可以&#xff1a;批量加载数据&#xf…...

    Ros2 - Moveit2 - DeepGrasp(深度抓握)

    本教程演示了如何在 MoveIt 任务构造器中使用抓握姿势检测 (GPD)和 Dex-Net 。 GPD&#xff08;左&#xff09;和 Dex-Net&#xff08;右&#xff09;用于生成拾取圆柱体的抓取姿势。 https://moveit.picknik.ai/main/_images/mtc_gpd_panda.gif 入门 如果您还没有这样做&am…...

    【DRAM存储器五十一】LPDDR5介绍--CK、WCK、RDQS单端模式、Thermal Offset、Temperature Sensor

    👉个人主页:highman110 👉作者简介:一名硬件工程师,持续学习,不断记录,保持思考,输出干货内容 参考资料:《某LPDDR5数据手册》 、《JESD209-5C》 目录 CK、WCK、RDQS单端模式 Thermal Offset Temperature Sensor...

    【springcloud学习(dalston.sr1)】Eureka 客户端服务注册(含源代码)(四)

    d该系列项目整体介绍及源代码请参照前面写的一篇文章【springcloud学习(dalston.sr1)】项目整体介绍&#xff08;含源代码&#xff09;&#xff08;一&#xff09; 这篇文章主要介绍Eureka客户端服务注册到eureka的server端。 上篇文章【springcloud学习(dalston.sr1)】Eurek…...

    数据结构 栈和队列

    文章目录 &#x1f4d5;1.栈(Stack)✏️1.1 栈的基本操作✏️1.2 栈的模拟实现&#x1f516;1.2.1 构造方法&#x1f516;1.2.2 扩容方法&#x1f516;1.2.3 判断栈是否为空或是否满&#x1f516;1.2.4 存储元素&#x1f516;1.2.5 删除元素&#x1f516;1.2. 6 获取栈顶元素 ✏…...

    [数据结构]5. 栈-Stack

    栈-Stack 1. 介绍2. 栈的实现2.1 基于链表的实现2.2 基于数组的实现 3. 栈操作CreateInitilizateDestoryPushPopTopEmptySize 1. 介绍 栈&#xff08;stack&#xff09; 是一种遵循先入后出逻辑的线性数据结构。顶部称为“栈顶”&#xff0c;底部称为“栈底”。把元素添加到栈…...

    Git的安装和配置(idea中配置Git)

    一、Git的下载和安装 前提条件&#xff1a;IntelliJ IDEA 版本是2023.3 &#xff0c;那么配置 Git 时推荐使用 Git 2.40.x 或更高版本 下载地址&#xff1a;CNPM Binaries Mirror 操作&#xff1a;打开链接 → 滚动到页面底部 → 选择2.40.x或更高版本的 .exe 文件&#xf…...

    QT-1.信号与槽

    一、信号与槽机制概述 四、信号与槽的连接 六、自定义信号与槽 思考 定义与作用 &#xff1a;信号与槽是Qt中的核心通信机制&#xff0c;用于实现对象间的数据交互和事件处理。当特定事件发生时&#xff0c;对象会发出信号&#xff0c;而与之相连的槽函数会被自动调用。 特点 …...

    常用的应用层网络协议对比

    概述 协议通信模式加密支持传输层主要特点典型应用场景WSS全双工是&#xff08;TLS/SSL&#xff09;TCP安全的实时双向通信实时聊天、在线游戏WebSocket (WS)全双工否TCP持久连接、低延迟协同编辑、实时通知HTTPS请求-响应是&#xff08;TLS/SSL&#xff09;TCP安全性强、兼容…...

    数据结构与算法:状压dp

    前言 状压dp在整个动态规划专题里特别重要,用位信息表示元素的思想更是重中之重。 一、状态压缩 1.内容 对于一些带路径的递归,通常来讲没法改记忆化搜索和严格位置依赖的动态规划。但如果这个路径的数据量在一定范围内,就可以考虑使用一个整数status的位信息0和1来存路…...

    Spring Cloud Gateway 聚合 Swagger 文档:一站式API管理解决方案

    前言 在微服务架构中&#xff0c;随着服务数量的增加&#xff0c;API文档管理变得越来越复杂。每个微服务都有自己的Swagger文档&#xff0c;开发人员需要记住每个服务的文档地址&#xff0c;这无疑增加了开发难度。本文将介绍如何使用Spring Cloud Gateway聚合所有微服务的Sw…...

    Android 适配之——targetSdkVersion 30升级到31-34需要注意些什么?

    在Android 16即将到来的之际。也就是targetSdkVersion即将出现36&#xff0c;而30已然会成为历史。那么我的项目已经停留在30很久了。是时候要适配一下适用市场的主流机型了。正常来查找资料的&#xff0c;无非就是已经升级和准备升级targetSdkVersion开发版本。所以你是哪一种…...

    网络运维过程中的常用命令

    一、通用网络命令 ping 作用&#xff1a;测试与目标 IP 或域名的连通性。 示例&#xff1a; ping www.baidu.com # 持续发送ICMP包 ping -c 4 8.8.8.8 # 发送4个包后停止 traceroute/tracert 功能&#xff1a;追踪数据包经过的路由节点。 示例&#xff1a; traceroute…...

    [Java实战]Spring Boot 3整合JWT实现无状态身份认证(二十四)

    [Java实战]Spring Boot 3整合JWT实现无状态身份认证&#xff08;二十四&#xff09; 一、JWT简介与核心概念 1. JWT是什么&#xff1f; JSON Web Token (JWT) 是一种开放标准&#xff08;RFC 7519&#xff09;&#xff0c;用于在各方之间安全地传输信息。JWT由三部分组成&am…...

    【Java-EE进阶】SpringBoot针对某个IP限流问题

    目录 简介 1. 使用Guava的RateLimiter实现限流 添加Guava依赖 实现RateLimiter限流逻辑 限流管理类 控制器中应用限流逻辑 2. 使用计数器实现限流 限流管理类 控制器中应用限流逻辑 简介 针对某个IP进行限流以防止恶意点击是一种常见的反爬虫和防止DoS的措施。限流策…...

    软考冲刺——案例分析题 MUX VLAN

    上一篇文章介绍了VLAN高级应用的Super VLAN&#xff0c;本次介绍MUX VLAN内容&#xff0c;MUX VLAN在2024.11月考察过选择题&#xff0c;案例题中有可能出现。 考点一&#xff1a;MUX VLAN原理及实现方式&#xff1b;通过简答题出现。 考点二&#xff1a;配置命令填空。 一&…...

    Git 用户名与邮箱配置全解析:精准配置——基于场景的参数选择

    目录 一、配置查看&#xff1a;理解多层级配置体系二、精准配置&#xff1a;基于场景的参数选择1. 仓库级配置&#xff08;推荐&#xff09;2. 用户级配置3. 系统级配置 三、历史提交信息修改1. 修改最近一次提交2. 修改多个历史提交&#xff08;危险操作&#xff09; 五、配置…...

    OpenHarmony平台驱动开发(十七),UART

    OpenHarmony平台驱动开发&#xff08;十七&#xff09; UART 概述 功能简介 UART指异步收发传输器&#xff08;Universal Asynchronous Receiver/Transmitter&#xff09;&#xff0c;是通用串行数据总线&#xff0c;用于异步通信。该总线双向通信&#xff0c;可以实现全双工…...

    仿生眼机器人(人脸跟踪版)系列之一

    文章不介绍具体参数&#xff0c;有需求可去网上搜索。 特别声明&#xff1a;不论年龄&#xff0c;不看学历。既然你对这个领域的东西感兴趣&#xff0c;就应该不断培养自己提出问题、思考问题、探索答案的能力。 提出问题&#xff1a;提出问题时&#xff0c;应说明是哪款产品&a…...

    Redis的Pipeline和Lua脚本适用场景是什么?使用时需要注意什么?

    Redis Pipeline 和 Lua 脚本详解 一、Pipeline&#xff08;管道&#xff09; 定义 一种批量执行命令的机制&#xff0c;客户端将多个命令一次性发送给服务器&#xff0c;减少网络往返时间&#xff08;RTT&#xff09; 适用场景 ✅ 批量数据操作&#xff08;如万级 key 的写入…...

    【Pycharm】pycharm修改注释文字的颜色

    一、默认颜色-灰色 这个默认的灰色视觉效果太弱&#xff0c;不便于学习时使用 二、修改颜色 打开Settings 也可以从右上角设置那里打开 还可以快捷键Ctrl&#xff0b;Alt&#xff0b;S打开 找到这个页面把这个√取消掉 然后就能自定义颜色啦...

    webgl2着色语言

    一、数据类型 标量&#xff1a;布尔型、整型、浮点型 向量&#xff1a;基本类型&#xff1a;bool、int、float 数量 &#xff1a; 2&#xff0c;3&#xff0c;4 矩阵&#xff1a; 移位、旋转、缩放等变换 采样器&#xff1a; 执行纹理采样的相关操作 结构体&#xff1a; 为开…...

    Nginx+Lua 实战避坑:从模块加载失败到版本冲突的深度剖析

    Nginx 集成 Lua (通常通过 ngx_http_lua_module 或 OpenResty) 为我们提供了在 Web 服务器层面实现动态逻辑的强大能力。然而,在享受其高性能和灵活性的同时,配置和使用过程中也常常会遇到各种令人头疼的问题。本文将结合实际案例,深入分析在 Nginx+Lua 环境中常见的技术问题…...

    什么是alpaca 或 sharegpt 格式的数据集?

    环境&#xff1a; LLaMA-Factory 问题描述&#xff1a; alpaca 或 sharegpt 格式的数据集&#xff1f; 解决方案&#xff1a; “Alpaca”和“ShareGPT”格式的数据集&#xff0c;是近年来在开源大语言模型微调和对话数据构建领域比较流行的两种格式。它们主要用于训练和微调…...

    C++效率掌握之STL库:map set底层剖析及迭代器万字详解

    文章目录 1.map、set的基本结构2.map、set模拟实现2.1 初步定义2.2 仿函数实现2.3 Find功能实现2.4 迭代器初步功能实现2.4.1 运算符重载2.4.2 --运算符重载2.4.3 *运算符重载2.4.4 ->运算符重载2.4.5 !运算符重载2.4.6 begin()2.4.7 end() 2.5 迭代器进阶功能实现2.5.1 set…...

    使用 Docker Desktop 安装 Neo4j 知识图谱

    一、简介 Neo4j是一个高性能的&#xff0c;基于java开发的&#xff0c;NOSQL图形数据库&#xff0c;它将结构化数据存储在网络上而不是表中&#xff1b;它是一个嵌入式的、基于磁盘的、具备完全的事务特性的Java持久化引擎。 Neo4j分为企业版和社区版&#xff0c;企业版可以创…...

    从构想到交付:专业级软开发流程详解

    目录 ​​一、软件开发生命周期&#xff08;SDLC&#xff09;标准化流程​​ 1. 需求工程阶段&#xff08;Requirement Engineering&#xff09; 2. 系统设计阶段&#xff08;System Design&#xff09; 3. 开发阶段&#xff08;Implementation&#xff09; 4. 测试阶段&a…...

    时源芯微| KY键盘接口静电浪涌防护方案

    KY键盘接口静电浪涌防护方案通过集成ESD保护元件、电阻和连接键&#xff0c;形成了一道有效的防护屏障。当键盘接口受到静电放电或其他浪涌冲击时&#xff0c;该方案能够迅速将过电压和过电流引导至地&#xff0c;从而保护后续电路免受损害。 ESD保护元件是方案中的核心部分&a…...

    数据库故障排查指南:从理论到实践的深度解析

    数据库作为现代信息系统的核心组件&#xff0c;承载着数据存储、查询和事务处理等关键任务。然而&#xff0c;数据库系统在运行过程中可能遭遇各种故障&#xff0c;从硬件故障到软件配置问题&#xff0c;从性能瓶颈到安全漏洞&#xff0c;这些问题都可能影响业务的连续性和数据…...

    电脑开机提示按f1原因分析及解决方法(6种解决方法)

    经常有网友问到一个问题,我电脑开机后提示按f1怎么解决?不管理是台式电脑,还是笔记本,都有可能会遇到开机需要按F1,才能进入系统的问题,引起这个问题的原因比较多,今天小编在这里给大家列举了比较常见的几种电脑开机提示按f1的解决方法。 电脑开机提示按f1原因分析及解决…...

    常用的Java工具库

    1. Collections 首先是 java.util 包下的 Collections 类。这个类主要用于操作集合&#xff0c;我个人非常喜欢使用它。以下是一些常用功能&#xff1a; 1.1 排序 在工作中&#xff0c;经常需要对集合进行排序。让我们看看如何使用 Collections 工具实现升序和降序排列&…...

    NC65开发环境(eclipse启动)在企业报表中的报表数据中心里计算某张报表时,一直计算不出数据的解决办法。

    NC65开发环境&#xff08;eclipse启动&#xff09;在企业报表中的报表数据中心里计算某张报表时&#xff0c;一直计算不出数据的解决办法。 如下图&#xff0c;在报表数据中心&#xff0c;针对现金内部往来明细表计算5月的数据&#xff0c;然后报表下面一张显示计算&#xff0c…...

    React 第三十九节 React Router 中的 unstable_usePrompt Hook的详细用法及案例

    React Router 中的 unstable_usePrompt 是一个用于在用户尝试离开当前页面时触发确认提示的自定义钩子&#xff0c;常用于防止用户误操作导致数据丢失&#xff08;例如未保存的表单&#xff09;。 一、unstable_usePrompt用途 防止意外离开页面&#xff1a;当用户在当前页面有…...

    《P4391 [BalticOI 2009] Radio Transmission 无线传输 题解》

    题目描述 给你一个字符串 s1​&#xff0c;它是由某个字符串 s2​ 不断自我连接形成的&#xff08;保证至少重复 2 次&#xff09;。但是字符串 s2​ 是不确定的&#xff0c;现在只想知道它的最短长度是多少。 输入格式 第一行一个整数 L&#xff0c;表示给出字符串的长度。…...

    使用ECS搭建云上博客wordpress(ALMP)

    一、需求分析与技术选型 1. 架构组成及含义 本文使用ECS云服务器&#xff0c;采用ALMP架构搭建wordpress。组件具体的含义如下表&#xff1a; 组件作用WordPress中的功能体现Linux操作系统基础&#xff0c;提供稳定运行环境支持PHP运行和服务器管理ApacheWeb服务器&#xff…...

    Scratch游戏 | 企鹅大乱斗

    有没有过无聊到抓狂的时刻&#xff1f;试试这款 企鹅大乱斗 吧&#xff01;超简单的玩法&#xff0c;让你瞬间告别无聊&#xff01; &#x1f3ae; 玩法超简单 等待屏幕出现 ”Go!” 疯狂点击&#xff0c;疯狂拍打企鹅&#xff01; &#x1f4a5; 游戏特色 解压神器&#x…...

    深入理解SpringBoot中的SpringCache缓存技术

    深入理解SpringBoot中的SpringCache缓存技术 引言 在现代应用开发中&#xff0c;缓存技术是提升系统性能的重要手段之一。SpringBoot提供了SpringCache作为缓存抽象层&#xff0c;简化了缓存的使用和管理。本文将深入探讨SpringCache的核心技术点及其在实际业务中的应用场景。…...

    URP相机如何将场景渲染定帧模糊绘制

    1&#xff09;URP相机如何将场景渲染定帧模糊绘制 2&#xff09;为什么Virtual Machine会随着游戏时间变大 3&#xff09;出海项目&#xff0c;打包时需要勾选ARMv7吗 4&#xff09;Unity是手动还是自动调用GC.Collect 这是第431篇UWA技术知识分享的推送&#xff0c;精选了UWA社…...

    嵌入式中深入理解C语言中的指针:类型、区别及应用

    在嵌入式开发中,C语言是一种基础且极为重要的编程语言,其中指针作为一个非常强大且灵活的工具,广泛应用于内存管理、动态数据结构的实现以及函数参数的传递等方面。然而,尽管指针的使用极为常见,很多开发者在掌握其基本使用后,往往对指针的深入理解还不够。本文将深入分析…...

    .NET程序启动就报错,如何截获初期化时的问题json

    一&#xff1a;背景 1. 讲故事 前几天训练营里的一位朋友在复习课件的时候&#xff0c;程序一跑就报错&#xff0c;截图如下&#xff1a; 从给出的错误信息看大概是因为json格式无效导致的&#xff0c;在早期的训练营里曾经也有一例这样的报错&#xff0c;最后定位下来是公司…...

    WeakAuras Lua Script ICC (BarneyICC)

    WeakAuras Lua Script ICC &#xff08;BarneyICC&#xff09; https://wago.io/BarneyICC/69 全量英文字符串&#xff1a; !WA:2!S33c4TXX5bQv0kobjnnMowYw2YAnDKmPnjnb4ljzl7sqcscl(YaG6HvCbxaSG7AcU76Dxis6uLlHNBIAtBtRCVM00Rnj8Y1M426ZH9XDxstsRDR)UMVCTt0DTzVhTjNASIDAU…...

    Sunsetting 创建 React App

    &#x1f916; 作者简介&#xff1a;水煮白菜王&#xff0c;一位前端劝退师 &#x1f47b; &#x1f440; 文章专栏&#xff1a; 前端专栏 &#xff0c;记录一下平时在博客写作中&#xff0c;总结出的一些开发技巧和知识归纳总结✍。 感谢支持&#x1f495;&#x1f495;&#…...