从Go编程看IO多路复用Select

IO多路复用通过某种机制使进程监听某些文件描述符,当文件描述符中有读或写就绪时,进程能够收到系统内核发送的相应通知从而进行相应的IO操作;IO多路复用有:select、poll、epoll等模式,这里主要介绍select;select本质上也是同步IO,调用时阻塞自己,IO事件就绪后被唤醒返回负责读写操作;

在Go中其函数定义如下:

func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout
*Timeval) (n int, err error)

FdSet定义:

type FdSet struct {
  Bits [16]int64
}

select函数实现IO多路复用,通过其参数通知内核:

1、关注的文件描述符
2、关心的文件描述符的哪种状态:可读、可写还是异常
3、等待时间,无限等待阻塞或是固定超时时间

通过上面的介绍可以知道我们需要有这么几种参数传递给select函数,所关注的描述符,所关注的状态、等待时间;

nfd(maxfd): 文件描述符集合中要监听的文件描述符个数,0-(maxfd-1)为需要检测的文件描述符;
r(readfds): 读监控文件描述符集,监控文件描述符集的读变化,如文件描述符集中有文件可读即通过该参数回传有变化的描述符,清空无变化的描述符;
w(writefds): 写监控文件描述符集,监控文件描述符集的写变化,如文件描述符集中有文件可写即通过该参数回传有变化的描述符,清空无变化的描述符;
e(exceptfds): 异常监控文件描述符集,监控文件描述符集的异常,如文件描述符集中有文件异常即通过该参数回传有变化的描述符,清空无变化的描述符;
timeout参数: 传入nil时函数无限阻塞等待,整数值为超时时间;

上面三个文件描述符集合如无需关注某一类状态可传入nil,则select将不监控文件描述符的读、写或异常;
tcp连接中可只需关注是否可读即可;

通过函数返回可知这么两类信息:
1、准备好的文件描述符个数
2、具体哪些文件描述符处于就绪可读、可写或异常状态

-1 发生错误
0 函数超时,当设置了超时时间,该时间内未有状态变化时
大于0 有满足读、写、异常的文件描述符,需检查文件描述符集

每次函数返回时都会将文件描述符集FdSet中未发生任何事件的fd清空,每次调用select时都需将所关注的fd重新加入FdSet中;
可监控文件描述符个数取决于 FdSet中Bits的位长度,每个bit代表一个文件描述符,默认情况下Go中的定义为:Bits [16]int64,也就是一个8字节整数数组,数组长度为16,第一个数组元素可存储的文件描述符为:0-63,第二个为:64-127依次类推;此时最多可以监听的文件描述符数为1024个;

1、内核将消息传递到用户空间需要执行系统拷贝,如监听了大量fd会导致性能下降
2、每次调用select都需要从用户态拷贝fd集合到内核态
3、每次调用select内核态都需要遍历传进来的所有fd集合
4、默认select支持的fd集合过小,只有1024;
5、轮询效率低,每次调用select、内核通知都需要轮询整个fd集合

func SelectIO(fd int) {
//读文件描述符集
fdReadSet := &syscall.FdSet{}
connect = &Connect{maxFd: fd, childsMap: map[string]int{}}
for {
    FD_ZERO(fdReadSet)
    FD_SET(fd, fdReadSet) //socket文件描述符
    for _, child := range connect.childsMap {
        FD_SET(child, fdReadSet) //连接监听
        if child > connect.maxFd {
            connect.maxFd = child
        } else {

        }
    }
    n, err := syscall.Select(connect.maxFd+1, fdReadSet, nil, nil, nil)
    if err != nil {
        log.Println(err)
        if err == syscall.EAGAIN { //非阻塞模型下资源限制或不满足条件返回eagain 异常 Resource temporarily unavailable
            continue
        }
    }
    log.Printf("n:%v,fdReadSet:%v", n, FD_ISSET(fd, fdReadSet))
    //-1 出错  >0就绪的文件描述符数  0 超时
    if n > 0 && FD_ISSET(fd, fdReadSet) {
        //接受连接
        nfd, naddr, err := syscall.Accept(fd) //阻塞模式下在此方法会阻塞
        if err != nil {
            log.Printf("接受连接出错:%v,%v", fd, err)
            continue
        }
        //设置非阻塞
        if err := syscall.SetNonblock(nfd, true); err != nil {
            log.Fatal(err)
        }
        var addr = naddr.(*syscall.SockaddrInet4)
        var ip = fmt.Sprintf("%d.%d.%d.%d:%d", addr.Addr[0], addr.Addr[1],
            addr.Addr[2], addr.Addr[3], addr.Port)
        log.Printf("新连接:%v", ip)
        processConn(nfd, ip)
    } else {
        readMsg(fdReadSet)
        }
    }
}

Original: https://www.cnblogs.com/softlin/p/16060353.html
Author: AiFly
Title: 从Go编程看IO多路复用Select

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/516386/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Go微服务框架go-kratos学习05:分布式链路追踪 OpenTelemetry 使用

    一、分布式链路追踪发展简介 1.1 分布式链路追踪介绍 关于分布式链路追踪的介绍,可以查看我前面的文章 微服务架构学习与思考(09):分布式链路追踪系统-dapper论文学习(ht…

    Go语言 2023年5月25日
    075
  • TCP粘”包”问题浅析及解决方案Golang代码实现

    一、粘”包”问题简介 在socket网络编程中,都是端到端通信, 客户端端口+客户端IP+服务端端口+服务端IP+传输协议就组成一个可以唯一可以明确的标识一…

    Go语言 2023年5月25日
    073
  • Go语言程序记录日志

    许多软件系统运行中需要日志文件。Go语言程序中,输出日志需要使用包”log”,编写程序十分简单。 像Java语言程序,输出日志时,往往需要使用开源的软件包来…

    Go语言 2023年5月29日
    056
  • 使用Go搭建并行排序处理管道笔记

    go;collapse:true;;gutter:true; package main</p> <p>import ( "bufio" …

    Go语言 2023年5月25日
    074
  • Go sort包

    sort包简介 官方文档Golang的sort包用来排序,二分查找等操作。本文主要介绍sort包里常用的函数,通过实例代码来快速学会使用sort包 sort包内置函数 sort.I…

    Go语言 2023年5月25日
    0132
  • 基于Go语言实现好用的HTTP接口请求requests

    使用Go自带的net/http库可以发送各种HTTP请求。然而各种类型请求发送方式有点不太一致,这里参考Python requests库的使用方式,简单封装了一下。代码如下: 文件…

    Go语言 2023年5月29日
    079
  • 《Go语言圣经》 读书笔记与个人思考 ① 第一章、包括源码分析

    《The Go Programming Language》 知识点记载,学习笔记、章节练习与个人思考。前言 · Go语言圣经 (itsfun.top) 标题后标记了小丑符号的表示还…

    Go语言 2023年5月25日
    0113
  • Go语言之高级篇Beego框架之爬虫项目实战

    一、爬虫项目 1、爬虫基础 a、网页上面会有相同的数据 b、去重处理 布隆过滤器哈希存储 c、标签匹配: 正则表达式beautiful soup或lxml这种标签提取库 d、动态内…

    Go语言 2023年5月29日
    072
  • Excelize 发布 2.6.1 版本,支持工作簿加密

    Excelize 是 Go 语言编写的用于操作 Office Excel 文档基础库,基于 ECMA-376,ISO/IEC 29500 国际标准。可以使用它来读取、写入由 Mic…

    Go语言 2023年5月25日
    086
  • Go语言之高级篇beego框架之layui框架应用

    1、layui前端框架 参考地址:https://www.layui.com Original: https://www.cnblogs.com/nulige/p/10396542…

    Go语言 2023年5月29日
    067
  • Golang实现set

    Golang语言本身未实现set,但是实现了map golang的map是一种无序的键值对的集合,其中键是唯一的 而set是键的不重复的集合,因此可以用map来实现set 由于ma…

    Go语言 2023年5月25日
    082
  • Go语言基础之并发

    并发是编程里面一个非常重要的概念,Go语言在语言层面天生支持并发,这也是Go语言流行的一个很重要的原因。 Go语言中的并发编程 并发与并行 并发:同一时间段内执行多个任务(你在用微…

    Go语言 2023年5月29日
    085
  • 这不会又是一个Go的BUG吧?

    hello,大家好呀,我是小楼。 最近我又双叒叕写了个BUG,一个线上服务死锁了,不过幸亏是个新服务,没有什么大影响。 出问题的是Go的读写锁,如果你是写Java的,不必划走,更要…

    Go语言 2023年5月25日
    094
  • 【Go语言】(一)环境搭建与了解VScode工具

    视频链接(p1~p8): golang入门到项目实战 [2022最新Go语言教程,没有废话,纯干货!] 参考链接: 用vscode开发go的时候,安装go包报错:connectex…

    Go语言 2023年5月25日
    097
  • Golang的JWT权限校验解析

    JWT校验 配置文件 package config type JWT struct { SigningKey string json:"signingKey" …

    Go语言 2023年5月25日
    0110
  • Go内存逃逸分析

    Go的内存逃逸及逃逸分析 Go的内存逃逸 分析内存逃逸之前要搞清楚一件事 我们编写的程序中的 函数和 局部变量默认是存放在栈上的(补充一点堆上存储的数据的指针 是存放在栈上的 因为…

    Go语言 2023年5月25日
    068
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球