BIO\NIO\多路复用 区别和浅解

Title:BIO\NIO\多路复用 区别和浅解

技术博客已迁移至个人页,欢迎查看 yloopdaed.icu

您也可以关注 JPP - 这是一个Java养成计划,需要您的加入。


前言

网络通信中BIO、NIO、多路复用相关的知识点非常多,网络上也有很多相关的技术文章。但是每个人的角度和切入点不同,知识涵盖的内容太多了,很难整理出一条清晰的思路。

我这两天尝试着脱离代码(因为加入代码Demo之后,篇幅增长,很多API也不是很熟悉,增加阅读的难度),单纯从操作系统网络通信的流程去梳理BIO、NIO,再到多路复用的优缺点。最终达到了解这些技术的联系和发展的目的。

BIO

此流程基于Java示例代码,代码可以查看 JPP /IOTest 类

BIO

主线程:

1 服务端等待客户端连接,阻塞进程

2 检测到客户端连接,返回对应的文件描述符fd,并将连接移动到子线程

3 开始下一次循环,跳回第1步

子线程:

1 子线程中服务端等待客户端响应,阻塞进程

2 接收到客户端响应,子线程处理

3 开始下一次循环,跳回第1步

优势:可以让每一个连接专注于自己的I/O并且编程模型简单,也不用过多考虑系统的过载、限流等问题。

问题:

1 线程的创建和销毁成本很高

2 线程本身占用较大内存

3 线程的切换成本是很高的

4 容易造成锯齿状的系统负载

为了解决BIO的缺点,进入NIO

NIO

此流程基于Java示例代码,代码可以查看 JPP /NIOTest 类

NIO

本文介绍的NIO是操作系统网络通信中的NON-BLOCKING IO,是Java NIO的基础。
上图中的两个 主线程 循环 指的是没有开辟子线程。意思是服务端接收客户端连接和处理客户端请求都在同一个线程中。

流程:

1 服务端请求客户端连接,非阻塞。如果没有直接返回null并向下执行

2 如果有客户端连接,请求消息响应,非阻塞。如果没有响应直接返回null并继续遍历客户端

3 遍历完所有客户端后,会重新开始循环,跳回第1步

优势:

1 规避多线程的问题

2 单线程解决多任务

问题:

客户端循环遍历时,不断进行用户态和内核态的切换,系统调用开销非常大

什么是用户态和内核态?
我的理解就是权限不同。用户态的进程能够访问的资源受操作系统的控制,而运行在内核态的进程才可以访问系统中的硬件设别,例如网卡。

所以上面客户端循环遍历询问消息响应,会不断进行用户态和内核态的切换。

开销大在哪?

  • 1 保护现场
  • 2 恢复现场
  • 3 软中断
  • 4 寻找中断向量表
  • 5 找回调函数

多路复用

在NIO的基础上,通过一次系统调用将连接客户端响应询问移动到内核处理,而不是反复进行用户态和内核态的切换。

如果把每次系统调用理解成一条通路,那么这种把多次系统调用合并成一次的方式,就叫做多路复用

例如:美团 Java NIO浅析

interface ChannelHandler{
    void channelReadable(Channel channel);
    void channelWritable(Channel channel);
}
class Channel{
    Socket socket;
    Event event;//读,写或者连接
}

//IO线程主循环:
class IoThread extends Thread{
    public void run(){
        Channel channel;
        while(channel=Selector.select()){//选择就绪的事件和对应的连接
            if(channel.event==accept){
                registerNewChannelHandler(channel);//如果是新连接,则注册一个新的读写处理器
            }
            if(channel.event==write){
                getChannelHandler(channel).channelWritable(channel);//如果可以写,则执行写事件
            }
            if(channel.event==read){
                getChannelHandler(channel).channelReadable(channel);//如果可以读,则执行读事件
            }
        }
    }
    Map<Channel,ChannelHandler> handlerMap;//所有channel的对应事件处理器
}

Selector中的select函数会执行系统内核的调用:Linux 2.6之前是select、poll,2.6之后是epoll。

此流程基于Java示例代码,代码可以查看 JPP /NIOEpollTest 类

多路复用

多路复用的实现方式

select

流程:

1 客户端建立连接后返回fd

2 用二进制位表bitmap标记fds对应的位置,并将这个bitmap从用户态拷贝到内核态

3 收到客户端响应,将对应的bitmap位标记为1

4 程序处理消息,重新创建bitmap,循环下一次

缺点:

1 bitmap默认最大限制1024位

2 bitmap不可重用,每次循环重新创建

3 用户态和内核态切换开销

4 轮询所有的客户端处理消息 O(n)

poll

流程:

与select相似,select中用bitmap标记fds,在poll中自己声明了结构体

struct pollfd{
    int fd;
    short events;
    short revents
}

传输pollfd数组,解决了select中fds限制1024位的问题

其次,内核将响应客户端对应的pollfd结构体中的revnets标记为1,说明这个客户端有消息响应

处理消息时将这个pollfd的revnets重置即可,不用在重新创建数组,所以select中每次循环重新创建bitmap的问题也被解决了

遗留问题:

1 由于fds文件描述符存储在用户空间,拷贝到内存空间处理一定会涉及到用户态和内核态的切换。这个系统调用有一定开销

2 每次内核态返回的信息是全量信息,要轮询处理,时间复杂度是 O(n),如果连接数过多,也会有很多无意义的开销

这两个问题留给 epoll

epoll

流程:

1 建立epoll对象时在内核分配资源,其数据结构是红黑树。添加和检索的时间复杂度 O(lgn)

int epoll_create(int size); 

2 建立连接时,在红黑树中存储 epoll_event结构体,其中包含fd和events等信息

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);  

3 调用epoll_wait收集响应的连接,放入一个单向链表

int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);  

到此为止,上面提到的多路复用的所有缺点都得以解决。

最后

网络通信

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容