【Linux】进程 信号的产生
🌻个人主页:路飞雪吖~
🌠专栏:Linux
目录
一、掌握Linux信号的基本概念
🌠前台进程 VS 后台进程
🌠 小贴士:
🪄⼀个系统函数 --- signal()
🪄查看信号 --- man 7 signal
二、软硬件上理解 : OS如何进行信号处理
✨硬件上理解
🌠 信号 VS 硬件中断
✨软件:如何理解信号处理?
三、✨信号产生的方式
🚩1. 键盘产生
🚩2. 系统指令产生 发送信号 ---- 底层使用的是系统调用编辑
🚩3. 系统调用 发送信号
🌠 发送信号的其他函数:
🪄 raise --- 函数可以给当前进程发送指定的信号(⾃⼰给⾃⼰发信号)
🪄 abort --- 自己给自己发了一个特定的终止自己的信号 --- 6号信号[SIGABRT]
🚩4. 由软件条件 产生信号
🌠OS内定时器
四、✨ alarm() 设置重复闹钟 --- 简单快速理解系统闹钟
🚩5. 异常 (野指针、 除 0)
🪄模拟 野指针
🪄 模拟 除 0
🌠OS怎么知道我们的进程内部出错了?为什么会陷入死循环?
☄️ 除 0 :
☄️ 野指针
🌠core VS Term
✨core 的使用 --- 调试
🌠 如果是子进程异常了呢? core 会不会出现?
🌠小结:
五、信号处理
1. 默认处理动作
2. 忽略:本身就是一种信号捕捉的方法,动作就是忽略
3. 自定义 捕捉一个信号
• 信号是内置的,进程认识信号,是程序员内置的特性;
• 信号的处理方法,在信号产生之前,就已经准备好了;
• 何时处理信号?先处理优先级很高的,可能并不是立即处理,在合适的时候处理;
• 怎么处理信号:a. 默认行为,b. 忽略信号,c. 自定义动作
信号产生 ---> 信号保存 ---> 信号处理
一、掌握Linux信号的基本概念
// MakefileBIN=sig
CC=g++
SRC=$(shell ls *.cc)
OBJ=$(SRC:.cc=.o)$(BIN):$(OBJ)$(CC) -o $@ $^ -std=c++11%.o:%.cc$(CC) -c $< -std=c++11.PHONY:clean
clean:rm -f $(BIN)
// Signal.cc#include <iostream>
#include <unistd.h>int main()
{while(true){std::cout << "hello world" << std::endl;sleep(1);}return 0;
}
🌠前台进程 VS 后台进程
• 前台进程【./sig】:在命令行所输入的所有东西都不能执行。
> Ctrl + c 终止前台进程:给的是shell,shell在前台 --> 信号发给进程。
• 后台进程【./sig &】:bash进程依旧可以进行命令行解释。
> kill -9 [pid] 终止后台进程
> fg [作业号]
🌠 小贴士:
<1>
【1 -- 31】:普通信号;
【34 -- 64】:实时信号。
<2>
ctrl + c --> 信号发给进程,被OS接受并解释成为2号【SIGINT】信号,发送给目标进程 -- 对2号信号的默认处理动作是 终止自己!。
🪄⼀个系统函数 --- signal()
#include <iostream>
#include <unistd.h>
#include <signal.h>void Handler(int signo)
{// 当对应的信号被触发,内核会将对应的信号编号,传递给自定义方法std::cout << "Get a signal, signal number is : " << signo << std::endl;
}int main()
{signal(SIGINT, Handler);// 默认终止 --改成了--> 执行自定义方法:Handler while(true){std::cout << "hello world" << std::endl;sleep(1);}return 0;
}
#include <iostream>
#include <unistd.h>
#include <signal.h>void Handler(int signo)
{// 当对应的信号被触发,内核会将对应的信号编号,传递给自定义方法std::cout << "Get a signal, signal number is : " << signo << std::endl;
}int main()
{signal(SIGQUIT, Handler);// 默认终止 --改成了--> 执行自定义方法:Handler while(true){std::cout << "hello world" << std::endl;sleep(1);}return 0;
}
ctrl + \ :3号信号,默认也是终止进程的。
🪄查看信号 --- man 7 signal
#include <iostream>
#include <unistd.h>
#include <signal.h>void Handler(int signo)
{// 当对应的信号被触发,内核会将对应的信号编号,传递给自定义方法std::cout << "Get a signal, signal number is : " << signo << std::endl;
}int main()
{// signal 为什么不放在循环里面? 不需要,只需要设置一次就可以了// signal:如果没有产生2或者3号信号呢? handler不被调用!// signal(SIGINT, Handler);// 默认终止 --改成了--> 执行自定义方法:Handler// signal(SIGQUIT, Handler);// 默认终止 --改成了--> 执行自定义方法:Handlerfor (int signo = 1; signo < 32; signo++)// 捕捉 1--31 号的信号,使得很多方法杀不掉进程,但是有一些信号捕捉不到{signal(signo, Handler);std::cout << "自定义捕捉信号:" << signo << std::endl;}while (true){std::cout << "hello world" << std::endl;sleep(1);}return 0;
}
二、软硬件上理解 : OS如何进行信号处理
✨硬件上理解
OS怎么知道键盘上面有数据?
当按下键盘 首先是被驱动程序识别到 按下的按键,OS如何知道键盘被按下了?--- 硬件中断 ,键盘一旦按下,在硬件上 键盘 和 CPU 是连接的,在控制信号,输入设备是直接和中央处理器连接的,输入设备首先会给中央处理器发送一个中断信号【硬件电路】,CPU中央处理器收到这个硬件电路之后,立马就会告诉OS,当前外设有一个外设准备好了,接下来让OS主动的去把外设的数据拷贝到内存里,此时OS再也不用主动轮询检测任何输入/输出设备了,只需要等待发生中断信号 --- 硬件和OS可以并行执行了!
🌠 信号 VS 硬件中断
• 信号是纯软件,模拟中断的行为;
• 硬件中断,纯硬件。
✨软件:如何理解信号处理?
• 键盘上的组合键 是先被OS系统识别到【OS是键盘真正的管理者,当系统在运行的时候,OS一直在检测键盘上有没有信息】再发给进程,当进程不能立即处理这个信号时,进程就会记录下这个信号【信号是从 1-31 连续的数字,进程是否收到1-31这个数字的信号 --- 在 task_struct 里通过位图记录 0000 0000 0000 0000 0000 0000 0000 0000,比特位的位置:信号的编号,比特位的内容:是否为0/1,是否收到对应的信号】,发送信号的本质是什么?【写入信号】OS修改目标进程的PCB中的信号位图【0 -> 1】,操作系统有没有权利修改进程PCB的位图?有,OS是进程的管理者,所以 无论以什么方式发送信号,最终 都是转换到OS,让OS写入信号,因为OS是进程的唯一管理者。
• 信号产生有很多种,但是信号发送只有OS;
• 判断进程是否收到信号:进程里面有信号处理的表结构 sighandler_t arr[32] 函数指针数组,根据PCB里面去查位图,根据位图就可以检测出那个比特位为1,拿到比特位为1,这个数字就可以作为该数组的下标【下标-1】,直接去调用函数指针上的方法。
三、✨信号产生的方式
🚩1. 键盘产生
当在可执行程序在前台进程产生之后,按住 ctrl+c ,键盘被按下,计算机的CPU识别到键盘又被按下的动作,唤醒OS,让OS去读取键盘山的 ctrl+c ,读到后将 ctrl + c 解释成 2号 信号【if(案件 == ctrl + c)】,OS会把 ctrl + c 转化成一段代码【向目标前台进程PCB写入2号信号,即把比特位 0 --> 1,OS信号发送完成】,发送完成之后,该进程在后续合适的时候调度运行,发现自己收到一个信号,当前进程默认就要执行自己对 2号 信号的处理动作。
🚩2. 系统指令产生 发送信号 ---- 底层使用的是系统调用
🚩3. 系统调用 发送信号
// MakefileBIN=mykill
CC=g++
SRC=$(shell ls *.cc)
OBJ=$(SRC:.cc=.o)$(BIN):$(OBJ)$(CC) -o $@ $^ -std=c++11%.o:%.cc$(CC) -c $< -std=c++11.PHONY:clean
clean:rm -f $(BIN)
// Signal.cc#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <string>
#include <signal.h>// 设置自己的killvoid Usage(std::string proc)
{std::cout << "Usage: " << proc << " signumber processid " << std::endl;
}// ./mykill 1信号 12345进程号
int main(int argc, char *argv[])
{if(argc != 3){Usage(argv[0]);exit(1);}int signumber = std::stoi(argv[1]);// 转换为整数pid_t id = std::stoi(argv[2]);int n = ::kill(id, signumber);if(n < 0){perror("kill");exit(2);}exit(0);
}
信号发送 本质都是操作系统OS发的!!!
🌠 发送信号的其他函数:
🪄 raise --- 函数可以给当前进程发送指定的信号(⾃⼰给⾃⼰发信号)
#include <iostream> #include <unistd.h> #include <signal.h>int main() {int cnt = 5;while(true){std::cout << "hahaha alive" << std::endl;cnt--;if(cnt <= 0)raise(9);sleep(1);} }
🪄 abort --- 自己给自己发了一个特定的终止自己的信号 --- 6号信号[SIGABRT]
int main() {int cnt = 5;while(true){std::cout << "hahaha alive" << std::endl;cnt--;if(cnt <= 0)// abort();sleep(1);} }
🚩4. 由软件条件 产生信号
SIGPIPE 是⼀种由软件条件产生的信号。
🪄 alarm 函数 --- 调⽤ alarm 函数可以设定⼀个闹钟,也就是告诉内核在 seconds 秒之后给当前进程发 (14号)SIGALRM 信号,该信号的默认处理动作是终⽌当前进程。
int main()
{// 统计我的服务器 1s 可以将计数器累加多少!alarm(1);// 我自己,会在 1s 之后收到一个SIGALARM信号int number = 0;while(true){printf("count: %d\n", number);}}
int number = 0;
void die(int signumber)
{printf("get a sig : %d, count: %d\n", signumber, number);exit(0);
}int main()
{// 统计我的服务器 1s 可以将计数器累加多少!alarm(1);// 我自己,会在 1s 之后收到一个SIGALARM信号signal(SIGALRM, die);while(true){number++;}}
这两个的写法 计数器累加的次数为什么会差别这么大呢?
【printf("count: %d\n", number);】里面的 IO 影响了计算的速度!,【while(true) number++;】纯CPU计算。
🌠OS内定时器
• 在操作系统内,要对 进程管理、文件管理、多线程、内存管理 进行管理,同时也要做定时管理,OS在开机之后,是会维护时间的,所以设置的闹钟,在OS底层就是设置了一个定时器。
• 许多进程都可以设置闹钟,OS里可以同时存在很多被设置的闹钟,所以OS就要管理定时器【先描述,再组织】[struct timer{}] ,OS在每次检测超时的时候,会把定时器所对应的节点,按顺序进行升序排序,变成有序的列表,当有超时的时候,只需要从前往后遍历,遇到第一个没有超时的,之前的全部是超时的,遍历的同时,把超时的【struct timer】执行对应的 【func_t f;函数,给目标进程发送SIGALRM信号】在操作系统内给相应的进程发信号。
• 用堆理解定时器的排序:维护成一个最小堆,用超时时间作为键值,所以要想知道定时器有没有超时,只需要查堆顶就可以了,若堆顶没有超时,则所有的节点都没有超时;若堆顶超时,就把堆顶pop出来【堆再重新构建】,去执行相应的操作,重复操作,直到不再超时。
• 所以当我们在调用alarm函数时,就相当于在OS内给我们获取当前时间【currenttime】和 当前的超时时间【seconds】,然后在内核中设置一个节点,放在堆里面,OS就会在超时之后,直接向目标进程发送SIGALRM信号,并且把该节点释放掉。
• 闹钟的返回值 是什么?闹钟剩余时间。
int main() {alarm(10);sleep(4);int n = alarm(0);// 0:取消闹钟std::cout << "n: " << n << std::endl; }
• 当软件条件就绪时【超时、闹钟、定时器】,OS就可以向目标进程发送信号。
四、✨ alarm() 设置重复闹钟 --- 简单快速理解系统闹钟
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <string>
#include <signal.h>
#include <functional>
#include <vector>// 闹钟
using func_t = std::function<void()>;int gcount = 0;
std::vector<func_t> gfuncs;// 把信号 更换 成为 硬件中断
void handler(int signo)
{for(auto &f : gfuncs){f();}std::cout << "gcount : " << gcount << std::endl;alarm(1);// 上面的闹钟响了之后,继续执行
}int main()
{gfuncs.push_back([](){ std::cout << "我是一个内核刷新操作" << std::endl;});gfuncs.push_back([](){ std::cout << "我是一个检测进程时间片的操作,如果时间片到了,我会切换进程" << std::endl;});gfuncs.push_back([](){ std::cout << "我是一个内存管理操作,定期清理操作系统内部的内存碎片" << std::endl;});alarm(1);// 一次性的闹钟,超时alarm会自动被取消signal(SIGALRM, handler);while(true) // gcount++;{pause();std::cout << "我醒来了..." << std::endl;gcount++;}
}
• 操作系统其实就是一个死循环,当OS启动之后,会接收外部固定的事件源【时钟中断,集成在CPU内部】,每隔很短的时间,向OS触发硬件中断,让OS去执行中断方法。
• OS的调度、切换、内存管理、系统调用,全都是依靠中断来完成的。所以OS不应该叫OS,应该叫一个中断程序。
🚩5. 异常 (野指针、 除 0)
🪄模拟 野指针
void handler(int signo)
{std::cout << "get a signo: " << signo << std::endl;// 我捕捉了 11 号信号,没执行默认动作,也没有退出进程
}int main()
{signal(11, handler);int *p = nullptr;*p = 100;while(true);
}
🪄 模拟 除 0
void handler(int signo)
{std::cout << "get a signo: " << signo << std::endl;// 我捕捉了 8 号信号,没执行默认动作,也没有退出进程
}int main()
{signal(8, handler);int a = 10;a /= 0;while(true);
}
C/C++中,常见的异常,进程崩溃了,原因是 OS给目标进程发送对应错误的信号,进而导致该进程退出。
🌠OS怎么知道我们的进程内部出错了?为什么会陷入死循环?
☄️ 除 0 :
状态寄存器里会有各种描述状态计算的结果,其中有一个叫做 溢出标记位,正常情况下计算完毕,溢出标记位为0,说明整个计算结果没有溢出,结果可靠,此时把结果写回内存里,就完成对应的复制。若对应的溢出标记位为1,就证明CPU内部计算结果出错了。
当出现溢出,即CPU内部结果出错了,OS就会知道【CPU[硬件]的中断有 内部中断 和 外部中断,CPU内部一旦出现溢出的错误,CPU内部硬件就会触发内部中断,就会告诉OS,OS是硬件的管理者,OS在调度、切换,就是在合理的使用CPU资源】,是哪个进程引起的硬件错误,OS就会通过信号杀掉进程!即当计算出现硬件错误,OS要识别到,就会给目标进程发送对应的信号,杀掉进程!
为什么会陷入死循环【不退出进程】?
这个进程一直没有退出,还要被调度,进程就会被切换、执行等等,【a /= 0】CPU里面的状态寄存器的 Eflags的溢出标记位一直都是1,CPU里面的数据一直都是这个进程的上下文内容不会更改,所以就会一直被捕捉,被调度,就会出现死循环,并且一直在触发8号信号。
☄️ 野指针
CR3 寄存器:保存页表的起始地址,负责虚拟地址和物理地址转化。
MMU【硬件】被集成在CPU内部:完成虚拟地址和物理地址转化。
ELP:读取可执行程序的起始地址。
当传递一个 0 地址,在MMU转化出来 0,我们没有权利访问最低的地址0,MMU这个硬件经过 转化或权限 方面上, 发现根本不能转化出来这个地址,使得MMU【硬件】报错,即CPU内部出错【与上面的类似】,OS就会知道,OS就会向目标进程去识别,就会杀掉这个进程。也要类似 状态寄存器的东西,转化错误,所以就会死循环。
OS怎么知道我们的进程内部出错了?程序内部的错误,其实都会表现在硬件错误上,OS就会知道是哪个地方出错,进而给对应的目标进程发送信号。
🌠core VS Term
Term :正常终止进程【不需要进行debug】。
Core:也是终止进程,但会多做一些处理。核心转储,在当前目录下形成文件【pid.core】,OS在进程崩溃的时候,将进程在内存中的部分信息保存起来【保存在磁盘上,持久化起来】,方便后续调试!这个文件【pid.core】一般都会被云服务器关掉。
把core功能关闭的原因:在云服务器上,若野指针、除 0 出现错误等等,一般都会在后端立即重启这个服务。若不关闭,当某个错误,一直重复 重启后挂掉,磁盘上就会出现大量的core文件,甚至磁盘爆满,就会影响系统 或 某些应用服务直接就挂掉,即 会因为系统问题,导致磁盘爆满。
✨core 的使用 --- 调试
🌠 如果是子进程异常了呢? core 会不会出现?
子进程会不会出现core取决于:退出信号是否终止动作 && 服务器是否开启 core 功能。
🌠小结:
• 键盘、系统指令、系统调用、软件条件、异常 的信号产生,最终都要有OS来执行,因为OS是进程的管理者,统一由OS来向目标进程发信号;
• 信号的处理方式:不是立即处理,而是在合适的时候处理;
• 信号如果不是被立即处理,信号就会暂时被进程记录下来,保存在进程对应的PCB中的信号位图;
• 一个进程在没有收到信号的时候,知道自己应该对合法信号作何处理:默认、忽略、自定义;
• OS向目标进程发送信号:实则是OS向目标进程写信号。
五、信号处理
1. 默认处理动作
void handler(int signo)
{std::cout << "get a signal: " << signo << std::endl;exit(1);
}int main()
{signal(2, SIG_DFL);// default:默认while(true){pause();}
}
2. 忽略:本身就是一种信号捕捉的方法,动作就是忽略
void handler(int signo)
{std::cout << "get a signal: " << signo << std::endl;exit(1);
}int main()
{signal(2, SIG_IGN);// 忽略(不做处理),本身就是一种信号捕捉的方法,动作就是忽略while(true){pause();}
}
3. 自定义 捕捉一个信号
void handler(int signo)
{std::cout << "get a signal: " << signo << std::endl;exit(1);
}int main()
{// 信号捕捉:// 1. 默认// 2. 忽略// 3. 自定义::signal(2, handler);// 自定义while(true){pause();}
}
如若对你有帮助,记得关注、收藏、点赞哦~ 您的支持是我最大的动力🌹🌹🌹🌹!!!
若有误,望各位,在评论区留言或者私信我 指点迷津!!!谢谢 ヾ(≧▽≦*)o \( •̀ ω •́ )/
相关文章:

【Linux】进程 信号的产生
🌻个人主页:路飞雪吖~ 🌠专栏:Linux 目录 一、掌握Linux信号的基本概念 🌠前台进程 VS 后台进程 🌠 小贴士: 🪄⼀个系统函数 --- signal() 🪄查看信号 --- man 7 sign…...
实时操作系统革命:实时Linux驱动的智能时代底层重构
一、智能时代对实时性的终极挑战 在万物互联的智能时代,人类对机器响应速度的期待已突破物理极限。当工业机器人以亚毫米级精度执行微米级加工任务,当自动驾驶系统在130公里时速下需在10毫秒内完成决策切换,当医疗机器人需在5毫秒内响应神经…...
NGINX HTTP/3 实验指南安装、配置与调优
一、HTTP/3 简介 基于 QUIC:在 UDP 之上实现的多路复用传输,内置拥塞控制与前向纠错,无需三次握手即可恢复连接。零 RTT 重连:借助 TLS 1.3,实现连接恢复时的 0-RTT 数据发送(视底层库支持)。多…...

机器学习中的维度、过拟合、降维
1. 维度灾难 当我们谈论机器学习模型在处理数据时遇到的困难,一个常常被提及的词便是“维度灾难”(Curse of Dimensionality)。这不是科幻小说里的情节,而是数学和计算世界里真实存在的困境。它指的正是:当数据集的特…...

关于git的使用
下载git 可以去git的官网下载https://git-scm.com/downloads 也可以去找第三方的资源下载,下载后是一个exe应用程序,直接点开一直下一步就可以安装了 右键任意位置显示这两个就代表成功,第一个是git官方的图形化界面,第二个是用…...

预约按摩小程序源码介绍
基于ThinkPHP、FastAdmin和UniApp开发的预约按摩小程序源码,ThinkPHP作为后端框架,以其高效稳定著称,能妥善处理数据逻辑与业务规则。FastAdmin作为后台管理框架,极大简化了后台管理系统的搭建与维护。UniApp则让小程序具备跨平台…...

Elasticsearch创建快照仓库报错处理
创建快照仓库报错: 根据报错提示的信息,问题可能出在 Elasticsearch 的配置中。当你尝试创建一个文件系统(fs)类型的快照仓库时,虽然已经指定了 location 参数,但 Elasticsearch 仍然报错,这通…...
LINUX安装运行jeelowcode前端项目
参考 JeeLowCode低代码社区,JeeLowCode低代码开发平台,JeeLowCode低代码开发框架,快速启动(VUE) 安装node 18 LINUX安装node/nodejs_linux安装node 安装到哪-CSDN博客 安装PNPM LINUX安装PNPM-CSDN博客 下载 git clone https://gitcode.com/jeelo…...

使用DDR4控制器实现多通道数据读写(十三)
一、概述 在上一章节中使用仿真简单验证了interconnect的功能,使用四个axi4的主端口同时发起读写命令,经过interconnect后,将这些读写指令依次发给ddr4控制器。Ddr4控制器响应后再依次将响应发送到各个通道。从而实现多通道读写ddr4控制器的功…...
如何描述BUG
一、如何描述BUG 1.1 版本信息 1.1.1 必填项 精确到构建版本号(如v2.3.1_build20240615) 获取方式: 代码版本控制系统(Git提交哈希值)应用内版本信息(App的About页面)持续集成工具生成的版本…...
Python进阶:如何通过组合模式实现高性能Vector类?
🔍 设计动机:为何抛弃继承选择组合? 在实现多维向量类Vector时,我们刻意采用组合模式而非继承,核心优势在于: 高扩展性:通过array数组存储分量,天然支持高维向量低耦合:…...

谷歌Veo vs Sora:AI视频生成技术的巅峰对决
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 ——从架构到实践,解析音画同步、物理模拟与长视频生成的破局之战 一、技术架构:双雄对垒,殊途同归? 谷歌…...

基于Spring boot+vue的中医养生系统的设计与实现(源码+论文+部署+安装+调试+售后)
感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,我会一一回复,希望帮助更多的人。 系统背景 在健康中国战略持续推进与全民健康意识显著提升的时代背景下,中医养生作为中…...

31.第二阶段x64游戏实战-封包-线程发包
免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 本次游戏没法给 内容参考于:微尘网络安全 上一个内容:30.第二阶段x64游戏实战-认识网络数据包发送流程 代码跳转 ws2_32.send跳转sen…...

Unity数字人开发笔记
开源工程地址:https://github.com/zhangliwei7758/unity-AI-Chat-Toolkit 先致敬zhangliwei7758,开放这个源码 一、建立工程 建立Unity工程(UnityAiChat)拖入Unity-AI-Chat-Toolkit.unitypackage打开chatSample工程,可…...

嵌入式开发--STM32G431无法正常运行程序,BOOT0与CAN冲突
故障现象 今天开发STM32G431时遇到一个问题,板子打样回来后,焊接完成,可以烧程序,可以读FLASH,却死活不能运行,也不能进仿真调试。 故障定位 经过排查,发现将隔离芯片π121M31拆除࿰…...
每天掌握一个Linux命令 - sqlite3
Linux 命令工具 sqlite3 使用指南 一、工具概述 sqlite3 是 SQLite 数据库的命令行工具,用于在 Linux 系统中直接操作 SQLite 数据库(轻量级、无服务器、嵌入式关系型数据库)。 核心特点: 无需安装数据库服务,直接通…...

程序环境与预处理
一、程序的翻译环境和执行环境 翻译环境:将源代码转化为可执行的机器指令 执行环境:执行代码 1、翻译环境 流程: 二、运行环境 程序执行过程: 三、预编译阶段 1、预定义符号 __FILE__ //进行编译的原文件名 __LINE__ //文…...
RT Thread Nano V4.1.1 rtconfig.h 注释 Configuration Wizard 格式
rtcomfig.h 以下是对 [rtconfig.h](file://c:\Users\admin\Downloads\rtthread-nano-master\rt-thread\bsp\stm32f407-msh\RT-Thread\rtconfig.h) 文件中每一个配置项的详细注释说明: 基本配置(Basic Configuration) [RT_THREAD_PRIORITY_MAX](file://c:\Users\admin\Downl…...

《Java 单例模式:从类加载机制到高并发设计的深度技术剖析》
【作者简介】“琢磨先生”--资深系统架构师、985高校计算机硕士,长期从事大中型软件开发和技术研究,每天分享Java硬核知识和主流工程技术,欢迎点赞收藏! 一、单例模式的核心概念与设计目标 在软件开发中,我们经常会遇…...
JSONP跨域原理全解析
JSONP(JSON with Padding)是一种绕过浏览器同源策略限制、实现跨域数据请求的“hack”式方案。其核心原理和流程如下: 同源策略限制 浏览器为了安全,只允许页面从与当前页面相同协议、域名、端口的服务器加载数据。而 <script&…...
【MySQL】第11节|MySQL 8.0 主从复制原理分析与实战(一)
一、MySQL主从复制基础 1. 核心概念 定义: MySQL主从复制是将主库(Source/Master)的数据变更同步到一个或多个从库(Replica/Slave)的机制,默认采用异步复制,支持全库、指定库或表的同步。 角…...

全志F1c200开发笔记——移植根文件系统
1.下载buildroot Index of /downloads/ 使用2018.02.11版本 直链下载 https://buildroot.org/downloads/buildroot-2018.02.11.tar.gz 2.配置 进入buildroot压缩包目录下,使用命令解压并进入工作目录 tar -xf buildroot-2018.02.11.tar.gz cd buildroot-2018.…...

[yolov11改进系列]基于yolov11引入自注意力与卷积混合模块ACmix提高FPS+检测效率python源码+训练源码
[ACmix的框架原理] 1.1 ACMix的基本原理 ACmix是一种混合模型,结合了自注意力机制和卷积运算的优势。它的核心思想是,传统卷积操作和自注意力模块的大部分计算都可以通过1x1的卷积来实现。ACmix首先使用1x1卷积对输入特征图进行投影,生成一组…...

Java NIO编程:构建高性能网络应用
1.Java NIO 核心概念与架构 1. 传统 BIO 与 NIO 的对比 特性 BIO (Blocking I/O) NIO (Non-blocking I/O) I/O 模型 阻塞 非阻塞 / 异步 线程模式 每个连接一个线程 单线程管理多个连接 数据处理单位 字节流 / 字符流 缓冲区 (Buffer) 核心组件 Socket, ServerSoc…...

如何实现高性能超低延迟的RTSP或RTMP播放器
随着直播行业的快速发展,RTSP和RTMP协议成为了广泛使用的流媒体传输协议,尤其是在实时视频直播领域,如何构建一个高性能超低延迟的直播播放器,已经成为了决定直播平台成功与否的关键因素之一。作为音视频直播SDK技术老兵ÿ…...
每天掌握一个Linux命令 - sar
Linux 系统监控工具 sar 使用指南 一、工具概述 sar(System Activity Reporter) 是 Linux 下功能强大的系统活动报告工具,属于 sysstat 软件包的核心组件。它通过采集系统资源(CPU、内存、磁盘、网络、进程等)的使用…...
RabbitMQ 集群与高可用方案设计(三)
五、高可用方案设计与实现 (一)负载均衡与代理 1. HAProxy 配置 HAProxy 是一款广泛应用的开源负载均衡器和代理服务器,它能够实现对 RabbitMQ 集群节点的负载均衡和健康检查,有效提高系统的可用性和性能。以下是使用 HAProxy …...
Linux的读写屏障
在 Linux 中,读写屏障(Read-Write Barriers,简称 RWB)是对内存访问顺序的一种控制机制,用来保证在多核处理器环境下,内存访问的正确顺序,避免因乱序执行导致的数据一致性问题。它是操作系统内核…...
Vue中的 VueComponent
VueComponent 组件的本质 Vue 组件是一个可复用的 Vue 实例。每个组件本质上就是通过 Vue.extend() 创建的构造函数,或者在 Vue 3 中是由函数式 API(Composition API)创建的。 // Vue 2 const MyComponent Vue.extend({template: <div…...