IO模塊
一 IO模型 分為:
1 阻塞IO (accept recv)
2 非阻塞IO
3 IO多路復用(監聽多個鏈接)
4 異步IO
5 驅動信號模型(不經常使用)
1 阻塞IO (blocking IO)
特點:全程阻塞(進程不能干其他的事兒)

當用戶進程調用了recvfrom這個系統調用,kernel就開始了IO的第一個階段:准備數據。對於network io來說,很多時候數據在一開始還沒有到達,這個時候kernel就要等待足夠的數據到來,而在用戶進程這邊,整個進程會被阻塞。
當kernel直等到數據准備好了,他就會將數據從kernel中拷貝到用戶內存,然后kernel返回結果,用戶進程才解除block的狀態,重新運行起來。
2 非阻塞IO(non-blocking IO)
特點:發送多次系統調用
優點:wait for data時無阻塞
缺點:多次系統調用,消耗,不能第一時間拿取數據
兩個階段:wait for data非阻塞
cope data是阻塞的

注意:在網絡IO時候,非阻塞IO也會進行recvfrom系統調用,檢查數據是否准備好,與阻塞IO不一樣,”非阻塞將大的整片時間的阻塞分成N多的小的阻塞,所以進程不斷地有機會’被CPU光顧’”。即每次recvfrom系統調用之間,cpu的權限還在進程手中,這段時間可以做其他事情。
也就是說非 阻塞的recvfrom系統調用,進程並沒有被阻塞,內核馬上返回給進程,如果數據還沒有准備好,此時會返回一個error。進程在返回之后,可以干點別的事情,然后在發起recvfrom系統調用,重復上面的過程。不斷重復進行recvfrom系統調用,這個過程被稱為輪詢,輪詢檢查內核數據,直到數據被准備好,再拷貝數據到進程,進行數據處理,需要注意,拷貝數據整個過程,進程仍然是屬於阻塞的狀態。
#服務端
import socket
sk=socket.socket()
sk.bind(("127.0.0.1",8000))
sk.listen(5)
sk.setblocking(False)
while True:
try:
print("waiting.........")
conn,addr=sk.accept()
print("++++",conn)
data=conn.recv(1024)
print(data.decode("utf8"))
except Exception as e:
print(e)
time.sleep(4)
#客戶端
import time
import socket
sk=socket.socket()
sk.connect(("127.0.0.1",8000))
while True:
data=input(">>")
sk.send(data.encode("utf-8"))
time.sleep(2)
執行結果:
waiting.........
[WinError 10035] 無法立即完成一個非阻止性套接字操作。
waiting.........
[WinError 10035] 無法立即完成一個非阻止性套接字操作。
waiting.........
[WinError 10035] 無法立即完成一個非阻止性套接字操作。
waiting.........
3 IO多路復用(IO multiplexing)
特點:1全程阻塞(wait for data, copy data)
2 能監聽多個文件描述符
實現並發
select, epoll,poll
select發起系統調用(監聽多個連接 實行並發)
對於文件描述符(套接字對象):
1 是一個非零整數,不會變
2 收發數據的時候,對於接收端而言,數據先到內核空間,然后通過copy到用戶空間,同時,內核空間數據清空。
IO multiplexing這個詞可能有點陌生,但是如果說select,epoll,大概就都能明白了,有些地方也稱這用IO方式為event driven IO。我們知道,select/epoll的好處就在於單個process就可以同時處理多個網絡連接的IO,它的基本原理就是selet/epoll這個function會不斷的輪詢所有的socket,當某個socket有數據到達了,就通知用戶進程。

當用戶進程調用了select,那么整個進程會被block,而同時,kernel會”監視”所有select負責的socket,當任何一個socket中的數據准備好了,select就回返回。這個時候用戶進程再調用read操作,將數據從kernel拷貝到用戶進程。
需要使用兩個system call(select和recvfrom),而blocking IO只調用了一個system call(recvfrom)。
注意1:select函數返回結果中如果有文件可讀了,那么進程就可以通過調用accept()或recv()
來讓kernel將位於內核中准備到數據copy到用戶區。
注意2:select的優勢在於可以處理多個連接,不適用於單個連接
#服務端
import socket
import select
sock=socket.socket()
sock.bind(("127.0.0.1",8000))
sock.listen(5)
# sock.setblocking(False)
inputs=[sock,]
while True:
r,w,e=select.select(inputs,[],[]) #監聽有變化的套接字,
for obj in r:
if obj==sock:
conn,addr=obj.accept()
inputs.append(conn) #l=[sock,conn]
else:
data=obj.recv(1024)
print(data.decode("utf8"))
send_data=input(">>>")
obj.send(send_data.encode("utf8"))
#客戶端
import socket
sock=socket.socket()
sock.connect(("127.0.0.1",8000))
while True:
data=input(">>>")
sock.send(data.encode("utf8"))
res=sock.recv(1024)
print(res.decode("utf8"))
sock.close()
4 異步IO(Asynchronous I/O)
特點:全程無阻塞

用戶進程發起read操作之后,立刻就可以開始去做其他的事兒,從另一方面,從kernel的角度,當它收到一個asynchronous read 之后,首先它會立刻返回,所以不會對用戶進程產生任何block,然后,kernel會 數據准備完成 ,然后將數據拷貝到用戶內存,當着一切都完成之后,kernel就給用戶進程發送一個signal,告訴它read操作完成了。
同步阻塞:包括(阻塞IO,非阻塞IO,IO多路復用)
異步阻塞:無阻塞 包括(異步IO)
各個IO Model的比較如果所示:

