- Published on
eBPF Deep Dive — Linuxカーネルをプログラマブルにした VM、Verifier、XDP、CO-RE 徹底解説 (2025)
- Authors

- Name
- Youngju Kim
- @fjvbn20031
TL;DR
- eBPF は Linux カーネル内でサンドボックス化されたバイトコードを実行する VM である。カーネルモジュールをビルドせずにカーネル動作を観測・変更できる。
- 11 レジスタ、64-bit RISC-like ISA。
clang -target bpfで C をバイトコードに、ロード時に JIT でネイティブ化。 - Verifier が安全性を保証する。ロード前に symbolic execution で全経路を検証: 無限ループ禁止、ポインタ境界チェック、未初期化レジスタ読み取り禁止。
- Maps はカーネル・ユーザ空間の双方で読み書きできる共有データ構造。Hash、Array、LRU、LPM Trie、Ring Buffer、Per-CPU 変種など数十種。
- プログラムタイプがフックポイントを決める。
kprobe、tracepoint、XDP、TC、LSM、cgroup_skb等。 - CO-RE + BTF がカーネル構造体のバージョン差問題を解決する。一度コンパイルすればどのカーネルでも動く。
- XDP は NIC ドライバ直後でパケットを処理し、1 コアで毎秒数千万パケットを捌ける。DPDK 級の性能 + カーネル統合。
- 実プロダクト: Cilium (K8s ネットワーキング/セキュリティ)、Pixie (可観測性)、Falco (ランタイムセキュリティ)、bpftrace (アドホックトレース)、Katran (L4 LB)。
1. なぜ eBPF か
1.1 カーネル拡張の古いジレンマ
カーネルに機能追加する手段は 3 つだった:
- カーネルパッチ: メインライン取り込みに数年。小さな機能は却下。
- カーネルモジュール (LKM):
insmodでロード。クラッシュでカーネル panic。バージョンごとに再ビルド。 - ユーザ空間実装: syscall/ioctl でカーネルに問い合わせ。遅く、コンテキストスイッチがボトルネック。
L4 ロードバランサ例: HAProxy は毎パケット往復、IPVS は速いがカーネル panic 懸念、DPDK は数千万 pps だが CPU 1 コア丸ごと消費 + カーネルスタックと共存不可。eBPF は第 4 の道を開いた: 「カーネル内でサンドボックス実行」、安全性は verifier が保証。
1.2 BPF の起源 — 1992
Van Jacobson と Steven McCanne が Berkeley Packet Filter を作成。tcpdump のカーネル内フィルタリング用。32-bit ISA、2 レジスタ。これが cBPF。
1.3 Alexei Starovoitov の 2014 年再設計
eBPF (extended BPF) 誕生:
- 11 レジスタ、64-bit 化。
- Maps 追加: カーネル・ユーザ間の状態共有。
- Helper 関数: 安全なカーネル API。
- JIT: ネイティブ機械語化。
- Verifier 強化: 複雑なプログラムも検証可能。
- ネットワーク以外のフック: kprobe、tracepoint、cgroup など。
「カーネルモジュールの力 + ユーザ空間コードの安全性」が本質。
1.4 2025 年の生態系
Meta Katran、Netflix FlowLogs、GKE、Cloudflare DDoS mitigation、Cilium (K8s CNI の事実上の標準)、RHEL 9 で bpftrace/bcc/libbpf 公式サポート。eBPF Foundation が Linux Foundation 配下に発足。
2. eBPF 仮想マシン
2.1 命令フォーマット
固定 64-bit (8 バイト):
struct bpf_insn {
__u8 code; // 8 bits: opcode
__u8 dst_reg:4; // 4 bits: destination register
__u8 src_reg:4; // 4 bits: source register
__s16 off; // 16 bits: signed offset
__s32 imm; // 32 bits: signed immediate
};
2.2 レジスタ
R0 : 戻り値
R1-R5: 引数 (C 呼び出し規約)
R6-R9: callee-saved
R10 : フレームポインタ (read-only、スタックアクセス用)
R10 はスタックアクセス専用の読み取り専用レジスタ。
2.3 例
int add(int a, int b) { return a + b; }
clang -target bpf -O2 で:
; 0000000000000000 <add>:
; 0: bf 10 00 00 00 00 00 00 r0 = r1 ; R0 = a
; 1: 0f 20 00 00 00 00 00 00 r0 += r2 ; R0 += b
; 2: 95 00 00 00 00 00 00 00 exit
2.4 JIT
カーネルは x86_64、ARM64、RISC-V、MIPS、PowerPC、s390x、SPARC すべてで JIT 対応。5.x 以降はデフォルト有効:
sysctl net.core.bpf_jit_enable # 1 = enabled
ネイティブ C とほぼ同等の性能。
2.5 命令数制限と Bounded Loop
- 5.2 未満: 関数 4,096 命令。
- 5.2+: 関数 1M 命令、verifier の複雑度予算あり。
- 5.3+: verifier が上限証明可能なら bounded loop 許可。
for (int i = 0; i < 64; i++) { /* OK */ }
#pragma unroll
for (int i = 0; i < 4; i++) { /* 常に OK */ }
for (int i = 0; i < x; i++) { /* NG: x unknown */ }
3. Verifier — eBPF の心臓
3.1 検証目的
- 終了性: 無限ループなし、全経路が
exitに到達。 - メモリ安全性: ポインタアクセスが確保領域内。
- 初期化保証: 読み取り前に書き込み。
- 型安全性: スカラーをポインタとして逆参照禁止。
- 複雑度制限: 検証自体が妥当な時間で終わる。
失敗時は bpf() syscall が -EINVAL を返し、詳細なログを出力。
3.2 Symbolic Execution
各レジスタ・スタックスロットの「取り得る値の範囲」を追跡し、分岐ごとに経路を fork:
int x = get_pid(); // x: [0, 4194304]
if (x > 100) {
// この分岐では x: [101, 4194304]
use(x);
}
s32_min/max、u32_min/max、s64_min/max およびポインタ種別・オフセットを追跡。
3.3 ポインタ型システム
PTR_TO_CTX : プログラムコンテキスト
PTR_TO_PACKET : ネットワークパケット
PTR_TO_PACKET_END : パケット終端
PTR_TO_MAP_KEY : マップキー
PTR_TO_MAP_VALUE : マップ値
PTR_TO_STACK : スタックメモリ
PTR_TO_SOCKET : ソケット
SCALAR_VALUE : 非ポインタスカラー
PTR_TO_MAP_VALUE は NULL チェック後にのみ逆参照可:
void *val = bpf_map_lookup_elem(&my_map, &key);
if (!val) return 0;
*(int *)val = 42;
3.4 パケット境界チェック
SEC("xdp")
int drop_port_80(struct xdp_md *ctx) {
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
struct ethhdr *eth = data;
if (data + sizeof(*eth) > data_end) // 必須
return XDP_PASS;
if (eth->h_proto == bpf_htons(ETH_P_IP)) { /* ... */ }
return XDP_PASS;
}
3.5 Verifier エラーの読み方
17: invalid access to packet, off=14 size=2, R1(id=0,off=14,r=0)
R1 offset is outside of the packet
対処: アクセス前に data + N > data_end チェックを追加。
3.6 複雑度予算
経路が多過ぎると "BPF program is too complex"。上限は 5.2+ で 1,000,000 状態。対策: bounded loop 優先、__always_inline で helper インライン化、state pruning (4.19+) 活用。
4. Maps
4.1 必要性
- スタック 512 バイト制限。
- プログラム間の直接共有不可。
- ユーザ空間通信が必要。
→ Maps が解決する型付き KV ストア。
4.2 主要タイプ
| タイプ | 用途 |
|---|---|
BPF_MAP_TYPE_HASH | PID 別統計 |
BPF_MAP_TYPE_ARRAY | カウンタ、設定 |
BPF_MAP_TYPE_PERCPU_HASH | ロックフリーカウンタ |
BPF_MAP_TYPE_LRU_HASH | コネクション追跡 |
BPF_MAP_TYPE_LPM_TRIE | ルーティングテーブル |
BPF_MAP_TYPE_STACK_TRACE | プロファイリング |
BPF_MAP_TYPE_RINGBUF | ユーザへのイベント (5.8+) |
BPF_MAP_TYPE_PROG_ARRAY | tail call |
BPF_MAP_TYPE_DEVMAP | XDP redirect |
BPF_MAP_TYPE_SK_STORAGE | ソケット別状態 |
BPF_MAP_TYPE_TASK_STORAGE | タスク別状態 |
4.3 宣言 (libbpf スタイル)
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, u32);
__type(value, u64);
__uint(max_entries, 10240);
} pid_counts SEC(".maps");
4.4 プログラムからのアクセス
u32 pid = bpf_get_current_pid_tgid() >> 32;
u64 *count = bpf_map_lookup_elem(&pid_counts, &pid);
if (!count) {
u64 one = 1;
bpf_map_update_elem(&pid_counts, &pid, &one, BPF_ANY);
} else {
__sync_fetch_and_add(count, 1);
}
4.5 Per-CPU マップ
キャッシュライン競合を回避し、CPU ごとに自スロットを持つ:
int ncpus = libbpf_num_possible_cpus();
u64 vals[ncpus];
bpf_map_lookup_elem(fd, &key, vals);
u64 total = 0;
for (int i = 0; i < ncpus; i++) total += vals[i];
4.6 Ring Buffer
Linux 5.8+、MPSC、back-pressure 検知、event loss カウンタ:
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 256 * 1024);
} events SEC(".maps");
SEC("tp/sched/sched_process_exec")
int handle_exec(void *ctx) {
struct event *e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
if (!e) return 0;
e->pid = bpf_get_current_pid_tgid() >> 32;
bpf_get_current_comm(&e->comm, sizeof(e->comm));
bpf_ringbuf_submit(e, 0);
return 0;
}
5. プログラムタイプとフックポイント
5.1 Kprobe / Kretprobe
カーネル関数のエントリ・終了にフック。ほぼ全てのカーネル関数に付けられるが、関数名・シグネチャがバージョン依存。
SEC("kprobe/do_sys_openat2")
int kprobe_openat(struct pt_regs *ctx) {
const char *filename = (const char *)PT_REGS_PARM2(ctx);
char buf[256];
bpf_probe_read_user_str(buf, sizeof(buf), filename);
return 0;
}
5.2 Tracepoint
カーネル開発者が明示的に定義した安定 ABI イベント。/sys/kernel/debug/tracing/events で一覧確認。
5.3 Fentry / Fexit (BPF Trampoline, 5.5+)
kprobe より高速。関数先頭の 5-byte NOP を call で上書き。オーバーヘッドほぼ 0。
SEC("fentry/tcp_v4_connect")
int BPF_PROG(connect_entry, struct sock *sk) { return 0; }
SEC("fexit/tcp_v4_connect")
int BPF_PROG(connect_exit, struct sock *sk, int ret) { return 0; }
5.4 XDP
NIC ドライバ直後、sk_buff 割当前に実行:
SEC("xdp")
int xdp_drop_tcp_80(struct xdp_md *ctx) {
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
struct ethhdr *eth = data;
if (data + sizeof(*eth) > data_end) return XDP_PASS;
if (eth->h_proto != bpf_htons(ETH_P_IP)) return XDP_PASS;
struct iphdr *ip = data + sizeof(*eth);
if ((void *)(ip + 1) > data_end) return XDP_PASS;
if (ip->protocol != IPPROTO_TCP) return XDP_PASS;
struct tcphdr *tcp = (void *)ip + ip->ihl * 4;
if ((void *)(tcp + 1) > data_end) return XDP_PASS;
if (tcp->dest == bpf_htons(80)) return XDP_DROP;
return XDP_PASS;
}
戻り値: XDP_PASS、XDP_DROP、XDP_TX、XDP_REDIRECT、XDP_ABORTED。
5.5 TC BPF
qdisc レベルで実行。egress 制御や Cilium の identity ベースフィルタに利用。
5.6 cgroup_skb / cgroup_sock
cgroup 単位のネットワーク制御。K8s ネームスペース隔離に使用。
5.7 LSM BPF (5.7+)
セキュリティ方針を eBPF で動的に実装。再コンパイルなしでデプロイ可能。
SEC("lsm/file_open")
int BPF_PROG(block_secret_file, struct file *file) {
char name[256];
bpf_probe_read_kernel_str(name, sizeof(name),
file->f_path.dentry->d_iname);
if (strcmp(name, "secret") == 0) return -EACCES;
return 0;
}
5.8 Sockops
ソケットライフサイクルにフック。Cilium は connect(svc_ip) 時点で DNAT して iptables を完全にスキップ。
6. CO-RE — Compile Once, Run Everywhere
6.1 問題
構造体フィールドオフセットがバージョンでずれ、ビルド済み .o が別カーネルで誤ったメモリを読む。BCC はランタイム LLVM コンパイルで対処したが、イメージが数百 MB。
6.2 BTF + CO-RE
BTF はカーネル型情報を /sys/kernel/btf/vmlinux に保存。CO-RE はフィールドアクセスを再配置可能に印付け、libbpf がロード時に現カーネルのオフセットにリライトする。
#include <vmlinux.h>
#include <bpf/bpf_core_read.h>
SEC("kprobe/tcp_sendmsg")
int trace_tcp_sendmsg(struct pt_regs *ctx) {
struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);
u16 sport;
BPF_CORE_READ_INTO(&sport, sk, __sk_common.skc_num);
return 0;
}
6.3 vmlinux.h の生成
bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
カーネルヘッダ不要。
6.4 Feature Detection
if (bpf_core_field_exists(task->comm_size)) {
/* 新カーネル */
} else {
/* 旧カーネル */
}
6.5 CO-RE が変えたこと
コンテナイメージは libbpf + CO-RE 可能な .bpf.o のみで数 MB。Cilium、Falco、Pixie、Inspektor Gadget 等が全て単一バイナリで配布。
7. XDP — 超高速パケット処理
7.1 配置
NIC -> XDP (ドライバ RX 直後) -> sk_buff 割当 -> TC ingress -> routing -> TC egress -> ドライバ TX -> NIC
XDP は sk_buff 割当前に実行され、数百 ns のオーバーヘッドを省く。
7.2 モード
- Native XDP: ドライバ統合、最速。
- Generic XDP: カーネルソフトウェアエミュレート、遅い。
- Offloaded XDP: SmartNIC でハードウェア実行。
7.3 性能数値
| 方式 | pps (1 コア) | レイテンシ |
|---|---|---|
| カーネルスタック (iptables) | 約 1 Mpps | 約 10 us |
| XDP Native | 約 24 Mpps | 約 1 us |
| DPDK | 約 30 Mpps | 約 0.5 us |
7.4 DDoS フィルタリング
LPM trie のブラックリスト、送信元別 SYN レートマップで XDP_DROP。Cloudflare は 5-10 Tbps の洪水を NIC 端で吸収。
7.5 Katran (Meta L4 LB)
- DSR (Direct Server Return)。
- Maglev ハッシュで均等分散 + 最小再割当。
- ホスト 1 台で数百万 pps。
8. Cilium
8.1 K8s ネットワーキングの課題
iptables モードはサービス数に対し O(n) 増加。IPVS でも sk_buff と netfilter のオーバーヘッド残存。
8.2 Cilium の解決
veth ペアの TC フックに eBPF を付け、サービス解決・ポリシー・LB・暗号化を直接処理。iptables をバイパス。
8.3 Identity ベースポリシー
ラベル組み合わせに 16-bit identity ID を発行。ポリシー評価は (src_identity, dst_identity, port) の O(1) マップ参照。
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: allow-frontend-to-backend
spec:
endpointSelector:
matchLabels:
app: backend
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: "8080"
protocol: TCP
8.4 kube-proxy 代替
sockops が connect() をフック、BPF マップで O(1) に backend を解決、DNAT 済みで接続開始。同一ノードなら sockmap redirect でカーネルスタックも完全に迂回。
9. Observability: bpftrace、bcc、Pixie
9.1 bpftrace
DTrace 類似のワンライナー:
bpftrace -e 'tracepoint:syscalls:sys_enter_* { @[probe] = count(); }'
bpftrace -e '
kprobe:tcp_v4_connect { @start[tid] = nsecs; }
kretprobe:tcp_v4_connect /@start[tid]/ {
@latency = hist((nsecs - @start[tid]) / 1000);
delete(@start[tid]);
}'
9.2 bcc
Python から BPF C を埋め込む。ランタイム LLVM 必要。CO-RE 登場後 libbpf へ移行中。
9.3 libbpf + skeleton — プロダクション標準
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 256 * 1024);
} events SEC(".maps");
SEC("tp/syscalls/sys_enter_openat")
int handle_openat(void *ctx) {
u32 *e = bpf_ringbuf_reserve(&events, sizeof(u32), 0);
if (!e) return 0;
*e = bpf_get_current_pid_tgid() >> 32;
bpf_ringbuf_submit(e, 0);
return 0;
}
char LICENSE[] SEC("license") = "GPL";
単一バイナリ、libbpf 依存のみ、CO-RE でカーネル非依存。
9.4 Pixie
DaemonSet が uprobe で libssl/libc/libgrpc にフックし、HTTP/gRPC/SQL をコード改修なしでデコード。
10. Falco — ランタイムセキュリティ
Falco は eBPF で syscall を全監視し、ルールマッチで通知:
- rule: Read sensitive file untrusted
desc: Detect reading sensitive files
condition: >
sensitive_files and open_read
and proc_name_exists
and not proc.name in (trusted_procs)
output: >
Sensitive file opened (user=%user.name
command=%proc.cmdline file=%fd.name)
priority: WARNING
カーネルモジュールから eBPF 移行後: K8s ノードに即デプロイ可、カーネル版差なし (CO-RE)、オーバーヘッド 1% 未満。
11. 限界と注意点
11.1 Verifier の壁
複雑なロジックは tail call や bpf_loop (5.17+) で分割する必要がある。
11.2 GPL 制約
bpf_printk、bpf_probe_read_kernel 等の有用な helper の多くは GPL:
char LICENSE[] SEC("license") = "GPL";
指定がないと verifier が拒否。
11.3 性能オーバーヘッド
XDP ベンチマーク:
- 空プログラム: 約 24 Mpps
- map lookup 1 回: 約 18 Mpps
- map lookup 3 回 + 簡単ロジック: 約 10 Mpps
11.4 デバッグの困難
bpf_printkはtrace_pipeへ (遅くグローバル)。- スタックトレースなし、GDB 不可。
代替: bpftool prog dump jited、verifier ログ精読、BPF_PROG_TEST_RUN によるテスト。
11.5 Observer Effect
uprobe/kprobe は呼出し毎に 5-30 ns 追加。高頻度関数では顕著。軽量フィルタを前置し、ring buffer へ転送する設計を推奨。
12. セキュリティ
12.1 eBPF は安全か
Verifier はメモリ安全性を証明するが意図的安全性は別。Spectre 系サイドチャネル、verifier DoS、特権ユーザによるカーネルメモリ読み取りが懸念。kernel lockdown と CAP_BPF で緩和。
12.2 権限体系
CAP_SYS_ADMIN: 従来の広範な権限。CAP_BPF(5.8+): BPF 専用に細分化。CAP_PERFMON: トレース用。CAP_NET_ADMIN: ネットワーク BPF 用。
12.3 非特権 BPF の衰退
Spectre 以降デフォルト無効 (kernel.unprivileged_bpf_disabled=1)。2025 年時点で事実上禁止状態。
13. ベンチマークとチューニング
13.1 XDP
ip link set dev eth0 xdp obj drop.o sec xdp
ip -s link show eth0
bpftool prog show + bpftool prog profile、perf stat -e cycles,instructions。
13.2 Verifier プロファイル
bpftool prog load prog.o /sys/fs/bpf/prog \
log_level 2 log_size 4194304 2>&1 | tee verifier.log
13.3 XDP 最適化
- 連続フィールドの境界チェックをまとめる。
bpf_xdp_adjust_head回避。- PCIe NIC の RX ring を拡大。
- RPS/RFS で IRQ を特定 CPU に固定。
- Huge page で TLB ミス削減。
14. Tail Call と BPF-to-BPF Call
14.1 Tail Call
BPF_MAP_TYPE_PROG_ARRAY 経由でプログラム間を飛ぶ:
struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(max_entries, 4);
__type(key, u32);
__type(value, u32);
} prog_array SEC(".maps");
SEC("xdp")
int entry(struct xdp_md *ctx) {
bpf_tail_call(ctx, &prog_array, NEXT_PROG);
return XDP_PASS;
}
スタック喪失、最大連鎖 33 段。
14.2 BPF-to-BPF Call (4.16+)
通常の関数呼び出し。スタック保持・戻り値あり。再帰不可 (verifier が終了証明不可)。
15. 未来とトレンド
15.1 eBPF for Windows
Microsoft が開発中。同一 ELF を Linux/Windows 両方で実行するのが目標。2025 年時点でベータ。
15.2 sched_ext (Linux 6.12)
プロセススケジューラ自体を eBPF で書ける。Meta 主導:
SEC("struct_ops/scx_example_enqueue")
void BPF_STRUCT_OPS(enqueue, struct task_struct *p, u64 enq_flags) {
scx_bpf_dispatch(p, SCX_DSQ_GLOBAL, SCX_SLICE_DFL, enq_flags);
}
CFS/EEVDF の代替として独自ポリシーを投入可能。
15.3 eBPF + Rust
aya フレームワークが成熟:
use aya_bpf::{macros::xdp, programs::XdpContext};
#[xdp]
pub fn my_xdp(ctx: XdpContext) -> u32 {
match unsafe { try_my_xdp(ctx) } {
Ok(ret) => ret,
Err(_) => xdp_action::XDP_PASS,
}
}
15.4 Service Mesh
Cilium Service Mesh は eBPF + per-node envoy モデルで Istio のサイドカーを置換。L4・identity・mTLS は eBPF、L7 パースのみ envoy。オーバーヘッド約 70% 削減。
16. 学習ロードマップ
- ebpf.io と公式 "What is eBPF" を読む。
- bpftrace のワンライナーを試す。
- BCC の
tools/を読む (tcp_latency、biosnoop、execsnoop 等 100 以上)。 - libbpf-bootstrap の例題、
vmlinux.h生成、skeleton パターンを学ぶ。 - Cilium
bpf/、Katran、Tetragon を読む。
書籍: "Learning eBPF" (Liz Rice)、"Linux Observability with BPF" (Calavera & Fontana)。カンファレンス: eBPF Summit、LPC BPF トラック、KubeCon。
17. 要約チートシート
eBPF Cheat Sheet
- VM: 11 regs、64-bit RISC-like、JIT、1M insn 制限
- Verifier: symbolic execution、メモリ安全性、ポインタ型付け
- Maps: Hash、Array、LRU、LPM trie、Ring buffer、Per-CPU
- Program types: kprobe、tracepoint、fentry/fexit、XDP、TC、cgroup_skb、LSM、sockops
- CO-RE: BTF リロケーション、単一バイナリ、全カーネル対応
- Production: Cilium、Katran、Pixie、Falco、bpftrace
- Tools: bpftool、libbpf、bcc、aya (Rust)
18. クイズ
Q1. Verifier がプログラムを拒否する最も多い理由は?
A. ポインタアクセス前の境界チェック欠如、または map lookup 結果を NULL チェックなしで逆参照。XDP では invalid access to packet, off=N size=M や R1 pointer arithmetic on PTR_TO_MAP_VALUE_OR_NULL が典型。
Q2. CO-RE が解決する問題は?
A. カーネル構造体のレイアウトがバージョンで変わり、ビルド済み eBPF プログラムが別カーネルで誤ったメモリを読む問題。BTF がカーネル型情報を持ち、libbpf がロード時にフィールドオフセットをリライトして単一バイナリで複数カーネルに対応。
Q3. XDP が iptables より速い理由は?
A. iptables は sk_buff 割当後に netfilter チェーンを走査する。XDP は NIC ドライバ直後、sk_buff 割当前に実行され、JIT ネイティブコードでルール解釈もない。結果として 1 コアで数千万 pps を実現。
Q4. Per-CPU マップが通常ハッシュより速い理由は?
A. キャッシュライン競合がない。複数 CPU が同一カウンタを更新すると MESI で行がコア間を往復し性能崩壊。Per-CPU は各 CPU が自スロットのみ書く。ユーザ空間読み取り時に合計。
Q5. Cilium はどのように iptables を迂回して kube-proxy を置き換えるか?
A. sockops が connect() をフックし、BPF マップを O(1) で参照して実 backend へ DNAT。カーネルスタックは最初から backend IP で接続。同一ノード Pod は sockmap redirect で localhost 通信化しスタックも迂回。
Q6. Tail Call の欠点は?
A. スタック喪失。呼出元のローカル変数が消える。状態共有は map 経由。最大連鎖 33 段。
Q7. ユーザ空間関数にフックするには?
A. uprobe / uretprobe。Pixie は libssl の SSL_read/write にフックし TLS 終端後の HTTPS 平文を観測。
本稿が役立ったら次もどうぞ:
- 「Linux ネットワークスタック Deep Dive」
- 「Cilium Architecture 徹底解説」
- 「io_uring Deep Dive」
- 「DPDK vs XDP 比較」