第二章:同步 I/O (Sync I/O)
第二章:同步 I/O (Sync I/O)
在扩展到分布式之前,我们先来弄明白单机 IO 的手段。同步/异步/Poller/线程池,眼花缭乱的名词,是否在故弄玄虚?
我工作使用的第一门编程语言是 Go,享受了大量原生 Goroutine、GMP 调度器的便利,我以为编程语言天生具备并发能力是一件很自然的事情。
从事分布式存储工作后,发觉有经验的同事在做系统设计时,一定会重点关注 IO 和线程模型。IO 包括用户请求到本机 IO 整个链路。而线程模型和 IO 是否阻塞、负载和性能需求息息相关。必须掌握这些,才能针对不同的用户需求设计出最佳的存储系统。
这也迫使我从系统编程语言的角度重新思考、实践了一些常见的 IO 模式。这个过程反而令我更加理解了 Goroutine 和 Go Runtime 的设计动机、了解了更多 syscall 的基础概念。
本文以小型原型为线索,记录了笔者在此主题上的所见所闻。受限于笔者的经验,本文讨论的 IO 模型以及代码实验限定在 Linux 平台上。如有谬误,感谢读者交流、指正!
Linux IO 可根据以下性质分类1
- 是否被 Kernel Page Cache 缓存(📦 Buffered/🎯 Direct)
- 是否会发生阻塞 (⏳ Sync/⚡Aysnc)
I/O Type | ⏳ Sync I/O (Blocking) | ⚡ Async I/O (Non-blocking) |
---|---|---|
📦 Buffered I/O | read() , write() |
io_uring , libaio |
🎯 Direct I/O | read() , write() with O_DIRECT flag |
io_uring , libaio with O_DIRECT flag |
你可以根据自己的需求选择任意一种、甚至多种合适的方式一起使用,比如写时候使用 Sync + Bufferd I/O
,读的时候使用 Async + Direct I/O
。
如果你是新手开发者,可以暂时搁置所有 async
相关的疑惑。不要急,今天就让我们只在 同步 IO (Sync I/O) 的世界四处看看!