【Linux系统】Linux进程信号详解
Linux进程信号
- 0 引言
- 1 认识信号
- 1.1 什么是信号
- 1.2 发送信号的本质
- 1.3 信号的处理
- 2 信号的产生
- 2.1 键盘产生
- 2.2 调用系统函数向进程发送信号
- 2.3 由软件条件产生信号
- 2.4 硬件异常产生信号
- 3 信号的保存
- 4 信号的处理
- 5 总结
0 引言
本篇文章会从Linux信号的产生到信号的保存,最后到信号的处理逐过程讲解
1 认识信号
1.1 什么是信号
在计算机科学中,信号是Unix、类Unix以及其他POSIX兼容的操作系统中进程间通讯的一种有限制的方式。它是一种异步的通知机制,用来提醒进程一个事件已经发生。当一个信号发送给一个进程,操作系统中断了进程正常的控制流程,此时,任何非原子操作都将被中断。如果进程定义了信号的处理函数,那么它将被执行,否则就执行默认的处理函数。
在Linux中使用 kill -l 命令来查看信号类型

在上图中,1-31的信号为分时信号,34-64为实时信号。本篇文章过多不讨论实时信号。
实时信号是当该类型信号被发送后,需要立即被处理。而分时信号可以等到合适的时机再去进行处理,进程只需要保存有没有产生该信号即可。
man 7 signal 查询各种信号的处理动作

Core Dump(核心转储)
首先解释什么是Core Dump。当一个进程要异常终止时,可以选择把进程的用户空间内存数据全部保存到磁盘上,文件名通常是core,这叫做Core Dump
在上图中 Action那列,信号的处理动作 Core和Term本质上都是终止掉进程,但是如果是Core这种动作的话,在发送这种类型的信号时,会把进程的用户空间内存数据全部保存到磁盘上。
如果只是Term (terminal),就只是终止掉该进程而已
进程异常终止通常是因为有Bug,比如非法内存访问导致段错误,事后可以用调试器检查core文件以查清错误原因,这叫做Post-mortem Debug(事后调试)。一个进程允许产生多大的core文件取决于进程的Resource Limit(这个信息保存 在PCB中)。默认是不允许产生core文件的,因为core文件中可能包含用户密码等敏感信息,不安全。在开发调试阶段可以用ulimit命令改变这个限制,允许产生core文件。
$ ulimit -a//查看

ulimit -c 字节大小 //修改core文件大小

1.2 发送信号的本质
信号是需要被进程保存的,那么就需要一种数据结构来进行保存。只需要记录某种信号是否产生或者不产生,用0或1表示即可。上面的分时信号是31个,而32位系统下一个整数的比特位是32,那么我们可以利用“位图”这种结构来保存。
进程的task_struct中也确实采用了位图这种数据来保存信号,用int表示:
uint32_t signals:
00000000 00000000 00000000 00000000
当9号信号产生时,直接把9号位置改为1即可
00000000 00000000 00000001 00000000
所以,所谓的发送信号,本质是写入信号,直接修改特定进程信号的位图中的特定比特位即可。
因为task_struct数据内核结构,只能由操作系统(OS)来修改,所以无论有多少种信号产生的方式,最终都必须让OS来完成最后的发送过程。
1.3 信号的处理
当进程收到信号时,需要对该信号进行处理。
信号的处理有三种:
1.默认动作
2.忽略信号
3.自定义动作
2 信号的产生
2.1 键盘产生
用户在Linux下执行一个前台进程,然后使用ctrl+c操作,会直接终止掉该前台进程。这是因为用户按下ctrl+c,这个键盘输入产生一个硬件中断,被OS获取,解释成信号,发送给目标前台进程,前台进程因为收到信号,进而引起进程退出。
ctrl+c组合键只能终止掉前台进程,对后台进程无效。这时我们可以使用kill -9指令来该终止进程。
验证如下:
我们在Linux下运行该代码:

这是前台进程,当我们按下ctrl+c的时候,进程会直接终止。

我们把它变为后台进程:
在运行命令后加一个 &

ctrl+c 命令无效
使用kill -9 进程才能正常杀掉
$ kill -9 2001//2001是进程的pid
ctrl+c本质上是2号信号,默认处理动作是终止进程,我们可以signa函数来自定义处理动作,验证一下。

利用signal函数验证ctrl+c指令本质上是2号信号

运行上面代码,按下ctrl+c后,进程确实终止了,而且是用了我们2号信号终止的。

值得一提的是,在这31个信号中,我们可以对除9号信号以外的所有信号进行自定义捕捉,把处理动作设置成我们需要的动作。
9号信号叫做管理员信号,操作系统不允许用户修改9号信号的动作。
假设可以被修改,我们的自定义动作如下:
void handler(int signo)//处理动作的方式
{std::cout << "get a signal: " << signo << std::endl;
}
整个代码无法让进程终止,1到31个信号没一个可以终止掉,因为动作都被修改为自定义的了。那么如果这个进程运行了,是不是就无法终止了,没有办法处理掉他了,这太危险了。所以至少要保留一个信号处理动作不能被用户随意修改,这就是9号信号为什么是管理员信号。
2.2 调用系统函数向进程发送信号
kill函数
其实我们上面使用的kill命令本质上就是调用了系统函数kill

raise函数
raise函数可以给当前的进程发送指定的信号。(就是自己给自己发送信号)

运行下列代码


abort函数
abort函数,给当前进程发送6号信号



2.3 由软件条件产生信号
在Linux进程间通信中使用管道时,SIGPIPE信号实际上就是一种由软件条件产生的信号,当进程在使用管道进行通信时,读端进程将读端关闭,而写端进程还在一直向管道写入数据,那么此时写端进程就会收到SIGPIPE信号进而被操作系统终止。
这就是软件条件产生信号,当满足条件时,就会发送信号终止进程。
本小节主要介绍alarm函数和SIGALRM信号(14号信号)
#include <unistd.h> unsigned int alarm(unsigned int seconds);
调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程。


在笔者这里确实是1秒钟后进程被终止了。
这个函数的返回值是0或者是以前设定的闹钟时间还余下的秒数。打个比方,某人要小睡一觉,设定闹钟为30分钟之后响,20分钟后被人吵醒了,还想多睡一会儿,于是重新设定闹钟为15分钟之后响,“以前设定的闹钟时间还余下的时间”就是10分钟。如果seconds值为0,表示取消以前设定的闹钟,函数的返回值仍然是以前设定的闹钟时间还余下的秒数
执行下面代码,我们使用 kill -14 命令提前唤醒闹钟


2.4 硬件异常产生信号
硬件异常被硬件以某种方式被硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释 为SIGFPE信号发送给进程。再比如当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。
3 信号的保存
信号的一些其他相关概念
1.实际执行信号的处理动作称为信号递达(Delivery)
2.信号从产生到递达之间的状态(该信号被保存了),称为信号未决(Pending)
3.进程可以选择阻塞(Block)某个信号
4.被阻塞的信号产生时将保持在未决状态,知道进程解除对该信号的阻塞,才执行递达的动作
5.阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是信号递达之后的一种处理动作
信号在内核中的表示示意图:

1.每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。信号
产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。在上图的例子中,SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作
2.SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞
3.SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。
如果在进程解除对某信号的阻塞之前这种信号产生过多次,将如何处理?POSIX.1允许系统递送该信号一次或多次。Linux是这样实现的:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。
sigset_t
从上图来看,每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。 阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略。
信号集操作函数
sigset_t类型对于每种信号用一个bit表示“有效”或“无效”状态,至于这个类型内部如何存储这些bit则依赖于系统实现,从使用者的角度是不必关心的,使用者只能调用以下函数来操作sigset_ t变量,而不应该对它的内部数据做任何解释,比如用printf直接打印sigset_t变量是没有意义的。
#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset (sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);
函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含 任何有效信号
函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置1,表示 该信号集的有效信号包括系统支持的所有信号。
注意,在使用sigset_ t类型的变量之前,一定要调 用sigemptyset或sigfillset做初始化,使信号集处于确定的
状态。初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信号
sigprocmask
调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)。
#include <signal.h> int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
返回值:若成功则为0,若出错则为-1
如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。如果set是非空指针,则 更改进程的信号屏蔽字,参数how指示如何更改。如果oset和set都是非空指针,则先将原来的信号 屏蔽字备份到oset里,然后 根据set和how参数更改信号屏蔽字。假设当前的信号屏蔽字为mask,下表说明了how参数的可选值

验证:

运行这段程序,预期结果是2号信号被阻塞了,对该进程无法使用2号信号来终止掉进程。
2号信号可以通过ctrl+c产生,可以看到2号信号对该进程无效

我们使用命令来尝试使用2号信号结束该进程

还是无效,这说明2号信号确实是被阻塞了

sigpending
#include <signal.h>
int sigpending(sigset_t *set);
读取当前进程的未决信号集,通过set参数传出。调用成功则返回0,出错则返回-1。

该程序的预期结果是:
先打印31个0,因为一开始没有信号未决。
然后当我们发送2号信号后,位图结构的第二个位置会从0变为1
原因是,2号信号被我们阻塞了,此时信号处于未决状态。

4 信号的处理
前面我们讲过,信号是在合适的时候被处理。原因是信号的产生是异步的,当前进程可能正在做更重要的事情。
所谓的“合适的时候”,是指当进程从“内核态”切换到“用户态”时,进程会在OS的指导下,进行信号的检测和处理(默认,忽略,自定义捕捉)。
用户态:执行用户所写的代码的时候,进程所处的状态
内核态:执行OS所写的代码的时候,进程所处的状态
当进程时间片到了或者用到系统调用时就要从用户态切换到内核态。
内核如何实现信号的捕捉
如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下: 用户程序注册了SIGQUIT信号的处理函数sighandler。 当前正在执行main函数,这时发生中断或异常切换到内核态。 在中断处理完毕后要返回用户态的main函数之前检查到有信号SIGQUIT递达。 内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函 数,sighandler和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是 两个独立的控制流程。 sighandler函数返
回后自动执行特殊的系统调用sigreturn再次进入内核态。 如果没有新的信号要递达,这次再返回用户态就是恢复main函数的上下文继续执行了

sigaction
#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
该函数的用法和signal一样,但是功能更强大一些

sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回0,出错则返回- 1。signo是指定信号的编号。若act指针非空,则根据act修改该信号的处理动作。若oact指针非 空,则通过oact传出该信号原来的处理动作。act和oact指向sigaction结构体
将sa_handler赋值为常数SIG_IGN传给sigaction表示忽略信号,赋值为常数SIG_DFL表示执行系统默认动
作,赋值为一个函数指针表示用自定义函数捕捉信号,或者说向内核注册了一个信号处理函 数,该函数返回
值为void,可以带一个int参数,通过参数可以得知当前信号的编号,这样就可以用同一个函数处理多种信
号。显然,这也是一个回调函数,不是被main函数调用,而是被系统所调用。


当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么 它会被阻塞到当前处理结束为止。 如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字
5 总结
以上就是Linux进程信号从产生到保存到处理的过程。
Linux 中的信号机制可以用于处理各种异常情况,如程序执行过程中的错误、非法操作、内存溢出等。在遇到这些异常情况时,系统会向进程发送相应的信号,进程可以安装信号处理函数来捕获并处理这些信号,以避免程序崩溃或出现严重错误。
下面是一些使用信号处理异常的常见场景:
1、程序崩溃处理
当程序出现异常情况时,如空指针访问、内存溢出等,系统会向进程发送 SIGSEGV 或 SIGABRT 信号,进程可以安装信号处理函数来捕获这些信号,打印出错信息、保存程序状态,最终退出程序或进行恢复操作。
2、非法操作处理
进程可以使用信号来捕获非法操作,如非法指令、非法访问等。在接收到这些信号时,进程可以使用信号处理函数来打印错误信息、终止程序或进行其他处理。
3、系统资源不足处理
当系统资源不足时,如内存不足、文件打开数达到最大限制等,系统会向进程发送相应的信号,进程可以使用信号处理函数来释放资源、减少内存使用、关闭文件等,以避免系统崩溃。
4、进程间通信错误处理
当进程间通信出现错误时,如通信超时、通信失败等,进程可以使用信号来捕获这些错误,打印错误信息、重新发送数据等。
总之,信号机制是 Linux 中一种非常有效的处理异常情况的方式,可以用于捕获各种类型的异常、进行错误处理、保护系统安全等。在编写程序时,需要注意对各种异常情况的处理,及时捕获信号并进行相应的处理,以提高程序的稳定性和可靠性。
相关文章:
【Linux系统】Linux进程信号详解
Linux进程信号 0 引言1 认识信号1.1 什么是信号1.2 发送信号的本质1.3 信号的处理 2 信号的产生2.1 键盘产生2.2 调用系统函数向进程发送信号2.3 由软件条件产生信号2.4 硬件异常产生信号 3 信号的保存4 信号的处理5 总结 0 引言 本篇文章会从Linux信号的产生到信号的保存&…...
阿里云u1服务器通用算力型CPU处理器性能测评
阿里云服务器u1通用算力型Universal实例高性价比,CPU采用Intel(R) Xeon(R) Platinum,主频是2.5 GHz,云服务器U1实例的基准vCPU算力与5代企业级实例持平,最高vCPU算力与6代企业级实例持平,提供2c-32c规格和1:1/2/4/8丰富…...
hive的详细使用文档和使用案例
目录 Hive 简介安装连接到Hive创建数据库创建表加载数据查询数据修改表删除表 使用案例结论 Hive 简介 Hive是一个基于Hadoop的数据仓库工具,可以将结构化数据映射到Hadoop HDFS上,并提供SQL查询功能。Hive的设计目标是让那些熟悉SQL语言的用户能够在Ha…...
KL散度
KL散度(Kullback-Leibler divergence),也称为相对熵(relative entropy),是用来衡量两个概率分布之间差异的一种指标。在机器学习中,KL散度常常用于度量两个概率分布之间的相似度或差异性。 具体…...
Java基础学习(16)多线程
Java基础学习多线程 一、多线程1.1 什么是多线程1.2 多线程的两个概念1.2.1 并发 1.3 多线程的实现方式1.4 多线程的成员方法1.5 线程的生命周期 二、线程安全1.6 同步方法1.7 锁lock1.8 死锁1.8 生产者和消费者 (等待唤醒机制)1.9 等待唤醒机制(阻塞队列方式实现)1…...
【一起啃书】《机器学习》第五章 神经网络
文章目录 第五章 神经网络5.1 神经元模型5.2 感知机与多层网络5.3 误差逆传播算法5.4 全局最小与局部极小5.5 其他常见神经网络5.6 深度学习 第五章 神经网络 5.1 神经元模型 神经网络是由具有适应性简单单元组成的广泛并行互连的网络,它的组织能够模拟生物神经系统…...
matlab实验二可视化
学聪明点,自己改,别把我卖了 一、实验目的及要求 要求 1、掌握 MATLAB常用的二维和三维绘图函数 2、掌握MATLAB的图形注释 3、熟悉MATLAB常用的图形修饰 4、熟悉MATLAB的图形动画 实验原理 1、MATLAB二维绘图:plot,fplot,fimplicit…...
(数据结构)栈的实现——再一次保姆级教学
目录 1. 栈 编辑 1.2 栈的实现 2. 代码的实现 2.1 初始化栈和销毁栈 2.2栈顶元素的插入 2.3栈顶元素的删除 栈元素删除 2.4栈顶元素的获取和栈元素的个数 1. 栈 1.1 栈的概念和结构 栈(Stack)是一种线性存储结构,它具有如下特点: ࿰…...
【5G RRC】RSRP、RSRQ以及SINR含义、计算过程详细介绍
博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持! 博主链接 本人就职于国际知名终端厂商,负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。 博客…...
K8s(Kubernetes)学习(一):k8s概念及组件
Kubernetes中文文档:https://kubernetes.io/zh-cn/docs/home/ Kubernetes源码地址:https://github.com/kubernetes/kubernetes 一:Kubernetes是什么 首先要了解应用程序部署经历了以下几个时代: 传统部署时代:在物理服务器上运…...
Web3 常用语和黑话你知道吗?
My friend Dave used to be a bagholder, but he FOMO’d and bought even more BTC. Now, he’s a big whale HODLing for that moon. …that’s a lot to take in for just two sentences. If you’re new to Bitcoin and the world of cryptocurrencies, we understand if …...
物联网和边缘计算:如何将数据处理和决策推向设备边缘
第一章:引言 当我们谈论物联网(IoT)时,我们通常指的是将各种设备连接到互联网,并通过数据交换来实现智能化的网络。然而,传统的物联网模型通常涉及将数据发送到云端进行处理和分析。然而,随着技…...
【Android学习专题】java基本语法和概念(学习记录)
学习记录来自菜鸟教程 Java 变量 Java 中主要有如下几种类型的变量 局部变量 在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁类变量(静态变量) 类变量也声…...
Android系统启动全流程分析
当我们买了一个手机或者平板,按下电源键的那一刻,到进入Launcher,选择我们想要使用的某个App进入,这个过程中,系统到底在做了什么事,伙伴们有仔细的研究过吗?可能对于Framework这块晦涩难懂的专…...
RabbitMQ --- 惰性队列、MQ集群
一、惰性队列 1.1、消息堆积问题 当生产者发送消息的速度超过了消费者处理消息的速度,就会导致队列中的消息堆积,直到队列存储消息达到上限。之后发送的消息就会成为死信,可能会被丢弃,这就是消息堆积问题。 解决消息堆积有三种…...
1.Buffer_Overflow-1.Basic_Jump
github上面的练习题 git clone https://github.com/Adamkadaban/LearnPwn 然后开始做 先进行 readelf 然后进行执行看看 是怎么回事 ./buf1发现就是一个输入和输出 我们checksec看看 发现stack 保护关闭 开启了NX保护 我们进入ida64看看反汇编 我习惯先看看字符串 SHITF…...
MySQL入门语法第三课:表结构的创建
数据表结构 定点数类型decimal(m,d) m表示数字总位数 d表示小数位数 ★创建数据表先要选择数据库 1 . CREATE TABLE 表名称 创建数据表 (字段名1 数据类型1 [,字段名2 数据名2] [, .....] ); 一个字段写一行 修改表名 alter table 旧表名 rename 新表名…...
SpringSecurity框架学习与使用
SpringSecurity框架学习与使用 SpringSecurity学习SpringSecurity入门SpringSecurity深入认证授权自定义授权失败页面权限注解SecuredPreAuthorizePostAuthorizePostFilterPreFilter 参考 SpringSecurity学习 SpringSecurity入门 引入相关的依赖,SpringBoot的版本…...
DHCP+链路聚合+NAT+ACL小型实验
实验要求: 1.按照拓扑图上标识规划网络。 2.使用0SPF协议进程100实现ISP互通。 3.私网内PC属于VLAN1O, FTP Server属于VLAN2O,网关分 别为所连接的接入交换机,其中PC要求通过DHCP动态获取 4:私网内部所有交换机都为三层交换机,请合理规划VLAN&#…...
西瓜书读书笔记整理(三)—— 第二章 模型评估与选择
第二章 模型评估与选择 第 2 章 模型评估与选择2.1 经验误差与过拟合1. 错误率 / 精度 / 误差2. 训练误差 / 经验误差 / 泛化误差3. 过拟合 / 欠拟合4. 学习能力5. 模型选择 2.2 评估方法1. 评估方法概述2. 留出法3. 交叉验证法4. 自助法5. 调参 / 最终模型 2.3 性能度量1. 回归…...
uni-app音频踩坑记:iOS静音模式下如何让createInnerAudioContext正常发声
uni-app音频开发实战:突破iOS静音模式限制的完整解决方案 在移动应用开发中,音频功能往往是最容易踩坑的领域之一。特别是当你的uni-app应用需要在iOS设备上运行时,静音模式这个"隐形杀手"可能会让你的音频功能完全失效。想象一下这…...
基于Docker的Grafana+Loki+Promtail日志监控与Prometheus主机监控实战指南
1. 为什么需要Docker化的监控系统? 现代应用架构越来越复杂,微服务、容器化部署已经成为标配。记得我第一次接手一个分布式系统时,面对几十个服务实例的日志排查问题,用传统的grep命令就像大海捞针。直到发现了GrafanaLokiPromtai…...
如何用Boss-Key老板键打造3秒隐私安全区:从技术原理到实战配置
如何用Boss-Key老板键打造3秒隐私安全区:从技术原理到实战配置 【免费下载链接】Boss-Key 老板来了?快用Boss-Key老板键一键隐藏静音当前窗口!上班摸鱼必备神器 项目地址: https://gitcode.com/gh_mirrors/bo/Boss-Key 在现代开放式办…...
保姆级教程:用Python+ROS2复现四旋翼无人机微分平坦轨迹规划(附完整代码)
从零实现四旋翼无人机轨迹规划:PythonROS2实战指南 四旋翼无人机的轨迹规划一直是机器人领域的热门研究方向。不同于传统轮式机器人,无人机在三维空间中的运动控制需要考虑更多复杂因素——从姿态稳定到避障路径优化,每一步都充满挑战。今天&…...
5个秘诀:用WeChatExporter永久保存你的微信聊天记忆宝库
5个秘诀:用WeChatExporter永久保存你的微信聊天记忆宝库 【免费下载链接】WeChatExporter 一个可以快速导出、查看你的微信聊天记录的工具 项目地址: https://gitcode.com/gh_mirrors/wec/WeChatExporter 你是否曾经因为手机内存不足而忍痛删除珍贵的聊天记录…...
2026实战:C#上位机+YOLOv11实现智能安防管控,危险区域实时报警(附完整代码)
最近接了一个工厂智能安防项目,需求是实现人员闯入危险区域(如高压设备区、化工原料区)实时报警、人员计数统计,还要支持现场画面实时预览和报警记录追溯。一开始用传统的红外传感器方案,误报率高达30%,而且无法区分人员和杂物,根本满足不了需求。最终采用 C#上位机+YOL…...
告别机翻!手把手教你安装dslrBooth.Pro 7.49.3.1专业汉化版,连语音都换成中文了
深度汉化实战:打造专业级中文版dslrBooth.Pro全流程指南 每次打开专业摄影软件时,满屏的英文菜单是否让你望而却步?网上那些机翻版本是否让你在关键操作时陷入术语混乱?作为从业十年的商业摄影师,我深知语言障碍对工作…...
千问3.5-2B助力嵌入式开发:智能调试与日志分析
千问3.5-2B助力嵌入式开发:智能调试与日志分析 1. 嵌入式开发的调试痛点 在STM32等嵌入式系统开发中,调试过程往往充满挑战。想象一下这样的场景:设备突然异常重启,控制台输出长达数百行的日志信息,其中混杂着硬件中…...
AI搜索排名怎么查?2026免费GEO监测手把手教你精准监控品牌AI可见性
一家工业设备制造商的市场总监最近很困惑:他们的官网在百度搜索“高精度传感器”这个关键词上排名前三,SEO团队为此自豪。但当客户用豆包、DeepSeek提问“哪个品牌的传感器精度最高”时,AI的答案里却完全没有他们的影子。客户流失了ÿ…...
突破Windows音频限制:Equalizer APO系统级音效处理的3大创新
突破Windows音频限制:Equalizer APO系统级音效处理的3大创新 【免费下载链接】equalizerapo Equalizer APO mirror 项目地址: https://gitcode.com/gh_mirrors/eq/equalizerapo Equalizer APO作为一款开源系统级音频处理引擎,通过直接集成到Windo…...
