python中的select模組執行個體教程

來源:互聯網
上載者:User

Python中的select模組專註於I/O多工,提供了select poll epoll三個方法(其中後兩個在Linux中可用,windows僅支援select),另外也提供了kqueue方法(freeBSD系統)

select方法

進程指定核心監聽哪些檔案描述符(最多監聽1024個fd)的哪些事件,當沒有檔案描述符事件發生時,進程被阻塞;當一個或者多個檔案描述符事件發生時,進程被喚醒。

當我們調用select()時:

  1、環境切換轉換為核心態

  2、將fd從使用者空間複製到核心空間

  3、核心遍曆所有fd,查看其對應事件是否發生

  4、如果沒發生,將進程阻塞,當裝置驅動產生中斷或者timeout時間後,將進程喚醒,再次進行遍曆

  5、返回遍曆後的fd

  6、將fd從核心空間複製到使用者空間

fd:file descriptor 檔案描述符

fd_r_list, fd_w_list, fd_e_list = select.select(rlist, wlist, xlist, [timeout])

參數: 可接受四個參數(前三個必須)

rlist: wait until ready for reading

wlist: wait until ready for writing

xlist: wait for an “exceptional condition”

timeout: 逾時時間

傳回值:三個列表

select方法用來監視檔案描述符(當檔案描述符條件不滿足時,select會阻塞),當某個檔案描述符狀態改變後,會返回三個列表

1、當參數1 序列中的fd滿足“可讀”條件時,則擷取發生變化的fd並添加到fd_r_list中

2、當參數2 序列中含有fd時,則將該序列中所有的fd添加到 fd_w_list中

3、當參數3 序列中的fd發生錯誤時,則將該發生錯誤的fd添加到 fd_e_list中

4、當逾時時間為空白,則select會一直阻塞,直到監聽的控制代碼發生變化

當逾時時間 = n(正整數)時,那麼如果監聽的控制代碼均無任何變化,則select會阻塞n秒,之後返回三個空列表,如果監聽的控制代碼有變化,則直接執行。


執行個體:

利用select實現一個可並發的服務端

import socketimport select s = socket.socket()s.bind(('127.0.0.1',8888))s.listen(5)r_list = [s,]num = 0while True: rl, wl, error = select.select(r_list,[],[],10) num+=1 print('counts is %s'%num) print("rl's length is %s"%len(rl)) for fd in rl:  if fd == s:   conn, addr = fd.accept()   r_list.append(conn)   msg = conn.recv(200)   conn.sendall(('first----%s'%conn.fileno()).encode())  else:   try:    msg = fd.recv(200)    fd.sendall('second'.encode())   except ConnectionAbortedError:    r_list.remove(fd) s.close()


import socket flag = 1s = socket.socket()s.connect(('127.0.0.1',8888))while flag: input_msg = input('input>>>') if input_msg == '0':  break s.sendall(input_msg.encode()) msg = s.recv(1024) print(msg.decode()) s.close()

在服務端我們可以看到,我們需要不停的調用select, 這就意味著:

  1 當檔案描述符過多時,檔案描述符在使用者空間與核心空間進行copy會很費時

  2 當檔案描述符過多時,核心對檔案描述符的遍曆也很浪費時間

  3 select最大僅僅支援1024個檔案描述符

poll與select相差不大,本文不作介紹

epoll方法:

epoll很好的改進了select:

  1、epoll的解決方案在epoll_ctl函數中。每次註冊新的事件到epoll控制代碼中時,會把所有的fd拷貝進核心,而不是在epoll_wait的時候重複拷貝。epoll保證了每個fd在整個過程中只會拷貝一次。

  2、epoll會在epoll_ctl時把指定的fd遍曆一遍(這一遍必不可少)並為每個fd指定一個回呼函數,當裝置就緒,喚醒等待隊列上的等待者時,就會調用這個回呼函數,而這個回呼函數會把就緒的fd加入一個就緒鏈表。epoll_wait的工作實際上就是在這個就緒鏈表中查看有沒有就緒的fd

  3、epoll對檔案描述符沒有額外限制


select.epoll(sizehint=-1, flags=0) 建立epoll對象

epoll.close()

Close the control file descriptor of the epoll object.關閉epoll對象的檔案描述符

epoll.closed

True if the epoll object is closed.檢測epoll對象是否關閉

epoll.fileno()

Return the file descriptor number of the control fd.返回epoll對象的檔案描述符

epoll.fromfd(fd)

Create an epoll object from a given file descriptor.根據指定的fd建立epoll對象

epoll.register(fd[, eventmask])

Register a fd descriptor with the epoll object.向epoll對象中註冊fd和對應的事件

epoll.modify(fd, eventmask)

Modify a registered file descriptor.修改fd的事件

epoll.unregister(fd)

Remove a registered file descriptor from the epoll object.取消註冊

epoll.poll(timeout=-1, maxevents=-1)

Wait for events. timeout in seconds (float)阻塞,直到註冊的fd事件發生,會返回一個dict,格式為:{(fd1,event1),(fd2,event2),……(fdn,eventn)}

事件:


EPOLLIN Available for read 可讀 狀態符為1

EPOLLOUT Available for write 可寫 狀態符為4

EPOLLPRI Urgent data for read

EPOLLERR Error condition happened on the assoc. fd 發生錯誤 狀態符為8

EPOLLHUP Hang up happened on the assoc. fd 掛起狀態

EPOLLET Set Edge Trigger behavior, the default is Level Trigger behavior 預設為水平觸發,設定該事件後則邊緣觸發

EPOLLONESHOT Set one-shot behavior. After one event is pulled out, the fd is internally disabled

EPOLLRDNORM Equivalent to EPOLLIN

EPOLLRDBAND Priority data band can be read.

EPOLLWRNORM Equivalent to EPOLLOUT

EPOLLWRBAND Priority data may be written.

EPOLLMSG Ignored.

水平觸發和邊緣觸發:

Level_triggered(水平觸發,有時也稱條件觸發):當被監控的檔案描述符上有可讀寫事件發生時,epoll.poll()會通知處理常式去讀寫。如果這次沒有把資料一次性全部讀寫完(如讀寫緩衝區太小),那麼下次調用 epoll.poll()時,它還會通知你在上沒讀寫完的檔案描述符上繼續讀寫,當然如果你一直不去讀寫,它會一直通知你!!!如果系統中有大量你不需要讀寫的就緒檔案描述符,而它們每次都會返回,這樣會大大降低處理常式檢索自己關心的就緒檔案描述符的效率!!! 優點很明顯:穩定可靠

Edge_triggered(邊緣觸發,有時也稱狀態觸發):當被監控的檔案描述符上有可讀寫事件發生時,epoll.poll()會通知處理常式去讀寫。如果這次沒有把資料全部讀寫完(如讀寫緩衝區太小),那麼下次調用epoll.poll()時,它不會通知你,也就是它只會通知你一次,直到該檔案描述符上出現第二次可讀寫事件才會通知你!!!這種模式比水平觸發效率高,系統不會充斥大量你不關心的就緒檔案描述符!!!缺點:某些條件下不可靠

epoll執行個體:

import socketimport select s = socket.socket()s.bind(('127.0.0.1',8888))s.listen(5)epoll_obj = select.epoll()epoll_obj.register(s,select.EPOLLIN)connections = {}while True: events = epoll_obj.poll() for fd, event in events:  print(fd,event)  if fd == s.fileno():   conn, addr = s.accept()   connections[conn.fileno()] = conn   epoll_obj.register(conn,select.EPOLLIN)   msg = conn.recv(200)   conn.sendall('ok'.encode())  else:   try:    fd_obj = connections[fd]    msg = fd_obj.recv(200)    fd_obj.sendall('ok'.encode())   except BrokenPipeError:    epoll_obj.unregister(fd)    connections[fd].close()    del connections[fd] s.close()epoll_obj.close()
import socket flag = 1s = socket.socket()s.connect(('127.0.0.1',8888))while flag: input_msg = input('input>>>') if input_msg == '0':  break s.sendall(input_msg.encode()) msg = s.recv(1024) print(msg.decode()) s.close()

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.