欧美日韩中文字幕一区二区高清_日本—中文字幕一级A片_2022国产高清精品一区二区_国产亚洲曝欧美_国产精品77777竹菊影视

首頁 >> 動態(tài)

RT-DETR:可以滿足實時性要求的DETR模型

2023-07-31 15:23:52來源:博客園

本文分享自華為云社區(qū)《高性能網(wǎng)絡(luò)設(shè)計秘笈:深入剖析Linux網(wǎng)絡(luò)IO與epoll》,作者: Lion Long 。

一、epoll簡介

epoll是Linux內(nèi)核中一種可擴展的IO事件處理機制,可替代select和poll的系統(tǒng)調(diào)用。處理百萬級并發(fā)訪問性能更佳。

二、select的局限性

(1)文件描述符越多,性能越差。單個進程中能夠監(jiān)視的文件描述符存在最大的數(shù)量,默認是1024(在linux內(nèi)核頭文件中定義有 #define _FD_SETSIZE 1024),當(dāng)然也可以修改,但是文件描述符數(shù)量越多,性能越差。


(資料圖)

(2)開銷巨大,select需要復(fù)制大量的句柄數(shù)據(jù)結(jié)構(gòu),產(chǎn)生了巨大的開銷(內(nèi)核/用戶空間內(nèi)存拷貝問題)。

(3)select需要遍歷整個句柄數(shù)組才能知道哪些句柄有事件。

(4)如果沒有完成對一個已經(jīng)就緒的文件描述符的IO操作,那么每次調(diào)用select還是會將這些文件描述符通知進程,即水平觸發(fā)。

(5)poll使用鏈表保存監(jiān)視的文件描述符,雖然沒有了監(jiān)視文件數(shù)量的限制,但是其他缺點依舊存在。

由于以上缺點,基于select模型的服務(wù)器程序,要達到十萬以上的并發(fā)訪問,是很難完成的。因此,epoll出場了。

三、epoll的優(yōu)點

(1)不需要輪詢所有的文件描述符

(2)每次取就緒集合,都在固定位置

(3)事件的就緒和IO觸發(fā)可以異步解耦

四、epoll函數(shù)原型

4.1、epoll_create(int size)

#include int epoll_create(int size);

功能:創(chuàng)建epoll的文件描述符。

參數(shù)說明:size表示內(nèi)核需要監(jiān)控的最大數(shù)量,但是這個參數(shù)內(nèi)核已經(jīng)不會用到,只要傳入一個大于0的值即可。當(dāng)size<=0時,會直接返回不可用,這是歷史原因保留下來的,最早的epoll_create是需要定義一次性就緒的最大數(shù)量;后來使用了鏈表以便便維護和擴展,就不再需要使用傳入的參數(shù)。

返回:返回該對象的描述符,注意要使用 close 關(guān)閉該描述符。

4.2、epoll_ctl

#include int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);// epoll_ctl對應(yīng)系統(tǒng)調(diào)用sys_epoll_ctl

功能:操作epoll的文件描述符,主要是對epoll的紅黑樹節(jié)點進行操作,比如節(jié)點的增刪改查。

參數(shù)說明:

4.2.1、event參數(shù)說明

struct epoll_event結(jié)構(gòu)體原型

typedef union epoll_data{void* ptr;int fd;uint32_t u32;uint64_t u64};struct epoll_event{uint32_t events;epoll_data_t data;}

events成員代表要監(jiān)聽的epoll事件類型

events成員:

data成員:

data 成員時一個聯(lián)合體類型,可以在調(diào)用 epoll_ctl 給 fd 添加/修改描述符監(jiān)聽的事件時攜帶一些數(shù)據(jù),方便后面的epoll_wait可以取出信息使用。

4.2.2、擴展說明:SYSCALL_DEFINE數(shù)字 的宏定義

跟著的數(shù)字代表函數(shù)需要的參數(shù)數(shù)量,比如SYSCALL_DEFINE1代表函數(shù)需要一個參數(shù)、SYSCALL_DEFINE4代表函數(shù)需要4個參數(shù)。

4.2.3、注意

epoll_ctl是非阻塞的,不會被掛起。

4.3、epoll_wait

函數(shù)原型

#include int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

功能:阻塞一段時間,等待事件發(fā)生

返回:返回事件數(shù)量,事件集添加到events數(shù)組中。也就是遍歷紅黑樹中的雙向鏈表,把雙向鏈表中的節(jié)點數(shù)據(jù)拷貝出來,拷貝完畢后把節(jié)點從雙向鏈表中移除。

五、epoll使用步驟

step 1:創(chuàng)建epoll文件描述符

int epfd = epoll_create(1);

step 2:創(chuàng)建struct epoll_event結(jié)構(gòu)體

struct epoll_event ev;ev.data.fd=listenfd;//保存監(jiān)聽的fd,以便epoll_wait的后續(xù)操作ev.events=EPOLLIN;//設(shè)置監(jiān)聽fd的可讀事件

step 3:添加事件監(jiān)聽

epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);

step 4:等待事件

struct epoll_event events[EVENTS_LENGTH];char rbuffer[MAX_BUFF]={ 0 };char wbuffer[MAX_BUFF]={ 0 };while(1){int nready = epoll_wait(epfd,events,EVENTS_LENGTH,-1);//-1表示阻塞等待int i=0;for(i=0;i0){rbuffer[ret]="\0";//剔除干擾數(shù)據(jù)printf("recv: %s\n",rbuffer);memcpy(wbuffer,rbuffer,MAX_BUFF);//拷貝數(shù)據(jù),做回傳示例//step 2:創(chuàng)建struct epoll_event結(jié)構(gòu)體struct epoll_event evt;evt.data.fd=clientfd;//保存監(jiān)聽的fd,以便epoll_wait的后續(xù)操作evt.events=EPOLLOUT;//設(shè)置監(jiān)聽fd的可寫事件// step 3:修改事件監(jiān)聽epoll_ctl(epfd,EPOLL_CTL_MOD,clientfd,&evt);}}else if(events[i].events &EPOLLOUT){int ret = send(clientfd,wbuffer,MAX_BUFF,0);printf("send: %s\n",wbuffer);//step 2:創(chuàng)建struct epoll_event結(jié)構(gòu)體struct epoll_event evt;evt.data.fd=clientfd;//保存監(jiān)聽的fd,以便epoll_wait的后續(xù)操作evt.events=EPOLLIN;//設(shè)置監(jiān)聽fd的可讀事件// step 3:修改事件監(jiān)聽epoll_ctl(epfd,EPOLL_CTL_MOD,clientfd,&evt);}}}

六、完整示例代碼

#include #include #include #include #include #include #include #include #include #define BUFFER_LENGTH 128#define EVENTS_LENGTH 128char rbuff[BUFFER_LENGTH] = { 0 };char wbuff[BUFFER_LENGTH] = { 0 };int main() {// blockint listenfd = socket(AF_INET, SOCK_STREAM, 0); //if (listenfd == -1) return -1;// listenfdstruct sockaddr_in servaddr;servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(9999);if (-1 == bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr))) {return -2;}#if 0 // nonblockint flag = fcntl(listenfd, F_GETFL, 0);flag |= O_NONBLOCK;fcntl(listenfd, F_SETFL, flag);#endiflisten(listenfd, 10);int epfd = epoll_create(1);struct epoll_event ev, events[EVENTS_LENGTH];ev.events = EPOLLIN;ev.data.fd = listenfd;epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);printf("epfd : %d\n", epfd);while (1){int nready = epoll_wait(epfd, events, EVENTS_LENGTH, -1);printf("nready --> %d\n",nready);int i;for (i = 0; i < nready;i++){int clientfd = events[i].data.fd;if (listenfd == clientfd){// acceptstruct sockaddr_in client;int len = sizeof(client);int conffd = accept(clientfd, (struct sockaddr*)&client,&len);printf("conffd --> %d\n",conffd);ev.events = EPOLLIN;ev.data.fd = conffd;epoll_ctl(epfd, EPOLL_CTL_ADD, conffd, &ev);}else if(events[i].events & EPOLLIN)//client{int ret=recv(clientfd, rbuff, BUFFER_LENGTH, 0);if (ret > 0){rbuff[ret] = "\0";printf("recv buffer: %s\n", rbuff);/*int j;for (j = 0; j < BUFFER_LENGTH;j++){buff[j] = "a" + (j % 26);}send(clientfd, buff, BUFFER_LENGTH, 0);*/memcpy(wbuff, rbuff, BUFFER_LENGTH);ev.events = EPOLLOUT;ev.data.fd = clientfd;epoll_ctl(epfd, EPOLL_CTL_MOD, clientfd, &ev);}}else if (events[i].events & EPOLLOUT){send(clientfd, wbuff, BUFFER_LENGTH, 0);printf("send --> %s\n",wbuff);ev.events = EPOLLIN;ev.data.fd = clientfd;epoll_ctl(epfd, EPOLL_CTL_MOD, clientfd, &ev);}}}return 0;}

七、epoll的缺點

讀寫使用相同的緩沖區(qū)。比如上述的示例中,wbuffer和rbuffer是使用同一個緩沖區(qū)的,所以需要rbuff[ret] = ‘\0’;去除雜數(shù)據(jù)。

八、水平觸發(fā)(LT)與邊沿觸發(fā)(ET)

8.1、兩者差異

1、水平觸發(fā)可以一次recv,邊沿觸發(fā)需要用循環(huán)來recv;

2、水平觸發(fā)可以使用阻塞模式,邊沿模式不能

3、兩者性能差異非常小,一般小數(shù)據(jù)使用水平觸發(fā)LT,大數(shù)據(jù)使用邊沿觸發(fā)ET

4、listen fd最好使用水平觸發(fā),盡量不要邊沿觸發(fā)

5、當(dāng)當(dāng)recv的buffer小于接受的數(shù)據(jù)時:

(1)水平觸發(fā)是只要有數(shù)據(jù)就一直觸發(fā),直到數(shù)據(jù)讀完;

(2)邊沿觸發(fā)是來一次連接觸發(fā)一次,如果接受數(shù)據(jù)的buffer不夠大,則數(shù)據(jù)會保留在緩沖區(qū),下次觸發(fā)繼續(xù)從緩沖區(qū)讀出來;

6、一般,水平觸發(fā)只需要一個recv,邊沿觸發(fā)需要搭配while從緩沖區(qū)讀完數(shù)據(jù)

8.2、設(shè)置觸發(fā)模式

默認是水平觸發(fā)模式,在事件中設(shè)置中 | EPOLLET 就可以設(shè)置邊沿觸發(fā),不設(shè)置則默認是水平觸發(fā)。

例如:

ev.events=EPOLL_IN | EPOLLET

九、常見疑惑問題

9.1、為什么提前先定義一個事件?

我們需要注冊,內(nèi)核才會有事件來的時候通知進程。比如生活中要退一個快遞,那么我們需要注冊一個快遞公司的賬戶,然后發(fā)送一個退快遞請求時快遞公司才能找到你并取快遞。

9.2、epoll events超出EVENTS_LENGTH?

epoll會循環(huán)拷貝紅黑樹結(jié)構(gòu)體中的雙向鏈表節(jié)點,讀取節(jié)點數(shù)據(jù),直到?jīng)]有事件。

9.3、緩沖區(qū)有多大空間時才返回可讀/可寫?

只要緩沖區(qū)有空間就返回可讀、可寫,不管空間多少。比如緩沖區(qū)是1024,但是有1023有數(shù)據(jù)了,這種極端條件也會返回可讀、可寫。

9.4、recv和send放在一起時,有什么問題?

發(fā)送給客戶端數(shù)據(jù)很大的時候(大于內(nèi)核緩沖區(qū)),就可能出現(xiàn)send不全,客戶端recv不全,最好用EPOLLOUT單獨處理發(fā)送數(shù)據(jù)事件。

總結(jié)

本文介紹了網(wǎng)絡(luò)IO模型,引入了epoll作為Linux系統(tǒng)中高性能網(wǎng)絡(luò)編程的核心工具。通過分析epoll的特點與優(yōu)勢,并給出使用epoll的注意事項和實踐技巧,該文章為讀者提供了寶貴的指導(dǎo)。通過掌握這些知識,讀者能夠構(gòu)建高效、可擴展和穩(wěn)定的網(wǎng)絡(luò)應(yīng)用,提供出色的用戶體驗。

點擊關(guān)注,第一時間了解華為云新鮮技術(shù)~

關(guān)鍵詞:

相關(guān)新聞