Linux进程信号

1.信号的认识

        生活中例如闹钟,红绿灯,电话铃声等都属于信号,所白了信号就是中断我们正在做的事情,属于进行事件异步通知机制

        在Linux中信号是发给进程的,信号的产生相较于进程是异步的。

信号的相关知识点

1.进程在信号没有产生的时候,已经知道了相关信号的处理方式

2.对信号的处理可以延迟

3.进程内置了对于信号识别和处理的方式

4.给进程产生信号的信号源非常多

      1.1 如何处理信号

进程受到信号进行的处理方式一共三种(处理的时间可以选择合适的时间):

1.默认处理动作

2.自定义信号处理动作

3.忽略处理

     1.2 理解给进程发送信号

信号产生后进程并不一定要立刻处理,所以这里要求进程要把信号记录下来,通过位图记录在struct task_struct,这是属于os内的数据结构对象,修改位图本质是修改内核数据,这是os自己实现的,并且是os自己提供发送信号的系统调用(kill)

2.信号的种类

在Linux中可用通过指令:kill -l来查看信号的种类

它列出了所有标准信号和实时信号的总范围(标准信号+实时信号)为64种

 3.信号的处理过程

sighandler signal(int signum,sighandler_t handler) 是C标准库中用于注册信号处理的函数

//函数原型

#include <signal.h>
typedef void (*sighandler_t)(int);  // 信号处理函数的类型定义

sighandler_t signal(int signum, sighandler_t handler);

参数:1.signum:要捕获的信号编号(如SIGINT,SIGTERM)

           2.handler:信号处理函数指针: (1)SIG_IGN:忽略该信号

                                                            (2)SIG_DFL:恢复系统默认处理方式

返回值:

  成功时返回旧的信号处理函数指针

  失败返回SIG_ERR

核心用途:

捕获Ctrl + C(当用户按下Ctrl+C时,会触发handle_sigint函数)

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void handle_sigint(int sig) {
    printf("捕获到信号 %d(SIGINT),退出程序\n", sig);
    _exit(0);  // 立即终止程序
}

int main() {
    // 注册信号处理函数
    signal(SIGINT, handle_sigint);

    while (1) {
        sleep(1);  // 模拟程序运行
    }
    return 0;
}

4.信号的产生

      4.1 前台和后台进程

进程分为前台进程(./XXX)和后台进程(./YYY&),只有一个前台进程,但后台进程有很多。

两者的核心区别

前台进程能从键盘获取标准输入(键盘只有一个,输入数据一定给一个确定的进程

后台进程无法从标准输入中获取内容

常用操作命令:

1.命令行末尾+&:进程在后台启动

sleep 60 &  //启动一个后台任务(休眠60s)

2.  查看后台进程:jobs

jobs -l  //显示所有后台进程及作业号

3.前后台切换

前台转后台:

 (1)先暂停前台进程:按Ctrl + Z

    (2)  再放入后台继续运行:bg%任务号(jobs来查看)

后台转前台:

    fg%任务号

4.终止进程:

前台进程:Ctrl +C

后台进程:

kill%1          //通过作业号终止

kill  12345   //通过进程ID终止

       4.2 键盘产生信号 

键盘可以产生很多信号,以下是常用的信号:

组合键对应信号默认行为
ctrl + CSIGINT终止前台进程
ctrl + ZSIGTSTP暂停前台进程
ctrl +\SIGQUIT强制终止前台进程并生成核心转储
ctrl + DEOF输入流结束

      4.3系统调用产生信号

 系统调用产生信号通过函数kill实现

函数原型:

#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);

参数:

  (1)  pid:目标进程的ID

(2)sig:要发送的信号,如果为0则只检查进程是否存在

返回值:

成功返回0,失败返回-1

核心功能:

1.向进程(组)发送信号

kill(12345,SIGTERM);

2.检查进程是否存在:

if(kill(12345,0)==0)

{
    printf("进程12345存在\n");

}

 常用信号:

信号作用
SIGTERM15请求终止进程(允许优雅退出)
SIGKILL9强制终止进程(不可被处理)
SIGINT2中断进程(如 Ctrl+C 触发)
SIGSTOP19暂停进程(不可被捕获)
SIGCONT18恢复被暂停的进程

     4.4 硬件异常产生信号

当硬件发生异常的时候os也会产生信号

注意部分信号不可捕获(如SIGKILL和SIGSTOP)

示例:

除以零触发SIGFPE

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

void handler(int sig)
{
    printf("捕获到信号 %d (SIGFPE)\n", sig);
    exit(1);
}

int main()

{
    signal(SIGFPE, handler);
    int a = 1 / 0;                   // 触发硬件异常
    return 0;
}

 运行结果:

捕获到信号

8 (SIGFPE)

             4.4.1 系统是如何知道进程有没有犯错

   OS通过CPU,寄存器,状态寄存器等硬件来判断进程是否出错,具体过程就不论述了,如有兴趣可自行了解。

       4.5 软件条件

软件条件信号是由程序内部逻辑或系统调用触发的信号,场景有:

1.定时器到期(如alarm触发的SIGALRM)

2.进程间通信(如kill触发的SIGTERM)

3.管道或套接字异常(如SIGPIPE)

其中最常见的软件条件产生信号的场景就是定时器到期

alarm函数的原型

#include <unistd.h>
unsigned int alarm(unsigned int seconds);

核心功能

1.定时触发信号:alarm(seconds),经过seconds秒后向进程发送SIGALRM信号

2.覆盖前次定时:如果之前设置了为未触发的alarm,新调用会取消原定时器,并替换为新的seconds时间

alarm(10);  // 设置 10 秒定时器
sleep(3);   // 等待 3 秒
alarm(5);   // 此时取消原定时器(剩余 7 秒),新定时器在 5 秒后触发

 3.取消定时器:调用alarm(0)会取消之前设置的定时器,并返回剩余时间

返回值

返回之前定时器的剩余秒数,如果未触发定时器则返回0

(调用alarm(10),5s后调用alarm(3),返回5)

典型用法

1.结合信号处理

配合signal捕获SIGALRM,实现超时机制:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void handler(int sig)

{
    printf("Alarm triggered!\n");
}

int main()

{
    signal(SIGALRM, handler);  // 注册信号处理函数
    alarm(3);                  // 3 秒后触发 SIGALRM
    pause();                   // 挂起进程等待信号(防止3s内进程就已经退出了,alarm还没有触发信号的发送)
    return 0;
}

 以下是对pause()函数的介绍:

函数原型

#include <unistd.h>
int pause(void);

核心行为

挂起进程:调用pause()后,进程会进入休眠状态,直到收到任意信号

信号触发唤醒:当进程收到未被忽略的信号,会先执行对应的信号,然后返回pause()

返回值:总是返回-1

2.超时控制

设置超时,对阻塞进行超时操作 

5.信号的保存

    5.1 基本的概念

1.实际执行信号的处理动作称为信号递达(自定义,默认,忽略)(常规信号在递达之前产生多次只计一次)

2.信号从产生到递达之间的状态,称为信号未决(此时信号存储在位图内)

3.进程可以选择阻塞某个信号

4.被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达动作

5.阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作

    5.2 信号管理的核心机制

进程的task_struct主要分为三张表,Linux提供信号操作一定是围绕这三张表展开

5.2.1 block (阻塞信号掩码)

数据结构:表格形式,标记哪些信号被阻塞

比特位的位置:表示的是第几个信号

比特位的内容:是否阻塞(0表示未被阻塞,1表示信号被阻塞暂存在pending中)

(SIGKILL和SIGSTOP不可阻塞的信号)

5.2.2 pending(待处理信号集)

数据结构:32位无符号整数,每个位对应一个信号

比特位的位置:表示的是第几个信号

比特位的内容:以信号2为例,比特为的内容为1,表示信号SIG_IGN已收到但未处理

作用:记录已发送但未被递送的信号

(当我们准备递达的时候要把pending信号集中对应的信号位图1->0)

5.2.3 handler(信号处理函数表)

数据结构:函数指针数组,每个元素对应一个信号的处理方式

常见值:

SIG_DFL:默认处理

SIG_IGN:忽略信号

用户自定义函数地址:捕获信号并执行自定义逻辑

 5.2.4 常用的函数

   5.2.4.1 sigprocmask

OS中用于管理信号掩码的核心函数,通过控制信号的阻塞与解除,决定进程是否接受特定的信号

函数原型

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

 参数解析

参数说明
how操作类型,(具体下面会介绍)
set指向信号集的指针,表示要添加/移除/设置的信号,如果为NULL,则how无效
oldset输出参数,保存修改前的信号掩码,如果为NULL,不保留旧掩码

how的取值:

SIG_BLOCK:将 set 中的信号加入阻塞列表​(原掩码 ∪ set

SIG_UNBLOCK:将 set 中的信号移出阻塞列表(原掩码 - set

SIG_SETMASK:直接设置信号掩码为 set(覆盖原掩码)

核心功能
1.阻塞信号

临时屏蔽某些信号,使其在解除阻塞前不会递送

sigset_t mask;
sigemptyset(&mask);          //用来初始化一个信号集(sigset_t类型),将其设置为空集合
sigaddset(&mask, SIGINT);          // 添加 SIGINT 到信号集
sigprocmask(SIG_BLOCK, &mask, NULL); // 阻塞 SIGINT

2.解除阻塞

 sigprocmask(SIG_UNBLOCK, &mask, NULL); // 解除阻塞 SIGINT

返回值 

成功:返回 0

失败:返回-1

示例代码

#include <signal.h>
#include <stdio.h>
#include <unistd.h>

int main()

{
    sigset_t new_mask, old_mask;

    // 初始化信号集:阻塞 SIGINT 和 SIGTERM
    sigemptyset(&new_mask);
    sigaddset(&new_mask, SIGINT);
    sigaddset(&new_mask, SIGTERM);

    // 应用阻塞(保存旧掩码到 old_mask)
    sigprocmask(SIG_BLOCK, &new_mask, &old_mask);

    printf("SIGINT/SIGTERM 已被阻塞,尝试 Ctrl+C 无效\n");
    sleep(5);  // 模拟关键操作

    // 恢复原信号掩码
    sigprocmask(SIG_SETMASK, &old_mask, NULL);
    printf("已解除阻塞,Ctrl+C 可终止进程\n");
    
    pause();  // 等待信号
    return 0;
}

  5.2.4.2 sigpending 

获取当前进程的未决信号集,即已发送但被阻塞的信号

函数原型

#include <signal.h>
int sigpending(sigset_t *set);        // set 为输出参数,保存未决信号集

参数set 为指向 sigset_t 类型的指针,用于返回挂起信号集。

返回值:成功返回 0,失败返回 -1 并设置 errno、

使用场景

1.阻塞信号后检查:阻塞SIGINT后,调用sigpending可检查是否有未处理的SIGINT

2.信号管理:结合sigprocmask和sigpending,可实现自定义信号处理逻辑

示例

sigset_t newmask, oldmask, pendmask;
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT);
sigprocmask(SIG_BLOCK, &newmask, &oldmask); // 阻塞 SIGINT

// ...(在此期间若收到 SIGINT,会被挂起)

sigpending(&pendmask); // 检查挂起信号
if (sigismember(&pendmask, SIGINT)) {
    // 存在挂起的 SIGINT
}

sigprocmask(SIG_SETMASK, &oldmask, NULL); // 解除阻塞,处理 SIGINT

    5.2.5 Core和Term类信号

类别默认行为典型信号示例
Core终止进程+生成Core dump文件SIGSEGV(段错误)、SIGABRT(主动终止)、SIGQUIT(Ctrl+\)
Term仅终止进程

SIGTERM(优雅终止)、SIGINT(Ctrl+C)、SIGHUP(终端挂起)

Core dump文件:程序崩溃时生成的内存快照文件,记录进程终止时的内存,寄存器,堆栈等状态信息,用于事后调试和分析崩溃原因 

6.信号的处理 

信号的处理,不是立即处理,而是在合适的时候进行信号的处理

    6.1 什么时候是合适的时候

首先先介绍一下信号捕捉的流程:

 由上图的可知处理信号的过程分为两个状态,用户态(User Mode)和  内核态(Kernel Mode)

所谓合适的时候就是进程从内核态返回到用户态的时候,在这个过程中包含着信号检查步骤(检查是否有待处理的信号需要递送给进程)

以下是信号捕捉过程中的关键环节:

步骤切换隐含的最关键环节
1→2保存上下文(保存进程的寄存器状态)、信号队列检查(遍历pending,检查是否有未阻塞需要递送的信号)、处理方式决策(默认和忽略直接在内核中处理,标记)
2→3构建用户态堆栈帧(插入信号处理函数参数和返回地址)、权限降级(CPU从内核态到用户态)
3→4触发sigreturn(重新进入内核态)、恢复原上下文(恢复原用户态程序寄存器状态)、清理堆栈
4→5载入原寄存器状态(让保存的寄存器状态载入CPU,准备恢复执行)、解除信号阻塞(将已处理的信号从pending中移除)

下面是流程中状态的切换: 

步骤操作权限切换方向
1→2主流程因系统调用/中断进入内核用户态→内核态(升级)
2→3内核递送信号,执行用户态处理函数内核态→用户态(降级)
3→4信号处理函数调用 sigreturn 返内核用户态→内核态(升级)
4→5内核恢复主流程,返回用户态内核态→用户态(降级)

  6.2 内核态和用户态

  中断对于内核态和用户态之间的切换有着重要的作用

          6.2.1 操作系统硬件中断是如何运行的

硬中断:外部设备主动通知CPU处理紧急任务

 下图是硬件中断的流程

操作系统是遵守冯诺依曼体系的,在之前的文章介绍过,但现在可以补充一点中央处理器并非跟输入设备无联系,在操作系统执行中断的时候外部设备通过中断控制器向CPU发送信号,有着间接联系

     6.2.2 软中断 

软中断是由软件主动触发的中断

核心作用

  • 下半部机制(Bottom Half)​:在硬件中断处理的上半部(快速响应)之后,处理耗时的任务(如网络协议栈解析、磁盘I/O队列调度)。
  • 内核线程协作:例如Linux中的ksoftirqd线程批量处理软中断队列,避免长时间关闭硬件中断。
    常见类型
    • 定时器软中断​(如TIMER_SOFTIRQ):驱动进程调度和时间管理。
    • 网络收发包软中断​(如NET_RX_SOFTIRQ/NET_TX_SOFTIRQ):处理网络数据包的分发和传输。
     6.2.2.1 系统如何触发软中断? 

(1)首先用户程序通过syscall或int 0x80来触发系统调用,从而切换到内核态

  ( 2)  在例如磁盘读写时,硬件完成I/O后触发硬件中断,内核再通过软中断处理协议栈或唤醒进程

 (3)中断向量表(如 0x80)是桥梁,将中断号映射到具体处理函数(如system_call)

特性​**syscall 指令**​**int 0x80 软中断**
架构支持x86-64 专用x86 32 位
性能更高效(无堆栈切换)较慢(需查中断向量表)
参数传递寄存器直接传递寄存器传递(ebxecx 等)
兼容性仅 64 位程序兼容 32/64 位

syscall和int 0x80是属于CPU指令集中的指令,当程序运行时被加载到内存中,由CPU的指令解码器进行解析和执行

   6.2.2 如何理解用户态和内核态

1.在Linux中每个进程的虚拟空间大小为4GB,其中分用户态空间(0-3GB,只能以用户的身份进行访问)和内核态空间(3-4GB,只能以内核的身份进行访问)

2.在CPU内存在一个代码段寄存器(cs),该寄存器存储了特权级(CPL)信息,从而反映系统目前是处于用户态还是内核态:

CPL=0:os处于内核态

CPL=3:os处于用户态

3.内核页表:操作系统只有一份,所有进程共享,负责将内核态空间的虚拟地址映射到物理内存

   用户页表:每个进程有独立的用户页表,负责映射用户态空间的虚拟地址到物理内存

 6.3 可重入函数

什么是可重入函数?

简单理解:相当于你在超市排队,当有人插队的时候,如果收银员操作步骤不好,可能会把你和别人的商品搞混从而导致账单混乱,而可重入函数就像一个“防插队收银流程”,即使有人插队,也能保证最终结果的正确

下图就是不可重入函数的例子:

  1. 插入 node1 时

    • 步骤1:node1->next = head(假设原 head 为空,node1->next = NULL
    • 步骤2:head = &node1 → 链表变为 head → node1
  2. 插入 node2 时被中断

    • 执行步骤1:node2->next = head(此时 head 指向 node1,所以 node2->next = node1
    • 中断发生:假设此时另一个信号处理函数也调用了 insert,插入 node3
      • node3->next = headhead 仍指向 node1
      • head = &node3 → 链表变为 head → node3 → node1
    • 中断返回后继续执行步骤2head = &node2
    • 最终链表head → node2,但 node3 的 next 仍指向 node1,导致链表断裂

为什么是不可重入?

  1. 依赖全局变量 head:多个执行流(主程序和信号处理函数)共享同一全局指针。
  2. 操作不原子化:两步操作(修改 next 和更新 head)之间可能被中断,导致中间状态暴露。
  3. 破坏数据一致性:中断插入的 node3 会导致 node2 的 next 指向过期的 head 值。

判断是否可重入口诀:

可重入:只用参数和局部变量,操作独立不共享

不可重入:依赖全局/静态数据,操作共享资源非原子(原子化操作指一个操作在执行的时候不会被中断) 

  6.4 volatile

volatile属于关键字,其核心作用是避免外部(例如信号处理函数)对变量随意的修改

运用场景:

   1.防止编译器误判

 比如一个全局变量flag,主循环在while(1)中,而信号处理函数会修改它,如果没有volatile,编译器可能优化为:主循环里的flag似乎没变过,直接缓冲到寄存器,导致永远读不到信号修改的值

    2. 强制"内存访问"

volatile要求每次读写变量都直接访问内存,确保信号处理函数的修改对主程序可见

典型代码模式:

在下面代码中volatile每次都强制编译器每次访问flag时都从内存中读取最新值

volatile sig_atomic_t flag = 0;  // 必须加 volatile!

void signal_handler(int sig)

{
    flag = 1;  // 信号处理函数修改变量
}

int main()

{
    signal(SIGINT, signal_handler);
    while (1)

   {
        if (flag)

        {  // 每次都会从内存读取 flag
            // 处理信号...
            flag = 0;
         }
   }
}

补充:跟const一样都有约束的感觉,但二者有着本质的区别

           const约束程序员("别修改这个变量") 

           volatile约束编译器(“别优化这个变量”)

  6.5 SIGCHLD信号

SIGCHLD信号就是当子进程结束时,这个信号就会发送给父进程告诉父进程子进程死了,让子进程退出从而避免子进程变成僵尸状态

代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>

// 处理 SIGCHLD 信号的函数
void handle_sigchld(int sig) {
    // 回收所有已终止的子进程
    while (waitpid(-1, NULL, WNOHANG) > 0);
}

int main() {
    // 注册信号处理函数
    signal(SIGCHLD, handle_sigchld);

    // 创建一个子进程
    pid_t pid = fork();
    if (pid == 0) {
        printf("子进程开始运行,3秒后退出...\n");
        sleep(3);
        exit(0);  // 子进程退出
    } else {
        printf("父进程等待子进程结束...\n");
        while (1) sleep(1);  // 父进程不阻塞,继续做其他事
    }
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java使用FFmpegFrameGrabber进行视频拆帧,结合Thumbnails压缩图片保存到文件夹

引入依赖 <dependency><groupId>net.coobird</groupId><artifactId>thumbnailator</artifactId><version>0.4.17</version></dependency><dependency><groupId>org.bytedeco</groupId><artifactId>ja…

c++项目-KV存储-模仿redis实现kv键值对存储的基本功能。

KV存储引擎的技术解析&#xff1a;数组、哈希与红黑树实现及其在网络I/O中的应用。 内容概要&#xff1a;本文档深入介绍了基于数组、哈希表和红黑树的键值存储引擎的设计与实现。文档首先阐述了系统的总体架构与类图关系&#xff0c;之后分别对底层存储结构进行了详细解释&am…

vue3:十一、主页面布局(优化页面跳转方式)

:router"true" 一、参考文章 vue3:十一、主页面布局(实现基本左侧菜单右侧内容效果)-CSDN博客 参考上述文章可知&#xff0c;页面跳转是通过在js中定义的菜单中携带的path&#xff0c;然后通过菜单的点击事件完成的跳转&#xff0c;现在可以进行优化&#xff0c;直…

深入解析 Java Stream API:筛选子节点的优雅实现!!!

&#x1f680; 深入解析 Java Stream API&#xff1a;筛选子节点的优雅实现 &#x1f527; 大家好&#xff01;&#x1f44b; 今天我们来聊聊 Java 8 中一个非常常见的操作&#xff1a;使用 Stream API 从 Map 中筛选出特定条件的元素。&#x1f389; 具体来说&#xff0c;我们…

统计学重要概念:自由度

在统计学中&#xff0c;自由度&#xff08;degrees of freedom&#xff0c;简称df&#xff09;是一个重要的概念&#xff0c;它表示在计算某个统计量时可以自由变化的值的数量。对于一个样本量为n的样本&#xff0c;自由度通常为n-1&#xff0c;这是因为我们需要用样本数据来估…

数据结构-排序

文章目录 1. 排序的概念2. 常见排序算法的实现2.1 插入排序1&#xff09;插入排序一&#xff09;基本思想二&#xff09;特性及时间复杂度三&#xff09;代码实现 2&#xff09;希尔排序&#xff08;缩小增量排序&#xff09;一&#xff09;基本思想二&#xff09;特性及时间复…

压缩壳学习

壳是什么 壳就是软件的一个保护套&#xff0c;防止软件被进行反编译或被轻易地修改。 其作用就是为了保护软件。 常见的大类壳有压缩壳、加密壳、VM 壳的分类。 压缩壳顾名思义就是用来减小软件的文件大小的&#xff1b;加密壳&#xff0c;通过加密软件来保护软件&#xff…

《AI大模型趣味实战》第6集:基于大模型和RSS聚合打造个人新闻电台

《AI大模型趣味实战》第6集&#xff1a;基于大模型和RSS聚合打造个人新闻电台 摘要 本文将带您探索如何结合AI大模型和RSS聚合技术&#xff0c;打造一个功能丰富的个人新闻电台系统。我们将使用Python和PyQt5构建一个桌面应用程序&#xff0c;该应用可以从多个RSS源抓取新闻&…

(学习总结29)Linux 进程概念和进程状态

Linux 进程概念 冯诺依曼体系结构软件运行与存储分级数据流动的理论过程 操作系统操作系统(Operator System) 概念操作系统的功能与作用系统调用和库函数概念 进程概念描述进程 - PCBtask_struct查看进程通过系统调用获取进程标示符 PID通过系统调用 fork 函数创建进程简单使用…

LLM - CentOS上离线部署Ollama+Qwen2.5-coder模型完全指南

文章目录 离线安装OllamaOllama下载Ollama硬件需求Ollama 常用命令参考Ollama安装Ollama 服务管理&开机启动开启局域网访问 Ollama 服务 离线安装模型gguf 文件格式下载Qwen2.5-Coder-7B-Instruct-GGUF格式选择 ( gguf 版本 )构建Modelfile文件加载并运行离线模型测试 集成…

Linux——信号

目录 Linux——信号1.信号的基础了解2.技术应用角度的信号3.产生信号3.1按键组合3.2系统调用产生信号3.2.1 kill()3.2.2 raise()3.2.3 abort() 3.3**.** 软件条件产生信号3.4硬件异常产生信号3.4.1 /0异常3.4.2 内存越界异常 4.理解信号的存在5.总结一下6.核心转储7.全部信号都…

向量叉积的应用——正反画画

1 解题思路 解题思路涉及的向量积相关知识 c实现 #include<iostream> #include<vector>using namespace std;struct TrianglePoint {int x;int y; };int momentForce(TrianglePoint A, TrianglePoint B, TrianglePoint C) {//AB向量&#xff1a;(B.x-A.x, B.y-A.…

构建自定义MCP天气服务器:集成Claude for Desktop与实时天气数据

构建自定义MCP天气服务器:集成Claude for Desktop与实时天气数据 概述 本文将指导开发者构建一个MCP(Model Control Protocol)天气服务器,通过暴露get-alerts和get-forecast工具,为Claude for Desktop等客户端提供实时天气数据支持。该方案解决了传统LLM无法直接获取天气…

Web安全策略CSP详解与实践

引言 &#xff1a;在黑客攻击频发的今天&#xff0c;你的网站是否像“裸奔”一样毫无防护&#xff1f;跨站脚本&#xff08;XSS&#xff09;、数据注入等攻击随时可能让用户数据泄露。今天我们将揭秘一个网站的隐形保镖——内容安全策略&#xff08;CSP&#xff09;&#xff0c…

HC-05与HC-06蓝牙配对零基础教程 以及openmv识别及远程传输项目的概述

这个是上一年的项目&#xff0c;之前弄得不怎么完整&#xff0c;只有一个openmv的&#xff0c;所以openmv自己去我主页找&#xff0c;这篇主要讲蓝牙 这个是我在使用openmv连接单片机1然后单片机1与单片机2通过蓝牙进行通信 最终实现的效果是&#xff1a;openmv识别到图形和数…

点云分割方法

点云分割 通过判断三维距离&#xff0c;实现对创建3团点云的分割 通过判断三维距离&#xff0c;实现对创建3团点云的分割 * 点云1 gen_object_model_3d_from_points (rand(100), rand(100),rand(100), Points1)* 点云2 gen_object_model_3d_from_points (rand(100), 2rand(100…

SpringBoot3使用CompletableFuture时java.util.ConcurrentModificationException异常解决方案

问题描述 在Spring Boot 3项目中&#xff0c;使用CompletableFuture进行异步编程时&#xff0c;偶发{"code":500,"msg":"java.util.ConcurrentModificationException"}异常&#xff0c;但代码中并未直接操作List或CopyOnWriteArrayList等集合类…

细说卫星导航:测距定位原理

测距定位原理 1. 伪距测量技术 核心原理&#xff1a;卫星发射信号&#xff0c;用户接收并记录传播时间&#xff0c;乘以光速得到距离&#xff08;伪距&#xff09;。 技术细节&#xff1a; 信号传播路径分析 信号结构&#xff1a; 卫星信号包含三部分&#xff1a; 载波&…

Linux系统管理与编程09:任务驱动综合应用

兰生幽谷&#xff0c;不为莫服而不芳&#xff1b; 君子行义&#xff0c;不为莫知而止休。 [环境] windows11、centos9.9.2207、zabbix6、MobaXterm、Internet环境 [要求] zabbix6.0安装环境&#xff1a;Lamp&#xff08;linux httpd mysql8.0 php&#xff09; [步骤] 5 …

RAG(Retrieval-Augmented Generation)基建之PDF解析的“魔法”与“陷阱”

嘿&#xff0c;亲爱的算法工程师们&#xff01;今天咱们聊一聊PDF解析的那些事儿&#xff0c;简直就像是在玩一场“信息捉迷藏”游戏&#xff01;PDF文档就像是个调皮的小精灵&#xff0c;表面上看起来规规矩矩&#xff0c;但当你想要从它那里提取信息时&#xff0c;它就开始跟…