1. 程式人生 > 實用技巧 >python IO多路複用之Select

python IO多路複用之Select

import select,socket,queue

server = socket.socket()
server.bind(('localhost',9000))
server.listen(1000)

server.setblocking(False)#設定不阻塞

msg_dic ={}
inputs = [server,]
#inputs = [server,conn]#conn

outputs = []

while True:
    readable,writeable,exceptional= select.select(inputs,outputs,inputs)
    print
(readable,writeable,exceptional) for r in readable: if r is server:#代表來了一個新連線 conn,addr=server.accept() print('來了個新連線',addr) inputs.append(conn)#是因為這個新建立的連線還沒發資料過來,現在就接受的話程式就報錯了 #所以要想實現這個客戶端發資料來時,server能知道,就需要讓select再檢測這個CONN msg_dic[conn]= queue.Queue()#
初始化一個佇列,後面存要返回給這個客戶端的資料 else: data =r.recv(1024)#多個連線不能用conn,不能確定是哪個conn,迴圈需要用r print('收到資料',data) msg_dic[r].put(data) outputs.append(r)#放入返回的連線佇列 # r.send(data) # print('send done...') for w in writeable :#要返回給客戶端的連線列表
data_to_client = msg_dic[w].get() w.send(data_to_client)#返回給客戶端源資料 outputs.remove(w)#確保下次迴圈的時候writeable,不返回這個已經處理完的連線 for e in exceptional: if e in outputs: outputs.remove(e) inputs.remove(e) del msg_dic[e]
server
import socket

HOST = 'localhost'  # The remote host
PORT = 9000  # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
while True:
    msg = bytes(input(">>:"), encoding="utf8")
    s.sendall(msg)
    data = s.recv(1024)
    # print(data)

    print('Received', repr(data))
s.close()
import asyncio
client

一 概念說明

使用者空間與核心空間

現在作業系統都是採用虛擬儲存器,那麼對32位作業系統而言,它的定址空間(虛擬儲存空間)為4G(2的32次方)。作業系統的核心是核心,獨立於普通的應用程式,可以訪問受保護的記憶體空間,也有訪問底層硬體裝置的所有許可權。為了保證使用者程序不能直接操作核心(kernel),保證核心的安全,操心繫統將虛擬空間劃分為兩部分,一部分為核心空間,一部分為使用者空間。針對linux作業系統而言,將最高的1G位元組(從虛擬地址0xC0000000到0xFFFFFFFF),供核心使用,稱為核心空間,而將較低的3G位元組(從虛擬地址0x00000000到0xBFFFFFFF),供各個程序使用,稱為使用者空間。

程序切換

為了控制程序的執行,核心必須有能力掛起正在CPU上執行的程序,並恢復以前掛起的某個程序的執行。這種行為被稱為程序切換。因此可以說,任何程序都是在作業系統核心的支援下執行的,是與核心緊密相關的。

從一個程序的執行轉到另一個程序上執行,這個過程中經過下面這些變化:
1. 儲存處理機上下文,包括程式計數器和其他暫存器。
2. 更新PCB資訊。

3. 把程序的PCB移入相應的佇列,如就緒、在某事件阻塞等佇列。
4. 選擇另一個程序執行,並更新其PCB。
5. 更新記憶體管理的資料結構。
6. 恢復處理機上下文。

總而言之就是很耗資源,具體的可以參考這篇文章:程序切換

注:程序控制塊(Processing Control Block),是作業系統核心中一種資料結構,主要表示程序狀態。其作用是使一個在多道程式環境下不能獨立執行的程式(含資料),成為一個能獨立執行的基本單位或與其它程序併發執行的程序。或者說,OS是根據PCB來對併發執行的程序進行控制和管理的。 PCB通常是系統記憶體佔用區中的一個連續存區,它存放著作業系統用於描述程序情況及控制程序執行所需的全部資訊

程序的阻塞

正在執行的程序,由於期待的某些事件未發生,如請求系統資源失敗、等待某種操作的完成、新資料尚未到達或無新工作做等,則由系統自動執行阻塞原語(Block),使自己由執行狀態變為阻塞狀態。可見,程序的阻塞是程序自身的一種主動行為,也因此只有處於執行態的程序(獲得CPU),才可能將其轉為阻塞狀態。當程序進入阻塞狀態,是不佔用CPU資源的

檔案描述符fd

檔案描述符(File descriptor)是電腦科學中的一個術語,是一個用於表述指向檔案的引用的抽象化概念。

檔案描述符在形式上是一個非負整數。實際上,它是一個索引值,指向核心為每一個程序所維護的該程序開啟檔案的記錄表。當程式開啟一個現有檔案或者建立一個新檔案時,核心向程序返回一個檔案描述符。在程式設計中,一些涉及底層的程式編寫往往會圍繞著檔案描述符展開。但是檔案描述符這一概念往往只適用於UNIX、Linux這樣的作業系統。

快取 I/O

快取 I/O 又被稱作標準 I/O,大多數檔案系統的預設 I/O 操作都是快取 I/O。在 Linux 的快取 I/O 機制中,作業系統會將 I/O 的資料快取在檔案系統的頁快取( page cache )中,也就是說,資料會先被拷貝到作業系統核心的緩衝區中,然後才會從作業系統核心的緩衝區拷貝到應用程式的地址空間。

快取 I/O 的缺點:
資料在傳輸過程中需要在應用程式地址空間和核心進行多次資料拷貝操作,這些資料拷貝操作所帶來的 CPU 以及記憶體開銷是非常大的。