IO操作
对于一次IO访问(以read为例),数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统的缓冲区拷贝到应用程序的地址空间,所以说,当一个read操作发生时,会经历两个阶段:
1.等待数据准备
2.将数据从内核拷贝到进程中
同步IO和异步IO
场景1:小明去打开水,而水塔此时没有水,小明在现场一直等待,或者不断的轮询查看是否有水,直到来水为止,这就是同步IO的一种案例。
同步IO的特点:
同步IO指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪。
同步IO的执行者是IO操作的发起者。
同步IO需要发起者进行内核态到用户态的数据拷贝过程,所以这里必须阻塞。
场景2:小明去打开水,而开水塔此时没有水,开水塔的阿姨叫小明把水壶放在这里,来水后会帮他打好水,并打电话叫他来取,这就是异步IO的一种案例。
异步IO的特点:
异步IO是指用户进程触发IO操作以后就立即返回,继续开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知。
异步IO的执行者是内核线程,内核线程将数据从内核态拷贝到用户态,所以这里没有阻塞。
Linux系统的五种网络IO模式:
1.阻塞IO(blocking IO)
2.非阻塞IO(nonblocking IO)
3.IO多路复用(IO multiplexing)
4.信号驱动IO(signal driven IO)
5.异步IO(asynchronous IO)
一.阻塞IO
小明同学着急用开水,打水时发现没水,他一直等待直到装满水才离开。这一过程就可以看成使用了阻塞IO模型。很显然,这种IO模型是同步的。
在Linux中,默认情况下所有的socket都是阻塞IO。
二:非阻塞IO
小明同学又一次急用开水,打水的时候发现又没水,因为有其他急事他马上离开了,过了一会他又过来看来水了没有…… 在中间离开的这段时间里,小明离开了水塔(回到了用户进程空间),可以做他自己的事,这既是非阻塞IO模型。但是它只有是检查无数据的时候是非阻塞的。在数据到达的时候依然要等待复制数据到用户空间,因此它还是同步IO。
当一个用户线程发起一个read操作后,并不需要等待,而是马上就得到一个结果。如果结果是一个erro时,它就知道数据还没准备好,于是它可以再次发送read操作。一旦内核中的数据准备好了,并且又再次收到了用户线程的请求,那么它马上就将数据拷贝到了用户线程,然后返回。
所以事实上,在非阻塞IO模型中,用户线程需要不断地询问内核数据是否就绪,也就是说非阻塞IO不会交出CPU,而会一直占用CPU。
典型的非阻塞IO模型一般如下:
设置非阻塞IO常用方式:
方式一:创建socket时指定
int sock = socket(AF_INET,SOCK_STREAM | SOCK_NONBLOCK,0);
方式二:通过fcntl设置
fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
…
…
…