python之IO多工之epoll

來源:互聯網
上載者:User

什麼是epoll

epoll是什嗎?在linux的網路編程中,很長的時間都在使用select來做事件觸發。在linux新的核心中,有了一種替換它的機制,就是epoll。當然,這不是2.6核心才有的,它是在2.5.44核心中被引進的(epoll(4) is a new API introduced in Linux kernel 2.5.44),它幾乎具備了之前所說的一切優點,被公認為Linux2.6下效能最好的多工I/O就緒通知方法。

相比於select,epoll最大的好處在於它不會隨著監聽fd數目的增長而降低效率。因為在核心中的select實現中,它是採用輪詢來處理的,輪詢的fd數目越多,自然耗時越多。

epoll工作原理

epoll同樣只告知那些就緒的檔案描述符,而且當我們調用epoll_wait()獲得就緒檔案描述符時,返回的不是實際的描述符,而是一個代表就緒描述符數量的值,你只需要去epoll指定的一個數組中依次取得相應數量的檔案描述符即可,這裡也使用了記憶體映射(mmap)技術,這樣便徹底省掉了這些檔案描述符在系統調用時複製的開銷。

另一個本質的改進在於epoll採用基於事件的就緒通知方式。在select/poll中,進程只有在調用一定的方法後,核心才對所有監視的檔案描述符進行掃描,而epoll事先通過epoll_ctl()來註冊一個檔案描述符,一旦基於某個檔案描述符就緒時,核心會採用類似callback的回調機制,迅速啟用這個檔案描述符,當進程調用epoll_wait()時便得到通知。

從以上可知,epoll是對select、poll模型的改進,提高了網路編程的效能,廣泛應用於大規模並發請求的C/S架構中。

python中的epoll

1、觸發方式:

邊緣觸發/水平觸發,只適用於Unix/Linux作業系統

2、原理圖

3、一般步驟

Create an epoll object——建立1個epoll對象

Tell the epoll object to monitor specific events on specific sockets——告訴epoll對象,在指定的socket上監聽指定的事件

Ask the epoll object which sockets may have had the specified event since the last query——詢問epoll對象,從上次查詢以來,哪些socket發生了哪些指定的事件

Perform some action on those sockets——在這些socket上執行一些操作

Tell the epoll object to modify the list of sockets and/or events to monitor——告訴epoll對象,修改socket列表和(或)事件,並監控

Repeat steps 3 through 5 until finished——重複步驟3-5,直到完成

Destroy the epoll object——銷毀epoll對象

4、相關用法

import select 匯入select模組

epoll = select.epoll()建立一個epoll對象

epoll.register(檔案控制代碼,事件類型)註冊要監控的檔案控制代碼和事件

事件類型:

  select.EPOLLIN 可讀事件

  select.EPOLLOUT 可寫事件

  select.EPOLLERR 錯誤事件

  select.EPOLLHUP 用戶端斷開事件

epoll.unregister(檔案控制代碼) 銷毀檔案控制代碼

epoll.poll(timeout) 當檔案控制代碼發生變化,則會以列表的形式主動報告給使用者進程,timeout

為逾時時間,預設為-1,即一直等待直到檔案控制代碼發生變化,如果指定為1

那麼epoll每1秒彙報一次當前檔案控制代碼的變化情況,如果無變化則返回空

epoll.fileno() 返回epoll的控制檔案描述符(Return the epoll control file descriptor)

epoll.modfiy(fineno,event)fineno為檔案描述符 event為事件類型 作用是修改檔案描述符所對應的事件

epoll.fromfd(fileno)從1個指定的檔案描述符建立1個epoll對象

epoll.close() 關閉epoll對象的控制檔案描述符

5 執行個體:用戶端發送資料 服務端將接收的資料返回給用戶端

服務端代碼

#!/usr/bin/env python#-*- coding:utf-8 -*-import socketimport selectimport Queue#建立socket對象serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#設定IP地址複用serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)#ip地址和連接埠號碼server_address = ("127.0.0.1", 8888)#綁定IP地址serversocket.bind(server_address)#監聽,並設定最大串連數serversocket.listen(10)print  "伺服器啟動成功,監聽IP:" , server_address#服務端設定非阻塞serversocket.setblocking(False)  #逾時時間timeout = 10#建立epoll事件對象,後續要監控的事件添加到其中epoll = select.epoll()#註冊伺服器監聽fd到等待讀事件集合epoll.register(serversocket.fileno(), select.EPOLLIN)#儲存串連用戶端訊息的字典,格式為{}message_queues = {}#檔案控制代碼到所對應對象的字典,格式為{控制代碼:對象}fd_to_socket = {serversocket.fileno():serversocket,}while True:  print "等待活動串連......"  #輪詢註冊的事件集合,傳回值為[(檔案控制代碼,對應的事件),(...),....]  events = epoll.poll(timeout)  if not events:     print "epoll逾時無活動串連,重新輪詢......"     continue  print "有" , len(events), "個新事件,開始處理......"    for fd, event in events:     socket = fd_to_socket[fd]     #如果活動socket為當前伺服器socket,表示有新串連     if socket == serversocket:            connection, address = serversocket.accept()            print "新串連:" , address            #新串連socket設定為非阻塞            connection.setblocking(False)            #註冊新串連fd到待讀事件集合            epoll.register(connection.fileno(), select.EPOLLIN)            #把新串連的檔案控制代碼以及對象儲存到字典            fd_to_socket[connection.fileno()] = connection            #以新串連的對象為索引值,值儲存在隊列中,儲存每個串連的資訊            message_queues[connection]  = Queue.Queue()     #關閉事件     elif event & select.EPOLLHUP:        print 'client close'        #在epoll中登出用戶端的檔案控制代碼        epoll.unregister(fd)        #關閉用戶端的檔案控制代碼        fd_to_socket[fd].close()        #在字典中刪除與已關閉用戶端相關的資訊        del fd_to_socket[fd]     #可讀事件     elif event & select.EPOLLIN:        #接收資料        data = socket.recv(1024)        if data:           print "收到資料:" , data , "用戶端:" , socket.getpeername()           #將資料放入對應用戶端的字典           message_queues[socket].put(data)           #修改讀取到訊息的串連到等待寫事件集合(即對應用戶端收到訊息後,再將其fd修改並加入寫事件集合)           epoll.modify(fd, select.EPOLLOUT)     #可寫事件     elif event & select.EPOLLOUT:        try:           #從字典中擷取對應用戶端的資訊           msg = message_queues[socket].get_nowait()        except Queue.Empty:           print socket.getpeername() , " queue empty"           #修改檔案控制代碼為讀事件           epoll.modify(fd, select.EPOLLIN)        else :           print "發送資料:" , data , "用戶端:" , socket.getpeername()           #發送資料           socket.send(msg)#在epoll中登出服務端檔案控制代碼epoll.unregister(serversocket.fileno())#關閉epollepoll.close()#關閉伺服器socketserversocket.close()

用戶端代碼:

#!/usr/bin/env python#-*- coding:utf-8 -*-import socket#建立用戶端socket對象clientsocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#服務端IP地址和連接埠號碼元組server_address = ('127.0.0.1',8888)#用戶端串連指定的IP地址和連接埠號碼clientsocket.connect(server_address)while True:    #輸入資料    data = raw_input('please input:')    #用戶端發送資料    clientsocket.sendall(data)    #用戶端接收資料    server_data = clientsocket.recv(1024)    print '用戶端收到的資料:'server_data    #關閉用戶端socket    clientsocket.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.