前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go语言 Go的网络轮询及IO机制

Go语言 Go的网络轮询及IO机制

作者头像
李海彬
发布2018-03-21 10:33:28
1.3K0
发布2018-03-21 10:33:28
举报

简介

这篇介绍了Go的运行时系统——网络I/O部分。

阻塞

Go语言中,所有的I/O都是阻塞的,因此我们在写Go系统的时候要秉持一个思想:不要写阻塞的interface和代码,然后通过goroutines和channels来处理并发,而不是用回调和futures。其中一个例子是“net/http"包中的http服务器,无论何时当http服务器接收一个连接,它都会创建一个新的goroutine处理来自这个连接的所有请求,这样我们就能写出很清晰的代码:先做什么,然后做什么。然而,不幸的是,操作系统提供的阻塞式I/O并不适合构建我们自己的阻塞式I/O接口(interface)。

在我之前有关Go运行时的文章中,其中一篇介绍了Go调度器如何处理系统调用。为了处理一个阻塞式的系统调用,我们需要一个操作系统线程,因此如果要在OS的I/O层之上构建我们自己的阻塞式I/O层,则需要为每一个goroutine客户端连接产生一个新的线程,因为这些连接执行系统调用的时候会阻塞。当你拥有10000个客户端线程时构建我们自己的阻塞式I/O层会变得异常昂贵,因为这些客户端都会阻塞在自己的系统调用上等待I/O操作成功。

这里Go通过使用OS提供的异步接口(epoll等)来解决上述这个问题,但是会阻塞那些正在执行I/O的goroutines。

网络轮询(Netpoller)

将异步式I/O转为阻塞式I/O是通过网络轮询(netpoller)这个部分来完成的,netpoller在自己的线程中,接收那些准备进行网络I/O的goroutines发来的消息,netpoller使用操作接口提供的接口来提供对网络sockets的轮询。在linux系统中,这个接口是epoll,在BSDs和Darwin中是kqueue,在Windows中是IoCompletionPort。这些接口的共同之处是它们为用户空间轮询网络IO状态提供了非常高效的方法。

无论何时在Go程序中打开或者接受一个连接,该连接背后的文件描述符都被设置为非阻塞模式。如果在文件描述符还没准备好的时候,试着使用该连接进行I/O,就会返回一个无法进行I/O的错误。当一个goroutine尝试对一个连接进行读或者写的时候,如果没有收到上述报错,则执行这次操作,否则调用netpoller,当可以执行I/O的时候让netpoller通知goroutine,随之goroutine就被从正在运行的线程上调度出去等待通知。

当操作系统通知netpoller可以在一个文件描述符上运行I/O后,它就会检查内部的数据结构,看看是否有goroutines正阻塞在那个文件上,如果有则通知这些goroutines,这些goroutines随后会重新尝试在之前导致它阻塞的I/O操作。

如果你觉得之前的介绍是在使用旧式的Unix系统中的select和poll方法,那恭喜你,基本猜对了。但是Go的netpoller查询的是能被调度的goroutine而不是那些函数指针、包含了各种状态变量的struct等,这样你就不用管理这些状态,也不用重新检查函数指针等,这些都是你在传统Unix网络I/O需要操心的问题,Go中不需要。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2016-04-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Golang语言社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档