常用的I/O模型
目录
参考:https://www.cnblogs.com/myJavaEE/p/6721127.html
http://www.cnblogs.com/fanzhidongyzby/p/4098546.html
概念区分
同步/异步
同步和异步是相对的。
描述的是用户线程与内核的交互方式:同步指用户线程发起I/O请求后需要等待或者沦胥内核I/O操作完成后才能继续执行;异步指用户线程发起I/O请求后仍继续执行,当内核I/O操作完成后会通知用户线程,或者调用用户线程注册的回调函数。
同步:前后两个任务,有严格的顺序一致性,按照顺序执行,执行完一个后再执行另一个,需要等待、协调运行
异步:对顺序的要求和依赖没那么强。多线程是实现异步的一种方式。
实际编程中,同步和异步体现在请求和响应交互中。
同步:发送请求,响应结果
异步:发送请求,后续将结果以通知或者回调的方式返回。
阻塞和非阻塞
阻塞和非阻塞是相对的。
描述的是用户线程调用内核I/O的操作方式。阻塞是I/O操作需要彻底完成后才返回到用户空间。非阻塞指的是I/O操作被调用后立即返回给用户一个状态值,无需等到I/O彻底完成。
常见I/O模型
同步阻塞I/O
最简单的I/O模型,用户线程在读写时被阻塞,不能做其他任何事情,对CPU利用不高。
{
// read阻塞
read(socket, buffer);
// 处理buffer
process(buffer);
}
同步非阻塞
用户线程不断发起I/O请求,数据未达到时系统返回状态值,数据达到后才真正读取数据。
{
// read非阻塞
while(read(socket, buffer) != SUCCESS);
process(buffer);
}
用户线程每次I/O请求都可以立即返回,但为了拿到数据需要不断轮询,消耗了大量的CPU,一般很少使用这种模型。
I/O多路复用
建立在内核提供的阻塞函数select上。
用户先将需要进行I/O操作的socket添加到select中,然后等待阻塞函数select返回。当数据到达后,socket被**,用户线程就能直接发起read请求。
数据达到内核后通知用户线程,用户线程负责从内核空间拷贝数据。
{
// 注册
select(socket);
// 轮询
while(true) {
// 阻塞
sockets = select();
// 数据到达, 解除阻塞
for(socket in sockets) {
if(can_read(socket)) {
// 数据已到达, 那么socket阻不阻塞无所谓
read(socket, buffer);
process(buffer);
}
}
}
}
可以给select注册多个socket,然后不断调用select读取被**的socket,实现在同一线程内同时处理多个I/O请求的效果。
异步I/O
当用户线程接收到通知时,数据已经被操作系统从内核拷贝到用户指定的缓冲区内,用户线程可以直接使用。