阻塞、非阻塞、同步、异步

阻塞与非阻塞I/O

阻塞和非阻塞主要是指调用某个系统函数时,这个函数是否会导致我们的进程进入sleep()(卡住休眠)状态而言的。

1、阻塞I/O

调用一个函数,这个函数卡在这里等待一个事情发生,只有这个事情发生了,这个函数才会往下走,如阻塞函数accept()。

这种阻塞并不好,效率很低,所以一般不会用阻塞方式来写服务器程序。

2、非阻塞I/O:不会卡住,充分利用时间片,执行效率更高。

【非阻塞模式的两个鲜明特点】

(1)不断地调用accept()、recvfrom()函数来检查有没有数据到来,如果没有,函数会返回一个特殊的错误标记来标识,这种标记可能是EWULDBLOCK,也可能是EAGAIN;如果数据没到来,那么这里有机会执行其他函数,但是也得不停的再次调用accept(),recvfrom()来检查数据是否到来,非常累;

(2)如果数据到来,那么就得卡在这里把数据从内核缓冲区复制到用户缓冲区,所以复制这个阶段是卡着完成的

 

同步与异步I/O

1、异步I/O

调用一个异步I/O函数时,要给这个函数指定一个接收缓冲区,我还要给定一个回调函数

调用完一个异步I/O函数后,该函数会立即返回,其余判断交给操作系统,操作系统会判断数据是否到来,如果数据到来了,操作系统会把数据拷贝到你所提供的缓冲区里,然后调用你所指定的这个回调函数来通知你。

【非阻塞和异步I/O区别】

a)非阻塞I/O要不停的调用I/O函数来检查数据是否来,如果数据来了,就得卡在I/O函数这里把数据从内核缓冲区复制到用户缓冲区,然后这个函数才能返回;

b)异步I/O根本不需要不停的调用I/O函数来检查数据是否到来,只需要调用一次,然后就可以干别的事情去了;内核判断数据到来,拷贝数据到你提供的缓冲区,调用你的回调函数来通知你,并没有被卡在那里的情况。

2、同步I/O

步骤一:调用select()判断有没有数据,有数据,走下来,没数据卡在那里;

步骤二:select()返回之后,用recvfrom()去取数据,当然取数据的时候(内核到用户)也会卡那么一下。

同步I/O感觉更麻烦,要调用两个函数才能把数据拿到手;但是同步I/O和阻塞式I/O比,有 I/O复用(2个函数的)能力。

所谓I/O复用,就是多个socket(多个TCP连接)可以弄成一捆,我可以用select等数据,因为select()的能力是等多条TCP连接上的任意一条有数据来都可以感知到,然后哪条TCP有数据来,我再用具体的比如recvfrom()去收。

所以,这种调用一个函数能够判断一堆TCP连接是否来数据的这种能力,叫I/O复用,英文I/O multiplexing。

很多资料把阻塞I/O,非阻塞I/O,同步I/O归结为一类 ,因为他们多多少少的都有阻塞的行为发生;也可以把阻塞I/O,非阻塞I/O 都归结为同步I/O模型,而把异步I/O单独归结为一类,因为异步I/O是真正的没有阻塞行为发生的。

【通俗理解】阻塞与否其实是针对第一阶段的,同步与否是针对第二阶段的。

阻塞与非阻塞关注的是在等待数据来的时候是不是在干别的事(这里的等应该指第一阶段),而同步与异步关注的是如何获取到数据来了这件事,是自己看到的还是别人告诉的(其实可以把看这个动作理解为第二阶段),所以阻塞和非阻塞都算有一点同步。现实中非阻塞异步是最符合我们常理的,好比我们去买煎饼,我跟摊煎饼的阿姨说,你摊好了给我打电话(不用自己看,所以是异步),然后我就去聊天、玩手机(程序先返回干别的,非阻塞),然后摊好了通知我(回调函数),我取走煎饼。最蠢的其实就是阻塞异步地做事,就好比你跟阿姨说煎饼好了你来通知我一下,然后你还在哪里一直等着数据来而不干别的事(阻塞),那你让阿姨通知你干嘛?同理,阻塞同步就是一直站在那里等煎饼出来,然后看(同步)到煎饼就拿走,因为是自己看而不是等通知所以是同步。非阻塞同步就是去聊天玩手机,然后每过一会儿就过来看一下煎饼摊好没有,摊好了就拿走,因为总归是自己看的,所以是同步。

 

总结

对于一次IO访问(以read举例),数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区,拷贝到应用程序的地址空间。所以说当一个read操作发生时,它会经历两个阶段:

1  等待数据准备 (Waiting for the data to be ready)

2  将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)

简单总结(其实只有5种IO模式)

1、阻塞:没数据就等着直到有数据,2个阶段都被阻塞了

2、非阻塞:没数据就先返回,但是循环调用看kernel有没有数据,第二个阶段依旧被阻塞

3、I/O 多路复用:IO multiplexing就是所说的select,poll,epoll,有些地方也称这种IO方式为event driven IO。select/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。它的基本原理就是select,poll,epoll这些function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。

I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回。

4、异步IO:用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。

5、信号驱动IO:不常用

【注意】这里有一个熟悉的同步IO,其实没有一个具体的模型,可以理解为1235都是同步IO模型,因为他们的第二阶段其实都是阻塞了的,只有异步IO是一步到位。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值