經常使用的IO模型有4種:linux
不經常使用的有:windows
阻塞IO就是全程阻塞,其中,全程指的是等待數據和 數據從內核態拷貝到用戶態。網絡
全程阻塞就是以上兩個步驟都阻塞。如圖:併發
系統調用兩個階段:app
非阻塞IO是部分阻塞,
等待數據時不會阻塞,而是在固定時間內循環發起系統調用,請求不到作本身的事情,等待下次請求,
而數據從內核態拷貝到用戶態仍是阻塞的。如圖:異步
系統調用兩個階段:socket
優勢:
等待數據無阻塞函數
缺點:
1.系統調用發送太多
2.數據不是即時接收的操作系統
ps:socket設置socket對象.setblocking(False)
設置阻塞狀態爲非阻塞code
IO多路複用:全程阻塞,監聽多個連接
系統調用兩個階段:
實現IO多路複用的經常使用方式有:
基本原理:
經過select/poll/epoll函數不斷輪詢所負責的全部socket套接字,當某個socket套接字有數據到達,就通知用戶進程。
特色:
就是單個process能夠同時處理多個網絡鏈接的IO,
ps:不一樣的操做系統提供的函數不一樣:
windows系統: select
linux系統: select、poll、epoll
系統調用經過select模塊完成wait for data的工做
示例:
select監聽多個socket對象(sock是socket對象),實現併發
r, w, e = select.select([sock,], [], []) # 等待連接 for obj in r: conn, addr = obj.accept()
示例升級:
inputs = [sock,] r, w, e = select.select(inputs, [], []) # inputs監聽有變化的套接字 inputs=[sock,conn1,conn2,...] for obj in r: # 第一次[sock,] 第二次[conn1,] if obj == sock: # 若是返回的r = sock,說明有鏈接請求 conn, addr = obj.accept() inputs.append(conn) # inputs=[sock, conn1, conn2] else: # 不然,能夠接收數據了 data = obj.recv(1024)
ps:關於文件描述符的tips(socket套接字)
1.每個套接字對象的本質就是一個非零整數,不會變(fb=4)
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 51963)>
2.收發數據的時候,對於接收端而言,數據先到內核空間,而後copy到用戶空間,同時內核空間的數據被清空
3.根據TCP協議,當發送端接收到接收端的確認信息後,清空內核空間的數據,不然不清空
select:
ps:select的特色也是其缺點,會致使效率降低:
poll:
epoll:
ps:回調函數
某一個函數或者某一個動做,成功完成以後,會觸發的函數
selectors是select的升級版
selectors基於select模塊實現IO多路複用,調用語句selectors.DefaultSelector()
建立selecters對象,特色是根據平臺自動選擇最佳IO多路複用機制,調用順序:epoll > poll > select
import selectors import socket sel = selectors.DefaultSelector() # 根據平臺自動選擇最佳IO多路複用機制 def accept(sock, mask): conn, addr = sock.accept() sel.register(conn, selectors.EVENT_READ, read) # 將conn和read()註冊到一塊兒,當conn有變化時執行read() def read(conn, mask): try: data = conn.recv(1000) print(data.decode('utf8')) inputs = input('>>:').strip() conn.send(inputs.encode('utf8')) except Exception: sel.unregister(conn) conn.close() sock = socket.socket() sock.bind(('127.0.0.1', 8080)) sock.listen(100) sock.setblocking(False) # 設置爲非阻塞IO sel.register(sock, selectors.EVENT_READ, accept) # 將sock和accept()註冊到一塊兒,當sock有變化時執行accept() while True: events = sel.select() # 監聽 [(key1,mask1),(key2),(mask2)] for key, mask in events: func = key.data # 1 key.data就是accept # 2 key.data就是read obj = key.fileobj # 1 key.fileobj就是sock # 2 key.fileobj就是conn func(obj, mask) # 1 accept(sock,mask) # 2read(conn,mask)
只要系統調用中存在阻塞就是同步IO,
因此,阻塞IO、非阻塞IO、IO多路複用都是同步IO
全程無阻塞,實現複雜
系統調用兩個階段: