【操作系统】信号Signal超详解|捕捉函数

🔥博客主页: 我要成为C++领域大神
🎥系列专栏:【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】
❤️感谢大家点赞👍收藏⭐评论✍️

本博客致力于知识分享,与更多的人进行学习交流

如何触发信号

信号是Linux下的经典技术,一般操作系统利用信号杀死违规进程,典型进程干预手段,信号除了杀死进程外也可以挂起进程

kill -l 查看系统支持的信号

32,33号信号预留给线程库NTPL

两种信号,经典信号/实时信号

1-31是unix经典信号,软件开发工程师使用,例如进程通信,信号捕捉等。

34-64是自定义信号,一般驱动开发使用,偏底层。

1、终端组合按键触发信号

Ctrl+/(SIGQUIT/3)系统向唯一的前台进程发送2号信号,目标进程被杀死

Ctrl+C(SIGINT/2)系统向唯一的前台进程发送2号信号,目标进程被杀死

Ctrl+Z(SIGTSTP/20)系统向唯一的前台进程发送20号信号,目标进程被挂起

终端组合按键触发的信号会发给唯一的前台进程

写一个无法退出的死循环,使用组合键杀死这个进程

按下Ctrl+C成功杀死这个死循环进程

2、命令发送信号

kill -signo pid

此命令可以向任意进程发送任意信号

kill命令成功杀死进程

3、函数发送信号

使用信号函数需要包含头文件 signal.h

常见信号函数:

kill(pid_t,int signo) 向任意进程发送任意信号

raise(int signo) 向自身进程发送任意信号

void abort(void) 向自身发送SIGABRT/6信号

关于kill函数的使用:

kill函数的第一个参数是进程ID,第二个参数是信号编号。这与kill命令正好相反,所以为了符合使用逻辑,在传入命令行参数时,将命令行第二个参数作为kill函数的第一个参数


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <signal.h>int main(int argc,char **argv)
{if(argc<3){printf("参数数量错误\n");exit(0);}kill(atoi(argv[2]),atoi(argv[1]));return 0;
}

自定义kill命令杀死进程

4、硬件异常产生信号

1)对只读内存进行写操作,属于违规操作硬件,系统向违规进程发送SIGSEGV(11)信号,段错误,杀死违规进程。

例如:当我们尝试修改常量区内存时:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>int main()
{char *str="this is const";str[1]='d';return 0;
}

2)SIGBUS(总线错误),越界访问,无效访问内存,系统向违规进程发送SIGBUS(7)信号,杀死违规进程。

3)SIGFPE(浮点数例外),CPU违规运算,运算异常,系统向违规进程发送SIGFPE(8),杀死进程。

5、软条件触发信号

软条件触发信号(Soft Condition Trigger Signal),指的是某些条件满足时触发相应的处理操作。例如,文件描述符变为可读时触发读操作;设置一个定时程序,定时器结束后触发操作;管道读端结束,写端向管道写数据(触发软条件),系统向写端进程发送SIGPIPE13信号杀死写端进程。

信号的三大行为,与五种默认处理动作

默认行为

信号处置进程后,可以通过结果分析信号的默认动作。

默认处理动作:

TERM:直接杀死目标进程,SIGKILL,SIGINT

CORE:直接杀死进程,但是转储核心处理文件(dump core),SIGQUIT,SIGSEGV SIGFPE,SIGBUS

IGN:通知回收信号 SIGCHLD(唯一的忽略信号),忽略信号,发到进程不会影响进程

STOP:挂起进程SIGSTP,SIGTSTP

CONT:唤醒进程,SIGCONT

忽略行为

忽略行为没有处理动作,直接丢弃,不会影响进程。忽略行为的优先级比动作要高。

捕捉行为

捕捉行为可以实现,信号绑定自定义任务。信号触发,执行捕捉函数,执行自定义任务。捕捉技术在开发中普遍使用,例如Qt的信号与槽机制。

 Dump Core

如果进程因为硬件异常被系统杀死,那么会(Dump Core),错误原因 xxx(核心已转储)

例如:对常量空间的非法访问。

Dumb Cor中存放的是错误信息,但是系统不会生产core文件,我们可以进行修改:

ulimit -a查看系统限制

ulimit -c 4096 生成一个块大小的core,让进程产生的错误生成core文件

后续调试可以通过gdp ./可执行文件名 core快速定位错误位置,无需逐步调试,节省编译时间

让信号失效的三种方式:屏蔽、忽略、捕捉

屏蔽(Block):延迟处理信号,直到信号解除屏蔽。

忽略(Ignore):完全忽略信号,不对信号作出任何响应。

捕捉(Catch):使用自定义的信号处理程序响应信号。

1. 屏蔽(Block)

屏蔽信号指的是将信号加入到进程的信号屏蔽集(blocked signal set)中。当信号被屏蔽时,即使信号被发送给进程,它也不会立即处理该信号,而是将其放入未决信号集(pending signal set),直到信号被解除屏蔽时才会处理。

2. 忽略(Ignore)

忽略信号指的是将信号处理程序设置为SIG_IGN,使得进程完全忽略某个特定的信号。当信号被忽略时,进程不会对该信号作出任何响应。

3. 捕捉(Catch)

捕捉信号指的是将信号处理程序设置为自定义函数,当信号到达时执行该函数。捕捉信号允许进程在信号到达时执行特定的操作而不会执行本来的功能。

如果所有的信号都被屏蔽、忽略、捕捉,那么不就会导致病毒无处可寻吗?

系统保留高权级信号,这类信号无法被屏蔽,捕捉和忽略,服务于内核,只要发出必然抵达。

SIGKILL(9),无法被屏蔽、捕捉、忽略,只要发出必然杀死

SIGSTOP(19),无法被屏蔽,捕捉忽略,只要发出必然挂起

信号的传递过程

当某个事件发生(例如用户按下Ctrl+C),内核会生成一个SIGINT信号,传递给目标进程虚拟内存中3-4G内核层PCB,PCB中存有信号处理信息。同时更新目标进程的PCB中的未决信号集。如果发送信号的行为是屏蔽,那么也会更新目标进程PCB中的屏蔽信号集,将对应信号位设置1, 可以实现阻塞信号的效果。当屏蔽解除或者信号不被屏蔽时,未决信号集中的信号将被处理。处理过程是由信号处理程序(Handler)来执行的。

未决信号集:是一个位图,每一位代表一个特定的信号。位值为1表示该信号是未决的。

当内核发送一个信号给进程时,它会将对应信号的位设置为1,表示此信号正在传递,还未处理。

屏蔽信号集:用于表示哪些信号当前被屏蔽(阻塞)。每个信号对应屏蔽信号集中的一位,位值为1表示该信号被屏蔽。

未决信号集和屏蔽信号集处理UNIX经典信号不支持信号排队处理。(因为一个信号即可达到目的)

自定义信号可以实现排队序列,多个相同信号触发也可以排队依次处理。(例如各种家电的遥控器,当连续按下多个按键时,可以连续执行。还有音乐播放器的切歌功能)

信号屏蔽的实现

#include <signal.h>
sigset_t 是一个数据类型,用于表示信号集。

sigemptyset(sigset_t set); 初始化信号集 set,将其设置为空,即不包含任何信号。

sigfillset(sigset_t set); 初始化信号集 set,将其设置为满,即包含所有信号。

sigaddset(sigset_t set, int signo); 将信号 signo 添加到信号集 set 中。

sigdelset(sigset_t set, int signo); 从信号集 set 中删除信号 signo

int sigismember(const sigset_t set, int signo); 检查信号 signo 是否是信号集 set 的成员。如果是,返回 1;否则,返回 0。

sigprocmask(int how, const sigset_t newset, sigset_t oldset); 检查并更改当前线程的信号屏蔽字。

how 可以是:

SIG_SETMASK(将信号屏蔽字设置为 newset

SIG_BLOCK(将 newset 中的信号添加到当前信号屏蔽字)

SIG_UNBLOCK(从当前信号屏蔽字中移除 newset 中的信号)

下面通过一个demo程序屏蔽SIGINT信号:


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <signal.h>int main(void)
{sigset_t set,oldset;sigemptyset(&set);//将信号屏蔽集全部置0sigaddset(&set,SIGINT);//将SIGINT加入屏蔽集sigprocmask(SIG_SETMASK,&set,&oldset);while(1) sleep(1);return 0;
}

结果:无论是哪种方式发送信号都无法实现

再添加SIGQUITSIGKILL为屏蔽信号

sigaddset(&set,SIGQUIT);//将SIGQUIT加入屏蔽集

sigaddset(&set,SIGKILL);//将SIGKILL加入屏蔽集(高优先级信号无法屏蔽)

SIGKILL无法被屏蔽,因为是高优先级信号

查看信号的屏蔽情况

信号已经被发出,抵达进程,进程中被屏蔽,要观察这种已触发被屏蔽的信号集只能查看未决信号集(只有读的权限)

获取进程的未决信号集,而后输出未决的每一位,0 or 1,查看信号屏蔽

sigpending(&pset)调用这个函数,系统会将进程的未决信号集传出到pset中

使用遍历循环结合sigismember,查看每一位的情况并输出。

查看未决信号集的demo程序:


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <signal.h>//通过打印未决信号集,查看被屏蔽的信号
void print_sigpending(sigset_t pset){int i=1;//信号是从1开始的for(i;i<32;++i){if(sigismember(&pset,i)){putchar('1');}elseputchar('0');}putchar('\n');}
int main(void)
{sigset_t set,oldset,pset;sigemptyset(&set);//将信号屏蔽集全部置0sigaddset(&set,SIGINT);//将SIGINT加入屏蔽集sigaddset(&set,SIGQUIT);//将SIGQUIT加入屏蔽集sigprocmask(SIG_SETMASK,&set,&oldset);while(1){sigpending(&pset);print_sigpending(pset);sleep(1);}return 0;
}

运行结果:

当键盘按下组合键发送SIGINTSIGQUIT后,未决信号集被置1

信号行为修改

使用struct sigaction 结构体对行为进行操作:

结构体成员:

act.sa_handler:

可以设置为: SIG_DFL(默认信号处理方式)SIG_IGN(忽略信号)。也可以指向自定义信号处理函数的指针

act.sa_flags:

标志位,用于控制信号处理行为。如果使用 sa_sigaction 处理函数,则将 flags 设置为 SA_SIGINFO

常用值: 0(默认,不使用附加选项)

act.sa_mask:

类型为 sigset_t 的信号集。用于在信号处理期间临时阻塞的信号。通常使用 sigemptyset 初始化。

函数 sigaction:用 newact 替换进程的信号处理行为,并将原有的信号处理行为保存在 oldact 中。

sigaction(int signo, struct sigaction *newact, struct sigaction *oldact);

signo:要处理的信号编号。

newact:指向包含新信号处理方式的 struct sigaction 结构体的指针。

oldact:指向用于保存原有信号处理方式的 struct sigaction 结构体的指针(可以为 NULL)。

下面写一个设置自定义的信号处理demo程序:当收到 SIGINT 信号时,会调用自定义信号捕捉函数SIG_CATCH并打印收到的信号编号。


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <signal.h>//act.sa_handler的类型是void (*fun)(int)
//是一个自定义函数指针类型,自定义的捕捉函数也要是这种类型
void SIG_CATCH(int signo){printf("SIGINT %d 捕捉信号\n",signo);
}int main()
{struct sigaction act,oldact;act.sa_handler=SIG_CATCH;//自定义的函数,用来输出捕捉到的信号编号act.sa_flags=0;sigemptyset(&act.sa_mask);//信号集类型sigaction(SIGINT,&act,&oldact);//替换信号行为成自定义的捕捉行为,并且将原来的行为保存到oldact中while(1)sleep(1);return 0;
}

当前进程收到SIGINT信号,触发捕捉函数

经典信号临时屏蔽

经典信号临时屏蔽是指在信号处理函数执行期间,临时阻塞某些信号,以避免这些信号在处理当前信号时再次被递送。 两个相同信号触发,可以最大排队一次。

在信号处理的上下文中,当信号被捕捉时,同一信号在默认情况下最多只能排队一次。

如果允许任意数量的信号排队,系统资源的消耗可能会显著增加。这种资源限制有助于系统避免因信号排队导致的资源枯竭。

信号排队行为

在这段代码中,如果按下 Ctrl+C 发送 SIGINT 信号,信号处理程序会执行,并在执行期间再次按下 Ctrl+C 发送的 SIGINT 信号将不会被排队处理,只能在当前信号处理完成后再处理一次。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <signal.h>//act.sa_handler的类型是void (*fun)(int)
//是一个自定义函数指针类型,自定义的捕捉函数也要是这种类型
void SIG_CATCH(int signo){int flag=2;while(flag--){printf("                flag%d\n",flag);sleep(1);}
}int main()
{struct sigaction act,oldact;act.sa_handler=SIG_CATCH;//自定义的函数,用来输出捕捉到的信号编号act.sa_flags=0;sigemptyset(&act.sa_mask);//信号集类型sigaction(SIGINT,&act,&oldact);//替换信号行为成自定义的捕捉行为,并且将原来的行为保存到oldact中while(1)sleep(1);return 0;
}

不管我们发送多少次SIGINT信号,也最多触发两次

捕捉函数的冲突

为了避免不同信号绑定相同的捕捉函数,引发冲突。

在进程处理某一个信号时,可以使用sa_mask临时屏蔽其他信号,等信号处理完再解除屏蔽,避免不同信号调用相同的捕捉冲突。

使用信号回收僵尸进程

使用waitwaitpid回收僵尸进程的操作都是主动回收,无论是阻塞回收还是非阻塞回收都会花费大量的时间片和资源。

操作系统在每次子进程结束后,都会发送SIGCHLD信号给父进程,这个信号默认处理行为会忽略,起到通知父进程的作用。那么我们就可以使用捕捉技术,绑定SIGCHLD信号与回收函数。当发送SIGCHLD信号后,自动调用回收函数,杀死僵尸进程。

下面是实现这一机制的demo程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <sys/fcntl.h>
#include <signal.h>
#include <sys/wait.h>
void SIG_KILLZOMB(int signo){int zpid;while((zpid=waitpid(-1,NULL,WNOHANG))>0){printf("已成功回收一个进程\n");}
}int main()
{struct sigaction act,oldact;act.sa_handler=SIG_KILLZOMB;//自定义的回收捕捉函数act.sa_flags=0;sigemptyset(&act.sa_mask);//将信号集类型全部置0sigaction(SIGCHLD,&act,&oldact);//替换捕捉行为为自定义的函数,并且将原来的函数保存到oldset中pid_t pid;pid=fork();if(pid>0){printf("Parent PID:%d is working\n",getpid());while(1) sleep(1);}else if(pid==0){printf("Child PID:%d is Exiting\n",getpid());exit(0);}return 0;
}

捕捉函数实现的流程

main函数先执行,执行过程中产生信号,系统执行捕捉函数,捕捉函数执行完,稍后回到主函数继续执行

内核切换到用户层执行捕捉函数,使用进程本身资源(时间片或内存)

1、进程在用户层执行特定指令

2、系统发出信号到内核层(等待处理)

当系统中某个事件(如硬件中断、软件异常等)发生时,会产生一个信号,这个信号会被传递到内核层进行处理。

3、进程状态发生转换,从用户空间切换到内核空间

用户进程的执行发生中断,CPU 从用户空间切换到内核空间,以便处理信号。

这种转换通常是通过系统调用中断指令实现的。

4、切换到内核层后完成调用

内核捕获信号后,根据信号的类型和处理程序完成相应的处理。内核找到对应的信号处理函数,并准备调用它。

5、完成调用后,返回用户空间,检测是否有未处理的信号,有则处理

内核处理完信号后,恢复进程的执行上下文,并返回用户空间。返回用户空间前,内核会检查是否有未处理的信号,如果有则继续处理。

6、如果信号的处理行为为捕捉行为,调用用户层捕捉函数,携高权限切换到用户层

如果信号有用户定义的处理函数,内核会将信号处理行为转换为调用用户层的捕捉函数。内核会提升权限,切换到用户空间以执行用户层的捕捉函数。

7、捕捉到信号

捕捉函数执行具体的信号处理逻辑。

8、系统调用完毕,返回用户空间

捕捉函数执行完毕后,系统调用 sigreturn 指令返回内核空间,恢复进程的执行上下文。

9、从 main 被中断的位置继续执行

最后,进程从 main 函数中断的位置继续执行。

到底什么是内核层与用户层?

内核层与用户层就是不同级别的CPU访问权限。

可以参考 Intel单核处理器的特权级别(Privilege Levels),也称为保护环(Protection Rings)。

这种模式在操作系统中也存在,用于管理和限制不同层次的软件对硬件资源的访问权限。防止低权限的代码(如用户进程)直接访问高权限的资源(如操作系统内核),减少系统被恶意攻击。

Level 0(环0)

特权级别:最高级别的CPU权限,内核层(Kernel Mode)。

访问权限:可以访问所有的硬件资源和执行所有的CPU指令。

操作系统内核运行在这一层,包括设备驱动程序、硬件抽象层等关键系统组件。

Level 3(环3)

特权级别:最低级别的CPU权限,用户层(User Mode)。

访问权限:受限,无法直接访问大多数硬件资源,只能通过系统调用访问操作系统提供的服务。

应用程序和用户进程运行在这一层,以确保系统的稳定性和安全性。

捕捉函数的可重入和不可重入

捕捉函数的可重入性(reentrancy)和不可重入性(non-reentrancy)是指函数在被中断后重新进入执行时的行为特性。

可重入函数

可重入函数指的是能够在被中断后,安全地再次被调用的函数。即使在另一个调用还没有完成的情况下,也不会出现数据不一致或其他问题。

int add(int a, int b) {return a + b;
}

add 函数是可重入的,因为它不依赖于任何共享状态或资源。

不可重入函数

不可重入函数是指在被中断后,再次调用可能会导致数据不一致或程序崩溃,通常因为它们依赖于静态或全局状态。

char* get_message() {static char message[] = "Hello, World!";return message;
}

get_message 函数是不可重入的,因为它返回了一个指向静态变量的指针。如果该函数在中断过程中被重新调用,会导致多个调用共享同一个静态变量,可能会引发数据不一致的问题。

捕捉函数中的可重入和不可重入

在信号处理(捕捉函数)过程中,假设信号处理程序调用了一个不可重入函数,而这个函数在被信号中断之前已经在运行,这时重新进入这个函数可能会导致程序崩溃或数据损坏。

例如在链表中间插入节点,插入操作进行一般时,调用了捕捉函数,主函数中断。而在捕捉函数中插入了S1节点,捕捉函数处理完毕后,M1节点的next指向了end。

虽然这种情况不会影响对链表的遍历,但是会产生垃圾节点和数据。

在编写信号处理程序时,应尽量使用可重入函数,避免使用不可重入的函数或在信号处理程序中调用不可重入的函数。

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

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

相关文章

【剖析】为什么说RBF神经网络的误差为0

本文来自《老饼讲解-BP神经网络》https://www.bbbdata.com/ 机器学习中的模型非常的多&#xff0c;但如果要问有没有这样的一个模型&#xff0c;它的训练误差为0&#xff0c;那么就非RBF神经网络莫属了&#xff01;下面我们来聊聊&#xff0c;为什么RBF神经网络的训练误差为0。…

了解请求参数与响应参数的区别:初学者指南

在 Web 的开发领域&#xff0c;无论你是前端开发还是后端开发人员&#xff0c;把握请求与响应参数的核心差异是极其重要的。这些参数在客户端和服务器之间的互动中扮演着关键角色。 请求参数的定义及类别 定义 当客户端向服务器提交信息时所使用的数据被称为请求参数。这些参…

【Docker】Docker下载安装_使用阿里云加速配置

1、下载安装 1.1前提条件 安装环境&#xff1a; 目前&#xff0c;CentOS 仅发行版本中的内核支持 Docker。Docker 运行在 CentOS 7 上&#xff0c;要求系统为64位、系统内核版本为 3.10 以上。Docker 运行在 CentOS-6.5 或更高的版本的 CentOS 上&#xff0c;要求系统为64位…

STM32上实现spwm调制原理分析

在STM32微控制器上实现SPWM&#xff08;正弦脉宽调制&#xff0c;Sinusoidal Pulse Width Modulation&#xff09;调制的核心是利用高频载波&#xff08;三角波&#xff09;与低频基波&#xff08;正弦波&#xff09;作比较得出。 那么在STM32里三角波和正弦波分别是什么&…

YzmCMS内核简约风非常不错的博客自媒体主题模板

本次发布的“Eric”主题模版文件中&#xff0c;已移除默认模版中一些非必要的模版&#xff0c;仅保留一些通用模版(首页、频道页、列表页、资源列表页、内容页、关于我/单页等)&#xff0c;当前模版主题中提供的模版文件已经能够满足大部分网站使用。 YzmCMS内核简约风非常不错…

Java数据结构4-链表

1. ArrayList的缺陷 由于其底层是一段连续空间&#xff0c;当在ArrayList任意位置插入或者删除元素时&#xff0c;就需要将后序元素整体往前或者往后搬移&#xff0c;时间复杂度为O(n)&#xff0c;效率比较低&#xff0c;因此ArrayList不适合做任意位置插入和删除比较多的场景…

明明设置允许跨域,为什么还会出现跨域请求的问题

一、问题 在微服务项目中&#xff0c;明明已经设置允许跨域访问&#xff1a; 为什么还会出现跨域请求问题&#xff1f; 二、为什么 仔细查看错误提示信息&#xff1a;When allowCredentials is true, allowedOrigins cannot contain the special value "*" since t…

Cesium如何高性能的实现上万条道路的流光穿梭效果

大家好&#xff0c;我是日拱一卒的攻城师不浪&#xff0c;专注可视化、数字孪生、前端、nodejs、AI学习、GIS等学习沉淀&#xff0c;这是2024年输出的第20/100篇文章&#xff1b; 前言 在智慧城市的项目中&#xff0c;经常会碰到这样一个需求&#xff1a;领导要求将全市的道路…

Jenkins定时构建自动化(二):Jenkins的定时构建

目录 ​编辑 一、 jenkins定时构建语法&#xff1a; 1. 语法规则&#xff1a; 2. 常见用法举例 3. 再次举例 接上一篇&#xff1a;Jenkins定时构建自动化(一)&#xff1a;Jenkins下载安装配置&#xff1a;Jenkins定时构建自动化(一)&#xff1a;Jenkins下载安装配置-CSDN博客 …

常见的LED显示屏拼接优缺点解析

LED显示屏拼接技术在现代显示技术中占据了重要地位。随着市场需求的不断增长&#xff0c;各种拼接屏技术也不断发展&#xff0c;每种技术都有其独特的优势和不足。本文将详细解析常见的几种拼接屏技术&#xff0c;包括LED显示屏拼接、投影DLP拼接和等离子PDP拼接。 LED显示屏拼…

STM32CubeIDE提示找不到头文件(No such file or directory)的解决办法

0 前言 最近在使用STM32CubeIDE时&#xff0c;发现为工程添加了头文件路径&#xff0c;但编译的时候还是报错&#xff0c;提示找不到头文件&#xff1a; 1 解决办法 1.1 为工程添加头文件路径 右键我们的工程&#xff0c;然后添加头文件路径&#xff08;最好是相对路径&am…

秋招突击——第八弹——Redis是怎么运作的

文章目录 引言正文Redis在内存中是怎么存储的面试重点 Redis是单线程还是多线程面试重点 内存满了怎么办&#xff1f;面试重点 持久化介绍面试重点 RDB持久化面试重点 AOF日志面试重点 总结 引言 差不多花了两天把redis给过了&#xff0c;早上也只背了一半&#xff0c;完成回去…

如何发现Redis热Key,有哪些解决方案?

什么是 hotkey&#xff1f; 如果一个 key 的访问次数比较多且明显多于其他 key 的话&#xff0c;那这个 key 就可以看作是 hotkey&#xff08;热 Key&#xff09;。例如在 Redis 实例的每秒处理请求达到 5000 次&#xff0c;而其中某个 key 的每秒访问量就高达 2000 次&#x…

Linux基础二

目录 一&#xff0c;tail查看文件尾部指令 二&#xff0c;date显示日期指令 三&#xff0c;cal查看日历指令 四&#xff0c;find搜索指令 五&#xff0c;grep 查找指令 六&#xff0c;> 和>> 重定向输出指令 七&#xff0c; | 管道指令 八&#xff0c;&&逻辑控…

Android开发神器:OkHttp框架源码解析

NetworkInterceptors CallServiceInterceptor 1.RealInterceptorChain.proceed() 2.EventListener.callStart()也是在RealCall.execute()嵌入到Request调用过程, EventListener.callEnd()位于StreamAllocation中调用 3.Request.Builder url (String/URL/HttpUrl) header …

Linux常用

很早以前的 ls: 查看文件夹内所有文件 rz: windows的文件传到linux服务器 sz filename: 将文件下载到windows本地 ctrlinsert:复制 shiftinsert:粘贴 ctrlD&#xff1a;退出spark-shell 运行脚本并输出日志 nohup sh filename.sh > log.log 2>&1 & 查看日…

STM32玩转物联网07-WIFI实验

前言 上一节我们学习了串口的简单使用&#xff0c;本节我们增加难度&#xff0c;做一个demo通过AT指令控制ESP8266&#xff0c;使用DMA方式接收ESP8266发来的数据&#xff0c;后续我们便开始通过ESP8266连接物联网云平台&#xff0c;敬请关注。 一、准备 1. ESP8266硬件准备 准…

在C++中,构造器(Builder)模式的思考(《C++20设计模式》及常规设计模式对比)

文章目录 一、前言二、为什么需要Builder Pattern,Builder Pattern到底解决了什么实际问题&#xff1f;为什么不用set()方法&#xff1f;2.1 初学者有那些对象的属性初始化方法呢&#xff1f;2.1.1 构造函数的弊端2.1.1.1 对于属性的初始化只能是固定的顺序 2.1.2 用set()函数初…

兰州理工大学24计算机考研情况,好多专业都接受调剂,只有计算机专硕不接收调剂,复试线为283分!

兰州理工大学&#xff08;Lanzhou University of Technology&#xff09;&#xff0c;位于甘肃省兰州市&#xff0c;是甘肃省人民政府、教育部、国家国防科技工业局共建高校&#xff0c;甘肃省高水平大学和“一流学科”建设高校&#xff1b;入选国家“中西部高校基础能力建设工…

redis主从复制、哨兵、集群

在实际的生活环境中&#xff0c;如果只使用一个redis进行读写操作&#xff0c;那么面对庞大的访问人群是崩溃的&#xff0c;所以可以有几个redis&#xff0c;一个用来做主机&#xff0c;提供修改数据操作&#xff0c;而这个主机用来控制其他redis&#xff0c;即将更新的发送&am…