8. 信号基础

8. 信号基础

  • 1. 基本概念
    • 1.1 信号的目的是用来通信的
    • 1.2 信号由谁处理、怎么处理
    • 1.3 信号是异步的
  • 2. 信号的分类
    • 2.1 可靠信号和不可靠信号
    • 2.2 实时信号和非实时信号
  • 3. 进程对信号的处理
    • 3.1 signal()
    • 3.2 sigaction()
      • 3.2.1 struct sigaction
      • 3.2.2 实例
  • 4. 向进程发送信号
    • 4.1 kill()
    • 4.2 raise()
  • 5. alarm() 和 pause()
    • 5.1 alarm()
    • 5.2 pause()
  • 6. 信号集
    • 6.1 初始化信号集
    • 6.2 向信号集中添加或删除信号
    • 6.3 测试信号是否在信号集中
  • 7. 获取信号的描述信息
    • 7.1 strsignal()
    • 7.2 psignal()
  • 8. 信号掩码,阻塞信号传递
  • 9. 阻塞等待信号sigsuspend()
  • 10. 实时信号
    • 10.1 sigpending()
    • 10.2 发送实时信号
  • 11. 异常退出 abort()

1. 基本概念

信号是事件发生时对进程的通知机制,也可以称为软件中断。信号与软件中断的相似之处在于能够打断程序当前执行的正常流程,其实是在软件层上对中断机制的一种模拟。可以使用kill -l 查看信号

1.1 信号的目的是用来通信的

信号可以由谁发出?

  • 硬件发生异常,即硬件检测到错误条件并通知内核,随即再由内核发送相应的信号给相关进程。
  • 用于在终端下输入能够产生信号的特殊字符。比如在终端下按快捷键 Ctrl+C 表示 2 号中断信号
  • 进程调用 kill() 系统调用可以将任意信号发送给另一个进程或进程组。但是接收信号的进程和发送信号的进程的所有者必须相同,或者发送信号的进程是 root
  • 用户可以通过 kill 命令将信号发送给其他进程。
  • 发生了软件事件,即当检测到某软件条件已经发生。
  • 进程也可向自身发送信号,然而发送给进程的诸多信号中,大多数都是来自于内核
    信号的目的都是用于通信,当发生某种情况下,通过信号将情况告诉给相应的进程,从而达到同步、通信的目的。

1.2 信号由谁处理、怎么处理

忽略信号: 当信号达到进程后,该进程直接忽略,所以信号不会对进程产生任何影响。但是有两种信号是不能被忽略的,SIGKILL 和 SIGSTOP,因为它们向内核和超级用户提供了使进程终止或停止的可靠方法。另外,如果忽略某些由硬件异常产生的信号,则进程的运行行为是未定义的。
捕捉信号: 当信号到达进程后,执行相应的信号处理函数。
执行系统默认操作: 进程不对该信号做处理,而是交由系统进行处理,每种信号都有对应的默认处理方式

1.3 信号是异步的

产生信号的事件对进程而言是随机的,进程无法预测该事件产生的准确时间,只有当信号到达时才回去执行相应的处理

2. 信号的分类

从可靠性方面分为可靠信号和不可靠信号;从实时性方面分为实时信号和非实时信号

2.1 可靠信号和不可靠信号

信号编号 1 ~ 34 的都是不可靠信号,34 ~ 64 对应的都是可靠信号。可靠信号支持排队,不会丢失

2.2 实时信号和非实时信号

非实时信号都不支持排队,都是不可靠信号;实时信号支持排队,是可靠信号。实时信号能够保证发送的多个信号都能被接受。一般把非实时信号(不可靠信号)称为标准信号。

3. 进程对信号的处理

3.1 signal()

#include <signal.h>
typedef void (*sig_t)(int);
sig_t signal(int signum, sig_t handler);
// signum:需要设置的信号,可以用信号名(宏)或信号的编号
// 函数指针,指向信号对应的处理函数。也可以设置 SIG_IGN 表示忽略或者 SIG_DFL 表示系统默认操作。函数的int类型只的是触发函数的信号。
#include <iostream>
#include <stdio.h>
#include <signal.h>
using namespace std;
static void handler(int sig)
{
	cout << "接收到的信号是: " << endl;
}
int main()
{
	sig_t ret=nullptr;
	ret=signal(SIGINT,(sig_t)handler);
	while(1){}
	return 0;
}

上述测试用例,在终端运行后,不停的按 Ctrl+C,就可以看到打印信息。如果要终止程序,可以在另打开一个终端使用ps -aux | grep test | grep -v grep查看 test 进程的 pid,然后使用kill -9 pid号就可以杀死进程

两种不同状态下信号的处理方式

  • 程序启动:当一个应用程序刚启动的时候,或者程序中没有调用 signal() 函数,通常情况下,进程对所有信号的处理方式都设置为系统默认操作。系统中并不会为 SIGINT 信号提供处理方法,所以常常用来终止进程
  • 进程创建:当一个进程创建子进程时,子进程将继承父进程的信号处理方式,因为子进程在开始时复制了父进程的内存映像,所以信号捕捉函数的地址在子进程中是有意义的。

3.2 sigaction()

这个函数虽然更复杂,但是更具有灵活性和移植性。允许单独获取信号的处理函数而不是设置,并且还可以设置各个属性对调用信号处理函数时的行为施以更加精确的控制

#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
// act:该结构体描述了信号的处理方式,如果不为空,则表示需要为信号设置新的处理方式,如果设置为空,表示无需改变处理方式
// oldact:如果不为空,将信号之前的处理方式等信息通过oldact返回出来,如果不想获取,就设置为空

3.2.1 struct sigaction

在这里插入图片描述

  • sa_handler: 指定信号处理函数
  • sa_sigaction: 也是指定信号处理函数,这是一个替代的信号处理函数,提供了更多的参数,可以通过该信号获取更多信息,这些信号通过 siginfo_t 结构体获取。sa_handler 和 sa_sigaction 是互斥的,不能同时设置,对于标准信号,使用上面一个就可以了,可以通过 SA_SIGINFO 标志进行选择
  • sa_mask: 定义了一组信号,当进程在执行 sa_handler 所定义的信号处理函数之前,会先将这组信号添加到进程的信号掩码字段中,当执行完处理函数之后,恢复信号掩码,将这组信号从信号掩码字段中删除。当进程在执行信号处理函数期间,可能又收到了同样的信号或其他信号,从而打断当前处理函数的执行,而函数掩码就可以防止信号处理函数不被打断。如果进程收到了信号掩码中的这些信号,那么这个信号将会被阻塞暂时不能得到处理,直到这些信号从进程的信号掩码中移除。在信号处理函数调用时,进程会自动将当前处理的信号添加到信号掩码中, 这样就保证了在处理一个给定信号时,如果此信号再次发生,那么将会被阻塞。如果用户还需要阻塞其他信号,则可以通过设置参数 sa_mask 来完成
  • sa_restorer: 已经过时,不再使用
  • sa_flags: 指定了一组标志,用于控制信号的处理过程,可设置为以下标志

sa_flags

  • SA_NOCLDSTOP: 如果 signum 是 SIGCHLD,则子进程停止或恢复时不会收到 SIGCHLD 信号
  • SA_NOCLDWAIT: 如果 signum 是 SIGCHLD,则在子进程终止时不要将其转变为僵尸进程
  • SA_NODEFER: 不要阻塞从某个信号自身的信号处理函数中接收此信号。也就是说进程此时正在执行某个信号的处理函数,默认情况下,进程会自动将该信号添加到进程的信号掩码字段中,从而阻塞该信号,防止发生竞态条件,如果设置了该字段,表示不阻塞
  • SA_RESETHAND: 执行完信号处理函数之后,将信号的处理方式设置为系统默认
  • SA_RESTART: 被信号中断的系统调用,在信号处理完成之后将会自动重新发起
  • SA_SIGINFO: 表示使用 sa_sigaction 作为信号处理函数,而不是 sa_handler

3.2.2 实例

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <iostream>
using namespace std;
static void sig_handler(int sig)
{
	cout << "接收到信号: " << sig<<endl;
}
int main()
{
	struct sigaction sig = {0};
	int ret;
	sig.sa_handler = sig_handler;
	sig.sa_flags = 0;
	ret = sigaction(SIGINT, &sig, NULL);
	while(1){}
	return 0;
}

4. 向进程发送信号

4.1 kill()

将信号发送给指定的进程或进程组中的每一个进程

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
/* pid:
 * pid>0:用于指定进程的pid
 * pid=0:将sig发送到当前进程的进程组中的每个进程
 * pid=-1:将sig发送到当前进程有权发送信号的每个进程,但1号进程除外
 * pid<-1:将sig发送到ID为-pid的进程组中的每个进程
 * /
sig:如果为0,表示不发送任何信号,但是仍检查,通常用于检查某个进程是否存在

4.2 raise()

可以向进程自身发送信号,这是个库函数

#include <signal.h>
int raise(int sig);

相当于kill(getpid(), sig);,getpid() 函数可以用于获取当前进程的pid

5. alarm() 和 pause()

5.1 alarm()

设置一个定时器,当时间到时,向进程发送 SIGALRM 信号

#include <unistd.h>
unsigned int alarm(unsigned int seconds);
// seconds:设置定时时间,以秒为单位。如果为0,表示取消之前设置的 alarm 闹钟
// 返回值:如果在调用该函数之前,已经设置过闹钟,而且没有到时,就返回之前闹钟的剩余时间,而之前设置的时钟会被替代

每个进程只能设置一个闹钟,而且不能循环触发,如果想要循环触发,可以在信号处理函数中再次调用此函数

5.2 pause()

可以暂停进程运行,进入休眠转台,直到进程捕获一个信号为止,只有执行了信号处理函数并返回后,pause 才返回 -1,并将 errno 设置为 EINTR

#include <unistd.h>
int pause();

6. 信号集

通常我们需要有一个能表示多个信号的数据类型,也就是信号集。信号集就是 sigset_t 结构体类型
在这里插入图片描述
使用这个结构体可以表示一组信号,将多个信号添加到该数据结构中

6.1 初始化信号集

#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);

sigemptyset() 使信号集不包含任何信号,而 sigfillset() 包含所有信号,包括实时信号

sigset_t set;
sigemptyset(&set);
sigfillset(&set);

6.2 向信号集中添加或删除信号

#include <signal.h>
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
sigaddset(&set, 2);
sigdelset(&set, 2);

6.3 测试信号是否在信号集中

#include <signal.h>
int sigismember(const sigset_t *set, int signum);
// 在就返回1,不在返回0

7. 获取信号的描述信息

每个信号都有一串与之对应的字符串描述信息,用于对该信号进行相应的描述。这些字符串位于 sys_siglist 数组中,sys_siglist 数组是一个 char* 类型的数组,每个元素存放的是一个字符串指针,指向一个信号描述信息。

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
int main()
{
	cout << sys_siglist[SIGINT]<<endl;
	cout << sys_siglist[SIGQUIT]<<endl;
}

7.1 strsignal()

#include <string.h>
char *strsignal(int sig);

直接传入对应的信号,就可以获取出信号字符串信息

7.2 psignal()

可以在标准错误上输出描述信息

#include <signal.h>
void psignal(int sig, const char *s);

后面的字符串信息是可以自己添加打印的内容

int main()
{
	psignal(SIGINT,"信号描述信息:");
}

8. 信号掩码,阻塞信号传递

内核为每个进程都维护了一个信号掩码,其实就是一个信号集,即一组信号。当进程接收到一个属于信号掩码中定义的信号时,该信号会被阻塞,无法传递给进程进行处理,那么内核会将其阻塞,直到该信号从信号掩码中移除。向信号掩码中添加一个信号,通常有以下方式:

  • 当应用程序调用 signal() 或 sigaction() 为某一个信号设置处理方式时,进程会自动将该信号添加到信号掩码中,如果此信号再次发生就被阻塞。当信号处理函数结束返回后,会自动将该信号从信号掩码中移除。
  • 使用 sigaction() 为信号设置处理方式后,可以额外指定一组信号。当调用信号处理函数时,将该组信号自动添加到信号掩码中。当处理函数返回后,再将这组信号从信号掩码中移除
  • 还可以使用 sigprocmask() 系统调用,随时可以显示的向信号掩码中添加或移除信号。
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
/* how:指定了调用函数时的一些行为
 * set:将指定的信号集中的所有信号添加到信号掩码中或者从掩码中移除,如果为空,表示无需对档期那信号掩码做改动
 * oldset:如果不为空,向掩码中添加新的信号后,获取到进程当前的信号掩码,存放在olset中,如果为空,表示不获取当前的信号掩码
 * /
how:
SIG_BLOCK:将set指向的添加到掩码中
SIG_UNBLOCK:将set指向的从掩码中移除
SIG_SETMASK:将掩码设置为set指向的
int main()
{
	sigset_t sig_set;
	sigemptyset(&sig_set);
	sigaddset(&sig_set, SIGINT);
	sigprocmask(SIG_BLOCK,&sig_set,NULL);	// 将2号信号添加进掩码
	sigprocmask(SIG_UNBLOCK,&sig_set,NULL);	// 将2号信号移除 
}

9. 阻塞等待信号sigsuspend()

将恢复信号掩码和 pause() 挂起这两个动作封装为一个原子操作。

#include <signal.h>
int sigsuspend(const sigset_t *mask);

该函数始终返回 -1,将 errno 设置为 EINTR,表示被信号中断;如果调用失败,设置为 EFAULT。函数会将 mask 所指向的信号集来替换进程的信号掩码,也就是将进程的信号掩码设置为参数 mask 所指向的信号集,然后挂起进程,直到捕捉到信号被唤醒,如果捕捉的信号是 mask 中的,就不会被唤醒,继续等待。一旦从信号处理函数返回,该函数会将进程的信号掩码恢复成调用前的值。

10. 实时信号

如果进程当前正在执行信号处理函数,在处理信号期间接收到了新的信号,如果该信号是信号掩码中的成员,就会被阻塞,将该信号添加到等待信号集中,为了确定进程中处于等待状态的是哪些信号,可以使用sigpending()函数获取

10.1 sigpending()

#include <signal.h>
int sigpending(sigset_t *set);
// 处于等待状态的信号会存放在set所指向的信号集中
int main()
{
	sigset_t sig_set;
	sigemptyset(&sig_set);
	sigpending(&sig_set);
	if(sigismember(&sig_set,SIGINT))
	{
		cout << "SIG处于等待"<<endl;
	}
}

10.2 发送实时信号

等待信号集只是一个掩码,仅表明一个信号是否发生,而不能表示其发生的次数,如果同一个信号在阻塞状态下产生了多次,那么会将该信号记录在等待信号集中,并在之后仅传第一次,这是标准信号的缺点。
实时信号较之于标准信号,其优势如下:

  • 实时信号的信号范围有所扩大,可应用于应用程序自定义的目的,而标准信号仅提供了两个信号可用于应用程序自定义使用: SIGUSR1 和 SIGUSR2。
  • 内核对于实时信号所采取的是队列化管理。如果将某一实时信号多次发送给另一个进程,那么将会多次传递此信号。相反, 对于某一标准信号正在等待某一进程,而此时即使再次向该进程发送此信号,信号也只会传递一次。
  • 当发送一个实时信号时,可为信号指定伴随数据(一整形数据或者指针值),供接收信号的进程在它的信号处理函数中获取。
  • 不同实时信号的传递顺序得到保障。如果有多个不同的实时信号处于等待状态,那么将率先传递具有最小编号的信号。换言之,信号的编号越小,其优先级越高,如果是同一类型的多个信号在排队,那么信号(以及伴随数据)的传递顺序与信号发送来时的顺序保持一致。
    Linux 内核定义了 31 个不同的实时信号,信号编号范围为 34~64,使用 SIGRTMIN 表示编号最小的实时信号,使用 SIGRTMAX 表示编号最大的实时信号,其它信号编号可使用这两个宏加上一个整数或减去一个整数。
    应用程序当中使用实时信号,需要有以下的两点要求:
  • 发送进程使用 sigqueue()系统调用向另一个进程发送实时信号以及伴随数据。
  • 接收实时信号的进程要为该信号建立一个信号处理函数,使用sigaction函数为信号建立处理函数,并加入 SA_SIGINFO,这样信号处理函数才能够接收到实时信号以及伴随数据,也就是要使用sa_sigaction 指针指向的处理函数,而不是 sa_handler,当然允许应用程序使用 sa_handler,但这样就不能获取到实时信号的伴随数据了。
#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);     
// pid:指定接收信号的进程对应的pid
// value:指定信号的伴随数据

在这里插入图片描述

11. 异常退出 abort()

只用 abort() 终止进程运行,会生成核心转储文件,可用于判断程序调用该函数的程序状态

#include <stdlib.h>
void abort();

该函数通常 SIGABRT 信号来终止调用该函数的进程,该信号的系统默认操作是终止进程运行,并生成核心转储文件。当调用该函数之后,内核会向进程发送该信号

static void sig_handler(int sig)
{
	cout << "接收到的信号是: "<<sig<<endl;
}
int main()
{
	struct sigaction sig={0};
	sig.sa_handler=sig_handler;
	sig.sa_flags=0;
	sleep(2);
	abort();
	while(1)
	{
		sleep(1);
	}
	return 0;
}

从现象看,即使捕捉了信号,但程序依旧会终止,所以,如果阻塞或忽略 SIGABRT 信号,该函数都不会受到影响,总会终止进程。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

欧拉操作系统

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 这个章节主要是介…

微信小程序中生命周期钩子函数

微信小程序 App 的生命周期钩子函数有以下 7 个&#xff1a; onLaunch(options)&#xff1a;当小程序初始化完成时&#xff0c;会触发 onLaunch&#xff08;全局只触发一次&#xff09;。onShow(options)&#xff1a;当小程序启动或从后台进入前台显示时&#xff0c;会触发 on…

CSS属性 display和visibility的区别

在CSS中&#xff0c;有两种让元素隐藏的方式&#xff0c;分别是display和visibility&#xff0c;他们有什么区别呢&#xff1f; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport"…

[Kubernetes]1.Kubernetes(K8S)介绍,基于腾讯云的K8S环境搭建集群以及裸机搭建K8S集群

一. Kubernetes(K8S)简介 Kubernetes (K8S) 是一个为 容器化应用 提供 集群部署 和 管理 的开源工具,和docker swarm类似,由 Google 开发. Kubernetes 这个名字源于希腊语,意为 “ 舵手 ” 或 “ 飞行员 ” , k8s 这个缩写是因为 k 和 s 之间有八个字符的关系, Google…

echarts 柱状图 定时自动轮播(非提示框轮播)

看了很多文档都是实现提示框轮播的&#xff0c;而我要实现的功能是&#xff1a;柱状图有多条数据时&#xff0c;轮播展示其中几条&#xff0c;比如我有100条数据&#xff0c;不能全部展示&#xff0c;设置轮播5条或者10条&#xff0c;依次显示数据&#xff0c;并形成闭环。 &a…

远程服务器——如何在Conda中安装R环境

目录 1. R的安装2. VScode 配置参考文献 1. R的安装 推荐使用anaconda或者miniconda&#xff0c;创建虚拟环R_env境然后安装R&#xff1b; 使用conda search r-base查看可下载的R的版本&#xff1b;R版本比较低&#xff0c;一般可以先增加源&#xff1a; % 增加源 conda con…

鸿蒙开发ArkTS语言—状态管理概述

概述 我们构建的页面多为静态界面。如果希望构建一个动态的、有交互的界面&#xff0c;就需要引入“状态”的概念。 图1 效果图 上面的示例中&#xff0c;用户与应用程序的交互触发了文本状态变更&#xff0c;状态变更引起了UI渲染&#xff0c;UI从“Hello World”变更为“Hel…

一.初始typescript

什么是ts 首先我们要确认typescript是一个语言&#xff0c;是等同于JavaScript层级得&#xff0c;并不是一些人认为得是JavaScript得类型规范工具或者插件。 ts与js的差异 从type script这个名字就可以看出&#xff0c;ts其实是JavaScript的一个类型化超集&#xff0c;它增…

Python自动化测试通过日志3分钟定位bug

一、简单使用 入门小案例 1 2 3 4 5 6 7 8 import logging logging.basicConfig(levellogging.DEBUG, #设置级别&#xff0c;根据等级显示 format%(asctime)s-[%(filename)s-->line:%(lineno)d]-%(levelname)s:% (message)s) # 设置输出格式 logging.debug(This is a…

2024年度AI策略报告:AI浪潮扬帆起航,传媒应用百花齐放

今天分享的人工智能系列深度研究报告&#xff1a;《2024年度AI策略报告&#xff1a;AI浪潮扬帆起航&#xff0c;传媒应用百花齐放》。 &#xff08;报告出品方&#xff1a;兴业证券&#xff09; 报告共计&#xff1a;52页 1、行情回顾&#xff1a;板块处于较低区间&#xff0…

Java中实现HTTPS连接的最佳实践

引言 大家好&#xff01;我是小黑。今天咱们来聊聊一个既热门又实用的话题&#xff1a;在Java中如何实现HTTPS连接。现在的网络世界&#xff0c;安全性是大家都非常关注的问题&#xff0c;特别是对于咱们这些程序员来说&#xff0c;更是如此。想想看&#xff0c;如果你的网站或…

免费文章生成器的种类,3款免费的文章生成器推荐

内容创作无疑是网络营销和品牌建设中不可或缺的一环。许多人在日常工作中可能会面临时间不足、灵感枯竭等问题&#xff0c;本文将深入聊聊免费文章生成器的种类&#xff0c;详细介绍使用方法。 1. 免费文章生成器的种类 基于模板的生成器 这类生成器通常提供一系列文章模板&…

【LeetCode】每日一题 2023_12_7 重新规划路线(DFS/BFS)

文章目录 刷题前唠嗑题目&#xff1a;重新规划路线题目描述代码与解题思路 刷题前唠嗑 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01; 这题好眼熟&#xff0c;怎么这几天都是这类问题&#xff01; 题目&#xff1a;重新规划路线 题目链接&#xff1a;1466.…

【头歌实训】分布式文件系统 HDFS

文章目录 第1关&#xff1a;HDFS的基本操作任务描述相关知识HDFS的设计分布式文件系统NameNode与DataNode HDFS的常用命令 编程要求测试说明答案代码 第2关&#xff1a;HDFS-JAVA接口之读取文件任务描述相关知识FileSystem对象FSDataInputStream对象 编程要求测试说明答案代码 …

uni-app 微信小程序之好看的ui登录页面(五)

文章目录 1. 页面效果2. 页面样式代码 1. 页面效果 2. 页面样式代码 <!-- 顶部蓝色 --> <template><view class"contaier"><view class"top-bg"><view class"text-white text-bold text-xxxl">前端铺子</view…

架构师-2.数据权限

数据权限,实现指定用户可以操作指定范围的数据。例如说,针对员工信息的数据权限: 上述的这个示例,使用硬编码是可以实现的,并且也非常简单。但是,在业务快速迭代的过程中,类似这种数据需求会越来越多,如果全部采用硬编码的方式,无疑会给我们带来非常大的开发与维护成…

【银行测试】第三方支付平台业务流,功能/性能/安全测试方法...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、第三方支付平台…

K8s 多租户方案的挑战与价值

在当今企业环境中&#xff0c;随着业务的快速增长和多样化&#xff0c;服务器和云资源的管理会越来越让人头疼。K8s 虽然很强大&#xff0c;但在处理多个部门或团队的业务部署需求时&#xff0c;如果缺乏有效的多租户支持&#xff0c;在效率和资源管理方面都会不尽如人意。 本…

CLASS60 DM蓝牙5.2双模热插拔PCB

键盘使用说明索引&#xff08;均为出厂默认值&#xff09; 软件支持&#xff08;驱动的详细使用帮助&#xff09;一些常见问题解答&#xff08;FAQ&#xff09;首次使用步骤蓝牙配对规则&#xff08;重要&#xff09;蓝牙和USB切换键盘默认层默认触发层0的FN键配置的功能默认功…

DOS 批处理 (一)

DOS 批处理 1. 批处理是什么&#xff1f;2. DOS和MS-DOS3. 各种操作系统shell的区别Shell 介绍图形用户界面&#xff08;GUI&#xff09;shell命令行界面&#xff08;CLI&#xff09;的 shell命令区别 1. 批处理是什么&#xff1f; 批处理(Batch)&#xff0c;也称为批处理脚本…