对于一个网络IO(network IO),它会涉及到两个系统对象,一个是调用这个IO的process (or thread),另一个就是系统内核(kernel)。当一个read操作发生时,该操作会经历两个阶段:
1.等待数据准备
2.将数据从系统内核拷贝到进程当中
当收到数据后,这些数据会先存放到系统所用的内存当中,之后在由系统将数据从内核中拷贝到使用的进程当中
不同的IO模型的区别就在于上述的两个阶段
recvfrom进行系统调用后,等待数据和拷贝数据的两个阶段都被阻塞了
recvfrom不断的向kernel要数据,如果没有数据就马上返回一个提示,紧接着recvfrom继续去要数据,直到数据准备好, 然后再从内核拷贝到进程中。
这里等待数据的阶段并没有阻塞,但是数据从内核中拷贝到使用的进程的过程中还是处于阻塞状态。
缺点:循环调用recv()将大幅度推高cpu占用率,任务完成的响应延迟增大了,因为每过一段时间才去轮询一次read操作,而任务可能在两次轮询之间的任意时间完成。这会导致整体数据吞吐量的降低。
当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。
这里两个阶段都处于阻塞状态,看似和阻塞IO没有太大区别,甚至比它效率更低,但是select能够处理多个连接。
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1<span style="color: #000000;">)
s.bind((<span style="color: #800000;">'<span style="color: #800000;">127.0.0.1<span style="color: #800000;">',8081<span style="color: #000000;">))
s.listen(5<span style="color: #000000;">)
s.setblocking(False)
read_l=<span style="color: #000000;">[s,]
<span style="color: #0000ff;">while<span style="color: #000000;"> True:
r_l,w_l,x_l=<span style="color: #000000;">select.select(read_l,[],[])
<span style="color: #0000ff;">print<span style="color: #000000;">(r_l)
<span style="color: #0000ff;">for ready_obj <span style="color: #0000ff;">in<span style="color: #000000;"> r_l:
<span style="color: #0000ff;">if ready_obj ==<span style="color: #000000;"> s:
conn,addr=<span style="color: #000000;">ready_obj.accept()
read_l.append(conn)
<span style="color: #0000ff;">else<span style="color: #000000;">:
<span style="color: #0000ff;">try<span style="color: #000000;">:
data=ready_obj.recv(1024<span style="color: #000000;">)
<span style="color: #0000ff;">if <span style="color: #0000ff;">not<span style="color: #000000;"> data:
read_l.remove(ready_obj)
<span style="color: #0000ff;">continue<span style="color: #000000;">
ready_obj.send(data.upper())
<span style="color: #0000ff;">except<span style="color: #000000;"> ConnectionResetError:
read_l.remove(ready_obj)
msg=input(<span style="color: #800000;">'<span style="color: #800000;">>>: <span style="color: #800000;">'<span style="color: #000000;">)
<span style="color: #0000ff;">if <span style="color: #0000ff;">not msg:<span style="color: #0000ff;">continue<span style="color: #000000;">
c.send(msg.encode(<span style="color: #800000;">'<span style="color: #800000;">utf-8<span style="color: #800000;">'<span style="color: #000000;">))
data=c.recv(1024<span style="color: #000000;">)
<span style="color: #0000ff;">print(data.decode(<span style="color: #800000;">'<span style="color: #800000;">utf-8<span style="color: #800000;">'))
异步IO两个阶段都没有阻塞
用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。