I/O模型学习笔记

TCP客户同时处理两个输入:标准输入和TCP套接口。它不能单纯阻塞在这两个源中某个特定源的输入,而是应该阻塞在其中任何一个源的输入上。这正是select和poll这两个函数的目的之一。

客户阻塞于标准输入调用期间,服务器进程被杀死。服务器TCP虽然正确地给客户TCP发送了一个FIN,但是客户进程正阻塞于从标准输入读入,它将看不到这个EOF,直到套接口读时为止。

这样进程需要一种预先告知内核的能力,使得内核一旦发现进程指定的一个或多个I/O条件就绪,他就通知进程,这个能力成为I/O复用。

 

I/O模型

一个输入操作通常包括两个不同的阶段:

1.等待数据准备好

2.从内核到进程拷贝数据

第一步通常涉及等待数据从网络中到达,当锁等待分组到达时,它被拷贝到内核中的某个缓冲区。第二步把数据从内核缓冲区拷贝到应用进程缓冲区。

下面的全部为unix系统模型:

同步阻塞I/O

进程调用recvfrom,其系统调用直到数据报到达且拷贝到应用进程的缓冲区或者发生错误才返回。常见的错误时系统调用被信号中断。

调用应用程序处于一种不再消费 CPU 而只是简单等待响应的状态, 在调用 read 系统调用时,应用程序会阻塞并对内核进行上下文切换。然后会触发读操作,当响应返回时(从我们正在从中读取的设备中返回),数据就被移动到用户空间的缓冲区中。然后应用程序就会解除阻塞(read 调用返回)。从应用程序的角度来说,read 调用会延续很长时间。

同步非阻塞I/O

进程把一个套接口设置成非阻塞是在通知内核:当所请求的I/O操作非得把本进程投入睡眠才能完成时,不要把本进程投入睡眠,而是返回一个错误(EWOULDBLOCK)。当一个应用进程像这样对一个非阻塞描述字循环调用recvfrom时,我们称之为轮询(polling)。应用进程持续轮询内核,以查看某个操作是否就绪。这么做耗费大量的CPU时间,导致吞吐量降低。

 

I/O复用模型

有了I/O复用,我们就可以调用select或poll,阻塞在这两个系统调用中的某一个之上,而不是阻塞在真正的I/O调用上。

我们阻塞于select调用,等待数据报套接口变为可读。当select返回套接口可读这一条件时,我们调用recvfrom把所读数据报拷贝到应用进程缓冲区。

在多线程中使用阻塞I/O,这种模型与上述十分相似,代替使用select阻塞在多个文件描述字上的是,使用多个线程,这样每个线程可以自用的调用诸如recvfrom之类的阻塞式I/O了。

 

信号驱动I/O模型

使用信号,让内核在描述字就绪时发送SIGIO信号通知我们,这种模型为信号驱动。

通过系统调用安装信号处理程序,此系统调用立即返回,进程继续工作。当数据报准备好之后,会为该进程生成一个SIGIO信号。随即可以在信号处理函数中调用recvfrom来处理数据,并通知主程序体数据已经准备好被处理了。

信号驱动I/O模型的优点是当数据报到达时,可以不阻塞,主循环可以继续执行,只是等待信号处理程序的通知,或者数据已准备好被处理,或者数据报已经准备好被读了。

 

异步I/O模型

异步I/O工作机制:告知内核启动某个操作,并让内核在整个操作完成(包括将数据从内核拷贝到我们自己的缓冲区)后通知我们。需要确定系统是否支持此I/O模型。

 

与信号驱动I/O的区别是:信号驱动是告诉我们何时可以启动一个I/O操作,而异步I/O是由内核通知我们I/O操作何时完成的。

 

各种I/O的比较

前四种模型主要区别于第一阶段,因为他们的第二阶段是一样的:在数据从内核拷贝到调用者的缓冲区期间,进程阻塞于recvfrom调用。相反,异步I/O模型在这里两个阶段都要处理,从而不同于其他4种模型。

用上面的模型,对下面的I/O进行实现:

同步阻塞:

BIO :一个线程负责连接,请求+应答 (1:1)   

伪异步I/O模型  :

采用线程池和任务队列可以实现伪异步I/O,当有新的客户端接入时,将客户端Socket封装成一个Task,投递到后端的线程池中处理,JDK的线程池为何一个消息队列和N各活跃线程,对消息队列中的任务进行处理。

虽然线程池资源可控,避免资源耗尽问题,但底层的通信仍然是同步阻塞模型。不能及时的从TCP缓冲区读取数据,会导致TCP的window size不断减小,发送方将不能再向TCP缓冲区写入消息。

 

NIO:

缓冲区Buffer:可以直接将数据写入或者读到Stream中,在读取数据时,直接读到缓冲区中的,写入数据时,写入缓冲区中。任何时候访问NIO中的数据,都是通过缓冲区进行操作的。

通道channel:网络数据通过Channel 读取和写入,双全工的,可以同时进行读写,

多路复用器 selector (m:1),Selector会不断地轮询注册在其上的Channel,如果某个Channel上面发生读或者写时间,这个Channel就处于就绪状态,会被Selector轮询出来,然后通过selectorKey可以获取就绪的Channel集合。epoll代替selector

优点:

客户端发起连接的操作是异步的,通过多路复用器注册,不需要同步阻塞。

SocketChannel的读写操作都是异步的,如果没有数据不会同步等待,直接返回,I/O通信线程就可以处理其他的链路

线程模型优化:通过epoll实现,没有连接句柄数的限制,这意味着一个selector线程可以同时处理成千上万个客户端连接,而且性能不会线性下降,非常适合做高性能、高负载的网络服务器。

 

AIO(M:0):

对应事件驱动I/O,由JDK底层的线程负责回调并驱动读写操作。

NIO:主动处理

AIO:被动调用,JDK处理

阻塞与同步异步

阻塞和非阻塞是一个I/O的概念,进程访问的数据如果尚未就绪,进程是否需要等待。

阻塞:从发送到到达或者error才返回,期间一直等待

非阻塞:轮询检查内核是否有数据到来

同步和异步是指访问数据的机制,是一个通讯的概念。

同步I/O操作导致请求进程阻塞,知道I/O操作完成

异步I/O操作,不导致请求进程阻塞。

前四种都为同步模型,因为其中有真正的I/O操作将阻塞进程。

 

 

欢迎关注公众号,让我们一起学习探讨问题

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值