This commit is contained in:
周伟
2022-05-11 19:04:14 +08:00
parent 9440ac7291
commit d9c5ffd627
826 changed files with 0 additions and 481675 deletions

View File

@@ -25,13 +25,7 @@
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
@@ -55,385 +49,196 @@
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/容器实战高手课/00 开篇词 一个态度两个步骤,成为容器实战高手.md.html">00 开篇词 一个态度两个步骤,成为容器实战高手.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/01 认识容器:容器的基本操作和实现原理.md.html">01 认识容器:容器的基本操作和实现原理.md.html</a>
</li>
<li>
<a class="current-tab" href="/专栏/容器实战高手课/02 理解进程1为什么我在容器中不能kill 1号进程.md.html">02 理解进程1为什么我在容器中不能kill 1号进程.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/03 理解进程2为什么我的容器里有这么多僵尸进程.md.html">03 理解进程2为什么我的容器里有这么多僵尸进程.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/04 理解进程3为什么我在容器中的进程被强制杀死了.md.html">04 理解进程3为什么我在容器中的进程被强制杀死了.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/05 容器CPU1怎么限制容器的CPU使用.md.html">05 容器CPU1怎么限制容器的CPU使用.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/06 容器CPU2如何正确地拿到容器CPU的开销.md.html">06 容器CPU2如何正确地拿到容器CPU的开销.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/07 Load Average加了CPU Cgroup限制为什么我的容器还是很慢.md.html">07 Load Average加了CPU Cgroup限制为什么我的容器还是很慢.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/08 容器内存:我的容器为什么被杀了?.md.html">08 容器内存:我的容器为什么被杀了?.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/09 Page Cache为什么我的容器内存使用量总是在临界点.md.html">09 Page Cache为什么我的容器内存使用量总是在临界点.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/10 Swap容器可以使用Swap空间吗.md.html">10 Swap容器可以使用Swap空间吗.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/11 容器文件系统:我在容器中读写文件怎么变慢了.md.html">11 容器文件系统:我在容器中读写文件怎么变慢了.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/12 容器文件Quota容器为什么把宿主机的磁盘写满了.md.html">12 容器文件Quota容器为什么把宿主机的磁盘写满了.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/13 容器磁盘限速:我的容器里磁盘读写为什么不稳定.md.html">13 容器磁盘限速:我的容器里磁盘读写为什么不稳定.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/14 容器中的内存与IO容器写文件的延时为什么波动很大.md.html">14 容器中的内存与IO容器写文件的延时为什么波动很大.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/15 容器网络我修改了procsysnet下的参数为什么在容器中不起效.md.html">15 容器网络我修改了procsysnet下的参数为什么在容器中不起效.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/16 容器网络配置1容器网络不通了要怎么调试.md.html">16 容器网络配置1容器网络不通了要怎么调试.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/17 容器网络配置2容器网络延时要比宿主机上的高吗.md.html">17 容器网络配置2容器网络延时要比宿主机上的高吗.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/18 容器网络配置3容器中的网络乱序包怎么这么高.md.html">18 容器网络配置3容器中的网络乱序包怎么这么高.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/19 容器安全1我的容器真的需要privileged权限吗.md.html">19 容器安全1我的容器真的需要privileged权限吗.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/20 容器安全2在容器中我不以root用户来运行程序可以吗.md.html">20 容器安全2在容器中我不以root用户来运行程序可以吗.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/加餐01 案例分析怎么解决海量IPVS规则带来的网络延时抖动问题.md.html">加餐01 案例分析怎么解决海量IPVS规则带来的网络延时抖动问题.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/加餐02 理解perf怎么用perf聚焦热点函数.md.html">加餐02 理解perf怎么用perf聚焦热点函数.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/加餐03 理解ftrace1怎么应用ftrace查看长延时内核函数.md.html">加餐03 理解ftrace1怎么应用ftrace查看长延时内核函数.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/加餐04 理解ftrace2怎么理解ftrace背后的技术tracepoint和kprobe.md.html">加餐04 理解ftrace2怎么理解ftrace背后的技术tracepoint和kprobe.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/加餐05 eBPF怎么更加深入地查看内核中的函数.md.html">加餐05 eBPF怎么更加深入地查看内核中的函数.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/加餐06 BCC入门eBPF的前端工具.md.html">加餐06 BCC入门eBPF的前端工具.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/加餐福利 课后思考题答案合集.md.html">加餐福利 课后思考题答案合集.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/结束语 跳出舒适区,突破思考的惰性.md.html">结束语 跳出舒适区,突破思考的惰性.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
@@ -443,9 +248,6 @@
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
@@ -453,9 +255,6 @@
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
@@ -485,9 +284,6 @@
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
@@ -511,13 +307,7 @@ function hide_canvas() {
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
@@ -599,9 +389,6 @@ root 8 1 0 07:25 ? 00:00:00 /usr/bin/coreutils --coreutils-p
root 9 0 6 07:27 pts/0 00:00:00 bash
root 22 9 0 07:27 pts/0 00:00:00 ps -ef
[<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="b5c7dadac1f580d6d6838c858683d782d787">[email&#160;protected]</a> /]# kill 1
[<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="9ae8f5f5eedaaff9f9aca3aaa9acf8adf8a8">[email&#160;protected]</a> /]# kill -9 1
@@ -617,9 +404,6 @@ root 9 0 0 07:27 pts/0 00:00:00 bash
root 23 1 0 07:27 ? 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 100
root 24 9 0 07:27 pts/0 00:00:00 ps -ef
</code></pre>
<p>当我们完成前面的操作,就会发现无论运行 kill 1 (对应 Linux 中的 SIGTERM 信号)还是 kill -9 1对应 Linux 中的 SIGKILL 信号),都无法让进程终止。</p>
@@ -669,9 +453,6 @@ init/main.c
* trying to recover a really broken machine.
*/
if (execute_command) {
ret = run_init_process(execute_command);
@@ -685,9 +466,6 @@ init/main.c
execute_command, ret);
}
if (!try_to_run_init_process(&quot;/sbin/init&quot;) ||
!try_to_run_init_process(&quot;/etc/init&quot;) ||
@@ -701,15 +479,9 @@ init/main.c
panic(&quot;No working init found. Try passing init= option to kernel. &quot;
&quot;See Linux Documentation/admin-guide/init.rst for guidance.&quot;);
$ ls -l /sbin/init
lrwxrwxrwx 1 root root 20 Feb 5 01:07 /sbin/init -&gt; /lib/systemd/systemd
</code></pre>
<p>在 Linux 上有了容器的概念之后,一旦容器建立了自己的 Pid Namespace进程命名空间这个 Namespace 里的进程号也是从 1 开始标记的。所以,容器的 init 进程也被称为 1 号进程。</p>
@@ -743,9 +515,6 @@ $ kill -l
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS
</code></pre>
<p>用一句话来概括信号Signal其实就是 Linux 进程收到的一个通知。这些通知产生的源头有很多种,通知的类型也有很多种。</p>
@@ -815,9 +584,6 @@ int main(int argc, char *argv[])
return 0;
}
</code></pre>
<pre><code># docker stop sig-proc;docker rm sig-proc
@@ -835,17 +601,11 @@ root 1 0 0 07:48 ? 00:00:00 /c-init-nosig
root 6 0 5 07:48 pts/0 00:00:00 bash
root 19 6 0 07:48 pts/0 00:00:00 ps -ef
[<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="91e3fefee5d1a4f5a2f5a5a3f0a1a2a0f3a0">[email&#160;protected]</a> /]# kill 1
[<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="cab8a5a5be8affaef9aefef8abfaf9fba8fb">[email&#160;protected]</a> /]# kill -9 1
[<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e99b86869da9dc8dda8ddddb88d9dad88bd8">[email&#160;protected]</a> /]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 07:48 ? 00:00:00 /c-init-nosig
@@ -853,9 +613,6 @@ root 1 0 0 07:48 ? 00:00:00 /c-init-nosig
root 6 0 0 07:48 pts/0 00:00:00 bash
root 20 6 0 07:49 pts/0 00:00:00 ps -ef
</code></pre>
<p>我们是不是这样就可以得出结论——“容器里的 1 号进程,完全忽略了 SIGTERM 和 SIGKILL 信号了”呢?你先别着急,我们再拿其他语言试试。</p>
@@ -871,9 +628,6 @@ root 20 6 0 07:49 pts/0 00:00:00 ps -ef
# cat go-init.go
package main
import (
&quot;fmt&quot;
@@ -881,9 +635,6 @@ import (
&quot;time&quot;
)
func main() {
fmt.Println(&quot;Start app\n&quot;)
@@ -891,9 +642,6 @@ func main() {
time.Sleep(time.Duration(100000) * time.Millisecond)
}
</code></pre>
<pre><code>
@@ -903,13 +651,7 @@ func main() {
# docker run --name sig-proc -d registry/sig-proc:v1 /go-init
# docker exec -it sig-proc bash
[<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="05776a6a71453736316437366464303c3267">[email&#160;protected]</a> /]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 1 08:04 ? 00:00:00 /go-init
@@ -917,21 +659,12 @@ root 1 0 1 08:04 ? 00:00:00 /go-init
root 10 0 9 08:04 pts/0 00:00:00 bash
root 23 10 0 08:04 pts/0 00:00:00 ps -ef
[<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="8efce1e1facebcbdbaefbcbdefefbbb7b9ec">[email&#160;protected]</a> /]# kill -9 1
[<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e1938e8e95a1d3d2d580d3d28080d4d8d683">[email&#160;protected]</a> /]# kill 1
[<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="285a47475c681a1b1c491a1b49491d111f4a">[email&#160;protected]</a> /]# [~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
</code></pre>
<p>对于这个测试结果,你是不是反而觉得更加困惑了?</p>
@@ -959,9 +692,6 @@ CONTAINER ID IMAGE COMMAND CREATED
<p>我们来看下面这串代码,这里表示一旦这三个子条件都被满足,那么这个信号就不会发送给进程。</p>
<pre><code>kernel/signal.c
static bool sig_task_ignored(struct task_struct *t, int sig, bool force)
{
@@ -969,27 +699,15 @@ static bool sig_task_ignored(struct task_struct *t, int sig, bool force)
void __user *handler;
handler = sig_handler(t, sig);
/* SIGKILL and SIGSTOP may not be sent to the global init */
if (unlikely(is_global_init(t) &amp;&amp; sig_kernel_only(sig)))
return true;
if (unlikely(t-&gt;signal-&gt;flags &amp; SIGNAL_UNKILLABLE) &amp;&amp;
handler == SIG_DFL &amp;&amp; !(force &amp;&amp; sig_kernel_only(sig)))
return true;
/* Only allow kernel generated signals to this kthread */
if (unlikely((t-&gt;flags &amp; PF_KTHREAD) &amp;&amp;
@@ -997,17 +715,8 @@ static bool sig_task_ignored(struct task_struct *t, int sig, bool force)
(handler == SIG_KTHREAD_KERNEL) &amp;&amp; !force))
return true;
return sig_handler_ignored(handler, sig);
}
</code></pre>
<p>接下来,我们就逐一分析一下这三个子条件,我们来说说这个&quot;!(force &amp;&amp; sig_kernel_only(sig))&quot;</p>
@@ -1037,13 +746,7 @@ static bool sig_task_ignored(struct task_struct *t, int sig, bool force)
p-&gt;signal-&gt;flags |= SIGNAL_UNKILLABLE;
}
/*
* is_child_reaper returns true if the pid is the init process
* of the current namespace. As this one could be checked before
@@ -1053,9 +756,6 @@ static bool sig_task_ignored(struct task_struct *t, int sig, bool force)
* with the pid number.
*/
static inline bool is_child_reaper(struct pid *pid)
{
@@ -1063,9 +763,6 @@ static inline bool is_child_reaper(struct pid *pid)
return pid-&gt;numbers[pid-&gt;level].nr == 1;
}
</code></pre>
<p>我们可以看出来,其实最关键的一点就是 handler == SIG_DFL 。Linux 内核针对每个 Nnamespace 里的 init 进程,把只有 default handler 的信号都给忽略了。</p>
@@ -1093,25 +790,16 @@ static inline bool is_child_reaper(struct pid *pid)
# cat /proc/1/status | grep -i SigCgt
SigCgt: fffffffe7fc1feff
# ## C init
# cat /proc/1/status | grep -i SigCgt
SigCgt: 0000000000000000
# ## bash init
# cat /proc/1/status | grep -i SigCgt
SigCgt: 0000000000010002
</code></pre>
<p>第二件事,给 C 程序注册一下 SIGTERM handler捕获 SIGTERM。</p>
@@ -1129,9 +817,6 @@ SigCgt: 0000000000010002
# include &lt;sys/wait.h&gt;
# include &lt;unistd.h&gt;
void sig_handler(int signo)
{
@@ -1145,17 +830,11 @@ void sig_handler(int signo)
}
}
int main(int argc, char *argv[])
{
signal(SIGTERM, sig_handler);
printf(&quot;Process is sleeping\n&quot;);
while (1) {
@@ -1167,9 +846,6 @@ int main(int argc, char *argv[])
return 0;
}
</code></pre>
<pre><code># docker stop sig-proc;docker rm sig-proc
@@ -1179,9 +855,6 @@ int main(int argc, char *argv[])
# docker exec -it sig-proc bash
[<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="c9bba6a6bd89f9fdfaaffdaffef8feaaabfc">[email&#160;protected]</a> /]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 09:05 ? 00:00:00 /c-init-sig
@@ -1191,27 +864,15 @@ root 6 0 18 09:06 pts/0 00:00:00 bash
root 19 6 0 09:06 pts/0 00:00:00 ps -ef
[<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="deacb1b1aa9eeeeaedb8eab8e9efe9bdbceb">[email&#160;protected]</a> /]# cat /proc/1/status | grep SigCgt
SigCgt: 0000000000004000
[<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="a4d6cbcbd0e4949097c290c2939593c7c691">[email&#160;protected]</a> /]# kill 1
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
</code></pre>
<p>好了,到这里我们可以确定这两点:</p>
@@ -1271,9 +932,6 @@ CONTAINER ID IMAGE COMMAND CREATED
</div>
</div>
</div>
</div>
@@ -1281,9 +939,6 @@ CONTAINER ID IMAGE COMMAND CREATED
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
@@ -1299,17 +954,11 @@ CONTAINER ID IMAGE COMMAND CREATED
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
@@ -1335,9 +984,6 @@ CONTAINER ID IMAGE COMMAND CREATED
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
@@ -1349,9 +995,6 @@ CONTAINER ID IMAGE COMMAND CREATED
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
@@ -1369,12 +1012,6 @@ CONTAINER ID IMAGE COMMAND CREATED
return "";
}
</script>
</html>