Linux中信号的保存
一、认识信号的其他相关概念
实际执行信号的处理动作称为信号递达
信号从产生到递达之间的状态,称为信号未决
进程可以选择阻塞某个信号
被阻塞的信号产生时将保持在未决状态,直到进程解除对该信号的阻塞,才进行递达的动作
阻塞和忽略是不同的,阻塞就不会递达。忽略是递达后可选择的一种处理动作
特定的信号被阻塞(屏蔽,与之前的IO阻塞不同),但是信号已经产生了,一定要把信号进行pending(保存),永远不递达,除非解除阻塞
二、task_struct中的三张表
这三张表横着看!
在进程的task_struct中,保存着上图三张表:block,pending,handler
pending表:
其实是一个位图,每个比特位其实就是对应的信号编号,比特位的内容:1/0 表示是否收到对应的信号。其实就是当前进程收到的信号列表
handler表:
其实是一个handler_t xxx[N]:函数指针数组,信号编号-1:就是函数指针数组的下标
block表:
本质也是一张位图,1-31,比特位的位置与pending表与handler表一一对应,比特位的位置依旧是信号编号,比特位的内容:0/1 是否阻塞/屏蔽特定的信号。假设block表中2号信号为1,也就是把2号信号给屏蔽了,这时pending表的2号信号为1,也不会执行handler表递达
如果没收到信号,能不能提前对信号进行屏蔽?
能!因为这是两张位图
三、sigset_t
每个信号只有一个比特位的未决标志,非0即1,不记录信号产生了多少次。因此未决和阻塞标志可以用相同的数据类型 sigset_t 来存储,称为信号集,这个类型可以表示每个信号的有效或无效状态。阻塞信号集也叫做当前进程的 信号屏蔽字(SignalMask)
四、对于位图的增删查改接口
sigprocmask
读取或更改进程的信号屏蔽字(阻塞信号集),也就是修改block表
返回值:成功为0,失败为-1
参数介绍:
int how 的可选值分别是:
SIG_BLOCK(增添屏蔽信号)
SIG_UNBLOCK(解除屏蔽信号)
SIG_SETMASK(覆盖原有的屏蔽信号)
const sigset_t *set: 输入型参数,一个新的信号集
sigest_t *oldset: 输出型参数,被修改前的信号集
sigpending
获取当前的pending表,参数是输出型参数
为什么没有对pending的修改的?其实我们已经是通过硬件,软件条件,指令,系统调用等方法对pending表进行修改了,所以sigpending函数不需要再额外提供修改pending表的方法
handler表的修改?通过signal
其他接口介绍
#include <signal.h>
//初始化set所指向的信号集,使其中所有信号的对应bit清零,表⽰该信号集不包含
任何有效信号。
int sigemptyset(sigset_t *set); //初始化set所指向的信号集,使其中所有信号的对应bit置位,表⽰该信号集的有效信号
包括系统⽀持的所有信号
int sigfillset(sigset_t *set); //增加信号集上的某个信号
int sigaddset(sigset_t *set, int signo); //删除信号集上的某个信号
int sigdelset(sigset_t *set, int signo); //查看某个信号是否在信号集里,在的话返回1,否则返回0
int sigismember(const sigset_t *set, int signo);
实验:屏蔽2号信号,打印pending表,验证pending表的变化
//1.屏蔽二号信号
//2.获取并打印pending表
//3.发送2号信号,观察效果
void PrintPending(const sigset_t &pending)
{std::cout<<"pending List:["<<getpid()<<"]:";for(int i=32;i>0;i--){//判断当前信号在不在信号集中if(sigismember(&pending,i)){std::cout<<1;}else{std::cout<<0;}}std::cout<<std::endl;
}
int main()
{//这两个数据结构是在用户栈上面开辟的,是乱码,所以我们需要对其进行清0,但是不建议手动清0sigset_t block,oblock;sigemptyset(&block);sigemptyset(&oblock);//将2号信号进行屏蔽sigaddset(&block,2);//并没有设置进内核中,只是在用户栈上设置了block的位图结构//设置进内核中sigprocmask(SIG_SETMASK,&block,&oblock);//获取pending表while(1){//int sigpending(sigset_t *set);//输出型参数sigset_t pending;sigpending(&pending);//打印PrintPending(pending);sleep(1);}return 0;
}
打开新终端,输入kill -2 进程号,OS向进程发送2号信号,相应的位图的2号位置置1,因为我们提前将二号信号进行屏蔽,所以2号信号无反应
那么我们该如何解除对2号信号的屏蔽呢?怎么屏蔽就怎么解屏蔽
void PrintPending(const sigset_t &pending)
{std::cout<<"pending List:["<<getpid()<<"]:";for(int i=32;i>0;i--){//判断当前信号在不在信号集中if(sigismember(&pending,i)){std::cout<<1;}else{std::cout<<0;}}std::cout<<std::endl;
}
void handler(int signo)
{std::cout<<"对2号信号进行了处理"<<std::endl;
}
int main()
{//这里是block二号信号后再解除2号信号的屏蔽的操作signal(2,handler);//这两个数据结构是在用户栈上面开辟的,是乱码,所以我们需要对其进行清0,但是不建议手动清0sigset_t block,oblock;sigemptyset(&block);sigemptyset(&oblock);//将2号信号进行屏蔽,先对位图进行修改sigaddset(&block,2);//并没有设置进内核中,只是在用户栈上设置了block的位图结构//设置进内核中,sigpromask是对block表的修改sigprocmask(SIG_SETMASK,&block,&oblock);int cnt=10;//获取pending表while(1){//int sigpending(sigset_t *set);//输出型参数sigset_t pending;sigpending(&pending);//打印PrintPending(pending);sleep(1);cnt--;if(cnt==0){std::cout<<"解除对2号信号的屏蔽"<<std::endl;//怎么屏蔽的就怎么解除屏蔽,oblock中存储了原本的位图sigprocmask(SIG_SETMASK,&oblock,nullptr);//SIG_SETMASK 覆盖原本的位图}}return 0;
}
运行后对进程发送2号信号,可以看到,发送2号信号后,位图相应的位置变成了1,当解除了对2号信号的屏蔽后,位图相应的位置变回了0
五、信号捕捉
sigaction
相比 signal
功能更丰富,能对信号处理进行更精细控制 。成功时返回 0 ,失败返回 -1 并设置 errno
其中,act和oldact是一个结构体!这个结构体的内容如下:
我们只需要关注红色箭头这两个参数即可,一个是对应的方法的函数指针
另一个是sa_mask
定义了在执行信号处理函数时需要阻塞的信号集合。也就是说,当信号处理函数开始执行时,sa_mask
中包含的信号将被阻塞,不会被立即处理,直到信号处理函数执行完毕,被阻塞的信号屏蔽字才会恢复到之前的状态。OS这样做规避了信号处理被嵌套的情况,避免了栈溢出!
验证OS不允许信号处理方法进行嵌套:
#include<iostream>
#include<signal.h>
#include<unistd.h>
//验证OS不允许信号处理方法进行嵌套
void handler(int signo)
{static int cnt=0;cnt++;while(1){std::cout<<"get a signal:"<<signo<<"cnt:"<<cnt<<std::endl;sleep(1);}exit(1);
}
int main()
{struct sigaction act, oact;//新的处理方法,旧的处理方法act.sa_handler=handler;sigaction(2,&act,&oact);//将2号信号的执行方法改成自定义while(1){pause();}return 0;
}
运行代码可以发现,处理方法并没有进行嵌套
信号处理函数执行完毕,被阻塞的信号屏蔽字才会恢复到之前的状态
void PirintBLock()
{sigset_t set, oset;sigemptyset(&set);sigemptyset(&oset);//SIG_BLOCK(增添屏蔽信号),新的信号集,旧的信号集sigprocmask(SIG_BLOCK, &set, &oset);//调用它的这个进程自己的信号屏蔽集std::cout << "block: ";//打印block表中31-1号信号的信号集for (int signo = 31; signo > 0; signo--){if (sigismember(&oset, signo)){std::cout << 1;}else{std::cout << 0;}}std::cout << std::endl;
}void handler(int signo)
{static int cnt = 0;cnt++;while (true){std::cout << "get a sig: " << signo << ", cnt: " << cnt << std::endl;PirintBLock();sleep(1);break;}}int main()
{struct sigaction act, oact;//结构体act中,我们只需要关注两个变量,handler与sa_mask,自定义函数指针与信号集act.sa_handler = handler;sigemptyset(&act.sa_mask);//对位图进行清0sigaddset(&act.sa_mask, 3);//将3号信号加入到信号集中sigaddset(&act.sa_mask, 4);sigaddset(&act.sa_mask, 5);sigaddset(&act.sa_mask, 6);sigaddset(&act.sa_mask, 7);::sigaction(2, &act, &oact);//更改2号信号的处理方法,处理方法为打印block表,打印一次后退出,2号信号旧不阻塞了,那么走到下面的while,位图恢复while (true){PirintBLock();pause();}
}
当我们发送2号信号时,执行自定义函数方法
现在,我们知道了,在进程执行信号方法时,block表中相应的信号会被置1,也就是把当前执行的信号进行屏蔽,当handler表中的方法执行完成后,再将block表进行回复。
收到信号后pending表的修改以及恢复
我们知道,当进程收到信号的时候,pending表相应的信号会置为一,相当于时进程收到了该信号
那么pending表从1变回0这个过程,是在handler表中的方法完成后变回0,还是handler表中的方法执行期间变回0的?
答案是执行handler表期间的方法时,pending表从1变回0的,如果是在执行完函数方法后再变回0的,那么pending表中的1代表的是之前收到的信号,还是新收到的信号?这样OS就分辨不清了
我们也可以通过代码来观察一下pending表中的信号集
在信号处理之间,pending表全为0,证明在handler之间把pending表清0
void PrintPending()
{sigset_t pending;::sigpending(&pending);//输出型参数,输出pending表的信号集std::cout << "Pending: ";for (int signo = 31; signo > 0; signo--){if (sigismember(&pending, signo))//查看某个信号是否在信号集里{std::cout << 1;}else{std::cout << 0;}}std::cout << std::endl;
}void handler(int signo)
{static int cnt = 0;cnt++;while (true){std::cout << "get a sig: " << signo << ", cnt: " << cnt << std::endl;PrintPending();sleep(1);}
}int main()
{struct sigaction act, oact;//结构体act中,我们只需要关注两个变量,handler与sa_mask,自定义函数指针与信号集act.sa_handler = handler;sigemptyset(&act.sa_mask);//对位图进行清0sigaddset(&act.sa_mask, 3);//将3号信号加入到信号集中sigaddset(&act.sa_mask, 4);sigaddset(&act.sa_mask, 5);sigaddset(&act.sa_mask, 6);sigaddset(&act.sa_mask, 7);::sigaction(2, &act, &oact);//更改2号信号的处理方法,处理方法为打印block表,打印一次后退出,2号信号就不阻塞了,那么走到下面的while,位图恢复while (true){PrintPending();pause();}
}
//当我们第二次发送2号信号时,pending表的2号信号又重新置为1,只是因为我们的block表中2号信号置1了
代码编译完成,运行,可以看到,pending表全为0,说明是在handler表的方法处理中把pending表清0的
六、编译器的优化与volatile
在我们的编译器中,其实有一个优化功能,gcc test.c -O1/O2/O3
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
int flag = 0;void change(int signo) // 信号捕捉的执行流
{(void)signo;flag = 1;printf("change flag 0->1, getpid: %d\n", getpid());
}int main()
{printf("I am main process, pid is : %d\n", getpid());signal(2, change);while (!flag); // 主执行流--- flag我们没有做任何修改!printf("我是正常退出的!\n");
}
不做任何的优化 ,每一次while,都要将flag从内存放到寄存器中做逻辑运算
如果做优化,提前把flag从内存放到寄存器中,省略了缓存这一步骤,所以我们的while时一直跑的
但是不同的平台对编译器的优化有差别,有的默认就会做优化,有的默认是不做优化,为了解决这种情况,我们可以使用volatile关键字对变量就行修饰,它的作用与static相反
volatile int flag = 0;
七、通过信号解决父进程等待子进程
验证子进程退出时向父进程发送17号信号
void handler(int signo)
{std::cout<<"Get a signal: "<<signo<<std::endl;
}
int main()
{signal(17,handler);if(fork()==0){sleep(5);exit(0);}while(1);return 0;
}
我们知道,父进程是需要回收子进程的,所以我们也可以加上waitpid()
void handler(int signo)
{std::cout << "Get a signal: " << signo << std::endl;
}
int main()
{signal(17, handler);if (fork() == 0){sleep(5);exit(0);}while (1){//这里直接waitpidwaitpid(-1, nullptr, 0);//回收任意子进程,阻塞等待sleep(2);std::cout<<"子进程退出成功"<<std::endl;exit(1);}return 0;
}
但是这样写并不好看,因为我们在父进程中把回收子进程写的太过于明显,而且如果我们没有回收子进程,waitpid后面的代码就无法运行,也就是我们的子进程与父进程无法并行 ! 因此,我们可以基于子进程结束时向父进程发送信号这一原理,直接在信号的调用方法动刀
void handler(int signo)
{std::cout << "get a sig: " << signo << " I am : " << getpid() << std::endl;while (true){pid_t rid = ::waitpid(-1, nullptr, WNOHANG);//回收任意一个进程,非阻塞等待,这样就可以解决10个进程回收6个,4个还在运行的情况if (rid > 0){std::cout << "子进程退出了,回收成功,child id: " << rid << std::endl;}else if(rid == 0){std::cout << "退出的子进程已经被全部回收了" << std::endl;break;}else{std::cout << "wait error" << std::endl;break;}}
}
int main()
{signal(17,handler);//多个子进程都向父进程发信号for (int i = 0; i < 10; i++){if (fork() == 0){sleep(5);std::cout << "子进程退出" << std::endl;// 子进程exit(0);}}//父进程一直运行while(1);return 0;
}
最优解:
SIGCHLD的处理动作置为SIG_IGN,这样fork出来的⼦进程在终⽌时会⾃动清理掉,不 会产⽣僵⼫进程,也不会通知⽗进程。
signal(SIGCHLD,SIG_IGN);
相关文章:
Linux中信号的保存
一、认识信号的其他相关概念 实际执行信号的处理动作称为信号递达 信号从产生到递达之间的状态,称为信号未决 进程可以选择阻塞某个信号 被阻塞的信号产生时将保持在未决状态,直到进程解除对该信号的阻塞,才进行递达的动作 阻塞和忽略是不同的…...
2024ICPC 南京 B 生日礼物
题目: 格莱美的生日快到了,她从朋友那里得到了一个序列 A 作为礼物。这个序列只有 0 、 1 和 2 。格莱美认为这个数列太长了,因此她决定修改 A 使其更短。 从形式上看,格莱美可以执行任意数量的运算。每次她都可以从以下三种运算…...
扫地机器人进化史:从人工智障到家政王者
1996年,瑞典伊莱克斯推出的"三叶虫"开启了扫地机器人的纪元。这款售价2000美元的"初代机"工作时像喝醉的水手,随机碰撞的清扫方式让用户直呼"买了个寂寞"。谁能想到,这个当初被戏称为"人工智障"的发…...
C 语 言 --- 数 据 类 型 的 存 储
C 语 言 --- 数 据 类 型 的 存 储 空 类 型大 小 端 存 储大 端 存 储 --- 正 着 放(从 小 到 大)小 端 存 储 --- 倒 着 放(从 大 到 小) 浮 点 型 在 内 存 中 的 存 储总结 💻作 者 简 介:曾 与 你 一 …...
3.8 字符串的常用函数
重点:字符串的常用函数 #1.测试转换大小写 lower:大写->小写 upper:小写->大写 swapcase:自动将大写转小写小写转大写 print("ABC".lower()) #abcprint("abc".upper()) #ABCprint…...
事件触发控制与响应驱动控制的定义、种类及区别
一、定义 事件触发控制(Event-Triggered Control, ETC) 事件触发控制是一种基于动态条件触发的控制策略,其核心在于通过预设的事件触发条件(如系统状态误差超过阈值、特定信号到达等)来决定何时更新控制信号或进行通信…...
Android离屏渲染
写在前面 与iOS同事聊天时聊到圆角会使用离屏渲染的方式绘制,影响性能;Android上有没有不知道,学习了一下整理了这篇文章。 Android 圆角与离屏渲染(Offscreen Rendering) 一、什么是离屏渲染? 离屏渲染…...
35. 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1,3,5,6], target 5 输出: 2示例 2: 输入:…...
网络的起点:深入解析计算机网络中的网络接口层
一、什么是网络接口层? 计算机网络的 网络接口层(Network Interface Layer),在 TCP/IP模型 中处于最底层,负责将数据从计算机传输到物理网络媒介,并在此基础上确保数据的正确传输。它位于数据链路层和物理…...
智能指针(内存泄漏)
一、RALL RALL的核心概念: 资源获取即初始化:资源的分配与对象的初始化绑定在一起,资源的释放与对象的销毁绑定在一起。 不是所有智能指针都用这个思想 可以有效避免死锁问题 二、智能指针 通过将new出的对象教给有生命周期的对象处理。 在…...
秒杀秒抢系统开发:飞算 JavaAI 工具如何应对高并发难题?
秒杀、秒抢活动已成为电商促销与吸引流量的常用手段。然而,此类活动所带来的高并发访问,对系统性能构成了巨大挑战。如何确保系统在高并发场景下依然能够稳定、高效运行,成为开发者亟待解决的关键问题。飞算 JavaAI 工具作为一款功能强大的开…...
AI在代码Review中的应用试验与推广策略
一、引言 在软件开发领域,代码审查(Code Review)是保障代码质量的关键环节。传统的代码审查依赖人工,不仅效率低下,而且容易出现疏漏。随着人工智能技术的发展,AI在代码审查中的应用逐渐成为可能。近期&am…...
自定义Jackson序列化和反序列化
为什么需要自定义Jackson序列化和反序列化?下面举个例子看看就知道了 问题描述 由于 Spring Boot 内置使用的就是 Jackson JSON 框架,所以,无需引入新的依赖,仅需添加自定义配置类即可,让其支持新的日期 API Controller层的代码…...
人工智能浪潮与生成式人工智能认证(GAI认证)的登场
当ChatGPT在2023年引爆全球对话时,人类已悄然站在人工智能发展史上的关键转折点。生成式人工智能(Generative AI)不再仅仅是实验室中的技术概念,而是正在重塑内容创作、商业决策、教育医疗等领域的底层逻辑。这场技术浪潮的冲击力,不仅体现在算法模型的指数级进化上,更在…...
Redis - 讲清楚集群模式(Redis Cluster)(上)
节点与集群构建 节点组成:Redis集群由多个独立节点组成,通过CLUSTER MEET命令实现节点握手,形成集群。 握手过程: 节点A为节点B创建clusterNode结构,发送MEET消息。 节点B接收后创建节点A的clusterNode结构ÿ…...
什么是 矩阵号 ?为什么要做海外矩阵?
简单说,就是——在海外平台批量搞账号/店铺,组团赚钱! 比如这样玩👇 ● TikTok 矩阵号:5 个账号,分别做美妆、3C、家居,覆盖不同老外兴趣圈。 ● 亚马逊矩阵店铺:A 店卖手机壳&#…...
10 穴 汽车连接器的15个设计特点
汽车行业严重依赖卓越的电气系统来确保功能和可靠性。这些系统的关键组件是 10 腔连接器,它为布线和信号传输提供解决方案。制造商和工程师必须仔细评估这些连接器的设计特性,以优化性能和安全性。 本博客研究了汽车 10 腔连接器的 15 个设计特征&#…...
WHAT - 动态导入模块遇到版本更新解决方案
文章目录 一、动态导入模块二、常见原因与解决方案1. 模块 URL 错误2. 开发人员发版用户停留在旧页面问题背景解决方案思路1. 监听错误,提示用户刷新2. 使用缓存控制策略:强制刷新3. 动态模块加载失败时兜底4. 使用 import.meta.glob() 或 webpack 的 __…...
4.15学习总结
学习了IO流和相关的字符集 完成一道算法题...
THCON 2025
Crypto OTPas_ouf 用10个字符异或加密的jpg图片,通过头得到key再恢复原图 Mammoths Personnal Slot Machine 梅森旋转恢复 from pwn import * from randcrack import RandCrack from tqdm import trange context.log_level errorp remote(74.234.198.209, 33…...
Linux 深入浅出信号量:从线程到进程的同步与互斥实战指南
知识点1【信号量概述】 信号量是广泛用于进程和线程间的同步和互斥。信号量的本质 是一个非负的整数计数器,它被用来控制对公共资源的访问 当信号量值大于0的时候,可以访问,否则将阻塞。 PV原语对信号量的操作,一次P操作使信号…...
github配置ssh,全程CV
1)随便找一个文件夹右键进入git bash 2)验证是否已有公私钥文件 cd ~/.ssh ls如果不存在则生成然后获取 生成时一直回车 ssh-keygen -t rsa -C "xxxxxx.com" cd ~/.ssh cat id_rsa.pub如果存在则直接获取 cd ~/.ssh cat id_rsa.pub3)复制 4…...
MySQL——存储
一、什么是存储过程 存储过程(Stored Procedure) 是预编译并存储在数据库中的一段SQL代码集合,支持参数传递、流程控制和返回值。通过类似“方法调用”的方式执行,存储过程将复杂业务逻辑封装在数据库层,简化应用开发…...
matlab中进行海浪模型仿真
matlab中进行海浪模型仿真,采用优化处理算法,进行防止干扰的海浪算法设计 BarhPlot.m , 180 wave.m , 1649...
边缘计算与隐私计算的融合:构建数据经济的“隐形护盾“
在数据成为核心生产要素的今天,边缘计算与隐私计算的交汇正在重塑技术生态。这并非简单的技术叠加,而是一场关于数据主权、算力分配与信任机制的深度博弈。本文将从"数据流动的拓扑学"视角,探讨二者融合如何重构数字社会的基础设施…...
实现表单验证
给Form.ITem组件绑定 name和rules字段 #增加表单验证的触发事件 失焦 onblur 添加多条验证逻辑 串行验证逻辑 实现表单提交验证 获得的值的属性名由form组件中的name属性决定 如果表单验证通过自动触发属性onFinish绑定的回调函数获得提交内容 封装request模块...
图论-BFS搜索图/树-最短路径问题的解决
续上篇~图论--DFS搜索图/树-CSDN博客 先看第一次学习的博客!!👇👇👇👇 👉 有一些问题是广搜 和 深搜都可以解决的,例如岛屿问题,这里我们记dfs的写法就好啦,…...
大数据学习(107)-sql中case使用场景
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一…...
数据战略新范式:从中台沉淀到服务觉醒,SQL2API 如何重塑数据价值链条?
一、数据中台退烧:从 “战略神话” 到 “现实拷问” 曾几何时,数据中台被视为企业数字化转型的 “万能解药”,承载着统一数据资产、打破业务壁垒的厚望。然而,大量实践暴露出其固有缺陷:某零售企业投入 500 万元建设中…...
MyBatis SqlSessionFactory 批量执行实战
在 MyBatis 中,批量操作是处理高并发数据写入的核心场景之一。通过 SqlSessionFactory 配置批处理执行器(ExecutorType.BATCH),可以显著提升数据库操作的效率。本文将结合 Spring 框架,深入解析如何高效配置和使用 MyB…...
【初阶数据结构】——算法复杂度
一、前言 1、数据结构是什么? 数据结构(Data Structure)是计算机存储、组织数据的⽅式,指相互之间存在⼀种或多种特定关系的数 据元素的集合。没有⼀种单⼀的数据结构对所有⽤途都有⽤,所以我们要学各式各样的数据结构, 如&…...
Oracle数据库数据编程SQL<00. 课外关注:rownum、rowid、level、row_number 对比详解与实战>
更多Oracle学习内容请查看:Oracle保姆级超详细系列教程_Tyler先森的博客-CSDN博客 目录 一、基本概念与区别 二、ROWNUM 详解与实战 1. 基本特性 2. 典型应用 2.1 分页查询(Oracle传统方式) 2.2 限制返回行数 2.3 随机抽样 3. 注意事…...
凸优化基础
文章目录 目录**第1讲:凸优化基础****第2讲:凸优化建模****第3讲:对偶理论****第4讲:梯度下降法****第5讲:牛顿法与内点法****第6讲:次梯度与近端方法****第7讲:分布式凸优化****第8讲ÿ…...
LeetCode面试热题150中12-18题学习笔记(用Java语言描述)
Day 03 12、 O ( 1 ) O(1) O(1)时间插入、删除元素和获取元素 需求:实现RandomizedSet 类: RandomizedSet() 初始化 RandomizedSet 对象bool insert(int val) 当元素 val 不存在时,向集合中插入该项,并返回 true ;否…...
开源模型集成接口
一、OpenRouter 概述 OpenRouter是一个开源的大模型API路由器,旨在将各种AI模型和服务集成到一个统一的接口中,使用户能够通过简单的配置调用不同大模型的能力。其主要功能包括智能路由用户请求到不同的AI模型,并提供统一的访问接…...
python成功解决AttributeError: can‘t set attribute ‘lines‘
文章目录 报错信息与原因分析解决方法示例代码代码解释总结 报错信息与原因分析 在使用 matplotlib绘图时,若尝试使用 ax.lines []来清除图表中的线条,会遇到AttributeError: can’t set attribute错误。这是因为 ax.lines是一个只读属性,不…...
宿舍管理系统(servlet+jsp)
宿舍管理系统(servletjsp) 宿舍管理系统是一个用于管理学生宿舍信息的平台,支持超级管理员、教师端和学生端三种用户角色登录。系统功能包括宿舍管理员管理、学生管理、宿舍楼管理、缺勤记录、添加宿舍房间、心理咨询留言板、修改密码和退出系统等模块。宿舍管理员…...
Unity UI 从零到精通 (第30天): Canvas、布局与C#交互实战
Langchain系列文章目录 01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南 02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖 03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南 04-玩转 LangChai…...
vue项目打包部署到maven仓库
需要的资源文件,都放在根目录下: 1. versionInfo.js const fs require(fs) const path require(path) const mkdirp require(mkdirp) const spawn require(child_process).spawnconst packageObj require(./package.json) const versionNo packa…...
【力扣】day1
文章目录 27.移除元素26. 删除有序数组的重复项 27.移除元素 26. 删除有序数组的重复项 我们仔细看一下这两道题的最后的返回值,为什么第一题返回slow 而第二题返回slow1 最后的返回值该如何返回绝对不是凭感觉,我们自己分析一下第一个slow,从0位置开始, 遇到val值就开始和fas…...
MySQL:B+树索引
InnoDB索引方案 为了使用二分法快速定位具体的目录项,假设所有目录项都可以在物理存储器上连续存储,有以下问题: InnoDB使用页为管理存储空间的基本单位,最多只能保证16KB的连续存储空间,记录数据量多可能需要非常大…...
如何建立可复用的项目管理模板
建立可复用的项目管理模板能够显著提高项目执行效率、减少重复劳动、确保项目管理标准化。在企业中,项目管理往往涉及多个步骤和多个团队,然而每次开始一个新项目时,如果都从头开始设计流程和文档,势必浪费大量的时间和精力。通过…...
Go:goroutine 和通道
goroutine f() // 等待 f() 返回 go f() // 新建一个调用 f() 的 goroutine,不用等待在 Go 语言里,goroutine 是并发执行的活动单元。与顺序执行程序不同,在有多个 goroutine 的并发程序中,不同函数可同时执行。程序启动时&…...
盛水最多的容器问题详解:双指针法与暴力法的对比与实现
文章目录 问题描述方法探讨方法一:暴力法(Brute Force)思路代码实现复杂度分析 方法二:双指针法(Two Pointers)思路正确性证明代码实现复杂度分析 方法对比总结 摘要 盛水最多的容器(Container …...
VMWare 16 PRO 安装 Rocky8 并部署 MySQL8
VMWare 16 PRO 安装 Rocky8 并部署 MySQL8 一.Rocky OS 下载1.官网 二.配置 Rocky1.创建新的虚拟机2.稍后安装系统3.选择系统模板4.设置名字和位置5.设置大小6.自定义硬件设置核心、运存和系统镜像7.完成 三.启动安装1.上下键直接选择安装2.回车安装3.设置分区(默认…...
日常学习开发记录-slider组件
日常学习开发记录-slider组件 从零开始实现一个优雅的Slider滑块组件前言一、基础实现1. 组件结构设计2. 基础样式实现3. 基础交互实现 二、功能增强1. 添加拖动功能2. 支持范围选择3. 添加垂直模式 三、高级特性1. 键盘操作支持2. 禁用状态 五、使用示例六、总结 从零开始实现…...
AIDL 中如何传递 Parcelable 对象
目录 1. 直接在 AIDL 中定义 Parcelable 对象2. 自定义 Parcelable 对象的传递3. 以 Rect 类为例的 Parcelable 实现4. 注意安全性5. 小结1. 直接在 AIDL 中定义 Parcelable 对象 背景说明 从 Android 10(API 级别 29)开始,AIDL 允许直接在 .aidl 文件中定义 Parcelable 对…...
LVGL实战训练——计算器实现
目录 一、简介 二、部件知识 2.1 按钮矩阵部件(lv_btnmatrix) 2.1.1 按钮矩阵部件的组成 2.1.2 按钮文本设置 2.1.3 按钮索引 2.1.4 按钮宽度 2.1.5 按钮属性 2.1.6 按钮互斥 2.1.7 按钮文本重着色 2.1.8 按钮矩阵部件的事件 2.1.9 按钮矩阵部件的 API 函数 2.2…...
代码随想录算法训练营Day30
力扣452.用最少数量的箭引爆气球【medium】 力扣435.无重叠区间【medium】 力扣763.划分字母区间【medium】 力扣56.合并区间【medium】 一、力扣452.用最少数量的箭引爆气球【medium】 题目链接:力扣452.用最少数量的箭引爆气球 视频链接:代码随想录 题…...
AIDL 语言简介
目录 软件包类型注释导入AIDL 的后端AIDL 语言大致上基于 Java 语言。AIDL 文件不仅定义了接口本身,还会定义这个接口中用到的数据类型和常量。 软件包 每个 AIDL 文件都以一个可选软件包开头,该软件包与各个后端中的软件包名称相对应。软件包声明如下所示: package my.pac…...