mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-09-30 23:26:43 +08:00
434 lines
27 KiB
HTML
434 lines
27 KiB
HTML
<!DOCTYPE html>
|
||
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
|
||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||
<head>
|
||
<head>
|
||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
|
||
<link rel="icon" href="/static/favicon.png">
|
||
<title>加餐福利 课后思考题答案合集.md.html</title>
|
||
<!-- Spectre.css framework -->
|
||
<link rel="stylesheet" href="/static/index.css">
|
||
<!-- theme css & js -->
|
||
<meta name="generator" content="Hexo 4.2.0">
|
||
</head>
|
||
<body>
|
||
<div class="book-container">
|
||
<div class="book-sidebar">
|
||
<div class="book-brand">
|
||
<a href="/">
|
||
<img src="/static/favicon.png">
|
||
<span>技术文章摘抄</span>
|
||
</a>
|
||
</div>
|
||
<div class="book-menu uncollapsible">
|
||
<ul class="uncollapsible">
|
||
<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 开篇词 一个态度两个步骤,成为容器实战高手</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/01 认识容器:容器的基本操作和实现原理.md.html">01 认识容器:容器的基本操作和实现原理</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/02 理解进程(1):为什么我在容器中不能kill 1号进程?.md.html">02 理解进程(1):为什么我在容器中不能kill 1号进程?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/03 理解进程(2):为什么我的容器里有这么多僵尸进程?.md.html">03 理解进程(2):为什么我的容器里有这么多僵尸进程?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/04 理解进程(3):为什么我在容器中的进程被强制杀死了?.md.html">04 理解进程(3):为什么我在容器中的进程被强制杀死了?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/05 容器CPU(1):怎么限制容器的CPU使用?.md.html">05 容器CPU(1):怎么限制容器的CPU使用?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/06 容器CPU(2):如何正确地拿到容器CPU的开销?.md.html">06 容器CPU(2):如何正确地拿到容器CPU的开销?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/07 Load Average:加了CPU Cgroup限制,为什么我的容器还是很慢?.md.html">07 Load Average:加了CPU Cgroup限制,为什么我的容器还是很慢?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/08 容器内存:我的容器为什么被杀了?.md.html">08 容器内存:我的容器为什么被杀了?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/09 Page Cache:为什么我的容器内存使用量总是在临界点.md.html">09 Page Cache:为什么我的容器内存使用量总是在临界点</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/10 Swap:容器可以使用Swap空间吗?.md.html">10 Swap:容器可以使用Swap空间吗?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/11 容器文件系统:我在容器中读写文件怎么变慢了.md.html">11 容器文件系统:我在容器中读写文件怎么变慢了</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/12 容器文件Quota:容器为什么把宿主机的磁盘写满了?.md.html">12 容器文件Quota:容器为什么把宿主机的磁盘写满了?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/13 容器磁盘限速:我的容器里磁盘读写为什么不稳定.md.html">13 容器磁盘限速:我的容器里磁盘读写为什么不稳定</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/14 容器中的内存与IO:容器写文件的延时为什么波动很大?.md.html">14 容器中的内存与IO:容器写文件的延时为什么波动很大?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/15 容器网络:我修改了procsysnet下的参数,为什么在容器中不起效?.md.html">15 容器网络:我修改了procsysnet下的参数,为什么在容器中不起效?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/16 容器网络配置(1):容器网络不通了要怎么调试.md.html">16 容器网络配置(1):容器网络不通了要怎么调试</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/17 容器网络配置(2):容器网络延时要比宿主机上的高吗.md.html">17 容器网络配置(2):容器网络延时要比宿主机上的高吗</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/18 容器网络配置(3):容器中的网络乱序包怎么这么高?.md.html">18 容器网络配置(3):容器中的网络乱序包怎么这么高?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/19 容器安全(1):我的容器真的需要privileged权限吗.md.html">19 容器安全(1):我的容器真的需要privileged权限吗</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/20 容器安全(2):在容器中,我不以root用户来运行程序可以吗?.md.html">20 容器安全(2):在容器中,我不以root用户来运行程序可以吗?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/加餐01 案例分析:怎么解决海量IPVS规则带来的网络延时抖动问题?.md.html">加餐01 案例分析:怎么解决海量IPVS规则带来的网络延时抖动问题?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/加餐02 理解perf:怎么用perf聚焦热点函数?.md.html">加餐02 理解perf:怎么用perf聚焦热点函数?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/加餐03 理解ftrace(1):怎么应用ftrace查看长延时内核函数?.md.html">加餐03 理解ftrace(1):怎么应用ftrace查看长延时内核函数?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/加餐04 理解ftrace(2):怎么理解ftrace背后的技术tracepoint和kprobe?.md.html">加餐04 理解ftrace(2):怎么理解ftrace背后的技术tracepoint和kprobe?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/加餐05 eBPF:怎么更加深入地查看内核中的函数?.md.html">加餐05 eBPF:怎么更加深入地查看内核中的函数?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/加餐06 BCC:入门eBPF的前端工具.md.html">加餐06 BCC:入门eBPF的前端工具</a>
|
||
</li>
|
||
<li>
|
||
<a class="current-tab" href="/专栏/容器实战高手课/加餐福利 课后思考题答案合集.md.html">加餐福利 课后思考题答案合集</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/结束语 跳出舒适区,突破思考的惰性.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() {
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
inner.classList.add('show')
|
||
}
|
||
function remove_inner() {
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
inner.classList.remove('show')
|
||
}
|
||
function sidebar_toggle() {
|
||
let sidebar_toggle = document.querySelector('.sidebar-toggle')
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let content = document.querySelector('.off-canvas-content')
|
||
if (sidebar_toggle.classList.contains('extend')) { // show
|
||
sidebar_toggle.classList.remove('extend')
|
||
sidebar.classList.remove('hide')
|
||
content.classList.remove('extend')
|
||
} else { // hide
|
||
sidebar_toggle.classList.add('extend')
|
||
sidebar.classList.add('hide')
|
||
content.classList.add('extend')
|
||
}
|
||
}
|
||
function open_sidebar() {
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
sidebar.classList.add('show')
|
||
overlay.classList.add('show')
|
||
}
|
||
function hide_canvas() {
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
sidebar.classList.remove('show')
|
||
overlay.classList.remove('show')
|
||
}
|
||
</script>
|
||
<div class="off-canvas-content">
|
||
<div class="columns">
|
||
<div class="column col-12 col-lg-12">
|
||
<div class="book-navbar">
|
||
<!-- For Responsive Layout -->
|
||
<header class="navbar">
|
||
<section class="navbar-section">
|
||
<a onclick="open_sidebar()">
|
||
<i class="icon icon-menu"></i>
|
||
</a>
|
||
</section>
|
||
</header>
|
||
</div>
|
||
<div class="book-content" style="max-width: 960px; margin: 0 auto;
|
||
overflow-x: auto;
|
||
overflow-y: hidden;">
|
||
<div class="book-post">
|
||
<p id="tip" align="center"></p>
|
||
<div><h1>加餐福利 课后思考题答案合集</h1>
|
||
<p>你好,我是程远,好久不见。</p>
|
||
<p>距离我们的专栏更新结束,已经过去了不少时间。我仍然会在工作之余,到这门课的留言区转一转,回答同学的问题。大部分的疑问,我都通过留言做了回复。</p>
|
||
<p>除了紧跟更新的第一批同学,也很开心有更多新朋友加入到这个专栏的学习中。那课程的思考题呢,为了给你留足思考和研究的时间,我选择用加餐的方式,给你提供参考答案。</p>
|
||
<p>这里我想和你说明的是,我这里给你提供的参考答案,都是我能够直接给你特定答案的问题。至于操作类的题目,有的我引用了同学回复的答案。</p>
|
||
<p>另外一类操作题,是为了帮你巩固课程内容知识的,相信你可以从课程正文里找到答案。我还是建议你自己动手实战,这样你的收获会更大。</p>
|
||
<h2>必学部分思考题</h2>
|
||
<p><strong>第 2 讲</strong></p>
|
||
<p>Q:对于这一讲的最开始,有这样一个 C 语言的 init 进程,它没有注册任何信号的 handler。如果我们从 Host Namespace 向它发送 SIGTERM,会发生什么情况呢?</p>
|
||
<p>A:即使在宿主机上向容器 1 号进程发送 SIGTERM,在 1 号进程没有注册 handler 的情况下,这个进程也不能被杀死。</p>
|
||
<p>这个问题的原因是这样的:开始要看内核里的那段代码,“ !(force && sig_kernel_only(sig))”,</p>
|
||
<p>虽然由不同的 namespace 发送信号, 虽然 force 是 1 了,但是 sig_kernel_only(sig) 对于 SIGTERM 来说还是 0,这里是个 &&, 那么 !(1 && 0) = 1。</p>
|
||
<pre><code>#define sig_kernel_only(sig) siginmask(sig, SIG_KERNEL_ONLY_MASK)
|
||
#define SIG_KERNEL_ONLY_MASK (\
|
||
rt_sigmask(SIGKILL) | rt_sigmask(SIGSTOP))
|
||
</code></pre>
|
||
<p><strong>第 3 讲</strong></p>
|
||
<p>Q:如果容器的 init 进程创建了子进程 B,B 又创建了自己的子进程 C。如果 C 运行完之后,退出成了僵尸进程,B 进程还在运行,而容器的 init 进程还在不断地调用 waitpid(),那 C 这个僵尸进程可以被回收吗?</p>
|
||
<p>A:这道题可以参考下面两位同学的回答。</p>
|
||
<p>Geek2014 用户的回答:</p>
|
||
<p>这时 C 是不会被回收的,只有等到 B 也被杀死,C 这个僵尸进程也会变成孤儿进程,被 init 进程收养,进而被 init 的 wait 机制清理掉。</p>
|
||
<p>莫名同学的回答:</p>
|
||
<p>C 应该不会被回收,waitpid 仅等待直接 children 的状态变化。</p>
|
||
<p>为什么先进入僵尸状态而不是直接消失?觉得是留给父进程一次机会,查看子进程的 PID、终止状态(退出码、终止原因,比如是信号终止还是正常退出等)、资源使用信息。如果子进程直接消失,那么父进程没有机会掌握子进程的具体终止情况。</p>
|
||
<p>一般情况下,程序逻辑可能会依据子进程的终止情况做出进一步处理:比如 Nginx Master 进程获知 Worker 进程异常退出,则重新拉起来一个 Worker 进程。</p>
|
||
<p><strong>第 4 讲</strong></p>
|
||
<p>Q:请你回顾一下基本概念中最后的这段代码,你可以想一想,在不做编译运行的情况下,它的输出是什么?</p>
|
||
<pre><code>#include <stdio.h>
|
||
#include <signal.h>
|
||
typedef void (*sighandler_t)(int);
|
||
void sig_handler(int signo)
|
||
{
|
||
if (signo == SIGTERM) {
|
||
printf("received SIGTERM\n\n");
|
||
// Set SIGTERM handler to default
|
||
signal(SIGTERM, SIG_DFL);
|
||
}
|
||
}
|
||
int main(int argc, char *argv[])
|
||
{
|
||
//Ignore SIGTERM, and send SIGTERM
|
||
// to process itself.
|
||
signal(SIGTERM, SIG_IGN);
|
||
printf("Ignore SIGTERM\n\n");
|
||
kill(0, SIGTERM);
|
||
//Catch SIGERM, and send SIGTERM
|
||
// to process itself.
|
||
signal(SIGTERM, sig_handler);
|
||
printf("Catch SIGTERM\n");
|
||
kill(0, SIGTERM);
|
||
//Default SIGTERM. In sig_handler, it sets
|
||
//SIGTERM handler back to default one.
|
||
printf("Default SIGTERM\n");
|
||
kill(0, SIGTERM);
|
||
return 0;
|
||
}
|
||
</code></pre>
|
||
<p>A:可以参考用户 geek 2014 同学的答案。输出结果如下:</p>
|
||
<p>Ignore SIGTERM</p>
|
||
<p>Catch SIGTERM</p>
|
||
<p>received SIGTERM</p>
|
||
<p>Default SIGTERM</p>
|
||
<p><strong>第 5 讲</strong></p>
|
||
<p>Q:我们还是按照文档中定义的控制组目录层次结构图,然后按序执行这几个脚本:</p>
|
||
<p>create_groups.sh</p>
|
||
<p>update_group1.sh</p>
|
||
<p>update_group4.sh</p>
|
||
<p>update_group3.sh</p>
|
||
<p>那么,在一个 4 个 CPU 的节点上,group1/group3/group4 里的进程,分别会被分配到多少 CPU 呢?</p>
|
||
<p>A:分配比例是: 2 : 0.5 : 1.5</p>
|
||
<p>可以参考 geek 2014 的答案:</p>
|
||
<p>group1 的 shares 为 1024,quota 3.5,尝试使用 4,</p>
|
||
<p>group2 的 shares 默认为 1024,quota 设置为 -1,不受限制,也即是,如果 CPU 上只有 group2 的话,那么 group2 可以使用完所有的 CPU(实际上根据 group3 和 group4,group2 最多也就能用到 1.5+3.5 core)</p>
|
||
<p>故而,group1 和 group2 各分配到 2。把 group2 分到的 2CPU,看作总量,再次分析 group3 和 group4。group3 和 group3 尝试使用的总量超过 2,所以按照 shares 比例分配,group3 使用 1/(1+3) * 2 = 0.5,group4 使用 3/(1+3) * 2 = 1.5</p>
|
||
<p><strong>第 6 讲</strong></p>
|
||
<p>Q:写一个小程序,在容器中执行,它可以显示当前容器中所有进程总的 CPU 使用率。</p>
|
||
<p>A:上邪忘川的回答可以作为一个参考。</p>
|
||
<pre><code>#!/bin/bash
|
||
cpuinfo1=$(cat /sys/fs/cgroup/cpu,cpuacct/cpuacct.stat)
|
||
utime1=$(echo $cpuinfo1|awk '{print $2}')
|
||
stime1=$(echo $cpuinfo1|awk '{print $4}')
|
||
sleep 1
|
||
cpuinfo2=$(cat /sys/fs/cgroup/cpu,cpuacct/cpuacct.stat)
|
||
utime2=$(echo $cpuinfo2|awk '{print $2}')
|
||
stime2=$(echo $cpuinfo2|awk '{print $4}')
|
||
cpus=$((utime2+stime2-utime1-stime1))
|
||
echo "${cpus}%"
|
||
</code></pre>
|
||
<p><strong>第 8 讲</strong></p>
|
||
<p>Q:在我们的例子脚本基础上,你可以修改一下,在容器刚一启动,就在容器对应的 Memory Cgroup 中禁止 OOM,看看接下来会发生什么?</p>
|
||
<p>A:通过“memory.oom_control”禁止 OOM 后,在容器中的进程不会发生 OOM,但是也无法申请出超过“memory.limit_in_bytes”内存。</p>
|
||
<pre><code># cat start_container.sh
|
||
#!/bin/bash
|
||
docker stop mem_alloc;docker rm mem_alloc
|
||
docker run -d --name mem_alloc registry/mem_alloc:v1
|
||
sleep 2
|
||
CONTAINER_ID=$(sudo docker ps --format "{{.ID}}\t{{.Names}}" | grep -i mem_alloc | awk '{print $1}')
|
||
echo $CONTAINER_ID
|
||
CGROUP_CONTAINER_PATH=$(find /sys/fs/cgroup/memory/ -name "*$CONTAINER_ID*")
|
||
echo $CGROUP_CONTAINER_PATH
|
||
echo 536870912 > $CGROUP_CONTAINER_PATH/memory.limit_in_bytes
|
||
echo 1 > $CGROUP_CONTAINER_PATH/memory.oom_control
|
||
cat $CGROUP_CONTAINER_PATH/memory.limit_in_bytes
|
||
</code></pre>
|
||
<p><strong>第 10 讲</strong></p>
|
||
<p>Q:在一个有 Swap 分区的节点上用 Docker 启动一个容器,对它的 Memory Cgroup 控制组设置一个内存上限 N,并且将 memory.swappiness 设置为 0。这时,如果在容器中启动一个不断读写文件的程序,同时这个程序再申请 1/2N 的内存,请你判断一下,Swap 分区中会有数据写入吗?</p>
|
||
<p>A:Memory Cgroup 参数 memory.swappiness 起到局部控制的作用,因为已经设置了 memory.swappiness 参数,全局参数 swappiness 参数失效,那么容器里就不能使用 swap 了。</p>
|
||
<p><strong>第 11 讲</strong></p>
|
||
<p>Q:在这一讲 OverlayFS 的例子的基础上,建立 2 个 lowerdir 的目录,并且在目录中建立相同文件名的文件,然后一起做一个 overlay mount,看看会发生什么?</p>
|
||
<p>A:这里引用上邪忘川同学的实验结果。</p>
|
||
<p>实验过程如下,结果是 lower1 目录中的文件覆盖了 lower2 中同名的文件, 第一个挂载的目录优先级比较高</p>
|
||
<pre><code>[[<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="9deff2f2e9ddf1f2fefcf1f5f2eee9">[email protected]</a> ~]# cat overlay.sh
|
||
#!/bin/bash
|
||
umount ./merged
|
||
rm upper lower1 lower2 merged work -r
|
||
mkdir upper lower1 lower2 merged work
|
||
echo "I'm from lower1!" > lower1/in_lower.txt
|
||
echo "I'm from lower2!" > lower2/in_lower.txt
|
||
echo "I'm from upper!" > upper/in_upper.txt
|
||
# `in_both` is in both directories
|
||
echo "I'm from lower1!" > lower1/in_both.txt
|
||
echo "I'm from lower2!" > lower2/in_both.txt
|
||
echo "I'm from upper!" > upper/in_both.txt
|
||
sudo mount -t overlay overlay \
|
||
-o lowerdir=./lower1:./lower2,upperdir=./upper,workdir=./work \
|
||
./merged
|
||
[<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="4a3825253e0a2625292b262225393e">[email protected]</a> ~]# sh overlay.sh
|
||
[<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="3745585843775b5854565b5f584443">[email protected]</a> ~]# cat merged/in_lower.txt
|
||
I'm from lower1!
|
||
</code></pre>
|
||
<p>第 12 讲</p>
|
||
<p>Q:在正文知识详解的部分,我们使用"xfs_quota"给目录打了 project ID 并且限制了文件写入的数据量。那么在做完限制之后,我们是否能用 xfs_quota 命令,查询到被限制目录的 project ID 和限制的数据量呢?</p>
|
||
<p>A:xfs_quota 不能直接得到一个目录的 quota 大小的限制,只可以看到 project ID 上的 quota 限制,不过我们可以用这段程序来获得目录对应的 project ID。</p>
|
||
<pre><code># xfs_quota -x -c 'report -h /'
|
||
...
|
||
Project ID Used Soft Hard Warn/Grace
|
||
---------- ---------------------------------
|
||
#0 105.6G 0 0 00 [------]
|
||
#101 0 0 10M 00 [------]
|
||
# ./get_proj /tmp/xfs_prjquota
|
||
Dir: /tmp/xfs_prjquota projectid is 101
|
||
</code></pre>
|
||
<p><strong>第 13 讲</strong></p>
|
||
<p>Q:这是一道操作题,通过这个操作你可以再理解一下 blkio Cgroup 与 Buffered I/O 的关系。</p>
|
||
<p>在 Cgroup V1 的环境里,我们在 blkio Cgroup V1 的例子基础上,把 fio 中“-direct=1”参数去除之后,再运行 fio,同时运行 iostat 查看实际写入磁盘的速率,确认 Cgroup V1 blkio 无法对 Buffered I/O 限速。</p>
|
||
<p>A: 这是通过 iostat 看到磁盘的写入速率,是可以突破 cgroup V1 blkio 中的限制值的。</p>
|
||
<p><strong>第 17 讲</strong></p>
|
||
<p>Q:在这节课的最后,我提到“由于 ipvlan/macvlan 网络接口直接挂载在物理网络接口上,对于需要使用 iptables 规则的容器,比如 Kubernetes 里使用 service 的容器,就不能工作了”,请你思考一下这个判断背后的具体原因。</p>
|
||
<p>A:ipvlan/macvlan 工作在网络 2 层,而 iptables 工作在网络 3 层。所以用 ipvlan/macvlan 为容器提供网络接口,那么基于 iptables 的 service 服务就不工作了。</p>
|
||
<p><strong>第 18 讲</strong></p>
|
||
<p>Q:在这一讲中,我们提到了 Linux 内核中的 tcp_force_fast_retransmit() 函数,那么你可以想想看,这个函数中的 tp->recording 和内核参数 /proc/sys/net/ipv4/tcp_reordering 是什么关系?它们对数据包的重传会带来什么影响?</p>
|
||
<pre><code>static bool tcp_force_fast_retransmit(struct sock *sk)
|
||
{
|
||
struct tcp_sock *tp = tcp_sk(sk);
|
||
return after(tcp_highest_sack_seq(tp),
|
||
tp->snd_una + tp->reordering * tp->mss_cache);
|
||
}
|
||
</code></pre>
|
||
<p>A: 在 TCP 链接建立的时候,tp->reordering 默认值是从 /proc/sys/net/ipv4/tcp_reordering(默认值为 3)获取的。之后根据网络的乱序情况,进行动态调整,最大可以增长到 /proc/sys/net/ipv4/tcp_max_reordering (默认值为 300) 的大小。</p>
|
||
<p><strong>第 20 讲</strong></p>
|
||
<p>Q:我在这一讲里提到了 rootless container,不过对于 rootless container 的支持,还存在着不少的难点,比如容器网络的配置、Cgroup 的配置,你可以去查阅一些资料,看看 podman 是怎么解决这些问题的。</p>
|
||
<p>A:可以阅读一下这篇文档。</p>
|
||
<h2>专题加餐</h2>
|
||
<p><strong>专题 03</strong></p>
|
||
<p>Q:我们讲 ftrace 实现机制时,说过内核中的“inline 函数”不能被 ftrace 到,你知道这是为什么吗?那么内核中的“static 函数”可以被 ftrace 追踪到吗?</p>
|
||
<p>A:inline 函数在编译的时候被展开了,所以不能被 ftrace 到。而 static 函数需要看情况,如果加了编译优化参数“-finline-functions-called-once”,对于只被调用到一次的 static 函数也会当成 inline 函数处理,那么也不能被 ftrace 追踪到了。</p>
|
||
<p><strong>专题 04</strong></p>
|
||
<p>Q:想想看,当我们用 kprobe 为一个内核函数注册了 probe 之后,怎样能看到对应内核函数的第一条指令被替换了呢?</p>
|
||
<p>A:首先可以参考莫名同学的答案:</p>
|
||
<p>关于思考题,想到一个比较笨拙的方法:gdb+qemu 调试内核。先进入虚拟机在某个内核函数上注册一个 kprobe,然后 gdb 远程调试内核,查看该内核函数的汇编指令(disass)是否被替换。应该有更简单的方法,这方面了解不深。</p>
|
||
<p>另外,我们用 gdb 远程调试内核看也可以。还可以通过 /proc/kallsyms 找到函数的地址,然后写个 kernel module 把从这个地址开始后面的几个字节 dump 出来,比较一下 probe 函数注册前后的值。</p>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div style="float: left">
|
||
<a href="/专栏/容器实战高手课/加餐06 BCC:入门eBPF的前端工具.md.html">上一页</a>
|
||
</div>
|
||
<div style="float: right">
|
||
<a href="/专栏/容器实战高手课/结束语 跳出舒适区,突破思考的惰性.md.html">下一页</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
|
||
</div>
|
||
<script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script><script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"709977bdacff3cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
|
||
</body>
|
||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
|
||
<script>
|
||
window.dataLayer = window.dataLayer || [];
|
||
function gtag() {
|
||
dataLayer.push(arguments);
|
||
}
|
||
gtag('js', new Date());
|
||
gtag('config', 'G-NPSEEVD756');
|
||
var path = window.location.pathname
|
||
var cookie = getCookie("lastPath");
|
||
console.log(path)
|
||
if (path.replace("/", "") === "") {
|
||
if (cookie.replace("/", "") !== "") {
|
||
console.log(cookie)
|
||
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
|
||
}
|
||
} else {
|
||
setCookie("lastPath", path)
|
||
}
|
||
function setCookie(cname, cvalue) {
|
||
var d = new Date();
|
||
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
|
||
var expires = "expires=" + d.toGMTString();
|
||
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
|
||
}
|
||
function getCookie(cname) {
|
||
var name = cname + "=";
|
||
var ca = document.cookie.split(';');
|
||
for (var i = 0; i < ca.length; i++) {
|
||
var c = ca[i].trim();
|
||
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
|
||
}
|
||
return "";
|
||
}
|
||
</script>
|
||
</html>
|