国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

高性能網(wǎng)絡(luò)設(shè)計(jì)秘笈:深入剖析Linux網(wǎng)絡(luò)IO與epoll

這篇具有很好參考價(jià)值的文章主要介紹了高性能網(wǎng)絡(luò)設(shè)計(jì)秘笈:深入剖析Linux網(wǎng)絡(luò)IO與epoll。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

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

一、epoll簡(jiǎn)介

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

二、select的局限性

(1)?文件描述符越多,性能越差。?單個(gè)進(jìn)程中能夠監(jiān)視的文件描述符存在最大的數(shù)量,默認(rèn)是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需要遍歷整個(gè)句柄數(shù)組才能知道哪些句柄有事件。

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

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

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

三、epoll的優(yōu)點(diǎn)

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

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

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

四、epoll函數(shù)原型

4.1、epoll_create(int size)

#include <sys/epoll.h>

int epoll_create(int size);

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

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

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

4.2、epoll_ctl

#include <sys/epoll.h>

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

// epoll_ctl對(duì)應(yīng)系統(tǒng)調(diào)用sys_epoll_ctl

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

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

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

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

4.2.3、注意

epoll_ctl是非阻塞的,不會(huì)被掛起。

4.3、epoll_wait

函數(shù)原型

#include <sys/epoll.h>

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

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

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

五、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;i<nready;i++)

{

int clientfd=events[i].data.fd;

if(clientfd==listenfd)

{

struct sockaddr_in client;

int len=sizeof(client);

int confd=accept(listenfd,(struct sockaddr*)&client,&len);

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

struct epoll_event evt;

evt.data.fd=confd;//保存監(jiān)聽的fd,以便epoll_wait的后續(xù)操作

evt.events=EPOLLIN;//設(shè)置監(jiān)聽fd的可讀事件

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

epoll_ctl(epfd,EPOLL_CTL_ADD,confd,&evt);

}

else if(events[i].events &EPOLLIN)

{

int ret = recv(clientfd,rbuffer,MAX_BUFF,0);

if(ret>0)

{

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 <stdio.h>

#include <sys/socket.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <fcntl.h>

#include <unistd.h>

#include <pthread.h>

#include <sys/epoll.h>

#include <string.h>

#define BUFFER_LENGTH 128

#define EVENTS_LENGTH 128

char rbuff[BUFFER_LENGTH] = { 0 };

char wbuff[BUFFER_LENGTH] = { 0 };

int main() {

// block

int listenfd = socket(AF_INET, SOCK_STREAM, 0); //

if (listenfd == -1) return -1;

// listenfd

struct 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 // nonblock

int flag = fcntl(listenfd, F_GETFL, 0);

flag |= O_NONBLOCK;

fcntl(listenfd, F_SETFL, flag);

#endif

listen(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)

{

// accept

struct 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的缺點(diǎn)

讀寫使用相同的緩沖區(qū)。比如上述的示例中,wbuffer和rbuffer是使用同一個(gè)緩沖區(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ù)時(shí):

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

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

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

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

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

例如:

ev.events=EPOLL_IN | EPOLLET

九、常見疑惑問題

9.1、為什么提前先定義一個(gè)事件?

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

9.2、epoll events超出EVENTS_LENGTH?

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

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

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

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

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

總結(jié)

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

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

?文章來源地址http://www.zghlxwxcb.cn/news/detail-594369.html

到了這里,關(guān)于高性能網(wǎng)絡(luò)設(shè)計(jì)秘笈:深入剖析Linux網(wǎng)絡(luò)IO與epoll的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • C++高性能服務(wù)器網(wǎng)絡(luò)框架設(shè)計(jì)與實(shí)現(xiàn)

    C++高性能服務(wù)器網(wǎng)絡(luò)框架設(shè)計(jì)與實(shí)現(xiàn)

    這篇文章將從兩個(gè)方面來介紹,一個(gè)是服務(wù)器中的基礎(chǔ)的網(wǎng)絡(luò)通信部件;另外一個(gè)是,如何利用這些基礎(chǔ)通信部件整合成一個(gè)完整的高效的服務(wù)器框架。注意:本文以下內(nèi)容中的客戶端是相對(duì)概念,指的是連接到當(dāng)前討論的服務(wù)程序的終端,所以這里的客戶端既可能是我們傳

    2024年02月04日
    瀏覽(22)
  • Linux高性能服務(wù)器編程 學(xué)習(xí)筆記 第五章 Linux網(wǎng)絡(luò)編程基礎(chǔ)API

    Linux高性能服務(wù)器編程 學(xué)習(xí)筆記 第五章 Linux網(wǎng)絡(luò)編程基礎(chǔ)API

    我們將從以下3方面討論Linux網(wǎng)絡(luò)API: 1.socket地址API。socket最開始的含義是一個(gè)IP地址和端口對(duì)(ip,port),它唯一表示了使用TCP通信的一端,本書稱其為socket地址。 2.socket基礎(chǔ)API。socket的主要API都定義在sys/socket.h頭文件中,包括創(chuàng)建socket、命名socket、監(jiān)聽socket、接受連接、發(fā)

    2024年02月07日
    瀏覽(41)
  • Linux 網(wǎng)絡(luò)編程學(xué)習(xí)筆記——十二、高性能 I/O 框架庫 Libevent

    Linux 網(wǎng)絡(luò)編程學(xué)習(xí)筆記——十二、高性能 I/O 框架庫 Libevent

    在處理 I/O 事件、信號(hào)和定時(shí)事件時(shí),需要考慮如下三個(gè)問題: 統(tǒng)一事件源:很明顯,統(tǒng)一處理這三類事件既能使代碼簡(jiǎn)單易懂,又能避免一些潛在的邏輯錯(cuò)誤。 可移植性:不同的操作系統(tǒng)具有不同的 I/O 復(fù)用方式,比如 Solaris 的 dev/poll 文件,F(xiàn)reeBSD 的 kqueue 機(jī)制,Linux 的

    2023年04月08日
    瀏覽(35)
  • GO 中高效 int 轉(zhuǎn)換 string 的方法與高性能源碼剖析

    GO 中高效 int 轉(zhuǎn)換 string 的方法與高性能源碼剖析

    Go 語言 中,將整數(shù)(int)轉(zhuǎn)換為字符串(string)是一項(xiàng)常見的操作。 本文將從逐步介紹幾種在 Go 中將 int 轉(zhuǎn)換為 string 的常見方法,并重點(diǎn)剖析這幾種方法在性能上的特點(diǎn)。另外,還會(huì)重點(diǎn)介紹 FormatInt 高效的算法實(shí)現(xiàn)。 最直接且常用的方法是使用 strconv 包中的 Itoa 函數(shù)。

    2024年01月21日
    瀏覽(28)
  • 深入了解 RabbitMQ:高性能消息中間件

    深入了解 RabbitMQ:高性能消息中間件

    在現(xiàn)代分布式系統(tǒng)中,消息隊(duì)列成為了實(shí)現(xiàn)系統(tǒng)間異步通信、削峰填谷以及解耦組件的重要工具。而RabbitMQ作為一個(gè)高效可靠的消息隊(duì)列解決方案,已經(jīng)成為許多企業(yè)廣泛采用的選擇。本文將介紹RabbitMQ的基本概念、主要特性以及常見應(yīng)用場(chǎng)景。 RabbitMQ 是一個(gè)開源的高性能、

    2024年02月08日
    瀏覽(31)
  • SambaNova 芯片:深入解析其架構(gòu)和高性能秘訣

    SambaNova 芯片:深入解析其架構(gòu)和高性能秘訣

    原創(chuàng)?AI蘇妲己? SambaNova——一家總部位于帕洛阿爾托的公司已經(jīng)籌集了超過10億美元的風(fēng)險(xiǎn)投資,不會(huì)直接向公司出售芯片。相反,它出售其定制技術(shù)堆棧的訪問權(quán)限,該堆棧具有專門為運(yùn)行最大的人工智能模型而設(shè)計(jì)的專有硬件和軟件。 最近,SambaNova宣布推出了其新型SN

    2024年04月10日
    瀏覽(25)
  • 深入詳解高性能消息隊(duì)列中間件 RabbitMQ

    深入詳解高性能消息隊(duì)列中間件 RabbitMQ

    ? 目錄 1、引言 2、什么是 RabbitMQ ? 3、RabbitMQ 優(yōu)勢(shì) 4、RabbitMQ 整體架構(gòu)剖析 4.1、發(fā)送消息流程 4.2、消費(fèi)消息流程 5、RabbitMQ 應(yīng)用 5.1、廣播 5.2、RPC VC++常用功能開發(fā)匯總(專欄文章列表,歡迎訂閱,持續(xù)更新...) https://blog.csdn.net/chenlycly/article/details/124272585 C++軟件異常排查從入

    2024年02月05日
    瀏覽(97)
  • “深入理解Redis:高性能緩存和數(shù)據(jù)存儲(chǔ)技術(shù)解析“

    標(biāo)題:深入理解Redis:高性能緩存和數(shù)據(jù)存儲(chǔ)技術(shù)解析 摘要:本文將深入探討Redis作為一種高性能緩存和數(shù)據(jù)存儲(chǔ)技術(shù)的原理和用法。我們將從Redis的基本特性入手,介紹其在緩存和數(shù)據(jù)存儲(chǔ)方面的優(yōu)勢(shì),并通過實(shí)際示例代碼展示如何使用Redis提升應(yīng)用程序的性能和可靠性。

    2024年02月16日
    瀏覽(20)
  • “深入理解Redis:高性能緩存與數(shù)據(jù)存儲(chǔ)的秘密“

    標(biāo)題:深入理解Redis:高性能緩存與數(shù)據(jù)存儲(chǔ)的秘密 在現(xiàn)代應(yīng)用程序的開發(fā)中,緩存和數(shù)據(jù)存儲(chǔ)是非常重要的組成部分。它們不僅可以提高應(yīng)用程序的性能,還可以減輕數(shù)據(jù)庫和網(wǎng)絡(luò)的負(fù)載。其中,Redis作為一種高性能的內(nèi)存數(shù)據(jù)存儲(chǔ)系統(tǒng),因其出色的性能和靈活的特性而備

    2024年02月16日
    瀏覽(26)
  • “深入解析Redis:高性能緩存與分布式數(shù)據(jù)存儲(chǔ)“

    標(biāo)題:深入解析Redis:高性能緩存與分布式數(shù)據(jù)存儲(chǔ) 摘要:本文將深入解析Redis,介紹其作為高性能緩存和分布式數(shù)據(jù)存儲(chǔ)的特點(diǎn)和功能,并提供示例代碼展示其使用方法。 正文: 一、引言 Redis是一個(gè)開源的內(nèi)存數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)系統(tǒng),它以其高性能、靈活的數(shù)據(jù)結(jié)構(gòu)以及豐富的

    2024年02月17日
    瀏覽(25)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包