RunC 是 OCI 运行时规范的参考实现,仅将符合规范的文件系统 bundle 通过 Linux 内核原语(命名空间、cgroups、pivot_root 等)在宿主机内核中启动为隔离进程,不抽象不调度不管理镜像与日志。

RunC 是 OCI 运行时规范的参考实现,它不抽象、不封装、不调度,只做一件事:把一个符合 OCI 规范的文件系统 bundle,在宿主机 Linux 内核上真正跑起来——这个“落地”过程,本质就是用内核原语把进程变成容器。
OCI bundle 是启动前提
RunC 不接受镜像名(比如 runc run nginx:latest 是错的),它只认一个目录:里面必须有 config.json(遵循 runtime-spec)和 rootfs/(挂载好的完整根文件系统)。这个目录叫 bundle,是镜像解包后的产物。Docker 或 buildkit 会通过 docker export 或 umoci unpack 等工具把镜像转成 bundle,RunC 才能接手。
核心动作靠 Linux 内核能力驱动
RunC 启动容器时,并不启动新内核或虚拟机,而是调用一组底层系统调用,直接在当前宿主机内核中构造隔离环境:
- clone() 创建新进程,同时指定 CLONE_NEW* 标志(如 NEWPID、NEWNS、NEWNET),启用对应命名空间
- unshare() 在已有进程中分离出新的命名空间(例如为 init 进程单独设置 mount namespace)
- setns() 把进程加入已创建的命名空间(多用于 join 模式或调试场景)
- cgroups v1/v2 接口写入:按 config.json 中 linux.resources 字段,把 PID 写入 cgroup.procs,设置 cpu.weight、memory.max 等限制
- chdir + pivot_root / chroot:切换到 rootfs 目录,再用 pivot_root 将其设为新根目录(--no-pivot 可跳过,但安全性下降)
容器进程就是普通进程,只是被“罩住了”
最终运行起来的容器,就是一个或一组普通 Linux 进程,PID 显示在宿主机上(ps aux | grep 能看到),但它被限制在自己的 PID 命名空间里看不到其他进程,网络栈走独立 netns,文件系统根目录被 pivot 到 bundle 的 rootfs 下,资源受 cgroup 控制。RunC 本身在容器启动后基本退出(除非 attach 或 --detach=false),后续生命周期由内核和 cgroup 子系统维持。
没有守护进程,也不管理镜像或日志
RunC 是纯粹的“一次性运行时工具”:
- 不监听端口、不维护状态数据库、不拉取镜像
- 日志输出直接连到 stdout/stderr,由调用方(如 containerd)接管并落盘
- 容器退出后,RunC 不清理 rootfs 或 cgroup,这些由上层(如 crictl、kubelet)负责回收
- 所有安全策略(seccomp、apparmor、capabilities)都来自 config.json 的 linux.seccomp 和 linux.capabilities 字段,RunC 只执行应用,不决策


















