Netty(一) 网络基础概念与5种IO模型

学习netty之前 我们首先需要知道一些基础概念,万丈高楼平地起,希望大家能啃下去.

一.基础概念

1.什么是文件

Linux下面万物皆文件,linux将文件作为一切可用资源的使用接口.“一切资源”包括内存、磁盘、其他各种设备、进程间的通信,还有网络通信。这是一种简化的思想,把一切资源都简化为文件展示出来,将这些对资源的操作简化为对文件的操作。

2.Socket

在计算机通信领域,socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。
Socket就是该模式的一个实现:即socket是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)。
Socket()函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。

总结就是应用之间通信就是通过socket这个对象去操作.

3.文件描述符 file descriptor
fd是(file descriptor/文件描述符) ,这种一般是BSD Socket的用法,用在Unix/linux系统上。在Unix/linux系统下,一个Socket句柄,可以看做是一个文件,在socket上收发数据,相当于读一个文件进行读写,所以一个socket句柄,通常也用表示文件句柄的fd来表示。

4.IO

流是一种抽象概念,它代表了数据的无结构化传递。按照流的方式进行输入输出,数据被当成无结构的字节序或字符序列。从流中取得数据的操作称为提取操作,而向流中添加数据的操作称为插入操作。用来进行输入输出操作的流就称为IO流。换句话说,IO流就是以流的方式进行输入输出

5.IO多路复用

I/O就是指的我们网络I/O,多路指多个TCP连接(或多个Channel),复用指复用一个或少量线程。串起来理解就是很多个网络I/O复用一个或少量的线程来处理这些连接.

如果有10W个客户端链接来连接服务端,我要开10W个线程去处理吗?如果开10W个线程去处理会发生什么问题?仔细想一想Redis也是这样的场景吗?

连接很多的时候,不能每个连接一个线程,会耗尽系统内存的。线程也不能阻塞在任何一个连接上,等新的数据来,这样就不能及时响应其他连接发来的数据了;也不能用非阻塞方式,轮询所有的连接,这会浪费掉大量CPU时间;只能告诉系统,我对哪些连接感兴趣,有消息来的时候,通知我处理。

6.线程上下文切换

上下文切换就是从当前执行任务切换到另一个任务执行的过程。但是,为了确保下次能从正确的位置继续执行,在切换之前,会保存上一个任务的状态。

CPU通过时间片分配算法来循环执行任务,例如线程A在执行100行的代码,其中第50行是 a=2,这个时候线程A的时间片用完了,CPU就跑路了,并和你说等下我再来,先去执行其他任务.那CPU下次回来的时候是不是要从第50行代码继续执行,并且这个时候a=2,这些都要保存下来,才能切到下一个任务.所以线程频繁上下文切换是很耗费资源的.

7.Linux操作系统中断
1、什么是中断?
我们知道现代计算机CPU在执行任务是都是将CPU的时间划分为很细粒度的时间片,一个任务每次只能执行这么多的时间,时间到了就必须交出使用权,那么时间到了之后系统做了什么?这就要用到中断了,时间片到了会有定时器触发一个软中断,然后进入相应的的处理历程。当然鼠标、键盘也可以触发中断,这就是硬中断。

中断是指在程序执行过程中,遇到急需处理的事件时,暂时停止CPU上现行程序的运行,转去执行相应的时间处理程序,待处理完成后再返回原程序被中断或调度其他程序执行的过程。

2、软中断和硬中断
软中断:是执行中断指令产生的,无外部施加中断请求信号,因此中断的发生不是随机的而是程序安排好的,比如编程异常(1/0),系统调用就是典型的软中断。
硬中断:是由与系统相连的外部设备产生的,比如磁盘、网卡、键盘、鼠标、时钟等,因此具有随机性和突发性,每个设备都有它自己的IRQ(中断请求)。基于IRQ,CPU可以将相应的请求分发到对应的硬件驱动(中断处理程序)上。

3.我们的epoll怎么感知到有事件发生呢?

靠的是中断,只要有事件发生,就会产生系统中断,然后通过回调函数去触发事件,可以减少很多轮训的时间.这句话可以看完后续再回来来看,对于操作系统内核来说通过中断方式主动通知CPU的方式减少了线程轮询判断,提高了线程执行效率。

8.用户空间和内核空间

用户空间:我们应用程序运行的地方

内核空间: Linux 内核的运行空间

他们两个是隔离开的,我们用户空间是不能直接获取socket的数据,当我们想从网络获取数据的时候会调用系统函数把数据从scoket先读到内核空间,再从内核空间复制到用户空间,到了用户空间我们的应用程序才能读到对应的数据.

下面user space是用户空间,kernel是内核空间

1、JVM发出read() 系统调用。
2、OS上下文切换到内核模式(第一次上下文切换)并将数据读取到内核空间缓冲区。(第一次拷贝:hardware ----> kernel buffer)
3、OS内核然后将数据复制到用户空间缓冲区(第二次拷贝: kernel buffer --> user buffer),然后read系统调用返回。而系统调用的返回又会导致一次内核空间到用户空间的上下文切换(第二次上下文切换)。
4、JVM处理代码逻辑并发送write()系统调用。
5、OS上下文切换到内核模式(第三次上下文切换)并从用户空间缓冲区复制数据到内核空间缓冲区(第三次拷贝: user buffer ——> kernel buffer)。
6、write系统调用返回,导致内核空间到用户空间的再次上下文切换(第四次上下文切换)。将内核空间缓冲区中的数据写到hardware(第四次拷贝: kernel buffer ——> hardware)。

为什么不能直接让磁盘控制器把数据送到应用程序的地址空间中呢?最简单的一个原因就是应用程序不能直接操作底层硬件。

9.进程的阻塞

正在执行的进程,由于期待的某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新工作做等,则由系统自动执行阻塞原语(Block),使自己由运行状态变为阻塞状态。可见,进程的阻塞是进程自身的一种主动行为,也因此只有处于运行态的进程(获得CPU),才可能将其转为阻塞状态。当进程进入阻塞状态,是不占用CPU资源的

10.同步和异步阻塞与非阻塞

阻塞非阻塞说的是线程的状态(调用方调用了这个函数之后是否会阻塞)
同步和异步说的是消息的通知机制

通常我们认知中 同步=阻塞,异步=非阻塞,我们不必纠结,从不同层面去看会有不同的观点.

例如从系统IO层面来看,IO操作分为两步 1.发起IO请求  2.如果IO请求发现有时间就绪就执行实际的IO操作.如果他们阻塞在第一步就是阻塞IO(即有事件就绪,连接事件,写时间等),如果第二步需要由调用方自己处理IO(即读取)就是同步.

这样看来Epoll(IO多路复用)是同步阻塞的.因为epoll会阻塞于epoll_wait函数,等待事件就绪,当事件就绪的时候需要调用方主动去处理IO,他就是同步的.

如果从我们Netty这个框架上面看,即程序员的宏观,Netty启动后会阻塞吗?不会,因为底层有个死循环一直去调用epoll(timeout)去查询有没有事件发生.那Netty怎么知道有事件发生的啊?一旦有事件发生epoll会通知我们,那就是异步. 所以不同层面有不同的见解,也有人认为Netty是同步非阻塞,因为是同步IO等,所以不必纠结.

二.I/O模型

通过这个IO线程模型,我们可以更深入的去了解阻塞/非阻塞,同步/异步的区别,这是基于网络IO,用户程序和内核的交互为基础进行讲解(因为网上很多令人迷惑的资料)

再来复习一下这个
        IO操作分两步:发起IO请求等待数据准备(数据会先被拷贝到操作系统内核的缓冲区中),实际IO操作(数据从内核缓冲区拷贝去用户控件)(洗衣服,晾衣服)
        同步须要主动读写数据,在读写数据的过程中还是会阻塞(好比晾衣服阻塞了你) 
        异步仅仅须要I/O操作完毕的通知。并不主动读写数据,由操作系统内核完毕数据的读写(机器人帮你自动晾衣服)

1.阻塞式IO

一阶段进程阻塞于recvfrom(从socket获取数据)调用,二阶段数据从内核复制到应用缓冲区(用户空间)

2.非阻塞IO

一阶段反复调用recvfrom查看数据有无准备好,没准备好就一直轮训,二阶段数据从内核复制到应用缓冲区(用户空间)

3.IO多路复用

为什么叫IO多路复用,可以看前面的阻塞IO和非阻塞IO,他们有什么缺点?

阻塞IO,如果有1W个连接过来想要及时响应数据得起1W个线程,并且线程如果无事件发生处于阻塞

非阻塞IO,如果有1W个连接过来想要及时响应数据得起1W个线程,并且不断忙轮训消耗CPU,看有没有事件就绪

我们有没有一种方式可以用少量线程就可以处理大量的连接或者读写事件呢?

这个时候我们的IO多路复用就出来了.当我们之间调用select函数的时候,我们先把连接事件和写事件注册到select(fd文件描述符)对象上,他就会监听有没有这些事件的发生.如果没有他会一直阻塞.当有感兴趣的事件发生了,即有TCP连接进来了,或者有TCP连接写数据,他就能感知到事件就绪,就会告诉应用程序你可以调用recvfrom来获取信息了.所有连接都阻塞于select函数,而不是阻塞于recvfrom函数了,而select函数是操作系统内核的函数,他通过中断感知事件的发生.

 一阶段进程阻塞于select调用,二阶段数据从内核复制到应用缓冲区(用户空间),他和上面两种的区别在于阻塞的点不同了一个是阻塞于recvfrom,一个阻塞于select函数.而select函数可以阻塞所有的连接.

4.信号驱动(比较少用)

应用进程使用 sigaction 系统调用,内核立即返回,应用进程可以继续执行,也就是说等待数据阶段应用进程是非阻塞的。内核在数据到达时向应用进程发送 SIGIO 信号,应用进程收到之后在信号处理程序中调用 recvfrom 将数据从内核复制到应用进程中。信号驱动 I/O 的 CPU 利用率很高。

信号驱动的作用在于在等待I/O可用的过程中可以执行其它的指令,这种方法从理论上看 倒是不错。由于进程已经休眠,就不会再占用CPU,仅当I/O可用时它才恢复执行。但是这种方法的问题在于信号处理的开销有点大。若只是少数的请求还没有问题,若是每分钟收到100个请求,那就几乎一直都在捕获信号。每秒钟捕获上百个信号的开销是相当大的,不单是进程,对于内核发送信号的开销而言也是一样的。

一阶段系统调用sigaction ,立即返回不阻塞,二阶段数据从内核复制到应用缓冲区(用户空间)

5.异步IO

第一阶段直接调用aio_read函数直接返回,当数据准备好,内核自动帮你把数据从内核拷贝到用户空间,再通知应用程序从用户空间直接取数据,第二阶段也不需要自己主动去复制数据到用户空间,而是内核全部帮你做了.

异步 I/O 与信号驱动 I/O 的区别在于,异步 I/O 的信号是通知应用进程 I/O 完成,而信号驱动 I/O 的信号是通知应用进程可以开始 I/O

让我们总结一下这5种IO模型

1.五种IO的模型:阻塞IO、非阻塞IO、多路复用IO、信号驱动IO和异步IO,前四种都是同步IO,在内核数据copy到用户空间时都是阻塞的

2.I/O复用(select,poll,epoll...);
I/O多路复用是阻塞在select,epoll这样的系统调用,没有阻塞在真正的I/O系统调用如recvfrom
进程受阻于select,等待可能多个套接口中的任一个变为可读

3.IO多路复用使用两个系统调用(select和recvfrom)  blocking IO只调用了一个系统调用(recvfrom)

4.select/epoll 核心是可以同时处理多个connection,而不是更快,所以连接数不高的话,性能不一定比多线程+阻塞IO好
多路复用模型中,每一个socket,设置为non-blocking,
阻塞是被select这个函数block,而不是被socket阻塞的

5.阻塞IO和非阻塞IO的区别在于第一步,发起IO请求是否会被阻塞,如果阻塞直到完成那么就是传统的阻塞IO,如果不阻塞,那么就是非阻塞IO。
同步IO和异步IO的区别就在于第二个步骤是否阻塞,如果实际的IO读写阻塞请求进程,那么就是同步IO,因此阻塞IO、非阻塞IO、IO复用、信号驱动IO都是同步IO,如果不阻塞,而是操作系统帮你做完IO操作再将结果返回给你,那么就是异步IO。

6.同步需要主动读写数据,异步是不需要主动读写数据
同步IO和异步IO是针对用户应用程序和内核的交互

7.如果还不理解可以看下面例子

洗衣机洗衣服 分为 洗衣机洗衣服     和   人去晾衣服两阶段

洗衣机洗衣服(无论阻塞式IO还是非阻塞式IO,都是同步IO模型)
同步阻塞:你把衣服丢到洗衣机洗,然后看着洗衣机洗完,洗好后再去晾衣服(你就干等,啥都不做,阻塞在那边) 

 同步非阻塞:你把衣服丢到洗衣机洗,然后会客厅做其他事情,定时去阳台看洗衣机是不是洗完了,洗好后再去晾衣服
(等待期间你可以做其他事情,比如用电脑打开看视频学习)

   
异步阻塞: 你把衣服丢到洗衣机洗,然后看着洗衣机洗完,洗好后再去晾衣服(几乎没这个情况,几乎没这个说法,可以忽略)
        
异步非阻塞:你把衣服丢到洗衣机洗,然后会客厅做其他事情,洗衣机洗好后会自动去晾衣服,晾完成后放个音乐告诉你洗好衣服并晾好了
 

参考: https://segmentfault.com/a/1190000003063859

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值