同步 & 异步
同步:如果有多个任务或事件发生,这个任务或事件必须逐个进行,一个任务或事件的执行会导致整个流程的暂时等待,这些事件没有办法并发执行。
异步:如果有多个任务或事件,这些任务或事件可以并发执行,一个任务或事件的执行不会导致整个流程的等待。
void fun1(){
}
void fun2(){
}
void function(){
fun1();
fun2()
}
void fun1(){
}
void fun2(){
}
void function(){
new Thread(){
public void run(){
fun1()
}
}.start();
new Thread(){
public void run(){
fun2()
}
}.start();
}
区别:发生多个任务或事件时,一个任务或事件的执行是否会导致整个流程的等待。
阻塞 & 非阻塞
阻塞:当某个事件或任务在执行过程中,它发出一个请求操作,如果请求操作需要的条件不满足,它会一直在那等待,直到条件满足。
非阻塞:当某个事件或任务在执行过程中,它发出一个请求操作,如果请求操作需要的条件不满足,会立即返回一个标志信息,告知条件不满足,不会一直等待。
区别:当发出一个请求操作时,如果条件不满足,是一直等待还是返回一个标志信息。
Eg:
假如我要读一个文件中的内容,如果此时文件没有内容可读,对应阻塞来说,就是会一直在那等待,直至文件中有内容可读; 而对于非阻塞来说,会直接返回一个标志信息告知文件中暂无内容可读。
[同步&异步] & [阻塞&非阻塞]
同步和异步重点在于多个任务的执行过程
阻塞和非阻塞重点在于发出一个请求操作
阻塞IO & 非阻塞IO
一次io请求的过程:
当用户线程发起一个IO请求操作(以读请求操作为例),内核会去查看要读取的数据是否就绪,对于阻塞IO来说,如果数据没有就绪,用户线程则会一直在那等待,直到数据就绪;对于非阻塞IO来说,如果数据没有就绪,则会返回一个标志信息告知用户线程当前要读的数据没有就绪。当数据就绪之后,便将数据拷贝到用户线程,这样才完成了一个完整的IO读请求操作,也就是说一个完整的IO读请求操作包括两个阶段:
1)查看数据是否就绪;
2)进行数据拷贝(将数据拷贝到用户线程)。
阻塞IO和非阻塞IO的区别在第一阶段,如果数据没有就绪,在查看数据是否就绪的过程是一直等待,还是返回一个标志信号。
同步IO & 非同步IO
同步IO:如果一个线程请求IO操作,在IO完成之前,该线程会被阻塞;
异步IO:如果一个线程请求IO操作,IO操作不会导致该线程阻塞。
同步IO与异步IO模型是针对用户线程和内核的交互来说的。
对于同步IO:当用户发出IO请求操作之后,如果数据没有就绪,需要通过用户线程(非阻塞IO)或者内核(复用IO)不断去轮询数据是否就绪,当数据就绪时,再将数据从内核拷贝到用户线程;
对于异步IO:只有IO请求的发出是由线程来进行的,IO操作的两个阶段都是由内核自动完成的,然后发送通知告诉用户线程IO操作已经完成。也就是说,在异步IO中,不会对用户线程产生任何阻塞。
同步IO与异步IO的区别:数据拷贝阶段是由用户线程来完成还是由内核来完成。
异步IO必须要有操作系统底层的支持。
阻塞IO和非阻塞IO与 同步IO和非同步IO是两组不同的概念。
阻塞IO和非阻塞IO是反映在当用户请求IO操作时,如果数据没有就绪,是用户线程一直等待数据就绪,还是会收到一个信息标识。阻塞IO和非阻塞IO是反映在IO操作的第一阶段,在查看数据是否就绪是如何处理的。
linux五种IO模型
阻塞IO模型
非阻塞IO模型
信号驱动IO模型
IO复用模型
异步IO模型
以下通过钓鱼的例子来解释这五种IO模型。
拿一次磁盘文件读取为例,我们要读取的文件是存储在磁盘上的,我们的目的是把它读取到内存中。可以把这个步骤简化成把数据从硬盘中读取到用户空间中。
钓鱼的时候,刚开始鱼是在鱼塘里面的,我们的钓鱼动作的最终结束标志是鱼从鱼塘中被我们钓上来,放入鱼篓中。
这里面的鱼塘就可以映射成磁盘,中间过渡的鱼钩可以映射成内核空间,最终放鱼的鱼篓可以映射成用户空间。一次完整的钓鱼(IO)操作,是鱼(文件)从鱼塘(硬盘)中转移(拷贝)到鱼篓(用户空间)的过程。
阻塞IO模型
我们钓鱼的时候,有一种方式比较惬意,比较轻松,那就是我们坐在鱼竿面前,这个过程中我们什么也不做,双手一直把着鱼竿,就静静的等着鱼儿咬钩。一旦手上感受到鱼的力道,就把鱼钓起来放入鱼篓中。然后再钓下一条鱼。
映射到Linux操作系统中,这就是一种最简单的IO模型,即阻塞IO。 阻塞 I/O 是最简单的 I/O 模型,一般表现为线程等待某个条件,如果条件不满足,则一直等下去。条件满足,则进行下一步操作。
这种钓鱼方式相对来说比较简单,对于钓鱼的人来说,不需要什么特制的鱼竿,拿一根够长的木棍就可以悠闲的开始钓鱼了(实现简单)。缺点就是比较耗费时间,比较适合那种对鱼的需求量小的情况(并发低,时效性要求低)。
非阻塞IO模型
我们钓鱼的时候,在等待鱼儿咬钩的过程中,我们可以做点别的事情,比如玩一把王者荣耀等等。但是,我们要时不时的去看一下鱼竿,一旦发现有鱼儿上钩了,就把鱼钓上来。
映射到Linux操作系统中,这就是非阻塞的IO模型。应用线程与内核交互,目的未达到之前,不再一味的等着,而是直接返回。然后通过轮询的方式,不停的去问内核数据准备有没有准备好。如果某一次轮询发现数据已经准备好了,那就把数据拷贝到用户空间中。
这种方式钓鱼,和阻塞IO比,所使用的工具没有什么变化,但是钓鱼的时候可以做些其他事情,增加时间的利用率。
缺点:用户线程需要不断的询问内核数据是否准备就绪。
信号驱动IO模型
我们钓鱼的时候,为了避免自己一遍一遍的去查看鱼竿,我们可以给鱼竿安装一个报警器。当有鱼儿咬钩的时候立刻报警。然后我们再收到报警后,去把鱼钓起来。
映射到Linux操作系统中,这就是信号驱动IO。应用线程在读取文件时通知内核,如果某个 socket 的某个事件发生时,请向我发一个信号。在收到信号后,信号对应的处理函数会进行后续处理。
应用进程预先向内核注册一个信号处理函数,然后用户线程返回,并且不阻塞,当内核数据准备就绪时会发送一个信号给线程,用户线程接受到信号后,便在信号处理函数中调用IO操作开始把数据拷贝的用户空间中。
这种方式钓鱼,和前几种相比,所使用的工具有了一些变化,需要有一些定制(实现复杂)。但是钓鱼的人就可以在鱼儿咬钩之前彻底做别的事儿去了。等着报警器响就行了。
同步or异步?
数据准备阶段是异步的,数据拷贝阶段是同步的,整个IO过程是同步的。
IO复用模型
我们钓鱼的时候,为了保证可以最短的时间钓到最多的鱼,我们同一时间摆放多个鱼竿,同时钓鱼。然后哪个鱼竿有鱼儿咬钩了,我们就把哪个鱼竿上面的鱼钓起来。
映射到Linux操作系统中,这就是IO复用模型。多个进程的IO可以注册到同一个管道上,这个管道会统一和内核进行交互。当管道中的某一个请求需要的数据准备好之后,进程再把对应的数据拷贝到用户空间中。
这里的IO复用模型,并没有向内核注册信号处理函数,所以,他是阻塞的。
这种方式的钓鱼,通过增加鱼竿的方式,可以有效的提升效率
异步IO模型
我们钓鱼的时候,采用一种高科技钓鱼竿,即全自动钓鱼竿。可以自动感应鱼上钩,自动收竿,更厉害的可以自动把鱼放进鱼篓里。然后,通知我们鱼已经钓到了,他就继续去钓下一条鱼去了。
映射到Linux操作系统中,这就是异步IO模型。应用线程把IO请求传给内核后,就可以去做其他事情了,从内核角度,内核会等待数据准备完成,然后将数据拷贝到用户空间。内核完成相关操作后,会发信号告诉应用线程本次IO已经完成。
这种方式的钓鱼,无疑是最省事儿的。啥都不需要管,只需要交给鱼竿就可以了。
异步IO需要操作系统底层的支持