os.pipe()仅适用于父子进程间通信,因其返回的内核匿名管道fd不带名字、不持久、不可跨独立进程访问;两个独立go run程序无法共享该fd,误用会导致broken pipe、立即eof或goroutine卡死。

os.Pipe() 只能在父子进程间用,别拿它跨进程
Go 的 os.Pipe() 返回一对 *os.File,本质是内核级匿名管道 fd,但它**不带名字、不持久、不可跨独立进程访问**。两个各自 go run main.go 启动的程序之间,os.Pipe() 完全无效——它们压根看不到对方的 fd。
常见错误现象:broken pipe、读写立刻返回 EOF、goroutine 卡死在 Read() 或 Write() 上。根源就是误把内存级管道当成了命名管道(FIFO)或 Unix Socket。
- 真正能跨任意进程的“管道”,得用系统级 FIFO:
mkfifo /tmp/myapp.fifo,再用os.OpenFile("/tmp/myapp.fifo", os.O_RDONLY, 0)和os.OpenFile("/tmp/myapp.fifo", os.O_WRONLY, 0)分别打开 - FIFO 是阻塞式:一端
open()时,若另一端没开,就会卡住;必须并发启动 reader/writer,或加context.WithTimeout - FIFO 不支持双向通信,要双向就得开两个 FIFO,或者直接换用
net.Listen("unix", ...)
Unix Domain Socket 是本地 IPC 的首选方案
同一台机器上进程通信,net.Listen("unix", socketPath) 性能远超 loopback TCP,且天然支持文件权限控制。关键不是“能不能用”,而是路径、权限、清理三件事必须做对。
常见错误现象:connect: connection refused(服务端没 listen 或 socket 文件残留)、permission denied(父目录缺 x 权限)、address already in use(旧 socket 文件没删干净)。
- 服务端启动前必须调用
os.RemoveAll(socketPath)清理旧文件——os.Remove()对残留的 socket 目录会失败 -
socketPath必须是绝对路径,且其**父目录要有可执行(x)权限**,否则客户端连不上(Linux/Unix 下路径遍历需要 x) - 监听后不要写
defer listener.Close()在 accept 循环外;正确做法是defer func() { _ = listener.Close() }(),或把Close()放在 shutdown 流程里 - 客户端连接失败时,别直接 panic;建议重试 3 次,每次
time.Sleep(100 * time.Millisecond),因为服务端可能刚启动还没 listen 完
exec.Command 管道链必须手动串联,不能写 cmd1 | cmd2
Go 不支持 shell 风格的管道语法,exec.Command("ls") | exec.Command("grep", "main") 这种写法根本不存在。想实现 ls | grep 效果,只有两种可靠方式。
常见错误现象:命令顺序执行而非管道、io.Copy 卡死、中间命令退出后下游读不到 EOF、goroutine 泄漏。
- 两命令简单管道:用
cmd1.StdoutPipe()获取 reader,再设cmd2.Stdin为该 reader,然后cmd1.Start()→cmd2.Start()→cmd1.Wait()→cmd2.Wait() - 三命令及以上(如
ps | grep | awk),直接用exec.Command("sh", "-c", `ps aux | grep go | awk '{print $2}'`),Linux/macOS 用sh -c,Windows 用cmd /c,语法不兼容但最稳 - 手动链式管道时,
cmd1.Wait()必须在cmd2.Run()之后调用;如果提前 Wait,cmd2可能因 stdin 关闭而无法读取完整数据 - 所有
io.Copy建议包一层context.WithTimeout,避免某一级卡死拖垮整个流程
channel 不是 IPC,只是协程通信机制
搜索 “Golang 管道 IPC” 时,很多人默认 Go 有类似 C 的 pipe(2) 系统调用直通内核——其实没有。chan 是 Go 运行时实现的内存队列,只在单个进程内有效,不跨进程、不持久、不与 fd 互通。
混淆 chan 和系统级 pipe 是 panic 和死锁的主因。比如用 channel 尝试让两个独立 go 进程通信,结果当然是收不到任何数据。
-
chan用于 goroutine 之间同步/异步传递数据,类型安全,close 后可读尽剩余值 - 发送方必须是**最后一个写入者**才能 close channel;接收方绝不 close,否则 panic
- 下游用
for range ch而不是for { select { case x := ,否则 close 后收不到 EOF 信号 - 如果某个处理环节可能提前退出(比如过滤空值后不发),仍要确保后续环节能收到关闭通知,否则 range 会永远阻塞
跨进程通信从来就不是 channel 的职责,它的边界非常清晰:只管进程内并发协作。真要 IPC,路径、权限、清理这三件事,比选什么 API 更关键。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











