深入理解python中的select模塊
簡介
Python中的select模塊專注于I/O多路復(fù)用,提供了select poll epoll三個(gè)方法(其中后兩個(gè)在Linux中可用,windows僅支持select),另外也提供了kqueue方法(freeBSD系統(tǒng))
select方法
進(jìn)程指定內(nèi)核監(jiān)聽哪些文件描述符(最多監(jiān)聽1024個(gè)fd)的哪些事件,當(dāng)沒有文件描述符事件發(fā)生時(shí),進(jìn)程被阻塞;當(dāng)一個(gè)或者多個(gè)文件描述符事件發(fā)生時(shí),進(jìn)程被喚醒。
當(dāng)我們調(diào)用select()時(shí):
1、上下文切換轉(zhuǎn)換為內(nèi)核態(tài)
2、將fd從用戶空間復(fù)制到內(nèi)核空間
3、內(nèi)核遍歷所有fd,查看其對(duì)應(yīng)事件是否發(fā)生
4、如果沒發(fā)生,將進(jìn)程阻塞,當(dāng)設(shè)備驅(qū)動(dòng)產(chǎn)生中斷或者timeout時(shí)間后,將進(jìn)程喚醒,再次進(jìn)行遍歷
5、返回遍歷后的fd
6、將fd從內(nèi)核空間復(fù)制到用戶空間
fd:file descriptor 文件描述符
fd_r_list, fd_w_list, fd_e_list = select.select(rlist, wlist, xlist, [timeout])
參數(shù): 可接受四個(gè)參數(shù)(前三個(gè)必須)
- rlist: wait until ready for reading
- wlist: wait until ready for writing
- xlist: wait for an “exceptional condition”
- timeout: 超時(shí)時(shí)間
返回值:三個(gè)列表
select方法用來監(jiān)視文件描述符(當(dāng)文件描述符條件不滿足時(shí),select會(huì)阻塞),當(dāng)某個(gè)文件描述符狀態(tài)改變后,會(huì)返回三個(gè)列表
1、當(dāng)參數(shù)1 序列中的fd滿足“可讀”條件時(shí),則獲取發(fā)生變化的fd并添加到fd_r_list中
2、當(dāng)參數(shù)2 序列中含有fd時(shí),則將該序列中所有的fd添加到 fd_w_list中
3、當(dāng)參數(shù)3 序列中的fd發(fā)生錯(cuò)誤時(shí),則將該發(fā)生錯(cuò)誤的fd添加到 fd_e_list中
4、當(dāng)超時(shí)時(shí)間為空,則select會(huì)一直阻塞,直到監(jiān)聽的句柄發(fā)生變化
當(dāng)超時(shí)時(shí)間 = n(正整數(shù))時(shí),那么如果監(jiān)聽的句柄均無任何變化,則select會(huì)阻塞n秒,之后返回三個(gè)空列表,如果監(jiān)聽的句柄有變化,則直接執(zhí)行。
實(shí)例:利用select實(shí)現(xiàn)一個(gè)可并發(fā)的服務(wù)端
import socket import select s = socket.socket() s.bind(('127.0.0.1',8888)) s.listen(5) r_list = [s,] num = 0 while 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 = 1 s = 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()
在服務(wù)端我們可以看到,我們需要不停的調(diào)用select, 這就意味著:
1 當(dāng)文件描述符過多時(shí),文件描述符在用戶空間與內(nèi)核空間進(jìn)行copy會(huì)很費(fèi)時(shí)
2 當(dāng)文件描述符過多時(shí),內(nèi)核對(duì)文件描述符的遍歷也很浪費(fèi)時(shí)間
3 select最大僅僅支持1024個(gè)文件描述符
poll與select相差不大,本文不作介紹
epoll方法:
epoll很好的改進(jìn)了select:
1、epoll的解決方案在epoll_ctl函數(shù)中。每次注冊(cè)新的事件到epoll句柄中時(shí),會(huì)把所有的fd拷貝進(jìn)內(nèi)核,而不是在epoll_wait的時(shí)候重復(fù)拷貝。epoll保證了每個(gè)fd在整個(gè)過程中只會(huì)拷貝一次。
2、epoll會(huì)在epoll_ctl時(shí)把指定的fd遍歷一遍(這一遍必不可少)并為每個(gè)fd指定一個(gè)回調(diào)函數(shù),當(dāng)設(shè)備就緒,喚醒等待隊(duì)列上的等待者時(shí),就會(huì)調(diào)用這個(gè)回調(diào)函數(shù),而這個(gè)回調(diào)函數(shù)會(huì)把就緒的fd加入一個(gè)就緒鏈表。epoll_wait的工作實(shí)際上就是在這個(gè)就緒鏈表中查看有沒有就緒的fd
3、epoll對(duì)文件描述符沒有額外限制
select.epoll(sizehint=-1, flags=0) 創(chuàng)建epoll對(duì)象 epoll.close() Close the control file descriptor of the epoll object.關(guān)閉epoll對(duì)象的文件描述符 epoll.closed True if the epoll object is closed.檢測epoll對(duì)象是否關(guān)閉 epoll.fileno() Return the file descriptor number of the control fd.返回epoll對(duì)象的文件描述符 epoll.fromfd(fd) Create an epoll object from a given file descriptor.根據(jù)指定的fd創(chuàng)建epoll對(duì)象 epoll.register(fd[, eventmask]) Register a fd descriptor with the epoll object.向epoll對(duì)象中注冊(cè)fd和對(duì)應(yīng)的事件 epoll.modify(fd, eventmask) Modify a registered file descriptor.修改fd的事件 epoll.unregister(fd) Remove a registered file descriptor from the epoll object.取消注冊(cè) epoll.poll(timeout=-1, maxevents=-1) Wait for events. timeout in seconds (float)阻塞,直到注冊(cè)的fd事件發(fā)生,會(huì)返回一個(gè)dict,格式為:{(fd1,event1),(fd2,event2),……(fdn,eventn)}
事件:
EPOLLIN Available for read 可讀 狀態(tài)符為1 EPOLLOUT Available for write 可寫 狀態(tài)符為4 EPOLLPRI Urgent data for read EPOLLERR Error condition happened on the assoc. fd 發(fā)生錯(cuò)誤 狀態(tài)符為8 EPOLLHUP Hang up happened on the assoc. fd 掛起狀態(tài) EPOLLET Set Edge Trigger behavior, the default is Level Trigger behavior 默認(rèn)為水平觸發(fā),設(shè)置該事件后則邊緣觸發(fā) 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.
水平觸發(fā)和邊緣觸發(fā):
Level_triggered(水平觸發(fā),有時(shí)也稱條件觸發(fā)):當(dāng)被監(jiān)控的文件描述符上有可讀寫事件發(fā)生時(shí),epoll.poll()
會(huì)通知處理程序去讀寫。如果這次沒有把數(shù)據(jù)一次性全部讀寫完(如讀寫緩沖區(qū)太小),那么下次調(diào)用 epoll.poll()
時(shí),它還會(huì)通知你在上沒讀寫完的文件描述符上繼續(xù)讀寫,當(dāng)然如果你一直不去讀寫,它會(huì)一直通知你?。?!如果系統(tǒng)中有大量你不需要讀寫的就緒文件描述符,而它們每次都會(huì)返回,這樣會(huì)大大降低處理程序檢索自己關(guān)心的就緒文件描述符的效率?。。?優(yōu)點(diǎn)很明顯:穩(wěn)定可靠
Edge_triggered(邊緣觸發(fā),有時(shí)也稱狀態(tài)觸發(fā)):當(dāng)被監(jiān)控的文件描述符上有可讀寫事件發(fā)生時(shí),epoll.poll()
會(huì)通知處理程序去讀寫。如果這次沒有把數(shù)據(jù)全部讀寫完(如讀寫緩沖區(qū)太小),那么下次調(diào)用epoll.poll()
時(shí),它不會(huì)通知你,也就是它只會(huì)通知你一次,直到該文件描述符上出現(xiàn)第二次可讀寫事件才會(huì)通知你!??!這種模式比水平觸發(fā)效率高,系統(tǒng)不會(huì)充斥大量你不關(guān)心的就緒文件描述符!??!缺點(diǎn):某些條件下不可靠
epoll實(shí)例:
import socket import 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 = 1 s = 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()
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
- Python網(wǎng)絡(luò)編程使用select實(shí)現(xiàn)socket全雙工異步通信功能示例
- python爬蟲之BeautifulSoup 使用select方法詳解
- Python基于select實(shí)現(xiàn)的socket服務(wù)器
- Python通過select實(shí)現(xiàn)異步IO的方法
- Python的Django框架中的select_related函數(shù)對(duì)QuerySet 查詢的優(yōu)化
- Python中使用select模塊實(shí)現(xiàn)非阻塞的IO
- pycharm 使用心得(九)解決No Python interpreter selected的問題
- Python select及selectors模塊概念用法詳解
相關(guān)文章
解決Numpy報(bào)錯(cuò):ImportError: numpy.core.multiarray faile
這篇文章主要介紹了解決Numpy報(bào)錯(cuò):ImportError: numpy.core.multiarray failed問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01Python實(shí)現(xiàn)mysql數(shù)據(jù)庫更新表數(shù)據(jù)接口的功能
這篇文章主要給大家介紹了關(guān)于Python如何實(shí)現(xiàn)mysql數(shù)據(jù)庫更新表數(shù)據(jù)接口功能的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11Python中線程的MQ消息隊(duì)列實(shí)現(xiàn)以及消息隊(duì)列的優(yōu)點(diǎn)解析
消息隊(duì)列(MQ,Message Queue)在消息數(shù)據(jù)傳輸中的保存作用為數(shù)據(jù)通信提供了保障和實(shí)時(shí)處理上的便利,這里我們就來看一下Python中線程的MQ消息隊(duì)列實(shí)現(xiàn)以及消息隊(duì)列的優(yōu)點(diǎn)解析2016-06-06Django密碼系統(tǒng)實(shí)現(xiàn)過程詳解
這篇文章主要介紹了Django密碼系統(tǒng)實(shí)現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-07-07pycharm日志總是彈出“無法運(yùn)行Git,未安裝Git”的問題
這篇文章主要介紹了pycharm日志總是彈出“無法運(yùn)行Git,未安裝Git”的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06django之從html頁面表單獲取輸入的數(shù)據(jù)實(shí)例
這篇文章主要介紹了django之從html頁面表單獲取輸入的數(shù)據(jù)實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-03-03python實(shí)現(xiàn)進(jìn)制轉(zhuǎn)化的示例代碼
本文主要介紹了python實(shí)現(xiàn)進(jìn)制轉(zhuǎn)化的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10