golang的runtime.netpoll以及IO模型

184 阅读3分钟

Go语言runtime中的netpoll是什么?

netpoll是 Go 语言runtime用来处理 IO 操作的关键组件,它使用了操作系统提供的 IO 多路复用机制增强程序的并发处理能力。netpoll不仅用于监控网络 IO,还能用于监控文件的 IO。说到这就不得不说说IO模型。

IO是什么?

IO:在操作系统中,数据在内核态和用户态之间的读、写操作。

IO模型是什么?

在 Unix 和类 Unix 操作系统中,文件描述符(File descriptor,FD)是用于访问文件或者其他 I/O 资源的抽象句柄,例如:网络套接字socket。而不同的 I/O 模型会使用不同的方式操作文件描述符。所以IO模型就是操作系统操作文件描述符的方式。常见的IO模型有阻塞IO模型、非阻塞IO模型、IO多路复用模型。

阻塞IO模型

一旦执行 IO 操作,如果读写缓冲区中的数据未就绪,线程就会陷入阻塞状态,不能执行其它操作,会产生线程切换和维护的系统开销。

非阻塞IO模型

当进行非阻塞IO请求时,当读写缓冲区中的数据未就绪时,线程不会陷入阻塞状态,而是继续向下执行其它操作。之后需要用户程序定期进行读写操作,查看读写缓冲区中的数据是否就绪,就绪后读取数据进行后续操作。优点是在等待数据就绪期间,可以执行其他操作,提高CPU利用率。缺点是需要应用程序多次进行系统调用来判断数据是否就绪,这样频繁进行用户态和内核态的切换,会降低CPU利用率。

IO多路复用模型

IO是在操作系统中,数据在内核态和用户态之间的读、写操作。而多路大部分情况下是指多个TCP连接,也就是多个Socket。复用是指复用一个或多个线程。IO多路复用意思就是说,一个或多个线程处理多个 TCP 连接。无需创建和维护过多的进程/线程,进而减少系统开销。IO多路复用是一种同步的IO模型。 利用IO多路复用模型,可以实现一个线程监视多个文件句柄。一旦某个文件句柄就绪,就能够通知到对应应用程序进行相应的读写操作;没有文件句柄就绪时就会阻塞应用程序,从而释放出CPU资源。不同的操作系统实现了各自的多路复用函数:select()\epoll()\poll()\kqueue()\evport()等,作为系统调用提供给用户态程序,多路复用函数会阻塞的监听一组文件描述符。

说了这么多,回到Go语言的netpoll

netpoll是把操作系统的IO多路复用机制与GO语言调度器关联起来的桥梁。 系统启动时,会启动一个独立的后台线程(不在Goroutine的调度线程池里),启动netpoll的轮询。当有Goroutine发起网络请求时,网络库会将fd(文件描述符)和pollDesc(用于描述netpoll的结构体,包含因为读/写这个fd而阻塞的Goroutine)关联起来,然后调用runtime.gopark方法,挂起当前的Goroutine。当后台的netpoll轮询获取到epoll(linux环境下)的event,会将event中的pollDesc取出来,找到关联的阻塞Goroutine,并进行恢复,把Groutine加入到执行队列中。 网络轮询器的作用就是在fd可操作时,恢复因为读写这个fd而阻塞的Groutine。进而由运行时的调度器调度执行。