色偷偷偷亚洲综合网另类,亚洲欧美另类在线观看,欧美午夜激情在线,久久久精品一区

當前位置:首頁 > 嵌入式培訓 > 嵌入式學習 > 講師博文 > 基于epoll的Linux并發服務器

基于epoll的Linux并發服務器 時間:2018-09-26      來源:未知

基于網絡的不同主機之間通信、我們經常使用socket(套接字)來實現,socket就是用于通信的endpoint(端點)。當我們基于socket發送或接受數據時,由于各種原因可能無法將數據發送出去,或者無法接收到數據。這時我們是選擇等待、還是立即返回。如果一直等待,我們稱之為阻塞I/O;立即返回,則稱之為非阻塞I/O。對于Linux系統,socket默認是阻塞的。一個典型的阻塞I/O如下圖:

當進程調用read系統調用時,由于沒有數據可讀,內核需要準備數據。當準備好數據、內核又需要將數據從內核空間拷貝到戶用空間。在此期間,應用程序一直在read函數上阻塞等待,直到數據到達。當有多個I/O同時讀寫時,阻塞I/O模式經常因為當前I/O不可讀寫而影響其他I/O的數據傳輸,使效率大大降低。如果使用非阻塞I/O就可以避免這種情況出現。但是如果采用非阻塞模式實現多I/O通信、就必須輪詢嘗試讀寫每一個I/O,而一些I/O并不可讀寫,白白浪費了時間,效率也受到影響。如圖:

當用戶進程調用read函數時,如果內核中的數據還沒有準備好,那么進程并不會阻塞,而是立刻返回錯誤,錯誤碼可能是EWOULDBLOCK,表示數據沒有準備好,于是可以選擇重新read。一旦內核中的數據準備好,并且用戶此時發起read操作,那么它馬上就將數據拷貝到了用戶空間內存,然后返回。

不停的檢測I/O是否可讀寫降低了系統效率,做了大量的無用功。幸運的是,有這樣一種機制,可以監視多個I/O,一旦某個I/O就緒(可讀或可寫),就能夠通知程序進行相應的讀寫操作,這種機制稱為I/O多路復用。實現這種機制Linux提供了select,poll和epoll函數。

select函數將I/O事件劃分成可讀,可寫和異常,而有時即使通知用戶可讀寫,但實際卻沒有數據,相當于誤報。另外,select監視的多路I/O文件描述符集合、同時也用來保存返回結果,也就是保存哪些描述符可讀、可寫或發生異常。這就導致我們總要不停的構造監視的I/O文件描述符集合。所以select監視多路I/O的效率不高,也不能監視太多的I/O,一般不應該超過FD_SETSIZE(默認為1024)個I/O。當返回結果后,還要檢查哪些描述符在返回的可讀,可寫或發生異常集中。

poll函數針對select函數的缺陷做了一些改進,將監視的文件描述符與監視的返回結果(哪些可讀寫)分開,所以poll不需要重新構造監視的I/O文件描述符集合。并且使用位圖表示更多的事件。但是poll對select的改進是有限的,都需要向內核拷貝需要監視的I/O文件描述符集合,也需要查看全部描述符,檢查哪些描述符已經可讀,可寫或發生異常等。雖然poll沒有描述符個數限制,但文件描述符太多會導致效率下降。

當客戶端連接大量增加時,select和poll的效率下降比較多。而且很多時候我們并不僅僅關心哪些描述符可讀寫,很可能是和該描述符關聯的其他任務要處理,而我們只通過文件描述符很難找到關聯的其他任務。epoll函數解決了select/poll函數的上述缺陷。它不需要向內核拷貝監視的文件描述符集合,當某些文件描述符就緒時、也不需要輪詢檢查哪些描述符可讀寫。當文件描述符可讀寫時,內核直接返回可讀寫的文件描述符,以及其相關聯的對象指針。這在實際程序中非常有意義。但epoll是Linux內核2.5.44引入,只在Linux系統上才可以使用。

epoll提供三個系統調用,分別如下:

1.int epoll_create(int size)

創建一個epoll的句柄。size用來告訴內核需要監聽的數目一共有多大,自從linux2.6.8之后,size參數是被忽略的,內核動態覺得size大小。需要注意的是,當創建好epoll句柄后,它就是會占用一個fd值,在使用完epoll后,必須調用close()關閉。

2.int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

epoll的事件注冊函數,它不同于select()是在監聽事件時告訴內核要監聽什么類型的事件,而是在這里先注冊要監聽的事件類型。

epfd參數是文件描述符,用來引用關聯的epoll實例。op表示要完成的操作,分別為:

EPOLL_CTL_ADD,添加新的文件描述符到epoll實例;

EPOLL_CTL_MOD,修改已經注冊的文件描述符的監聽事件;

EPOLL_CTL_DEL,刪除一個文件描述符;

fd就是要操作的文件描述符,event參數告訴內核需要監聽什么事,epoll_event結構如下:

typedef union epoll_data {

void *ptr;

int fd;

__uint32_t u32;

__uint64_t u64;

} epoll_data_t;

Struct epoll_event {

__uint32_t events; /* Epoll events */

epoll_data_t data; /* User data variable */

};

Events是一個32位整數,每一位表示一種事件。用一下宏表示:

EPOLLIN ,表示對應的文件描述符可讀(包括對端SOCKET正常關閉);

EPOLLOUT,表示對應的文件描述符可寫;

EPOLLPRI,表示對應的文件描述符有緊急的數據可讀;

EPOLLERR,表示對應的文件描述符發生錯誤;

EPOLLHUP,表示對應的文件描述符被掛斷;

EPOLLET,將EPOLL設為邊緣觸發(Edge Triggered)模式,這是相對于水平觸發(Level Triggered)來說的。

EPOLLONESHOT,只監聽一次事件,當監聽完這次事件之后,如果還需要繼續監聽這個socket的話,需要再次把這個socket加入到epoll實例中。

3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

等待epfd指向的epoll實例上發生關心的事件。參數events是分配好的epoll_event結構體數組,epoll將會把發生的事件賦值到events數組中。maxevents告之內核這個events有多大,這個maxevents的值不能大于創建epoll_create()時的size,參數timeout是超時時間,單位是毫秒,如果該值為0,即使沒有事件發生也會立即返回,-1表示一直阻塞到有事件發生。如果函數調用成功,返回對應I/O上已準備好的文件描述符數目,如返回0表示已超時。

epoll的man手冊上有一個比較好的例子可以參考,代碼如下:

#define MAX_EVENTS 10

structepoll_eventev, events[MAX_EVENTS];

intlisten_sock, conn_sock, nfds, epollfd;

/* Set up listening socket, 'listen_sock' (socket(),

* bind(), listen()) */

/* 創建epoll實例 */

epollfd = epoll_create(10);

if (epollfd == -1) {

perror("epoll_create");

exit(EXIT_FAILURE);

}

/* 將socket創建的監聽描述符listen_sock添加到epoll實例,以便監聽EPOLLIN事件 */

ev.events = EPOLLIN;

ev.data.fd = listen_sock;

if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {

perror("epoll_ctl: listen_sock");

exit(EXIT_FAILURE);

}

for (;;) {

/* 等待epollfd指向的epoll實例有關心的事件發生,并將其對應的epoll_event結構存入events指向的內存 */

nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);

if (nfds == -1) {

perror("epoll_pwait");

exit(EXIT_FAILURE);

}

for (n = 0; n

/* 如果有客戶端發起連接、就accept建立連接 */

if (events[n].data.fd == listen_sock) {

conn_sock = accept(listen_sock, (structsockaddr *) &local, &addrlen);

if (conn_sock == -1) {

perror("accept");

exit(EXIT_FAILURE);

}

/* 將新連接設置為非阻塞模式 */

setnonblocking(conn_sock);

/* 將新連接添加到epoll實例 */

ev.events = EPOLLIN | EPOLLET;

ev.data.fd = conn_sock;

if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock, &ev) == -1) {

perror("epoll_ctl: conn_sock");

exit(EXIT_FAILURE);

}

} else {

/* 如果不是有客戶端發起連接,而是有文件描述符發生關心的事件,這里就可以處理該事件 */

do_use_fd(events[n].data.fd);

}

}

}

上一篇:JNI開發簡介及實例演示

下一篇:什么是"文件表項"

熱點文章推薦
華清學員就業榜單
高薪學員經驗分享
熱點新聞推薦
前臺專線:010-82525158 企業培訓洽談專線:010-82525379 院校合作洽談專線:010-82525379 Copyright © 2004-2022 北京華清遠見科技集團有限公司 版權所有 ,京ICP備16055225號-5京公海網安備11010802025203號

回到頂部

色偷偷偷亚洲综合网另类,亚洲欧美另类在线观看,欧美午夜激情在线,久久久精品一区
主站蜘蛛池模板: 18久久久久久| 久久精品2019中文字幕| 久久精品国产欧美激情| 2019中文字幕在线| 欧美大学生性色视频| 在线观看欧美日韩国产| 国产一区二区色| 国产一区二区香蕉| 亚洲自拍高清视频网站| 亚洲国产精品va| 欧美日韩xxx| 精品成人av一区| 久久精品国产久精国产一老狼 | 亚洲最大av在线| 久久久国产91| 国产一区二区香蕉| 懂色av中文一区二区三区天美| 国产成人久久久| 久久视频免费在线播放| 国产噜噜噜噜久久久久久久久| 久久久91精品国产| 成人中心免费视频| 欧美激情啊啊啊| 亚洲深夜福利在线| 日韩男女性生活视频| 夜色77av精品影院| 国产精品精品久久久| 久久在线免费视频| 日韩av在线一区二区| 国产91成人在在线播放| 北条麻妃一区二区三区中文字幕| 国产精品久久久久久影视 | 国产精品直播网红| 久久91亚洲人成电影网站| 亚洲激情小视频| 国产第一区电影| 欧美大全免费观看电视剧大泉洋| 国产香蕉97碰碰久久人人| 国产精品中文在线| 26uuu亚洲国产精品| 免费91麻豆精品国产自产在线观看|