【Linux】信号:信号保存和处理

🔥个人主页:Quitecoder
🔥专栏:linux笔记仓

目录
- 01.阻塞信号
- 信号集
- 02.捕捉信号
- sigaction
- 可重入函数
- volatile
- SIGCHLD
01.阻塞信号
- 实际执行信号的处理动作称为信号递达:每个信号都有一个默认行为,例如终止进程、忽略信号或生成 Core Dump,进程可以为信号注册自定义处理函数
- 信号从产生到递达之间的状态,称为信号未决(Pending)
- 进程可以选择阻塞 (Block )某个信号:被阻塞的信号产生时将一直保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作
- 忽略信号:信号递达后,进程选择不采取任何行动
- 注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作
如果一个信号阻塞了,和它有没有未决没有关系

signal(2,handler);
signal(2,SIG_IGN);
signal(2,SIG_DFL);
三种处理方式:自定义捕捉,忽略信号,默认行为
pending位图表(32位),比特位的位置:代表信号编号,比特位的内容:代表信号是否收到
handler信号处理表:一个数组,其中每个条目对应一个信号编号,并记录该信号的处理方式
block位图表:与pending类型一样,比特位位置代表信号编号,内容代表信号是否阻塞
每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。在上图的例子中,SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作
SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
sighandler_t就是函数指针类型,handler表就是一个函数指针数组

每一个信号的编号,相当于该数组的下标
signal(2,handler)就是找到2号信号函数指针数组的索引,传入我们自己写的handler函数的地址
信号集
从上图来看,每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。 阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略
sigset_t的定义
sigset_t是一个不透明的数据类型,通常定义为一个位掩码。具体实现可能因操作系统而异,但它的本质是一个整数或结构体,用于存储信号的状态。
在 Linux 中,sigset_t 通常定义如下:
typedef struct {unsigned long sig[NSIG / (8 * sizeof(unsigned long))];
} sigset_t;
其中,NSIG 是系统中信号的总数。
sigset_t 主要用于以下系统调用:
• 阻塞信号:sigprocmask() 使用 sigset_t 来设置或修改进程的信号掩码。
• 检查挂起信号:sigpending() 使用 sigset_t 来获取当前挂起的信号集。
• 设置信号处理函数:sigaction() 使用 sigset_t 来指定在信号处理函数执行期间需要阻塞的信号。
操作 sigset_t 的函数
以下是一些用于操作 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置位(置一),表示 该信号集的有效信号包括系统支持的所有信号。
注意,在使用sigset_ t类型的变量之前,一定要调 用sigemptyset或sigfillset做初始化,使信号集处于确定的状态。初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信号
这四个函数都是成功返回0,出错返回-1。sigismember是一个布尔函数,用于判断一个信号集的有效信号中是否包含
某种 信号,若包含则返回1,不包含则返回0,出错返回-1
sigprocmask
调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
返回值:若成功则为0,若出错则为-1
how取值

sigpending
#include <signal.h>
int sigpending(sigset_t *set);
获取当前进程的未决信号集,通过set参数传出,pending表的内容是由内核根据信号的发送和阻塞状态自动管理的,因此不需要提供直接修改挂起信号表的系统调用,这里set为输出型参数
sigpending() 会将内核中保存的挂起信号数据复制到用户提供的 sigset_t 变量中
示例
以下是一个使用 sigset_t 的示例,展示了如何阻塞和解除阻塞信号:
#include <unistd.h>
#include <iostream>
#include <signal.h>using namespace std;// 信号处理函数
void handler(int signum) {cout << "Caught SIGINT (signal number: " << signum << ")" << endl;
}int main() {// 注册信号处理函数signal(SIGINT, handler);// 保存旧的信号掩码sigset_t oldset;// 阻塞 SIGINT 信号sigset_t mask;sigemptyset(&mask);sigaddset(&mask, SIGINT);if (sigprocmask(SIG_BLOCK, &mask, &oldset) == -1) {perror("sigprocmask");return 1;}cout << "SIGINT is blocked. Press Ctrl+C within 5 seconds to send SIGINT." << endl;sleep(5);// 检查挂起的信号sigset_t pending;if (sigpending(&pending) == -1) {perror("sigpending");return 1;}if (sigismember(&pending, SIGINT)) {cout << "SIGINT is pending." << endl;} else {cout << "SIGINT is not pending." << endl;}// 恢复旧的信号掩码if (sigprocmask(SIG_SETMASK, &oldset, NULL) == -1) {perror("sigprocmask");return 1;}cout << "SIGINT is unblocked. Pending signal will be handled." << endl;return 0;
}
这里来处理二号信号
oldset 是一个 sigset_t 类型的指针,用于保存当前的信号掩码,如果需要恢复之前的信号掩码,可以将 oldset 传递给 sigprocmask()

pending位图对应的信号清零是在递达之前清零的
02.捕捉信号
信号可能不会立即被处理,在合适的时候进行处理:进程从内核态返回到用户态的时候进行处理

操作系统不能直接转过去执行用户提供的handler方法,必须使用用户身份来执行handler
如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下:用户程序注册了SIGQUIT信号的处理函数sighandler。 当前正在执行main函数,这时发生中断或异常切换到内核态。在中断处理完毕后要返回用户态的main函数之前检查到有信号SIGQUIT递达。 内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函数,sighandler和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是 两个独立的控制流程。sighandler函数返回后自动执行特殊的系统调用sigreturn再次进入内核态。如果没有新的信号要递达,这次再返回用户态就是恢复main函数的上下文继续执行了

内核态是 CPU 的一种运行模式,在这种模式下:
• 代码可以执行所有特权指令(如直接操作硬件、修改内存管理单元等)。
• 可以访问整个系统的内存空间(包括用户空间和内核空间)。
• 操作系统内核运行在内核态,负责管理硬件资源、进程调度、内存管理等核心功能。
| 特性 | 内核态 | 用户态 |
|---|---|---|
| 权限 | 最高权限,可以执行所有指令 | 受限权限,只能执行非特权指令 |
| 内存访问 | 可以访问整个系统的内存空间 | 只能访问用户空间的内存 |
| 硬件访问 | 可以直接操作硬件 | 不能直接操作硬件,必须通过系统调用 |
| 运行代码 | 操作系统内核代码 | 用户程序代码 |
| 安全性 | 不受限制,可能影响系统稳定性 | 受限制,不会直接破坏系统 |
用户程序通常运行在用户态,当需要执行特权操作时,必须通过 系统调用(System Call) 切换到内核态。
为了确保系统的安全性和稳定性,操作系统通过以下方式隔离内核态和用户态:
• 特权级别:CPU 提供了不同的特权级别(如 x86 架构的 Ring 0 和 Ring 3),内核态运行在最高特权级别(Ring 0),用户态运行在最低特权级别(Ring 3)。
• 内存保护:通过内存管理单元(MMU)隔离用户空间和内核空间,防止用户程序访问内核内存。
• 系统调用接口:用户程序必须通过系统调用接口访问内核功能,不能直接执行特权指令。

0-3GB是给用户用的,有用户级页表,3-4有内核级页表,核心工作时将内核地址空间和操作系统之间进行映射的
意味着操作系统本身就在我们的地址空间中
进程中,用户级页表有很多份,但是内核级页表只有一份,我们通过访问3-4的地址空间可以找到OS所有代码和数据
我们访问操作系统,其实还是在我们的地址空间中进行的,和我们访问库函数没区别
用户访问3-4地址空间,只能通过系统调用

键盘输入数据会向cpu发送硬件中断,内存加载时会加载函数指针数组,有的会被预先加载,比如有读磁盘的,读网卡的,每一种设备有自己的中断号,终端号就是该数组的数组下标。开机的时候,操作系统先把这张表加载到内存
cpu执行的代码就是OS的代码,把数据从外设读到内存,键盘有数据会通过硬件中断告诉cpu,cpu得到中断号来执行操作系统方法
我们学习的信号就是模拟中断实现的
操作系统本质就是一个死循环+时钟中断 不断调度系统的任务的

sigaction
前面我们用signal进行捕捉
#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回0,出错则返回- 1。signo是指定信号的编号。若act指针非空,则根据act修改该信号的处理动作。若oact指针非 空,则通过oact传出该信号原来的处理动作。act和oact指向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); // 未使用
};
sigaction() 的使用步骤
(1)定义信号处理函数
编写一个函数来处理信号,例如:
void handler(int signum) {printf("Caught signal %d\n", signum);
}
(2)设置 struct sigaction
初始化 struct sigaction 结构体,指定信号处理函数和其他行为:
struct sigaction sa;
sa.sa_handler = handler; // 指定信号处理函数
sigemptyset(&sa.sa_mask); // 清空阻塞信号集
sa.sa_flags = 0; // 无特殊标志
(3)调用 sigaction()
调用 sigaction() 设置信号处理行为:
if (sigaction(SIGINT, &sa, NULL) == -1) {perror("sigaction");return 1;
}
#include <stdio.h>
#include <signal.h>
#include <unistd.h>// 信号处理函数
void handler(int signum) {printf("Caught signal %d\n", signum);
}int main() {// 设置信号处理行为struct sigaction sa;sa.sa_handler = handler; // 指定信号处理函数sigemptyset(&sa.sa_mask); // 清空阻塞信号集sa.sa_flags = 0; // 无特殊标志if (sigaction(SIGINT, &sa, NULL) == -1) {perror("sigaction");return 1;}printf("Press Ctrl+C to send SIGINT.\n");while (1) {sleep(1);}return 0;
}
如果当前正在对2号信号进行处理。默认2号信号会被自动屏蔽,对2号信号处理完成的时候,会自动解除对二号信号的屏蔽
可重入函数

可重入函数是指在执行过程中可以被中断,并且在中断后再次调用时仍能正确执行的函数
main函数调用insert函数向一个链表head中插入节点node1,插入操作分为两步,刚做完第一步的 时候,因为硬件中断使进程切换到内核,再次回用户态之前检查到有信号待处理,于是切换 到sighandler函数,sighandler也调用insert函数向同一个链表head中插入节点node2,插入操作的 两步都做完之后从sighandler返回内核态,再次回到用户态就从main函数调用的insert函数中继续 往下执行,先前做第一步之后被打断,现在继续做完第二步。结果是,main函数和sighandler先后 向链表中插入两个节点,而最后只有一个节点真正插入链表中了。
像上例这样,insert函数被不同的控制流程调用,有可能在第一次调用还没返回时就再次进入该函数,这称为重入,insert函数访问一个全局链表,有可能因为重入而造成错乱,像这样的函数称为不可重入函数,反之,如果一个函数只访问自己的局部变量或参数,则称为可重入(Reentrant) 函数。想一下,为什么两个不同的控制流程调用同一个函数,访问它的同一个局部变量或参数就不会造成错乱?
如果一个函数符合以下条件之一则是不可重入的:
- 调用了malloc或free,因为malloc也是用全局链表来管理堆的。
- 调用了标准I/O库函数。标准I/O库的很多实现都以不可重入的方式使用全局数据结构
volatile
#include <stdio.h>
#include <signal.h>
int flag = 0;
void handler(int sig)
{printf("chage flag 0 to 1\n");flag = 1;
}
int main()
{signal(2, handler);while(!flag);printf("process quit normal\n");return 0;
}
标准情况下,键入 CTRL-C ,2号信号被捕捉,执行自定义动作,修改 flag=1 , while 条件不满足,退出循环,进程退出
^Cchage flag 0 to 1
^Cchage flag 0 to 1
^Cchage flag 0 to 1
优化情况下,键入 CTRL-C ,2号信号被捕捉,执行自定义动作,修改 flag=1 ,但是 while 条件依旧满足,进程继续运行!但是很明显flag肯定已经被修改了,但是为何循环依旧执行?很明显, while 循环检查的flag,并不是内存中最新的flag,这就存在了数据二异性的问题。 while 检测的flag其实已经因为优化,被放在了CPU寄存器当中。如何解决呢?很明显需要 volatile
volatile 作用:保持内存的可见性,告知编译器,被该关键字修饰的变量,不允许被优化,对该变量的任何操作,都必须在真实的内存中进行操作
SIGCHLD
进程一章讲过用wait和waitpid函数清理僵尸进程,父进程可以阻塞等待子进程结束,也可以非阻塞地查询是否有子进程结束等待清理(也就是轮询的方式)。采用第一种方式,父进程阻塞了就不 能处理自己的工作了;采用第二种方式,父进程在处理自己的工作的同时还要记得时不时地轮询一 下,程序实现复杂。其实,子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自定义SIGCHLD信号的处理函数,这样父进程只需专心处理自己的工作,不必关心子进程了,子进程 终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void handler(int sig)
{pid_t id;while ((id = waitpid(-1, NULL, WNOHANG)) > 0){printf("wait child success: %d\n", id);}printf("child is quit! %d\n", getpid());
}
int main()
{signal(SIGCHLD, handler);pid_t cid;if ((cid = fork()) == 0){ // childprintf("child : %d\n", getpid());sleep(3);exit(1);}while (1){printf("father proc is doing some thing!\n");sleep(1);}return 0;
}
相关文章:
【Linux】信号:信号保存和处理
🔥个人主页:Quitecoder 🔥专栏:linux笔记仓 目录 01.阻塞信号信号集 02.捕捉信号sigaction可重入函数volatileSIGCHLD 01.阻塞信号 实际执行信号的处理动作称为信号递达:每个信号都有一个默认行为,例如终…...
应用权限组列表
文章目录 使用须知位置相机麦克风通讯录日历运动数据身体传感器图片和视频音乐和音频跨应用关联设备发现和连接剪切板文件夹文件(deprecated) 使用须知 在申请目标权限前,建议开发者先阅读应用权限管控概述-权限组和子权限,了解相关概念,再合…...
MATLAB实现基于“蚁群算法”的AMR路径规划
目录 1 问题描述 2 算法理论 3 求解步骤 4 运行结果 5 代码部分 1 问题描述 移动机器人路径规划是机器人学的一个重要研究领域。它要求机器人依据某个或某些优化原则 (如最小能量消耗,最短行走路线,最短行走时间等),在其工作空间中找到一…...
【深度学习】多目标融合算法(五):定制门控网络CGC(Customized Gate Control)
目录 一、引言 二、CGC(Customized Gate Control,定制门控网络) 2.1 技术原理 2.2 技术优缺点 2.3 业务代码实践 2.3.1 业务场景与建模 2.3.2 模型代码实现 2.3.3 模型训练与推理测试 2.3.4 打印模型结构 三、总结 一、引言 上一…...
【NLP 42、实践 ⑪ 用Bert模型结构实现自回归语言模型的训练】
如果结局早已注定,那么过程就将大于结局 —— 25.3.18 自回归语言模型:由前文预测后文的语言模型 特点:单向 训练方式:利用前n个字预测第n1个字,实现一个mask矩阵,送入Bert模型,让其前文看不到…...
TCP | 序列号和确认号 [逐包分析] | seq / ack 详解
注 : 本文为 “TCP 序号(seq)与确认序号(ack)” 相关文章合辑。 英文引文,机翻未校。 中文引文,略作重排。 如有内容异常,请看原文。 Understanding TCP Seq & Ack Numbers […...
在Linux、Windows系统上安装开源InfluxDB——InfluxDB OSS v2并设置开机自启的保姆级图文教程
一、进入InfluxDB下载官网 InfluxData 文档https://docs.influxdata.com/Install InfluxDB OSS v2 | InfluxDB OSS v2 Documentation...
考研复习之队列
循环队列 队列为满的条件 队列为满的条件需要特殊处理,因为当队列满时,队尾指针的下一个位置应该是队头指针。但是,我们不能直接比较 rear 1 和 front 是否相等,因为 rear 1 可能会超出数组索引的范围。因此,我们需…...
Day 21: 数组中的逆序对
在股票交易中,如果前一天的股价高于后一天的股价,则可以认为存在一个「交易逆序对」。请设计一个程序,输入一段时间内的股票交易记录 record,返回其中存在的「交易逆序对」总数。 示例 1: 输入:record [9…...
【C++教程】setw()函数的使用方法
setw 是 C 中用于设置输出字段宽度的函数,属于 <iomanip> 头文件。以下是其使用方法及注意事项: 基本用法 包含头文件: #include <iostream> #include <iomanip> // 必须包含此头文件 using namespace std; // 避免写 std…...
智慧高速,安全护航:视频监控平台助力高速公路高效运营
随着我国高速公路里程的不断增长,交通安全和运营效率面临着前所未有的挑战。传统的监控方式已难以满足现代化高速公路管理的需求,而监控视频平台的出现,则为高速公路的安全运营提供了强有力的技术支撑。高速公路视频监控联网解决方案 高速公路…...
Jboss漏洞再现
一、CVE-2015-7501 1、开环境 2、访问地址 / invoker/JMXInvokerServlet 出现了让下载的页面,说明有漏洞 3、下载ysoserial工具进行漏洞利用 4、在cmd运行 看到可以成功运行,接下来去base64编码我们反弹shell的命令 5、执行命令 java -jar ysoserial-…...
C语言三大程序结构 单分支语句
核心概念:程序就像流水线,通过顺序、选择、循环三种结构完成复杂任务 🌟 一、三大程序结构图解 结构类型形象比喻代码示例顺序直行马路 → 不拐弯printf("A"); printf("B");选择岔路口 → 二选一if...else循环环形跑道 …...
【Linux系统】Linux权限讲解!!!超详细!!!
目录 Linux文件类型 区分方法 文件类型 Linux用户 用户创建与删除 用户之间的转换 su指令 普通用户->超级用户(root) 超级用户(root) ->普通用户 普通账户->普通账户 普通用户的权限提高 sudo指令 注: Linux权限 定义 权限操作 1、修改文…...
Winform零基础从入门到精通(5)——WinForm菜单与工具栏开发详解
一、核心控件与功能 MenuStrip(顶部菜单栏) • 功能:创建应用程序主菜单,支持多级子菜单和快捷键。 • 关键操作: ◦ 添加菜单项:直接在菜单栏输入文字(如“文件(F)”),…...
2.创建Collection、添加索引、加载内存、预览和搜索数据
milvus官方文档 milvus2.3.1的官方文档地址: https://milvus.io/docs/v2.3.x 使用attu创建collection collection必须要有一个主键字段、向量字段 确保字段类型与索引类型兼容 字符串类型(VARCHAR)通常需要使用 Trie 索引,而不是 AutoInd…...
AIGC 新势力:探秘海螺 AI 与蓝耘 MaaS 平台的协同创新之旅
探秘海螺AI:多模态架构下的认知智能新引擎 在人工智能持续进阶的进程中,海螺AI作为一款前沿的多功能AI工具,正凭借其独特的多模态架构崭露头角。它由上海稀宇科技有限公司(MiniMax)精心打造,依托自研的万亿…...
一文解读DeepSeek在法律商业仲裁细分行业的应用
引言 当AI闯入法律界:DeepSeek如何把商业仲裁变成“纠纷快车道” AI技术正在像水电煤一样渗透生活,随着DeepSeek的爆火出圈,全国各行各业都在如火如荼地接入DeepSeek,以期望利用DeepSeek的“超能力”来重塑各自行业的效能和格局&a…...
快速入手-基于Django的主子表间操作mysql(五)
1、如果该表中存在外键,结合实际业务情况,那可以这么写: 2、针对特殊的字典类型,可以这么定义 3、获取元组中的字典值和子表中的value值方法 4、对应的前端页面写法...
HTTPS协议—加密算法和中间攻击人的博弈
活动发起人小虚竹 想对你说: 这是一个以写作博客为目的的创作活动,旨在鼓励大学生博主们挖掘自己的创作潜能,展现自己的写作才华。如果你是一位热爱写作的、想要展现自己创作才华的小伙伴,那么,快来参加吧!…...
【大模型理论篇】CogVLM:多模态预训练语言模型
1. 模型背景 前两天我们在《Skywork R1V: Pioneering Multimodal Reasoning with Chain-of-Thought》中介绍了将ViT与推理模型结合构造多模态推理模型的案例,其中提到了VLM的应用。追溯起来就是两篇前期工作:Vision LLM以及CogVLM。 今天准备回顾一下Cog…...
AI知识补全(一):tokens是什么?
名人说:苔花如米小,也学牡丹开。——袁枚《苔》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 一、什么是Tokens?二、为什么Tokens如此重要?1.模型的输入输出限制2.…...
Wpf Avalonia-实现中英文切换工程
文章目录 language工程项目代码创建获取资源文件string工程图片主项目引用LanguageView中使用ViewModel中使用language工程项目 <Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><ImplicitUsings>enable</ImplicitUsings><TargetFrame…...
pyqt5报错:qt.qpa.plugin: Could not find the Qt platform plugin “xcb“(已解决)
我在使用pyqt库的时候报错: qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in \ "/mnt/private_disk/anaconda3/envs/aot-manip/lib/python3.8/site-packages/PyQt5/Qt5/plugins/platforms" even though it was found. This ap…...
【MySQL数据库】触发器与事件
MySQL触发器 trigger,在表的插入insert、更新update、删除delete操作发生时自动执行MySQL语句。 学过Qt的都知道信号槽,一旦发出某个信号,那么就会触发关联的信号槽函数。触发器就类似于这个操作。 创建触发器时需要给出一些信息ÿ…...
【LC插件开发】基于Java实现FSRS(自由间隔重复调度算法)
😊你好,我是小航,一个正在变秃、变强的文艺倾年。 🔔本文讲解【LC插件开发】基于Java实现FSRS(自由间隔重复调度算法),期待与你一同探索、学习、进步,一起卷起来叭! 目录…...
java后端接收数组,数组长度超256个就会报错
1.原因 DataBinder 中默认限制了list最大只能增长到256。 2.解决方案 1.在BaseController添加InitBinder方法,其余继承BaseController InitBinder //类初始化是调用的方法注解public void initBinder(WebDataBinder binder) {//给这个controller配置接收list的长…...
第45章:配置更新与应用热重载策略
第45章:配置更新与应用热重载策略 作者:DogDog_Shuai 阅读时间:约25分钟 难度:中级 目录 1. 引言2. 配置更新挑战3. Kubernetes原生配置更新机制4. 应用热重载技术5. 配置更新最佳实践...
数据库MVCC详解
MVCC 1.基本介绍 数据库:MySQL。【很多主流数据库都使用了MVCC,比如MySQL的InnoDB引擎、PostgreSQL、Oracle】 MVCC,全称Multi-Version Concurrency Control,即多版本并发控制。是数据库管理系统中的一种并发控制方法。 MVCC的…...
MySQL数据库基础篇
目录 SQL的分类 数据定义语言(DDL)---Data Definition Language 数据操作语言(DML) ---Data Manipulation Language 数据查询语言(DQL) ---Data Query Language 数据控制语言(DCL) ---Data Control Language 事务控制语言(TCL) --- Transaction Cont…...
