我们都知道,unix下可用的IO模型有五种,分别是

  1. 阻塞式IO
  2. 非阻塞式IO
  3. I/O复用(select和epoll)
  4. 信号驱动式I/O
  5. 异步IO

下面通过一个个实例来解释各种IO之间的区别

1.阻塞式IO

阻塞式IO是最常见的一种IO模型,默认情况下所有的套接字都是阻塞的。阻塞式IO可以用下面这张图来描述

应用程序调用一个系统调用向内核请求数据,此时如果数据没有准备好,那么进程在此阻塞,切换至内核态准备数据。系统调用在内核准备好数据并复制到应用进程的缓冲区或者发生错误后才会返回,一次IO操作结束。这里最常见的错误就是系统调用被信号中断。系统调用返回之后,应用进程开始处理数据。

2.例子

在这里我们写一个小例子,这个小例子实现客户端向服务端发送数据。服务端收到数据后,会打印到控制台。如果客户端发送88,那么关闭连接,程序退出。代码如下:
服务端程序:

import socket               # 导入 socket 模块
import time    
s = socket.socket()         # 创建 socket 对象
host = socket.gethostname() # 获取本地主机名
port = 12345                # 设置端口
s.bind((host, port))        # 绑定端口
s.listen(5)                 # 等待客户端连接
c, addr = s.accept()        # 建立客户端连接。
# c.setblocking(True)
print("connected from :%s:%s"%(addr[0],addr[1]))
while True:
    print("wating for client input")
    data = c.recv(1024)
    data = str(data,'utf-8')
    print(data)
    if data == '88':
        c.close()
        break
print("client closed connection")
s.close()

客户端程序:

import socket               # 导入 socket 模块
s = socket.socket()         # 创建 socket 对象
host = socket.gethostname() # 获取本地主机名
port = 12345                # 设置端口号
s.connect((host, port))
print("connected to server")
while(True):
    data = input()
    s.send(bytes(data,'utf-8'))
    if data == '88':
        s.close()
        break

看一下程序运行效果。首先,启动服务端程序后,发现服务端没有任何输出,此时程序阻塞在第8行,等待客户端连接。

`
然后我们启动客户端程序

发现服务器端打印出

connected from :192.168.1.174:7454
wating for client input

客户端打印出

connected to server

此时,服务器端阻塞在13行,等待客户端发送数据。在客户端发送一条消息

服务器端读取客户端发送过来的数据,打印出来,然后再次在第13行阻塞,继续等待客户端发送数据
客户端发送88后,程序打印出结束信息,然后结束运行。

上面就是阻塞式IO的运行效果,这种IO在1.内核准备数据、2.从内核缓冲区拷贝数据至用户缓冲区这两个阶段都会发生阻塞,这是最简单也是最流行的一种IO。