分析
我们搭建完集群了以后,可以使用最简单的方式创建一个pod,随意你建立什么pod,去访问相应node上执行docker ps
就会看到有一种pause容器,但是你可能从来没有启用
etrics-scraper_dashboard-metrics-scraper-7b59f7d4df-pqntv_kubernetes-dashboard_0516991e-e13a-4830-826f-6bb3a543b314_2
9a69b76f1410 lizhenliang/pause-amd64:3.0 "/pause" 2 hours ago Up 2 hours k8s_POD_dashboard-metrics-scraper-7b59f7d4df-pqntv_kubernetes-dashboard_0516991e-e13a-4830-826f-6bb3a543b314_2
1dab2142e86c lizhenliang/pause-amd64:3.0 "/pause" 2 hours ago Up 2 hours k8s_POD_calico-node-9w6vz_kube-system_26c6db9f-a21a-46da-b22e-32b5c980db3c_2
那么它是啥呢?看看源码(C语言写的,简单看看吧)
知识普及
软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。在软件层次上是对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是进程间通信机制中唯一的异步通信机制,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。信号机制除了基本通知功能外,还可以传递附加信息。
SIGHUP 1 A 终端挂起或者控制进程终止
SIGINT 2 A 键盘中断(如break键被按下)
SIGQUIT 3 C 键盘的退出键被按下
SIGILL 4 C 非法指令
SIGABRT 6 C 由abort(3)发出的退出指令
SIGFPE 8 C 浮点异常
SIGKILL 9 AEF Kill信号
SIGSEGV 11 C 无效的内存引用
SIGPIPE 13 A 管道破裂: 写一个没有读端口的管道
SIGALRM 14 A 由alarm(2)发出的信号
SIGTERM 15 A 终止信号
SIGUSR1 30,10,16 A 用户自定义信号1
SIGUSR2 31,12,17 A 用户自定义信号2
SIGCHLD 20,17,18 B 子进程结束信号
SIGCONT 19,18,25 进程继续(曾被停止的进程)
SIGSTOP 17,19,23 DEF 终止进程
SIGTSTP 18,20,24 D 控制终端(tty)上按下停止键
SIGTTIN 21,21,26 D 后台进程企图从控制终端读
SIGTTOU 22,22,27 D 后台进程企图从控制终端写
strcasecmp用忽略大小写比较字符串.,通过strncasecmp函数可以指定每个字符串用于比较的字符数,strcasecmp用来比较参数s1和s2字符串前n个字符,比较时会自动忽略大小写的差异。
fprintf 函数的功能是: Print formatted data to a stream格式化输出数据到流
strerr是作为程序运行过程中的错误显示出来的
#include <signal.h> #角度啊啥的函数库
#include <stdio.h> #C最基础的库
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> #系统类型的函数库
#include <sys/wait.h> #系统进程wait控制函数库
#include <unistd.h> #从开始到现在都是调用库,会用到库里面的一些函数库
#define STRINGIFY(x) #x
#define VERSION_STRING(x) STRINGIFY(x)
#ifndef VERSION
#define VERSION HEAD
#endif #到这里是重定义一些变量,不会C的可以理解为alias
static void sigdown(int signo) {
psignal(signo, "Shutting down, got signal"); #一个函数,意思让signal软中断信号停止
exit(0);
}
static void sigreap(int signo) {
while (waitpid(-1, NULL, WNOHANG) > 0) #循环waitpid函数获取相应的pid
;
}
int main(int argc, char **argv) { #主函数终于来了***************
int i;
for (i = 1; i < argc; ++i) { #循环判断
if (!strcasecmp(argv[i], "-v")) { #比较字符串组 argv[i]版本是否与需求版本一致
printf("pause.c %s\n", VERSION_STRING(VERSION)); #不是这输出pause版本
return 0;
}
}
if (getpid() != 1) #判断获取的pid号是否为1
/* Not an error because pause sees use outside of infra containers. */
fprintf(stderr, "Warning: pause should be the first process\n"); #将错误信息打印,并打印提示信息
if (sigaction(SIGINT, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0) #A 键盘中断(如break键被按下)
return 1;
if (sigaction(SIGTERM, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0) # A 终止信号
return 2;
if (sigaction(SIGCHLD, &(struct sigaction){.sa_handler = sigreap,
.sa_flags = SA_NOCLDSTOP},
NULL) < 0) # B 子进程结束信号
return 3;
for (;;) #死循环
pause(); #等待
fprintf(stderr, "Error: infinite loop terminated\n"); #将错误信息打印,并打印提示信息
return 42;
}
由以上我们差不多读懂了,这个玩意很傲娇啊,抢到了pid为1后执行一个等待函数,就死循环等待了。
那么它为啥要抢pid等于1的init进程呢?
先了解一下init进程
描述init进程,它是内核启动的第一个用户级进程。init有许多很重要的任务,比如像启动getty(用于用户登录)、实现运行级别、以及处理孤立进程。
x86和x86-64是Linux作业系统的常用指令集架构。
BIOS/UEFI针对实际的硬件平台执行硬件初始化任务。
由启动程式载入initrd/initramfs,并由启动程式载入Linux核心。
内核将配置系统功能,譬如配置硬件,称为start_kernel(),这会执行大部分系统配置(中断、内存管理、设备和驱动程式初始化等)。然后它分别启动内存管理进程、init进程等在用户空间执行的进程。
Init有特定的运行级别(System V)或目标(Systemd),每个运行级别或目标都是由特定的一组服务(守护进程)组成。
一个典型的桌面环境从X显示管理器开始初始化,X显示管理器显示登入画面,成功登入后由X显示管理器启动桌面环境(如GNOME、KDE)。
关机时,init会结束所有用户空间处理程序。init随后终止,内核自行关闭。
Init是Linux的根进程。进程号为1,它是所有进程的父进程
我们知道pod内的所有容器共用一个namespace(名字空间),就意味着,他们之间pid号是公用的,根据init的意思,给其他容器的pid造了一个爹(一切进程的父进程)出来。它活着还好,init没了,pod就嘎了。
那么这个爹有啥用呢?
站在进程角度,它可以回收僵尸进程。哦哦哦,可以回收僵尸进程啊,真棒(听懂了耶)
算了吧,那我们还是一块说说什么是僵尸进程吧。
书上说:
僵尸进程是已停止运行但是进程表条目依然存在的进程,父进程尚未通过wait系统调用进行检索。僵尸进程无法通过kill命令清除。只能通过父进程wait进行索检。
系统有过多僵尸进程将会占用大量操作系统进程表资源
如果父进程在子进程完成前退出,OS将子进程分配给init进程,init进程就“收养”子进程并成为其父进程。
简单的说,就是pod的其他容器意外退出时候产生的僵尸子进程会被pause容器进行收养,并且wait系统函数回收减少进程表占用。
好的第一个功能占用pid 1说完了,那么循环执行pause()暂停函数几个意思?
一个意思,作为pod共享namespace的基础,pod内其他所有容器都是用它的名字空间
namespace即“命名空间”,也称“名称空间” 。是许多编程语言使用的一种代码组织的形式,通过命名空间来分类,区别不同的代码功能,避免不同的代码片段(通常由不同的人协同工作或调用已有的代码片段)同时使用时由于不同代码间变量名相同而造成冲突。
这么说又蒙了,docker这种容器不是有分割名字空间能力吗?要它干啥?
几个问题
你重启docker后,IP变了吗?
重启docker以后你的容器hostname改变还存在吗?
你要运行三个软件作为一个pod,难道运行在一个docker里面吗?以达到在同一名字空间下的目的
那要是一百个软件呢?那还是容器吗?还能体现容器的优越性吗?
我们说的单pod单ip段,单pod单namespace就是靠它来的
它利用暂停的容器,一直占用着属于它pod的namespace,防止应用容器的死亡,导致整个pod资源被释放,给我的感觉就是像火种,不论部落里有啥变化,火种不灭,生生不息。
总结
一共有俩作用
pod的其他容器意外退出时候产生的僵尸子进程会被pause容器进行收养,并且wait系统函数回收减少进程表占用
作为pod共享namespace的基础,pod内其他所有容器都是用它的名字空间
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/156032.html