Golang为何不使用netpoller统一处理文件I/O和网络I/O?

Golang为何不使用netpoller统一处理文件I/O和网络I/O? Go 在网络 I/O 方面比文件 I/O 表现更好。为什么不以处理网络 I/O 的相同方式来处理文件 I/O?它们之间有什么区别?

6 回复

谢谢。

更多关于Golang为何不使用netpoller统一处理文件I/O和网络I/O?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


@cwww3,欢迎来到论坛。

你所说的“更好”是指什么?在哪些方面更好?

我的理解是,文件I/O会消耗M并阻塞它。然后P会将M移交给新找到的M。 但是网络I/O不会阻塞M。

虽然更新有些迟了,但或许仍有参考价值(或对其他读者有帮助):Bill Kennedy(Ardan Labs)的这篇文章从 goroutine 调度的角度,详细探讨了异步与同步 I/O 的区别:

ardanlabs.com

Go 中的调度:第二部分 - Go 调度器

Ardan Labs Logo

前言 这是三部分系列文章中的第二篇,旨在帮助理解 Go 调度器背后的机制和语义。本篇重点介绍 Go 调度器。 三部分系列索引:

  1. Go 中的调度:第一部分 - …

(请向下滚动至“异步系统调用”段落)

这篇文章是关于并发和 Go 调度器的三篇系列文章的一部分。内容非常长,但读起来很有趣。

(对于其他读者,@cwww3 指的是 Go 的工作窃取调度器 这一概念,正如 rakyll 在链接的博客文章中所解释的那样。)

我对 Go 中文件 I/O 与网络 I/O 的内部实现并不熟悉,因此在这里我主要需要参考其他资料。希望这些资料能对本话题有所贡献。

这次提交(在这里找到)表明 Go 在可能的情况下会使用运行时轮询器进行文件 I/O。操作系统似乎是这里的限制因素;例如,Linux 上的 epoll 不支持普通文件。另请参阅 Go 议题 #6817 中的这条评论

Go语言在网络I/O和文件I/O处理上的差异主要源于两者的本质特性和操作系统支持的不同。网络I/O天生就是异步的,而传统文件I/O在大多数Unix-like系统上本质上是同步的。

核心区别:

  1. 操作系统支持差异

    • 网络I/O:操作系统提供了成熟的异步接口(epoll/kqueue/IOCP)
    • 文件I/O:Linux的异步文件I/O(AIO)存在诸多限制,其他系统支持不统一
  2. 性能考量

    • 网络I/O通常涉及更高的延迟,异步处理能显著提升并发性能
    • 文件I/O在内存映射或缓冲足够时,同步操作的性能损失相对较小

示例代码对比:

// 网络I/O - 使用netpoller(非阻塞)
func handleNetworkIO() {
    conn, _ := net.Dial("tcp", "example.com:80")
    conn.SetDeadline(time.Now().Add(time.Second))
    
    go func() {
        buf := make([]byte, 1024)
        n, _ := conn.Read(buf) // 非阻塞,由netpoller调度
        fmt.Printf("Read %d bytes\n", n)
    }()
}

// 文件I/O - 当前实现(阻塞)
func handleFileIO() {
    file, _ := os.Open("data.txt")
    buf := make([]byte, 1024)
    
    // 在goroutine中执行以避免阻塞
    go func() {
        n, _ := file.Read(buf) // 阻塞操作
        fmt.Printf("Read %d bytes\n", n)
    }()
}

// 实验性的异步文件I/O(Go 1.16+)
func handleAsyncFileIO() {
    file, _ := os.Open("data.txt")
    
    go func() {
        buf := make([]byte, 1024)
        n, _ := file.Read(buf) // 仍然阻塞,但goroutine调度器会处理
        
        // 使用io_uring的实验性支持(Linux特定)
        // 目前需要第三方库或特定内核版本
    }()
}

技术现状:

Go团队已经在探索统一I/O处理的方式:

  • 在支持io_uring的Linux系统上,实验性地通过GOEXPERIMENT=iouring启用
  • Windows的IOCP已经统一处理文件和网络I/O
  • 其他平台仍使用线程池处理阻塞的文件I/O

性能数据示例:

// 基准测试显示差异
func BenchmarkNetworkIO(b *testing.B) {
    // 网络I/O可轻松处理数万并发连接
}

func BenchmarkFileIO(b *testing.B) {
    // 文件I/O受限于线程池大小(默认限制)
}

当前限制主要来自:

  1. 跨平台一致性要求
  2. 现有代码的兼容性
  3. 不同文件系统特性的巨大差异

Go团队正在逐步推进I/O处理的统一,但这需要时间来解决底层系统差异和保证向后兼容性。

回到顶部