kubernetes中pause容器的作用与源码详解

news2024/11/24 12:53:27

概述

摘要:上一篇文章我们介绍了kubernetes是如何通过pause容器来构建一个pod。本文我们对pause容器做一个总结,并再此基础上次深入浅出,从pause容器的源码详细了解pause容器的实现原理。

正文

pause容器是什么

  • 在 Kubernetes 中,Pause 容器是一种特殊类型的容器,它的主要作用是为其他容器提供一个可靠的、隔离的运行环境

  • Pause 容器还可以为 Pod 中的容器提供一个稳定的网络环境(net命名空间),确保容器的网络连接可靠性。

  • Pause 容器是一种轻量级的容器,它本身不包含任何业务逻辑。

  • Pause 容器的实现是基于 Docker 的 pause 镜像,可以在创建其他容器之前将其加载到 Pod 中,以确保 Pod 中的其他容器在 Pause 容器的基础上运行。

  • kubelet启动的时候必须通过–pod-infra-container-image=指定pause容器的。

注意: kubernetes中的sandbox或infra容器就是指pause容器。

pause容器的作用

  • 1、与 Kubernetes 其他组件的交互
    Pause 容器作为 Pod 中的一个组件,与 Kubernetes 的其他组件(如 API 服务器、Controller Manager 等)进行交互。当 Pod 被创建时,API 服务器将 Pause 容器的配置信息存储在 etcd 中,并通知 Controller Manager 对 Pod 进行处理。Controller Manager 将 Pause 容器与 Pod 中的其他容器一起调度到节点上运行。

  • 2、管理容器的生命周期
    Pause 容器通过使用名为 /pause 的进程来管理其他容器的生命周期。当 Pod 中的其他容器启动时,/pause 进程会将这些容器的启动信息记录在特定的文件中。当 Pod 被删除时,/pause 进程会检测到该文件并停止记录,以确保 Pod 中的容器在 Pause 容器的基础上一致地执行生命周期操作。

  • 3、实现容器之间的协调合作
    Pause 容器还通过使用 cni 网络插件来实现容器之间的协调合作。当 Pod 中的其他容器启动时,cni 网络插件将为这些容器分配 IP 地址并设置网络连接。这确保了 Pod 中的各个容器可以互相通信,实现协调合作。

pause容器进入的方法

默认情况下,kubenets会将pod中的pause容器隐藏,无法通过kubectl get pod -oyaml查看,也无法通过kubectl exec 方式进入 pause容器。那么如何才能进入到pause容器内呢?答案是使用nsenter工具

不会显示pause容器

root@dg02-k8s-pnode1:~# kubectl get pod blog -oyaml |grep -A 5 containerID |grep name
    name: nginx
    name: wordpress

无法进入pause容器

root@dg02-k8s-pnode1:~# kubectl exec -it  blog -c nginx  -- hostname
blog
root@dg02-k8s-pnode1:~# kubectl exec -it  blog -c wordpress   -- hostname
blog
root@dg02-k8s-pnode1:~# kubectl exec -it  blog -c pause  -- hostname
Error from server (BadRequest): container pause is not valid for pod blog

登录宿主,可以看到名为pod的blog的包括3个容器,其中0ba9e933f876就是pause容器,从容器的启动命令是/pause可以确定

root@dg02-k8s-pnode3:~# docker ps |grep blog
92ed1b5ec240        5848c6a70fbb                                        "/start.sh"              23 hours ago        Up 23 hours                             k8s_wordpress_blog_default_ae1cef6f-fb64-48b1-85ab-c2741531f62c_0
f65ef066d577        5848c6a70fbb                                        "/start.sh"              23 hours ago        Up 23 hours                             k8s_nginx_blog_default_ae1cef6f-fb64-48b1-85ab-c2741531f62c_0
0fbd3a8cdf87        mirrors.myoas.com/nebula-docker/seg/pod/pause:3.1   "/pause"                 23 hours ago        Up 23 hours                             k8s_POD_blog_default_ae1cef6f-fb64-48b1-85ab-c2741531f62c_0

从宿主上尝试docker exec进入容器也不行,因为pause容器中没有sh环境

root@dg02-k8s-pnode3:~# docker exec -it 0fbd3a8cdf87 sh
OCI runtime exec failed: exec failed: container_linux.go:345: starting container process caused "exec: \"sh\": executable file not found in $PATH": unknown
root@dg02-k8s-pnode3:~#

查看pause容器在宿主中的pid

root@dg02-k8s-pnode3:~# docker inspect 0fbd3a8cdf87  |grep -i pid
            "Pid": 324460,
            "PidMode": "",
            "PidsLimit": 0,

查看pause容器对于的pid的状态

root@dg02-k8s-pnode3:~# cat /proc/324460/status |head
Name:	pause
Umask:	0022
State:	S (sleeping)
Tgid:	324460
Ngid:	0
Pid:	324460
PPid:	324437
TracerPid:	0
Uid:	0	0	0	0
Gid:	0	0	0	0

通过nsenter进入pause容器的网络命名空间

可以看到pause容器为pod提供一个网络命名空间,路由是192.168.26.0/24。而非宿主的路由表。

root@dg02-k8s-pnode3:~# nsenter -t  324460 -n bash
root@dg02-k8s-pnode3:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
3: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
    link/ether d6:1c:8e:24:f4:5b brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.26.103/24 brd 192.168.26.255 scope global eth0
       valid_lft forever preferred_lft forever
root@dg02-k8s-pnode3:~# ip route show
default via 192.168.26.1 dev eth0
192.168.0.0/16 via 192.168.26.1 dev eth0
192.168.26.0/24 dev eth0  proto kernel  scope link  src 192.168.26.103
root@dg02-k8s-pnode3:~#

从pause容器对于的网络命名空间退出,查看路由表,则显示的是宿主的路由信息

root@dg02-k8s-pnode3:~# exit
exit
root@dg02-k8s-pnode3:~# ip route show
default via 10.234.0.1 dev eth0
10.234.0.0/18 dev eth0  proto kernel  scope link  src 10.234.12.77
169.254.0.0/16 dev eth0  scope link  metric 1000
192.168.3.0/24 dev docker0  proto kernel  scope link  src 192.168.3.1 linkdown
192.168.26.0/24 dev cni0  proto kernel  scope link  src 192.168.26.1
192.168.38.0/24 via 192.168.38.0 dev flannel.1 onlink
192.168.60.0/24 via 192.168.60.0 dev flannel.1 onlink
root@dg02-k8s-pnode3:~#

pause容器的源码解析

说明:基于 kubernetes v1.12.0 源码分析

源码路径:k8s.io/kubernetes/build/pause/pause.c

源码中相关代码结构:

在这里插入图片描述

pause容器的相关代码主要包括三个文件:

pause.c:pause容器中的pause程序的源码

Makefile:由于pause.c程序代码是C语言编写,这里用Makefile来构建编译成二进制可执行文件

Dockerfile: 用于将二进制程序pause,打包成docker镜像

pause.c源码解读

pause.c 源码中主要做三件事情.

  • 如果参数是-v,则打印版本号后退出

  • 定义处理三种信号的两个hanlder处理函数。主要处理两类信息:SIGINT、SIGTERM 退出信号和 SIGCHLD 信号. 当收到 SIGINT 或是 SIGTERM 后, pause 进程可直接退出. 收到 SIGCHLD 信号, 则调用 waitpid 进行回收进程.

  • 主进程 for 循环调用 pause() 函数,使进程阻塞并处于永久"睡觉"状态, 不占用 cpu 资源, 直到被终止或是收到信号。

注意特别说明:

  • pod运行后,pause进程会作为 pod中PID 1的角色,当僵尸进程被父进程孤立时,pause.c代码中通过调用waitpid() 可以来捕获僵尸进程(参见sigreap)。这样我们就不会让僵尸在Kubernetes pod的PID命名空间中堆积。

  • 什么是僵尸进程 ? 简单说当子进程已经退出, 但因为其父进程没有回收释放, 导致仍然在进程表中的存在. 这里父进程需要调用 waitpid 系统调用来回收进行. 其实直接在 main 里配置忽略 SIGCHLD 信号也可以, 这样子进程的僵尸回收交给了 init 首进程, 也就是内核进程帮忙回收, 但不适合容器场景,因为容器里面init不是PID 1号进程.

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define STRINGIFY(x) #x
#define VERSION_STRING(x) STRINGIFY(x)

#ifndef VERSION
#define VERSION HEAD
#endif

/* 当收到 SIGINT, SIGTERM 两种信号后,会调用该函数. */
static void sigdown(int signo) {
  /* psignal() 函数用于将信号的编号转换为对应的信号名称,并将以指定格式打印到标准错误(stderr)。 */
  psignal(signo, "Shutting down, got signal");
  /* SIGINT 和 SIGTERM 都是正常干掉进程, exit code 为 0. */
  exit(0);
}

/* 当收到 SIGCHLD 信号也就是有子进程退出时,会调用该函数. */
static void sigreap(int signo) {
  /* waitpid 监听进程组的子进程退出, WNOHANG 是非阻塞标记, 当没有找到子进程退出时, 不会阻塞. */
  /* -1 是什么? 1 为 pod 主进程, 通常也是pgid, -1 则是表示等待任意的子进程  */
  /* NULL 表示不获取子进程的退出状态 */
  while (waitpid(-1, NULL, WNOHANG) > 0)
    ;
}

/* 函数入口 */
int main(int argc, char **argv) {
  int i;
  
  /* 打印 pause.c 版本 */
  for (i = 1; i < argc; ++i) {
    if (!strcasecmp(argv[i], "-v")) {
      printf("pause.c %s\n", VERSION_STRING(VERSION));
      return 0;
    }
  }

  
  
  if (getpid() != 1)
    /* 如果不是 1 号进程, 则打印错误.pause 进程在容器中必须已 1号进程运行 */
    fprintf(stderr, "Warning: pause should be the first process\n");

  
  
  /* 注册 signal 信号对应的回调方法 */
  if (sigaction(SIGINT, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
    // 当收到 SIGINT 信号,则调用上面定义的 sigdown()函数处理 
    return 1;
  if (sigaction(SIGTERM, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
     // 当收到 SIGTERM 信号,则调用上面定义的 sigdown()函数处理 
    return 2;
  if (sigaction(SIGCHLD, &(struct sigaction){.sa_handler = sigreap,
                                             .sa_flags = SA_NOCLDSTOP},
                NULL) < 0)
    // 当收到 SIGCHLD 信号,则调用上面定义的 sigreap()函数处理 
    return 3;

  // 循环等待
  for (;;)
    pause(); // 阻塞并等待 signal 信号
  fprintf(stderr, "Error: infinite loop terminated\n");
  return 42;
}

源码是有C语言编写,为了深刻理解pause.c代码,接下来我们对源码涉及的函数与系统调用方法进行逐一讲解与验证。

singal的种类

linux中信号用于通知进程发生了特定的事件或在进程之间进行通信。以下是一些常见的信号(不完整列表)以及它们的基本含义:

SIGTERM:终止信号,用于请求进程正常终止。
SIGKILL:强制终止信号,用于立即终止进程。
SIGSTOP:停止信号,用于暂停进程的执行。
SIGCONT:继续信号,用于恢复被停止的进程的执行。
SIGCHLD:子进程状态改变信号,用于通知父进程子进程的退出或停止。
SIGUSR1 和 SIGUSR2:用户自定义信号,可用于进程间自定义通信。
SIGHUP:终端挂起信号,用于通知进程它的终端口是否断开连接。
SIGPIPE:管道破裂信号,用于通知进程写入了一个已关闭的管道。
SIGSEGV:段错误信号,用于通知进程访问了无效的内存地址。
SIGBUS:总线错误信号,用于通知进程发生了总线错误。
SIGILL:非法指令信号,用于通知进程执行了非法的机器指令。
SIGFPE:浮点数异常信号,用于通知进程发生了浮点数运算异常。
SIGALRM:定时器到时信号,用于定时器事件。
SIGUSR1SIGUSR2:用户自定义信号,可用于进程间自定义通信

psingal()

UNIX-like 系统中,psignal() 函数用于将信号的编号转换为对应的信号名称,并将以指定格式打印到标准错误(stderr)。
psignal() 函数位于 <signal.h> 头文件中,其函数原型如下:

void psignal(int signum, const char *msg);

参数说明:
signum:要转换的信号编号。
msg:附加的用户自定义消息字符串,可以为空。
关于信号编号,可以使用 <signal.h> 中定义的常量,如 SIGINT、SIGTERM 等。
以下是一个简单的示例程序,演示 psignal() 函数的用法:

root@dg02-k8s-pnode1:~/pause# cat psignal.c
#include <stdio.h>
#include <signal.h>

int main() {
    /* int signum = SIGILL;*/
    int signum = SIGINT;

    psignal(signum, "Shutting down, got signal");

    return 0;
}

上述程序中调用了 psignal() 函数,将信号编号 SIGILL 转换为对应的信号名称并输出到标准错误流。可通过在终端运行该程序,触发信号 SIGILL 来查看结果。
输出类似于:

root@dg02-k8s-pnode1:~/pause# gcc psignal.c  -o psingal.out
root@dg02-k8s-pnode1:~/pause# ./psingal.out
Shutting down, got signal: Interrupt

注意:
psignal() 函数会将错误消息输出到标准错误流,因此在终端中可能无法捕捉到输出,但可以通过重定向或其他方法进行处理。
psignal() 函数是线程安全的,在多线程环境下也可以正常使用

waitpid()

waitpid() 是一个在操作系统中非常常见的函数,用于等待一个子进程的终止。它的主要作用是将父进程挂起,直到指定的子进程退出或被信号中断。 以下是该函数的介绍:

pid_t waitpid(pid_t pid, int *status, int options);

pid 参数指定要等待的子进程的进程ID:
pid > 0:等待指定进程ID的子进程
pid = -1:等待任意子进程
pid = 0:等待与调用进程属于同一进程组的所有子进程。
pid < -1:等待与进程组ID等于pid绝对值的任意子进程。
status 参数是一个整型指针,用于存储子进程的终止状态。可以通过一些宏和函数来解释状态:
WIFEXITED(status):如果子进程正常退出,则返回true。
WEXITSTATUS(status):如果WIFEXITED()返回true,则返回子进程的退出状态。
WIFSIGNALED(status):如果子进程由于未捕获的信号而终止,则返回true。
WTERMSIG(status):如果WIFSIGNALED()返回true,则返回导致子进程终止的信号编号。
options 参数是一个整型值,用于指定等待的行为和选项,例如:
WNOHANG:如果没有可用的子进程退出,则立即返回,而不挂起父进程。
WUNTRACED:如果子进程进入暂停状态,也返回。

while (waitpid(-1, NULL, WNOHANG) > 0)

waitpid(-1, NULL, WNOHANG) 表示等待任意一个子进程退出,参数中的 -1 表示等待任意子进程,NULL 表示不获取子进程的退出状态,WNOHANG 表示非阻塞方式。该函数会立即返回:
如果有子进程退出,返回值为子进程的进程ID(PID);
如果没有子进程退出,返回值为 0。

waitpid(-1, NULL, WNOHANG) > 0中,检查waitpid()返回值是否大于 0,即是否有子进程退出。如果有子进程退出,进入循环体内执行相应的操作。
通过这段代码,父进程可以在不阻塞的情况下,检查是否有子进程退出,并采取相应的处理机制。

注意,该代码片段在一个循环内while (waitpid(-1, NULL, WNOHANG) > 0),那么就可以连续获得多个子进程退出的信息。
以下是一个示例说明该代码的用途(C语言):

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    pid_t childPid;
    int i;

    for (i = 0; i < 5; i++) {
        childPid = fork();

        if (childPid == 0) {
            // 子进程
            sleep(i + 1);
            return i + 1;
        }
    }

    // while 循环等待所有子进程的退出
    while (waitpid(-1, NULL, WNOHANG) > 0) {
        // 等待子进程退出,并进行相关处理
        printf("子进程退出\n");
    }

    printf("所有子进程退出\n");

    return 0;
}

代码编译、运行与输出

root@dg02-k8s-pnode1:~/pause# gcc mywaitpid.c -o mywaitpid.out
root@dg02-k8s-pnode1:~/pause# ./mywaitpid.out
所有子进程退出

上述代码创建了5个子进程,每个子进程在不同的时间间隔后退出。父进程使用 waitpid(-1, NULL, WNOHANG) 在循环中检查是否有子进程退出,如果有子进程退出,则输出相应的信息。当所有子进程都退出后,跳出循环并输出相应的信息。

pause()

UNIX-like 系统中, pause() 是一个系统调用函数,用于使调用进程暂停执行,直到收到一个信号为止。pause() 函数可以用于等待信号的到来,然后根据信号的类型采取相应的行动。
pause() 函数位于 <unistd.h> 头文件中,其函数原型如下:

int pause(void);

该函数没有参数,返回值为 -1 且设置 errno 为 EINTR 在收到信号时。
以下是一个简单的示例程序,演示 pause() 函数的用法:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void handler(int signum) {
    printf("收到信号 %d\n", signum);
}

int main() {
    printf("等待信号...\n");

    // 注册信号处理函数
    signal(SIGINT, handler);

    // 阻塞并等待信号
    pause();

    printf("退出程序\n");

    return 0;
}

代码编译、运行与输出

root@dg02-k8s-pnode1:~/pause# gcc pause.c -o pause.out
root@dg02-k8s-pnode1:~/pause# ./pause.out
等待信号...
^C收到信号 2
退出程序

上述程序中先注册了信号处理函数 handler()。程序会在 pause() 函数处等待信号的到来,如果收到 SIGINT 信号(例如按下 Ctrl+C),则调用信号处理函数输出相应信息并返回程序继续执行。
需要注意的是,pause() 函数虽然看起来像是一个阻塞式函数,但实际上当接收到一个信号时,它会立即返回并设置 errno 为 EINTR。因此,如果没有正确的信号处理函数,pause() 函数可能会导致程序一直阻塞。

pause容器镜像的构建

利用Makefile将pause.c源码编译成二进制可执行文件pause后,放到bin/pause-${ARCH}目录下后.接下来就可以使用Dockerfile,将二进制文件打包成docker镜像了。由于Makefile涉及特定的语法,这里就不展开描述,如感兴趣可以查看源码。

接下来再看看源码中的Dokerfile内容:

FROM scratch
ARG ARCH
ADD bin/pause-${ARCH} /pause
ENTRYPOINT ["/pause"]

内容很简单,基础镜像是scratch,将pause二进制可执行文件,放到镜像的/目录下,再设置启动命令/pause

总结

本文从详细介绍pause.c源码的执行逻辑,在此基础上并总结了pause容器的作用。进过本文的学习我们对pause容器的实现有了本质的认识。

参考文档

https://linux.die.net/man/2/waitpid

https://github.com/rfyiamcool/notes/blob/main/kubernetes_pause_code.md

https://cloud.tencent.com/developer/article/1356655

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

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

相关文章

echarts 实现中国geo地图自定义贴图实例

文章目录 1. 实现效果2. 设置自定义图片&#xff0c;做好准备3. echarts 实现贴图 1. 实现效果 实现自定义背景图&#xff0c;给echarts地图贴背景图效果&#xff0c; 先准备两张背景图片&#xff0c;一张是默认的&#xff0c;一张是鼠标悬浮替换的&#xff0c;如下两张图 2.…

基于EchoMimic加速版,可编辑标志点控制实现逼真音频驱动的肖像动画

EchoMimic 是蚂蚁集团终端技术部门开发的一项技术,旨在通过音频驱动生成逼真的肖像动画。对于那些初次接触这项技术的用户,本教程将带你逐步了解如何设置开发环境、获取项目代码、安装依赖,并最终成功运行示例生成自己的肖像动画。 文章目录 项目代码安装依赖业务拓展参数调…

webpack的热更新原理

Webpack热更新&#xff08; Hot Module Replacement&#xff0c;简称 HMR&#xff09;&#xff0c;无需完全刷新整个页面的同时&#xff0c;更新所有类型的模块&#xff0c;是 Webpack 提供的最有用的功能之一。 保留在完全重新加载页面期间丢失的应用程序状态。只更新变更内容…

【bug】通过lora方式微调sdxl inpainting踩坑

报错内容 ValueError: Attempting to unscale FP16 gradients. 报错位置 if accelerator.sync_gradients:params_to_clip (itertools.chain(unet_lora_parameters, text_lora_parameters_one, text_lora_parameters_two)if args.train_text_encoderelse unet_lora_parameters…

温湿度传感器SHT20的功能介绍和时序分析

目录 概述 1 认识SHT20 1.1 SHT20介绍 1.2 SHT20属性 1.3 接口介绍 1.4 SHT20的相关命令 1.5 转换时间 2 寄存器操作 2.1 复位操作 2.2 User Register 2.3 CRC Checksum 3 温湿度计算 3.1 相对湿度转换 3.2 温度换算 3.3 转换公式的C语言实现 概述 本文主要介绍…

ChatGLM-6B部署到本地电脑

引言 ChatGLM-6B是由清华大学开源的双语对话大模型&#xff0c;该模型有62亿参数&#xff0c;但在经过量化后模型体积大幅下降&#xff0c;因此不同于其他需要部署到服务器上的大模型&#xff0c;该模型可以部署到本地电脑&#xff0c;那么接下来我们来看看如何部署该模型。 …

OpenAI API key not working in my React App

题意&#xff1a;OpenAI API 密钥在我的 React 应用中不起作用 问题背景&#xff1a; I am trying to create a chatbot in my react app, and Im not able to generate an LLM powered response. Ive been studying documentation and checking out tutorials but am unable …

【CMake】使用CMake在Visual Studio中配置glad和glfw

下载glad和glfw g l a d glad glad下载&#xff1a;glad下载 这个是 g i t h u b github github上的资源&#xff0c;进不去的话就开开魔法。 g l f w glfw glfw下载&#xff1a;glfw下载 下载CMake C M a k e CMake CMake下载&#xff1a; CMake下载 根据自己的平台选择&…

【Java 优选算法】双指针(下)

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 有效三角形的个数 题目链接 解法 解法1:暴力枚举--->O(n^3) 解法2:利用单调性,使用双指针来解决---->O(n^2) 优化:对整个数组进行排序先固定最大数在最大数的左…

文心智能体 城市印象之漫行北京 开发分享

城市印象之漫行北京 请点击文心智能体平台AgentBuilder | 想象即现实 (baidu.com) 一、开发灵感 这个智能体为笔者“城市印象”系列当中的作品之一&#xff0c;这个智能体侧重于为用户提供丈量北京的个性化城市之旅&#xff0c;或漫步历史文化街区细细品味&#xff0c;或领略…

动态内存管理之malloc,free,calloc和realloc函数

Hello&#xff0c;各位小伙伴们&#xff0c;小编在这里祝福各位中秋佳节快乐呀&#xff0c;今天让我们来学习一下动态内存管理吧&#xff01; 引言 像我们之前在开辟一段空间的时候你可能会使用整型变量来申请一块空间&#xff0c;或者使用数组来申请一段连续的空间&#xff…

网络协议全景:Linux环境下的TCP/IP、UDP

目录 1.UDP协议解析1.1.定义1.2.UDP报头1.3.特点1.4.缓冲区 2.TCP协议解析2.1.定义2.2.报头解析2.2.1.首部长度&#xff08;4位&#xff09;2.2.2.窗口大小2.2.3.确认应答机制2.2.4.6个标志位 2.3.超时重传机制2.4.三次握手四次挥手2.4.1.全/半连接队列2.4.2.listen2.4.3.TIME_…

复选按钮QCheckBox

使用场景&#xff1a;多选多 文本 // 获取和设置显示的文本 QString text() const void setText(const QString &text) 三态 复选按钮有三种状态 Qt::Checked 选中Qt::Unchecked 非选中Qt::PartiallyChecked 半选中&#xff0c;比如一组复选按钮中&#xff0c;只选择了…

PointNet++改进策略目录

后续我将如何使用文章中创新点加入的PointNet中代码实现部分进行更新 题目原理解析代码改进PointNet改进策略 &#xff1a;模块改进 | LFA | RandLA-Net&#xff0c;通过随机采样与局部特征聚合提升大规模3D点云处理效率✔️❌PointNet改进策略 &#xff1a;模块改进 | Residua…

Renesas R7FA8D1BH (Cortex®-M85)控制SHT20

目录 概述 1 硬件接口介绍 2 SHT20模块 2.1 SHT20简介 2.2 SHT-20模块电路 3 I2C接口实现 3.1 FSP配置I2C 3.2 I2C驱动程序实现 4 SHT20驱动程序 4.1 SHT20驱动代码结构 4.2 源代码文件 5 测试 5.1 测试功能介绍 5.2 测试代码实现 5.3 运行代码 概述 本文主要介…

ubuntu虚拟机装载共享文件夹导致的诡异错误

最近使用vmware station 15 安装了 ubuntu22.04 的虚拟机。在装载共享文件夹不久后便会出现诡异的错误。目前在网络上好像没有人把这归结到装载共享文件夹的问题上&#xff0c;故以供参考。 第一次&#xff1a; 在装载之后大概第二次开机&#xff0c;出现报错界面。 提示蓝牙…

C++二叉搜索树学习

目录 一、二叉搜索树概念 二、二叉搜索树的性能分析 三、二叉搜索树的构建 一、二叉搜索树概念 二叉搜索树又叫做二叉排序树&#xff0c;它可以是一颗空树&#xff0c;或者是具有以下性质的二叉树&#xff1a; 若该树的左子树不为空&#xff0c;那么左子树上的任一节点都小…

硬件工程师笔试面试——存储器件

目录 16、存储器件 16.1 基础 存储器件实物图 16.1.1 概念 16.1.2 常见的存储器件及其特点 16.2 相关问题 16.2.1 不同类型的存储器件在成本和性能上有哪些具体的差异 16.2.2 如何根据应用需求选择合适的存储器件? 16.2.3 存储器件的耐用性和可靠性是如何影响其在不同…

【Unity】 HTFramework框架(五十六)MarkdownText:支持运行时解析并显示Markdown文本

更新日期&#xff1a;2024年9月15日。 Github源码&#xff1a;[点我获取源码] Gitee源码&#xff1a;[点我获取源码] 索引 MarkdownText支持的Markdown语法标题强调文本表格嵌入图像超链接 使用MarkdownText设置项运行时属性解析使用ID模式嵌入图像 MarkdownText MarkdownText…

【算法思想·二叉搜索树】基操篇

本文参考labuladong算法笔记[二叉搜索树心法&#xff08;基操篇&#xff09; | labuladong 的算法笔记] 1、概述 我们前文 东哥带你刷二叉搜索树&#xff08;特性篇&#xff09; 介绍了 BST 的基本特性&#xff0c;还利用二叉搜索树「中序遍历有序」的特性来解决了几道题目&am…