同步, 就是要本身去轮询状态好了没有linux
异步, 就是会有信号通知你web
阻塞, 就是当前线程什么活也不能干缓存
非阻塞, 就是当前线程先去干其余的网络
网络IO的本质:多线程
阻塞IO模型:并发
# 1.进程运行, 而后经过recvfrom进行系统调用, 至关于调用了内核中的一个函数异步
# 2.系统从运行态转到内核态, 完成系统调用的相关操做, 此时本来的应用程序处于阻塞态(这个阻塞态是应用程序本身进行阻塞的)socket
# 3.系统从内核态回到运行态, 应用程序便从阻塞态进入就绪态async
非阻塞IO模型:函数
就是轮询, 发出系统调用, 此时网络数据没有彻底写满到内核缓冲区的时候, 系统调用返回一个错误的状态码, 而后进程该干吗干吗.
以后不断重复上面这个动做, 知道内核缓冲去写满了, 系统调用返回一个正确的状态码
IO多路复用:
上面两个是内核和进程, 这里多加了两个进程一块儿玩, 一个是select, 一个是poll
1. 进程调用select, 而不是去进行系统调用
2. select只管看内核缓冲区里网络数据是否是写满了, 经过非阻塞轮询, 写满了以后, select就返回一个可读的状态码, 而后由select进行recvfrom的系统调用
故事:老王烧开水。
出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。
老王想了想,有好几种等待方式
1.老王用水壶煮水,而且站在那里,无论水开没开,每隔必定时间看看水开了没。-同步阻塞
老王想了想,这种方法不够聪明。
2.老王仍是用水壶煮水,再也不傻傻的站在那里看水开,跑去寝室上网,可是仍是会每隔一段时间过来看看水开了没有,水没有开就走人。-同步非阻塞
老王想了想,如今的方法聪明了些,可是仍是不够好。
3.老王此次使用高大上的响水壶来煮水,站在那里,可是不会再每隔一段时间去看水开,而是等水开了,水壶会自动的通知他。-异步阻塞
老王想了想,不会呀,既然水壶能够通知我,那我为何还要傻傻的站在那里等呢,嗯,得换个方法。
4.老王仍是使用响水壶煮水,跑到客厅上网去,等着响水壶本身把水煮熟了之后通知他。-异步非阻塞
老王豁然,这下感受轻松了不少。
同步和异步
同步就是烧开水,须要本身去轮询(每隔一段时间去看看水开了没),异步就是水开了,而后水壶会通知你水已经开了,你能够回来处理这些开水了。
同步和异步是相对于操做结果来讲,会不会等待结果返回。
阻塞和非阻塞
阻塞就是说在煮水的过程当中,你不能够去干其余的事情,非阻塞就是在一样的状况下,能够同时去干其余的事情。阻塞和非阻塞是相对于线程是否被阻塞。
其实,这二者存在本质的区别,它们的修饰对象是不一样的。阻塞和非阻塞是指进程访问的数据若是还没有就绪,进程是否须要等待,简单说这至关于函数内部的实现区别,也就是未就绪时是直接返回仍是等待就绪。
而同步和异步是指访问数据的机制,同步通常指主动请求并等待I/O操做完毕的方式,当数据就绪后在读写的时候必须阻塞,异步则指主动请求数据后即可以继续处理其它任务,随后等待I/O,操做完毕的通知,这可使进程在数据读写时也不阻塞。
网络IO的模型大体包括下面几种
网络IO的本质是socket的读取,socket在linux系统被抽象为流,IO能够理解为对流的操做。对于一次IO访问,数据会先被拷贝到操做系统内核的缓冲区中,而后才会从操做系统内核的缓冲区拷贝到应用程序的地址空间,因此通常会经历两个阶段:
对于socket流而言:
这也是最经常使用的模型,默认状况下全部的套接字都是 阻塞
的;
咱们把recvfrom函数视为系统调用,由于咱们正区分进程和内核,系统调用通常都会从在应用进程空间中运行切换到内核空间中运行,一段时间后又再切换回来;
# 1.进程运行, 而后经过recvfrom进行系统调用, 至关于调用了内核中的一个函数
# 2.系统从运行态转到内核态, 完成系统调用的相关操做, 此时本来的应用程序处于阻塞态(这个阻塞态是应用程序本身进行阻塞的)
# 3.系统从内核态回到运行态, 应用程序便从阻塞态进入就绪态
应用进程从 进行系统调用
到 复制数据报到应用进程的缓冲区完成
的整段时间内是被阻塞的;在这个过程当中,要么正确到达,要么系统调用被信号打断;直到数据报被复制到用户进程完成后,用户进程才解除阻塞的状态,固然,这是用户进程本身进行的阻塞;
非阻塞,当所请求的I/O操做非得把当前进程设置成睡眠才能完成时,不要把当前进程设置成睡眠,而是返回一个错误信息(数据报没有准备好的状况下),此时当前进程能够作其它的事情,不用阻塞;
前三次系统调用时都没有数据能够返回,内核均返回一个 EWOULDBLOCK
,而且不会阻塞当前进程,直到第四次询问内核缓冲区是否有数据的时候,此时内核缓冲区中已经有一个准备好的数据,所以将内核数据复制到用户空间,此时系统调用则返回成功;
当一个应用进程像这样对一个非阻塞socket循环调用
recv/recvfrom
时,则称为轮询;应用进程持续轮询内核,以查看某个操做是否就绪,这么作每每消耗大量的CPU时间。
优势:相较于阻塞模型,非阻塞不用再等待任务,而是把时间花费到其它任务上,也就是这个当前线程同时处理多个任务;
缺点:致使任务完成的响应延迟增大了,由于每隔一段时间才去执行询问的动做,可是任务可能在两个询问动做的时间间隔内完成,这会致使总体数据吞吐量的下降。
有了I/O复用,咱们就能够调用 select或poll
,让其阻塞在两个系统调用(1.询问数据是否准备好而且直到数据准备好才返回;2.内核是否把数据所有复制完成到用户进程)中的某一个之上
阻塞于 select
调用,等待数据报套接字变为可读。当select返回套接字可读这一条件的时候,则调用 recvfrom
把所读数据报复制到应用进程缓冲区;
以前的同步非阻塞方式须要用户进程不停的轮询,可是IO多路复用不须要不停的轮询,而是派别人去帮忙循环查询多个任务的完成状态,UNIX/Linux 下的 select、poll、epoll
就是干这个的;select调用是内核级别的,select轮询相对非阻塞的轮询的区别在于---前者能够等待多个socket,能实现同时对多个IO端口进行监听,当其中任何一个socket的数据准好了,就能返回进行可读,而后进程再进行recvform系统调用,将数据由内核拷贝到用户进程,固然这个过程是阻塞的。select或poll调用以后,会阻塞进程,与blocking IO阻塞不一样在于,此时的select不是等到socket数据所有到达再处理, 而是有了一部分数据(网络上的数据是分组到达的)就会调用用户进程来处理。如何知道有一部分数据到达了呢?监视的事情交给了内核,内核负责数据到达的处理。
我认为上面那句话中存在两个重要点:1.对多个socket进行监听,只要任何一个socket数据准备好就返回可读;2.不等一个socket数据所有到达再处理,而是一部分socket的数据到达了就通知用户进程;
其实 select、poll、epoll
的原理就是不断的遍历所负责的全部的socket完成状态,当某个socket有数据到达了,就返回可读并通知用户进程来处理;
高并发的程序通常使用同步非阻塞方式而非多线程 + 同步阻塞方式。要理解这一点,首先要扯到并发和并行的区别。好比去某部门办事须要依次去几个窗口,办事大厅里的人数就是并发数,而窗口个数就是并行度。也就是说并发数是指同时进行的任务数(如同时服务的 HTTP 请求),而并行数是能够同时工做的物理资源数量(如 CPU 核数)。经过合理调度任务的不一样阶段,并发数能够远远大于并行度,这就是区区几个 CPU 能够支持上万个用户并发请求的奥秘。在这种高并发的状况下,为每一个任务(用户请求)建立一个进程或线程的开销很是大。而同步非阻塞方式能够把多个 IO 请求丢到后台去,这就能够在一个进程里服务大量的并发 IO 请求。
首先开启套接字的信号驱动式IO功能,而且经过 sigaction
系统调用安装一个信号处理函数,该函数调用将当即返回,当前进程没有被阻塞,继续工做;当数据报准备好的时候,内核则为该进程产生 SIGIO
的信号,随后既能够在信号处理函数中调用 recvfrom
读取数据报,而且通知主循环数据已经准备好等待处理,也能够通知主循环让它读取数据报;(其实就是一个待读取的通知和待处理的通知);
咱们调用 aio_read
函数,给内核传递描述符、缓冲区指针、缓冲区大小和文件偏移,而且告诉内核当整个操做完成时如何通知咱们。该函数调用后当即返回,不被阻塞;