容器运行时云原生【免费下载链接】docker-ce:warning: This repository is deprecated and will be archived (Docker CE itself is NOT deprecated) see the https://github.com/docker/docker-ce/blob/master/README.md :warning:项目地址https://gitcode.com/gh_mirrors/do/docker-ce点击查看免费下载导读containerd/console 是一个专为 Go 语言设计的控制台console/终端操作库它“轻依赖、API 简洁”是 docker-ce 的 CLI 组件在 BuildKit 镜像构建场景下实现 TTY 进度条渲染的底层支撑。本指南将以该库的 README 为核心骨架结合仓库中 vendored 源码与 docker-cli 的实际调用链系统讲解如何修改当前进程的终端状态raw 模式、读取/调整窗口尺寸并深入剖析其背后的 pty 创建、epoll 异步 I/O 与跨平台实现让你在读完本指南后既能直接上手使用其 API也能理解它在 docker build 全链路中的真实作用。项目概览一个轻量的终端操作库该库的定位在 README 中写得很直白Golang package for dealing with consoles. Light on deps and a simple API用于处理控制台的 Go 包依赖轻、API 简单。它仅有的两个外部依赖是github.com/pkg/errors与golang.org/x/sys见 go.mod属于典型的“薄封装”系统调用库。在 docker-ce 仓库中它以固定版本v1.0.2commit2f1e3d2b6afd18e8b2077816c711205a0b4d8769被 vendor 进components/cli记录在 vendor.conf。它的使用场景很明确docker-cli 通过 BuildKit 后端执行docker build时用该库把 stderr 终端切成 raw 模式并实时渲染构建进度条BuildKit 的--progressauto|plain|tty参数具体调用点位于 build_buildkit.go。注意本仓库的顶层 README 已声明 docker-ce 仓库本身处于 deprecated/archived 状态Docker CE 本身并不废弃本文讨论的是其中被复用的 containerd 子项目源码与 API不涉及仓库状态判断。修改当前进程的终端状态核心入门示例README 中给出的“修改当前进程”示例是整个库最常用的用法它演示了获取当前终端 → 进入 raw 模式 → 读取窗口尺寸 → 变更窗口尺寸 → 恢复原始状态这一完整生命周期current : console.Current() defer current.Reset() if err : current.SetRaw(); err ! nil { // 处理进入 raw 模式失败的情况 } ws, err : current.Size() current.Resize(ws)各步骤语义拆解console.Current()返回当前进程的终端对象。其实现位于 console.go会依次尝试os.Stderr、os.Stdout、os.Stdin三个标准流只要其中一个能被识别为终端即返回——这样即使 stdout 被重定向比如管道输出只要 stderr 仍是终端也能拿到可操作的 console。current.Reset()把终端恢复成进入前的原始状态。在 Unix 实现中构造master时会用tcget缓存一份原始的unix.Termios见 console_unix.goReset则通过tcset将这份原始配置写回同文件 L76-L81。配合defer使用可以在函数退出时保证终端状态不被污染——这是写交互式 CLI 工具时最该养成的习惯。current.SetRaw()将终端切入 raw 模式。Linux 实现里它以cfmakeraw为基础额外打开OPOST输出后处理具体逻辑见 console_unix.go。current.Size()读取终端窗口的行/列尺寸。current.Resize(ws)把终端窗口调整为指定尺寸。SetRaw 到底改了什么cfmakeraw的实现位于 tc_unix.go它对 termios 的四个标志位域做了如下修改标志位域关闭的选项含义Iflag输入IGNBRK、BRKINT、PARMRK、ISTRIP、INLCR、IGNCR、ICRNL、IXON关闭断行符处理、奇偶校验剥离、CR/LF 转换与输入流控Oflag输出OPOST随后又被SetRaw重新打开关闭输出后处理但该库为了让 pty 表现像真实终端又单独置位OPOSTLflag本地ECHO、ECHONL、ICANON、ISIG、IEXTEN关闭回显、规范行缓冲模式、信号生成与扩展功能Cflag控制CSIZE、PARENB、CS8关闭字符大小与奇偶校验同时把VMIN设为 1、VTIME设为 0即每次 read 最少返回 1 字节且不设超时。这正是交互式终端程序如 vim、docker build进度界面需要的“字节级直通”模式。核心 API 全览Console接口定义在 console.go它嵌入了File接口即io.ReadWriteCloserFd()Name()同文件 L27-L34因此一个Console本身就是可读写、可关闭的流方法说明Resize(WinSize) error把终端调整为指定窗口尺寸ResizeFrom(Console) error把调用方终端缩放到传入终端的大小常用于父子进程间的尺寸同步SetRaw() error切入 raw 模式输入/输出不做行缓冲与字符处理DisableEcho() error仅关闭回显保留其余终端行为Reset() error恢复终端到创建时的原始状态Size() (WinSize, error)返回终端窗口尺寸窗口尺寸类型WinSize由Height、Width两个公开字段及内部私有的x、y像素字段组成console.go。在 Unix 上它通过TIOCGWINSZ/TIOCSWINSZioctl 与内核的unix.Winsize互相转换tc_unix.go读取时把内核的Row/Col/Xpixel/Ypixel映射到Height/Width/x/y写入时反向映射。从任意文件句柄获取 Console除了Current()库还提供ConsoleFromFile(f File) (Console, error)console.go。它先通过checkConsole校验传入的文件是否真的连接到一个终端Unix 实现尝试对该 fd 执行tcgetconsole_unix.goWindows 实现则尝试GetConsoleModeconsole_windows.go校验失败返回哨兵错误ErrNotAConsoleprovided file is not a console定义于 console.go。这给了库一个非常实用的能力在不确定 fd 是否指向终端时先探测再决定要不要渲染 TTY 界面——这正是 docker-cli 中--progressauto的实现思路见下文。创建自己的伪终端pty库还提供NewPty() (Console, string, error)用于创建一个新的 pty 对console_unix.go返回的第二个字符串是 pty slave 的路径。其底层流程是openpt()以O_RDWR|O_NOCTTY|O_CLOEXEC打开/dev/ptmxpty_unix.goptsname()通过TIOCGPTNioctl 拿到 slave 编号并拼出/dev/pts/Ntc_linux.gounlockpt()通过TIOCSPTLCKioctl 解锁 slave之后才能打开 slave 端同文件 L34-L41newMaster(f)将 master 包装成Console并缓存原始 termios。该能力可用来把子进程的标准输入输出挂到 pty 上实现“有终端的子进程”效果——这在需要运行交互式进程并捕获其输出时非常有用。此外库还暴露了ClearONLCR(fd)/SetONLCR(fd)两个工具函数console_unix.go用于控制 pty 的ONLCR输出时把 LF 转成 CRLF行规约Linux unix98 pty 默认开启onlcr对于终端模拟器以外的中继场景应调用ClearONLCR关闭避免收到多余的\r。Linux 专属Epoller 与 EpollConsole 的异步 I/O这是该库最具“docker build 实战价值”的部分也是源码注释里直接引用 moby 议题moby/moby#27202的部分。Linux 平台专属的Epoller/EpollConsole位于 console_linux.go目的是用 edge-triggered边缘触发的 epoll 统一管理多个 console fd 的读写避免反复被EPOLLIN/EPOLLHUP事件唤醒。典型用法源码注释中的完整示例epoller, _ : NewEpoller() epollConsole, _ : epoller.Add(console) go epoller.Wait() var ( b bytes.Buffer wg sync.WaitGroup ) wg.Add(1) go func() { io.Copy(b, epollConsole) wg.Done() }() // perform I/O on the console epollConsole.Shutdown(epoller.CloseConsole) wg.Wait() epollConsole.Close()工作流程可拆解为四步NewEpoller()通过unix.EpollCreate1(unix.EPOLL_CLOEXEC)创建 epoll 实例L66-L74。Add(console)把 console 的 fd 设为非阻塞并以EPOLLIN | EPOLLOUT | EPOLLRDHUP | EPOLLET边缘触发注册进 epoll同时构造EpollConsoleL80-L104。Wait()循环调用EpollWait阻塞等待、-1 超时读到读就绪事件就signalRead()写就绪就signalWrite()若被信号打断EINTR则继续循环L108-L136。读写逻辑EpollConsole.Read/WriteL178-L248在底层 console 返回EAGAIN/EIO时认为“对端暂时消失”而挂起在各自的sync.Cond上等 epoll 事件到来再继续当读不到任何数据时优雅返回io.EOF写不满时返回io.ErrShortWrite。Shutdown(close func(int) error)负责唤醒所有等待者并置closed标记随后回调通常是epoller.CloseConsole即把 fd 从 epoll 注销完成收尾L256-L266。这套设计解决的核心痛点是TTY 设备在慢速输入、窗口尺寸变化、对端挂断等场景下行为复杂而边缘触发 epoll 配合非阻塞 fd 可以把“就绪”信号和“阻塞重试”解耦让上层可以用普通的io.Copy语义消费数据无需自建事件循环。moby/moby#27202 正是 Docker 构建输出因 TTY 唤醒风暴而 CPU 飙升的经典问题这个 Epoller 即是对其的修复方案。Windows 平台的实现差异Windows 实现console_windows.go与 Unix 差异显著核心事实如下master直接持有 stdin/stdout/stderr 三个windows.Handle及各自的模式缓存L69-L78且只允许从这三个标准流构造 console——newMaster对非标准流直接返回 creating a console from a file is not supported on windowsL209-L216。initStdios()会探测并开启虚拟终端VT支持输入侧检测ENABLE_VIRTUAL_TERMINAL_INPUT输出侧设置ENABLE_VIRTUAL_TERMINAL_PROCESSINGL32-L67。SetRaw通过makeInputRaw关闭ENABLE_ECHO_INPUT、ENABLE_LINE_INPUT、ENABLE_MOUSE_INPUT、ENABLE_WINDOW_INPUT、ENABLE_PROCESSED_INPUT再开启ENABLE_EXTENDED_FLAGS、ENABLE_INSERT_MODE、ENABLE_QUICK_EDIT_MODE并在支持 VT 时加上ENABLE_VIRTUAL_TERMINAL_INPUTL173-L199。Size()通过GetConsoleScreenBufferInfo根据屏幕缓冲区窗口矩形计算行列L113-L126Resize与ResizeFrom返回ErrNotImplementedClose为空操作。DisableEcho关闭ENABLE_ECHO_INPUT并开启ENABLE_PROCESSED_INPUT、ENABLE_LINE_INPUTL136-L146。也就是说Windows 侧目前只覆盖“当前进程标准流”这一最常用场景pty 创建与窗口 resize 尚未支持。实战docker build 中 console 的完整调用链README 只给出了库的独立用法要真正理解其价值需要看它在 docker-ce 中的落地。在components/cli中docker buildBuildKit 后端有三处直接调用1. 输出文件到终端时的保护性探测build_buildkit.go当--output typetar,dest-需要把 tar 包写到 stdout 时先用console.ConsoleFromFile(os.Stdout)探测 stdout 是否是终端若是则拒绝执行refusing to write output to console——防止二进制流被 TTY 污染。2. 进度展示时获取 console同一文件的displayStatus函数L284-L301var c console.Console // TODO: Handle tty output in non-tty environment. if cons, err : console.ConsoleFromFile(out); err nil (options.progress auto || options.progress tty) { c cons }当--progress为auto或tty且 stderr 确实是终端时把console.Console传给progressui.DisplaySolveStatus渲染动态进度界面。3. BuildKit 侧 progresswriter 的同步实现buildkit 的 printer.go 逻辑一致auto/tty模式下尝试console.ConsoleFromFile(out)若tty模式却拿不到 console 则直接报错plain模式跳过并支持BUILDKIT_PROGRESS环境变量覆盖auto时优先取环境变量。buildkit 还暴露了TTY_DISPLAY_RATE毫秒来控制进度界面刷新间隔display.go。动态尺寸与刷新机制拿到 console 后progressui的display结构体会在每次刷新前调用disp.c.Size()获取当前终端行列display.go使进度条自动适配窗口宽度默认每 150ms tick 一次、经rate.Limiter限流后重绘同文件 L36-L50。这也是为什么在终端里缩小/拉大窗口时docker build的进度条会实时跟着变化——背后正是console.Size()的功劳。--progress参数的合法值ValidateProgressOutputopts.go限定合法值为auto、plain、tty取值行为console 的使用auto自动判断stdout 是终端时走 TTY 进度条否则退化为纯文本探测成功才使用plain纯文本日志输出适合 CI/管道不做 TTY 控制不使用tty强制 TTY 进度条拿不到 console 时报错必须使用深入理解从源码结构看平台与编译约束从文件布局可以清晰看出该库的平台策略构建约束写在文件首行文件构建约束职责console.go全平台核心接口、WinSize、Current/ConsoleFromFile入口console_unix.godarwin/freebsd/linux/netbsd/openbsd/solarismaster实现、NewPty、checkConsoleconsole_windows.gowindowsWindows 控制台 API 封装console_linux.golinuxEpoller/EpollConsole异步 I/Otc_linux.golinuxunlockpt/ptsname等 pty ioctltc_unix.godarwin/freebsd/linux/netbsd/openbsd/solaristermios/winsize 的 ioctl 封装与cfmakerawpty_unix.godarwin/linux/netbsd/openbsd/solarisopenpt()打开/dev/ptmxpty_freebsd_cgo.go 等freebsd/openbsd/solariscgo/无 cgo 变体对应平台 pty 打开方式值得留意的是pty_freebsd_cgo.go/pty_freebsd_nocgo.go、tc_openbsd_cgo.go/tc_openbsd_nocgo.go、tc_solaris_cgo.go/tc_solaris_nocgo.go这类成对文件——从源码结构可以推断部分 BSD 与 Solaris 平台为兼容 cgo 与纯 Go 两种编译模式提供了双实现保证在不同构建环境下都能工作。总结与适用边界containerd/console 通过一套极简接口把“获取当前终端 → 切换 raw 模式 → 读写 → 读取/调整尺寸 → 恢复状态”的完整生命周期封装起来并在 Linux 上提供了基于边缘触发 epoll 的高效多路复用层。在 docker-ce 中它是docker build --progressauto|tty交互式进度条的直接依赖同时承担着“探测 fd 是否为终端”的职责避免把二进制输出写进 TTY 或把进度条画进非终端流。需要注意的边界以本仓库 vendor 的 v1.0.2 为准Resize/ResizeFrom在 Windows 上返回ErrNotImplementedWindows 下只能从 stdin/stdout/stderr 构造 consoleCurrent()在三个标准流都无法识别为终端时会panicconsole.go调用前应确认至少一个标准流是终端。如需在 docker-ce 上下文中进一步阅读相关实现可继续查看 console.go、console_linux.go、console_windows.go以及调用侧 build_buildkit.go 与 buildkit 的 printer.go。赞分享容器运行时云原生【免费下载链接】docker-ce:warning: This repository is deprecated and will be archived (Docker CE itself is NOT deprecated) see the https://github.com/docker/docker-ce/blob/master/README.md :warning:项目地址https://gitcode.com/gh_mirrors/do/docker-ce点击查看免费下载相关推荐containerd/consoleGo 语言终端Console与 PTY 处理库实战指南containerd/consoleGo 语言终端Console与 PTY 处理库实战指南 导读 github.com/containerd/consol云原生容器运行时docker-ce 中的 cgroups v1 控制库containerd/cgroups 包 API 全解与 dockerd 使用场景docker ce 中的 cgroups v1 控制库containerd/cgroups 包 API 全解与 dockerd 使用场景 本文以 docker容器运行时云原生Docker CE 依赖的 containerd console 包从 PTY 创建到终端 Raw 模式的 API 与实践Docker CE 依赖的 containerd console 包从 PTY 创建到终端 Raw 模式的 API 与实践 本文以 docker ce 仓库中容器运行时云原生上一篇Qt-Material 主题27 套配色 3 行代码接入 PySide/PyQt 的指南下一篇QKeyMapper按键映射不碰注册表、不重启10分钟让手柄玩键鼠游戏创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?