【Linux】进程通信 — 信号(上篇)

文章目录

  • 📖 前言
  • 1. 什么是信号
    • 1.1 认识信号:
    • 1.2 信号的产生:
    • 1.3 信号的异步:
    • 1.4 信号的处理:
  • 2. 前后台进程
  • 3. 系统接口
    • 3.1 signal:
      • 3.1 - 1 不能被捕捉的信号
    • 3.2 kill:
      • 3.2 - 1 killall
    • 3.3 raise:
    • 3.4 abort:
    • 3.5 alarm:
  • 4. 崩溃的本质是什么
    • 4.1 Core Dump:

📖 前言

本章我们将讲解Linux信号这部分的内容,本章将介绍信号的产生,发送,信号的捕捉,屏蔽等操作,将对信号进行一些列系统的了解与学习。目标已经确定,接下来就要搬好小板凳,准备开讲了…🙆🙆🙆🙆


1. 什么是信号

1.1 认识信号:

  • 在我们学习信号之前,我们先来回忆一下生活中的各种信号,例如:红绿灯、铃声、闹钟……
  • 我们在能够认识这些场景下的信号以及所表示的含义:
    • 即便这个信号还没有产生,我们就已经具备了处理这个信号的能力。
  • 我们早就知道了信号产生后要做什么:
    • 即便当前信号还没有产生,我们已经提前知道了这个信号的处理方法。
  • 信号是给进程发送的,进程要具备处理信号的能力。
  • 可以说程序员通过编写代码来利用操作系统提供的接口和功能,实现了处理信号的能力。
    • 该能力一定是预先已经早就有了的。
    • 进程能够识别对应的信号。
    • 进程能够处理对应信号。

对于进程来讲,即便是信号还没有产生,我们进程已经具有识别和处理这个信号的能力了。

  • 信号的种类:

使用kill -l命令罗列出来的内容叫做信号,我们可以看到目前Linux系统下64种不同的类型:

在这里插入图片描述

  • 没有32、33、0号信号。
  • 第一批1 ~ 31(普通信号)
  • 第二批34 ~ 64(实时信号)

信号左侧的数字和右侧的名称是一回事,其实都是宏,大写的字母是宏名称,宏的值就是左侧对应的编号。

这二者的差别是:早期有实时操作系统,我们现在用的是分时操作系统。
基于时间片轮转,基于优先级抢占的调度算法。

1.2 信号的产生:

有很多情况会产生信号:

  1. 系统接口(kill命令)
  2. 键盘产生(Ctrl + C,Ctrl + \ )
  3. 软件条件(进程停止,进程运行完退出)
  4. 硬件异常(比如除0错误)

信号发送的本质:

  • 键盘是产生了信号,但是信号是操作系统发的。
  • 在位图中,将对应的位置设置为1,就完成了信号的发送。
  • 与其叫发送,不如叫操作系统向进程写入信号。

信号都是由操作系统向系统写入的:

  • 计算机要是想向一个PCB进程发信号,本质上因为操作系统是进程的管理者。
  • 可以直接以自身的身份来对进程的PCB数据结构的位图做任意修改。

崩溃现象就是底层代码引起了硬件的问题,进而被操作系统识别,然后操作系统将硬件问题识别成信号,然后向进程发送,然后终止进程。

1.3 信号的异步:

何为异步:

以点外卖为例,当外卖到了时,你可能正在忙着做其他事情,外卖员给你发了条取餐消息,但是你并不能立即去取。
此时我们知道自己的外卖到了(知道收到了信号),等手上的活忙完了再去取(过一会再去处理信号)。

同步和异步:

  • 当节奏会受某个因素影响时,这叫同步。
  • 当节奏不会受某个因素影响时,这叫异步。

信号可能在任何时候都能产生,可能是用户产生,也可能是操作系统产生的,这个产生对进程来讲是异步的。

因为信号产生是异步的:

  • 当信号产生的时候,对应的进程可能正在做更重要的事情,我们进程可以暂时不处理这个信号!
  • 也就是说进程可能不需要立即处理这个信号!
  • 但是并不代表这个信号不会被处理!

1.4 信号的处理:

处理信号的三种行为:

  1. 默认动作。
  2. 忽略。
  3. 自定义动作。

信号的处理,也叫做信号的的捕捉,递达处理动作。

必须记住这个信号有没有,是什么信号:

  • 信号被记录在了进程的PCB当中的:
    • 有没有产生【比特位的内容1/0】
    • 是什么信号产生【比特位的位置】

只有操作系统有这个权利,能直接修改这个task_struct内的数据位图!
OS是进程的管理者,进程的所有的属性的获取和设置,只能由OS来!!
无论信号怎么产生,最终一定只能是OS帮我们进行信号的设置的!


2. 前后台进程

Ctrl + C的本质是向前台进程发送信号。

在这里插入图片描述
我们死循环打印Hello World,在一直死循环期间,我们输入命令ls并不会列出该目录下的文件名。

myproce跑起来之后,再输入其他指令是没用的,因为这个进程占用了前面bash所对应的终端。当前bash没法做命令行响应,此时这种进程叫做前台进程。

将进程放到后台:

在这里插入图片描述

26733是进程编号。

后台进程,可以执行命令行指令,但是用Ctrl + C终止不了了。

在这里插入图片描述
jobs查看后台进程:

在这里插入图片描述

fg 1就将该进程提至前台了,再次Ctrl + C就可以了。

补充:

  • 前后端混打的时候,虽然会打印乱掉,混乱是很正常的因为缺少访问控制,信息交叉在一起。根据冯·诺依曼体系,我们输入的内容一定是先被进程拿到的,显示器之所以能看到,是因为给显示器也拷贝了一份,这叫回显。
  • 不回显也可以的,就像Linux输入密码,不回显但是确实输进去了。

任务管理:

在这里插入图片描述
在Linux中,作业列表中的符号+-表示了作业的状态。

  • 符号"+"表示当前前台运行的作业。
  • 符号"-"表示当前后台运行的作业。

如果没有"+“和”-"符号显示在作业列表中,则表示当前没有前台或后台运行的作业。作业列表可能是空的,也就是没有任何正在运行的作业。这通常发生在你没有在前台执行命令或将任何作业放到后台时

bg指令:
要将一个正在前台运行的作业切换到后台运行,可以按下"Ctrl + Z",这会将该作业暂停,并返回到命令行界面。然后,可以使用"bg"命令将作业放到后台继续运行,此时作业会继续执行,但不会再占用终端。


3. 系统接口

3.1 signal:

在这里插入图片描述

Ctrl + C是向前台发送二号信号。

代码演示:

#include <iostream>
#include <unistd.h>
#include <signal.h>using namespace std;void handler(int signo)
{cout << "我是一个进程,刚刚获取了一个信号: " << signo << endl;
}int main()
{// 给该信号设置了回调捕捉,自定义动作// 这里不是调用handler方法,这里只是设置了一个回调,让SIGINT产生的时候,该方法才会被调用。// 如果不产生SIGINT,该方法不会被调用!// 当二号信号产生的时候,才调用后面的方法。signal(SIGINT, handler);signal(3, handler);sleep(3);cout << "进程已经设置完了" << endl; sleep(3);while (true){cout << "我是一个正在运行中的进程: " << getpid() << endl;sleep(1);}return 0;
}

注意:

  • 给该信号设置了回调捕捉,自定义动作。
  • 这里不是调用handler方法,这里只是设置了一个回调,让SIGINT产生的时候,该方法才会被调用。
  • 如果不产生SIGINT,该方法不会被调用!!
  • 当二号信号产生的时候,才调用后面的方法。

函数指针,回调函数:

  • 函数指针类型,允许用户对信号自定义处理,忽略,自定义,默认。
  • 大部分信号都有默认动作,而signal方法可以让进程对特定的信号自定义设置。

在这里插入图片描述

  • Ctrl + C本质是给前台产生了2号信号,发送给目标进程,其中目标进程默认对2号信号的处理,是终止自己。
  • 更改了对二号信号处理,设置了用户自定义处理方法。
  • 还有一种终止进程的方法是发送3号信号

3.1 - 1 不能被捕捉的信号

  • 到这里我们不禁思索一番,我们之前讲过kill -9 + 进程ID信号可以杀掉进程,那我们能否将kill -9 + 进程ID的信号捕捉了呢?
  • 经过实验,得到结论,kill -9 + 进程ID该信号并不能被捕捉,这是为了防止一些恶意进程杀不掉的情况。
  • 因为9号信号不能被设置捕捉动作。永远都是默认动作,叫做管理员信号。
  • 9号信号几乎可以杀掉所有进程,除了曾经讲的D状态的进程。进程状态复习-传送门
  • 在Linux中,有一些信号被称为"不可捕捉信号",它们无法被用户进程捕捉或处理。这些信号是:
  • SIGKILL (信号编号为9):用于立即终止一个进程。无论进程是否希望接收该信号,都无法阻止或忽略它。
  • SIGSTOP (信号编号为19或17):用于暂停一个进程的执行。与SIGKILL类似,无法被捕捉或忽略。
  • SIGCONT (信号编号为18或19):用于继续一个被暂停的进程的执行。与前两个信号不同,SIGCONT是可以被捕捉的,但在默认情况下,它会立即恢复进程的执行。

这些不可捕捉信号通常由操作系统或其他系统级实体发送,用于管理进程的状态和行为。在正常情况下,用户进程无法阻止或修改这些信号的执行。

3.2 kill:

kill不仅是命令而且也是系统调用接口:

在这里插入图片描述

  • 向指定进程发送指定信号,成功了返回0,失败了返回-1。
  • 支持向任意进程发送任意信号。
  • 杀进程也是要有权限的。

不能杀掉不是自己的进程:

在这里插入图片描述
有了上述接口,再加上我们之前学的main函数的几个参数,我们可以手搓一个kill指令:

#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>using namespace std;static void Usage(const string& proc)
{cout << "Usage:\n\t" << proc << "signo pid" << endl;
}// 自己实现一个kill命令
// mykill 9 1234
int main(int argc, char* argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}if (kill(static_cast<pid_t>(atoi(argv[2])), atoi(argv[1])) == -1){cerr << "kill: " << strerror(errno) << endl;exit(2);}return 0;
}

3.2 - 1 killall

根据进程名字杀掉某个进程:killall + 进程名

  • 在Linux中,killall命令用于终止同名进程。
  • killall命令默认会发送SIGTERM(信号编号为15)信号给目标进程。
  • 不过,你也可以使用参数"-s"或"–signal"来指定其他信号,例如SIGKILL(信号编号为9)。
  • 这个命令非常有用,特别是当你想要快速终止所有同名进程时。
  • 需要注意的是,使用killall命令要小心,确保只终止你想要终止的进程,以免造成意外的影响。

3.3 raise:

kill是给任意进程发任意信号,raise是给自己发任意信号:

在这里插入图片描述
进程不断地给自己发送2号信号:

#include <iostream>
#include <unistd.h>
#include <signal.h>using namespace std;void handler(int signo)
{cout << "我是一个进程,刚刚获取了一个信号: " << signo << endl;
}int main()
{// 这里没有调用对应的handler方法,仅仅是注册signal(2, handler);while (true){// 每次循环都给自己发送2号信号sleep(1);raise(2);}// 每隔1秒都会收到一个2号信号return 0;
}

在这里插入图片描述

3.4 abort:

向自己发送6号SIGABRT信号:

#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>using namespace std;void handler(int signo)
{cout << "我是一个进程,刚刚获取了一个信号: " << signo << endl;
}int main()
{// 这里没有调用对应的handler方法,仅仅是注册signal(6, handler);while (true){sleep(1);abort();// exit(), ahort();}return 0;
}

终止进程:

在这里插入图片描述
注意:

  • abort()是即使捕捉了,但是依然会退出进程。
  • 除了9号信号不能被捕捉,对6号信号进行捕捉,但是依旧会退出。

硬件是在推着操作系统做一系列动作:

  • 时钟硬件 —— 给操作系统发送时钟中断。
  • CPU主频越高调度的频率就越高,效率就越高。

3.5 alarm:

#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>using namespace std;int cnt = 0;void handler(int signo)
{cout << "我是一个进程,刚刚获取了一个信号: " << signo << "cnt: " << cnt << endl;exit(1);
}// 信号闹钟
int main()
{// 未来一秒钟之后会超时signal(SIGALRM, handler);alarm(1);// 如果没有自定义操作,默认alarm会自定义终止,会收到SIGALRM信号// 统计该进程一秒钟cnt++多少次while (1){cnt++;// cout << "hello: " << cnt++ << endl;}return 0;
}

相比于CPU独立做计算,IO非常慢。

在这里插入图片描述

  • 在Linux中,默认情况下,当alarm定时器到期时,会生成一个SIGALRM信号。 如果进程没有捕获和处理该信号,那么该进程会被终止。
  • SIGALRM信号是用于告知进程某个定时器已经超时的信号。它通常由内核或通过使用alarm函数设置的定时器触发。当定时器超时时,内核向进程发送SIGALRM信号,进程可以选择捕获和处理该信号,或者使用默认操作(即终止进程)。
  • 如果进程没有显式地设置对SIGALRM信号的处理方式(通过信号处理函数或信号处理器),那么SIGALRM信号将以默认操作的方式处理,即终止进程。这意味着如果定时器超时并且进程没有捕获该信号,进程会被终止。

4. 崩溃的本质是什么

在Linux中越界访问都叫段错误。

所谓的崩溃,本质是什么呢?

  • 是该进程收到了异常信号,应该叫进程崩溃了。

进程崩溃是因为收到了异常信号,那么为什么会收到异常信号呢?

  • 除零问题:计算是在CPU内部,内有状态寄存器,该寄存器是用来表征该本次计算是否出现问题。
  • 如果有问题,状态寄存器中特定标志位会被计位。

C++ try catch:

  • 崩溃了,一定会导致进程终止吗?不一定!!
    • 崩溃,本质是什么呢?
    • 进程崩溃的本质,是该进程收到了异常信号!
  • 为什么呢?
  • 因为硬件异常,而导致OS向目标进程发送信号,进而导致进程终止的现象!
    • 除零: CPU内部,状态寄存器,当我们除0的时候,CPU内的状态寄存器会被设置成为,有报错:浮点数越界,CPU的内部寄存器(硬件),OS就会识别到CPU内有报错啦:
      • 1.谁干的?2.是什么报错(OS -> 构建信号) -> 目标进程发送信号 -> 目标进程在合适的时候 -> 处理信号 -> 终止进程。
    • 越界 && 野指针: 我们在语言层面使用的地址(指针),实都是虛拟地址 -> 物理地址 -> 物理内存 -> 读取对应的数据和代码的。
    • 如果虚拟地址有问题,地址转化的工作是由(MMU(硬件) + 页表(软件)),转化过程就会引起问题 -> 表现在硬件MMU上 -> OS发现硬件出现了问题:
      • 1.谁干的?2.是什么报错(OS -> 构建信号) -> 目标进程发送信号 -> 目标进程在合适的时候 -> 处理信号 -> 终止进程。

MMU是内存管理单元(Memory Management Unit)的简称。

实操注意:

  • 当进程崩溃时,对某个信号进行时捕捉时:
    • 要将自定义的handler函数最后exit(1);退出进程,不然会一直发信号,就会一直调用handler函数。
    • 因为一般进程崩溃时,操作系统会给进程发送对应的信号并终止进程。

在这里插入图片描述

  • 如果不加上最后的退出进程,会一直打印刷屏。
  • 一旦我们不进行信号捕捉,会直接终止。
  • 而我们捕捉之后如果没有对信号做处理,没有终止进程的话,会一直刷屏,进程没有被终止。

因为没有解决这个问题,这个异常一直都在,所以操作系统一直给进程发信号,所以刷屏了。

4.1 Core Dump:

Core Dump会把进程在运行中,对应的异常上下文数据,core dump到磁盘上,方便调试。

发上云服务器是设置成0的,禁止发生core dump(一般是关掉的),但是可以打开。

打开方式:

在这里插入图片描述
Core不光光要终止,还要发生Core dump。

在这里插入图片描述
8号信号本身就要产生core文件的,然后指令发现多了一个文件,里面是乱码。

在这里插入图片描述
当一个进程异常退出时,收到了某些信号,系统为了便于用户调试,会告诉用户触发core dump机制,core dump机制叫做核心转储。

在这里插入图片描述

核心转储(Core Dump)是指在程序运行过程中发生了严重错误导致程序崩溃时,系统将程序内存的完整快照保存到一个核心转储文件中。

  • 这个文件包含了程序崩溃时的内存状态、寄存器的内容以及其他相关的调试信息。
  • 核心转储文件主要用于程序崩溃分析和调试目的。通过分析核心转储文件,开发人员可以了解程序崩溃时的内存状态,定位错误的原因,并进行问题排查和修复。
  • 核心转储文件通常具有可读性较低的二进制格式,需要使用调试工具或分析器来解析和分析。
  • 在许多操作系统上,默认情况下,当程序崩溃时会自动生成核心转储文件。开发人员也可以在程序中通过设置相应的参数或使用调试工具来控制核心转储的生成及其行为。
  • 需要注意的是,由于核心转储文件可能会包含敏感信息,如内存中的数据,因此在进行排查和分析时需要遵守相应的隐私保护规定,并确保核心转储文件的安全性。

生成的Core Dump文件很大:

在这里插入图片描述

Core Dump一般配台gdb使用:

在这里插入图片描述
每次执行出错都会产生core dump文件,这种调试策略叫做事后调试。

云服务器关掉的原因:

因为如果大型程序,一旦代码有问题,会自动重启程序,如果一重启就挂掉,就会产生core文件,所以一直重启就会产生大量的core文件,就很大概率将磁盘空间被打满,此时就会危及到操作系统正常工作了。

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

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

相关文章

vue 简单实验 自定义组件 局部注册

1.概要 2.代码 <html> </html> <script src"https://unpkg.com/vuenext" rel"external nofollow" ></script> <body><div id"counter"><component-a></component-a></div> </body&g…

浅尝OpenResty

文章目录 1. 写在前面2. 下载安装openresty2.1 下载Openresty2.2 设置nginx启动 3. 嵌入lua脚本4. 实践5. 小结 1. 写在前面 当一个域名中衍生出多个服务的时候&#xff0c;如果想要保持对外服务始终是一个域名&#xff0c;则需要通过nginx反向代理来实现。如果在转发的时候需…

HyperMotion高度自动化云迁移至华为HCS8.1解决方案

项目背景 2020 年以来&#xff0c;金融证券已经成为信创落地最快的领域。2021 年证监会发布的《证券期货业科技发展十四五规划》中&#xff0c;将“加强信创规划与实施”作为证券行业重点建设任务之一。为了符合国家信创标准&#xff0c;某证券企业计划将网管系统、呼叫中心管…

sql server 、mysql CTE 公用表表达式

sql server 详细 mysql CTE CTE 是一个命名的临时结果集&#xff0c;作用范围是当前语句。CTE可以理解成一个可以复用的子查询&#xff0c;当然跟子查询还是有点区别的&#xff0c;CTE可以引用其他CTE&#xff0c;但子查询不能引用其它子查询。所以&#xff0c;开发中建议…

6、Spring_Junit与JdbcTemplate整合

Spring 整合 1.Spring 整合 Junit 1.1新建项目结构 1.2导入依赖 导入 junit 与 Spring 依赖 <!-- 添加 spring 依赖--> <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version…

es的索引管理

概念 &#xff08;1&#xff09;集群&#xff08;Cluster&#xff09;&#xff1a; ES可以作为一个独立的单个搜索服务器。不过&#xff0c;为了处理大型数据集&#xff0c;实现容错和高可用性&#xff0c;ES可以运行在许多互相合作的服务器上。这些服务器的集合称为集群。 &…

【Terraform学习】使用 Terraform 将 EC2 实例作为 Web 服务器启动(Terraform-AWS最佳实战学习)

使用 Terraform 将 EC2 实例作为 Web 服务器启动 实验步骤 前提条件 安装 Terraform&#xff1a; 地址 下载仓库代码模版 本实验代码位于 task_ec2 文件夹中。 变量文件 variables.tf 在上面的代码中&#xff0c;您将声明&#xff0c;aws_access_key&#xff0c;aws_secr…

wx.request配置服务器域名,只能包含英文大小写字母、数字,解决办法

前言.小程序服务器域名配置常见错误及解决方法 1.配置入口&#xff1a; 小程序后台->-开发->开发设置->服务器域名 2.常见错误及原因分析&#xff1a; 3.实战中出现的错误 4.解决办法&#xff1a;应把域名后边的路径去掉&#xff0c;只写域名即可

Leetcode78. 子集

给你一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返回该数组所有可能的子集&#xff08;幂集&#xff09;。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 回溯法 class Solution {public List<List<Integer>> subsets(int[] nums) {List…

腾讯云V265/TXAV1直播场景下的编码优化和应用

// 编者按&#xff1a;随着视频直播不断向着超高清、低延时、高码率的方向发展&#xff0c; Apple Vision的出现又进一步拓展了对3D, 8K 120FPS的视频编码需求&#xff0c;视频的编码优化也变得越来越具有挑战性。LiveVideoStackCon 2023上海站邀请到腾讯云的姜骜杰老师分享腾…

结合源码拆解Handler机制

作者&#xff1a;Pingred 前言 当初在讲App启动流程的时候&#xff0c;它的整个流程涉及到的类可以汇总成下面这张图&#xff1a; 那时着重讲了AMS、PMS、Binder这些知识点&#xff0c;有一个是没有对它进行详细讲解的&#xff0c;那就是常见的Handler&#xff0c;它不仅在这个…

k8s之工作负载、Deployment、DaemonSet、StatefulSet、Job、CronJob及GC

文章目录 1、工作负载1.1、定义1.2、分类 2、Deployment2.1、定义2.2、Deployment创建2.3、Deployment 更新机制2.3.1、比例缩放&#xff08;Proportional Scaling&#xff09;2.3.2、HPA&#xff08;动态扩缩容&#xff09;2.3.2.1、需要先安装metrics-server2.3.2.2、配置hpa…

STM32--SPI通信与W25Q64(1)

文章目录 前言SPI通信硬件电路移位过程 SPI时序起始与终止条件交换一个字节 W25Q64硬件电路框图 FLASH操作注意事项软件SPI读写W25Q64 前言 USART串口链接入口 I2C通信链接入口 SPI通信 SPI&#xff08;Serial Peripheral Interface&#xff09;是一种高速的、全双工、同步的串…

实战:大数据Spark简介与docker-compose搭建独立集群

文章目录 前言技术积累Spark简介Spark核心功能及优势Spark运行架构 Spark独立集群搭建安装docker和docker-composedocker-compose编排docker-compose编排并运行容器 Spark集群官方案例测试写在最后 前言 很多同学都使用过经典的大数据分布式计算框架hadoop&#xff0c;其分布式…

c++11 标准模板(STL)(std::basic_istringstream)(五)

定义于头文件 <sstream> template< class CharT, class Traits std::char_traits<CharT> > class basic_istringstream;(C11 前)template< class CharT, class Traits std::char_traits<CharT>, class Allocator std::allo…

小程序中的全局配置以及常用的配置项(window,tabBar)

全局配置文件和常用的配置项 app.json: pages:是一个数组&#xff0c;用于记录当前小程序所有页面的存放路径&#xff0c;可以通过它来创建页面 window:全局设置小程序窗口的外观(导航栏&#xff0c;背景&#xff0c;页面的主体) tabBar:设置小程序底部的 tabBar效果 style:是否…

C#-集合小例子

目录 背景&#xff1a; 过程: 1.添加1-100数: 2.求和: 3.平均值: 4.代码:​ 总结: 背景&#xff1a; 往集合里面添加100个数&#xff0c;首先得有ArrayList导入命名空间&#xff0c;这个例子分为3步&#xff0c;1.添加1-100个数2.进行1-100之间的总和3.求总和的平均值&…

数据结构(5)

堆 堆可以看作一颗完全二叉树的数组对象。 特性&#xff1a; 1.堆是完全二叉树&#xff0c;除了树最后一层不需要满&#xff0c;其余层次都需要满&#xff0c;如果最后一层不是满的&#xff0c;那么要求左满右不满 2.通常使用数组实现&#xff0c;将二叉树结点依次放入数组中…

Redis 重写 AOF 日志期间,主进程可以正常处理命令吗?

重写 AOF 日志的过程是怎样的&#xff1f; Redis 的重写 AOF 过程是由后台子进程 bgrewriteaof 来完成的&#xff0c;这么做有以下两个好处。 子进程进行 AOF 重写期间&#xff0c;主进程可以继续处理命令请求&#xff0c;从而避免阻塞主进程子进程带有主进程的数据副本。这里…

远程控制:用了向日葵控控A2后,我买了BliKVM v4

远程控制电脑的场景很多&#xff0c;比如把办公室电脑的文件发到家里电脑上&#xff0c;但是办公室电脑旁边没人。比如当生产力用的电脑一般都比较重&#xff0c;不可能随时带在身边&#xff0c;偶尔远程操作一下也是很有必要的。比如你的设备在工况恶劣的环境中&#xff0c;你…