那曲檬骨新材料有限公司

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux--IO多路復(fù)用(select,poll,epoll)

Rice嵌入式開(kāi)發(fā)技術(shù)分享 ? 2024-11-06 16:13 ? 次閱讀

IO多路復(fù)用——select,poll,epoll

IO多路復(fù)用是一種操作系統(tǒng)技術(shù),旨在提高系統(tǒng)處理多個(gè)輸入輸出操作的性能和資源利用率。與傳統(tǒng)的多線程或多進(jìn)程模型相比,IO多路復(fù)用避免了因阻塞IO而導(dǎo)致的資源浪費(fèi)和低效率問(wèn)題。它通過(guò)將多個(gè)IO操作合并到一個(gè)系統(tǒng)調(diào)用中,允許程序同時(shí)等待多個(gè)文件描述符(如sockets、文件句柄等)變?yōu)榭勺x或可寫(xiě)狀態(tài),然后再執(zhí)行實(shí)際的IO操作。

在IO多路復(fù)用的實(shí)現(xiàn)中,常用的系統(tǒng)調(diào)用包括select()poll()epoll()。這些機(jī)制允許程序監(jiān)視多個(gè)描述符,一旦某個(gè)描述符就緒(通常是讀就緒或?qū)懢途w),程序就會(huì)被通知進(jìn)行相應(yīng)的讀寫(xiě)操作。這個(gè)過(guò)程通常涉及兩個(gè)階段:

  1. 等待數(shù)據(jù)到達(dá):程序等待數(shù)據(jù)從IO設(shè)備傳輸?shù)絻?nèi)核空間。在這個(gè)階段,IO多路復(fù)用的系統(tǒng)調(diào)用會(huì)阻塞,直到至少有一個(gè)描述符準(zhǔn)備好進(jìn)行IO操作。
  2. 數(shù)據(jù)復(fù)制:當(dāng)一個(gè)或多個(gè)描述符就緒時(shí),程序負(fù)責(zé)將數(shù)據(jù)從內(nèi)核空間復(fù)制到用戶空間(進(jìn)程或線程的緩沖區(qū))。這第二個(gè)階段是實(shí)際的讀寫(xiě)操作,它在IO多路復(fù)用的上下文中是同步的,因?yàn)槌绦蛐枰约簣?zhí)行數(shù)據(jù)的讀寫(xiě)。

盡管select()poll()epoll()都是同步IO操作,但它們提供了一種有效的方式來(lái)處理并發(fā)IO,降低了系統(tǒng)開(kāi)銷,并提高了并發(fā)處理能力。與此不同,異步IO(AIO)模型進(jìn)一步簡(jiǎn)化了IO操作,因?yàn)樗试S操作系統(tǒng)自動(dòng)處理數(shù)據(jù)從內(nèi)核到用戶空間的復(fù)制過(guò)程,無(wú)需程序顯式調(diào)用讀寫(xiě)操作。這意味著在異步IO模型中,讀寫(xiě)操作由操作系統(tǒng)在后臺(tái)完成,從而進(jìn)一步提高了應(yīng)用程序的效率和響應(yīng)性。

select

概述

  • 系統(tǒng)提供了select函數(shù)來(lái)實(shí)現(xiàn)多路復(fù)用輸入/輸出模型
  • select系統(tǒng)調(diào)用是用來(lái)讓我們的程序監(jiān)視多個(gè)文件描述符的狀態(tài)變化的
  • 程序會(huì)停在select函數(shù)等待,直到被監(jiān)視的文件描述符有一個(gè)或者多個(gè)發(fā)生了狀態(tài)改變。

函數(shù)

intselect(intnfds,fd_set*readfds,fd_set*writefds,
fd_set*exceptfds,structtimeval*timeout)
;
  • 函數(shù)參數(shù):
參數(shù)說(shuō)明
nfds是需要監(jiān)視的最大的文件描述符值+1
readfds需要檢測(cè)的可讀文件描述符的集合
writefds需要檢測(cè)的可寫(xiě)文件描述符的集合
exceptfds需要檢測(cè)的異常文件描述符的集合
timeout當(dāng)timeout等于NULL:則表示select()沒(méi)有timeout,select將一直被阻塞,直到某個(gè)文件描述符上發(fā)生了事件;
當(dāng)timeout為0:僅檢測(cè)描述符集合的狀態(tài),然后立即返回,并不等待外部事件的發(fā)生。
當(dāng)timeout為特定的時(shí)間值:如果在指定的時(shí)間段里沒(méi)有事件發(fā)生,select將超時(shí)返回。
返回——
> 0返回文件描述詞狀態(tài)已改變的個(gè)數(shù)
== 0代表在描述詞狀態(tài)改變前已超過(guò)timeout時(shí)間,沒(méi)有返回
< 0錯(cuò)誤原因存于errno,此時(shí)參數(shù)readfds,writefds, exceptfds和timeout的值變成不可預(yù)測(cè),錯(cuò)誤值可能為:
EBADF:文件描述詞為無(wú)效的或該文件已關(guān)閉
EINTR:此調(diào)用被信號(hào)所中斷
EINVAL:參數(shù)n 為負(fù)值
ENOMEM:核心內(nèi)存不足
  • 其中:可讀,可寫(xiě),異常文件描述符的集合是一個(gè)fd_set類型,fd_set是系統(tǒng)提供的位圖類型,位圖的位置是否是1,表示是否關(guān)系該事件。例如:
輸入時(shí):假如我們要關(guān)心0123文件描述符
00000000->00001111比特位的位置,表示文件描述符的編號(hào)
比特位的內(nèi)容0or1表示是否需要內(nèi)核關(guān)心
輸出時(shí):
00000100->此時(shí)表示文件描述符的編號(hào)
比特位的內(nèi)容0or1哪些用戶關(guān)心的fd上面的讀事件已經(jīng)就緒了,這里表示2描述符就緒了
  • 系統(tǒng)提供了關(guān)于fd_set的接口,便于我們使用位圖:
voidFD_CLR(intfd,fd_set*set);//用來(lái)清除描述詞組set中相關(guān)fd的位
intFD_ISSET(intfd,fd_set*set);//用來(lái)測(cè)試描述詞組set中相關(guān)fd的位是否為真
voidFD_SET(intfd,fd_set*set);//用來(lái)設(shè)置描述詞組set中相關(guān)fd的位
voidFD_ZERO(fd_set*set);//用來(lái)清除描述詞組set的全部位

執(zhí)行流程:

  1. 執(zhí)行fd_set set; FD_ZERO(&set);則set用位表示是0000,0000。
  2. 若fd=5,執(zhí)行FD_SET(fd,&set);后set變?yōu)?001,0000(第5位置為1) 。
  3. 若再加入fd=2,fd=1,則set變?yōu)?001,0011 。
  4. 執(zhí)行select(6,&set,0,0,0)阻塞等待,表示最大文件描述符+1是6,監(jiān)控可讀事件,立即返回。
  5. 若fd=1,fd=2上都發(fā)生可讀事件,則select返回,此時(shí)set變?yōu)?000,0011。注意:沒(méi)有事件發(fā)生的fd=5被清空。

優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn):
  1. 可監(jiān)控的文件描述符個(gè)數(shù)取決與sizeof(fd_set)的值。一般大小是1024,但是fd_set的大小可以調(diào)整。
  2. 將fd加入select監(jiān)控集的同時(shí),還要再使用一個(gè)數(shù)據(jù)結(jié)構(gòu)array保存放到select監(jiān)控集中的fd。①是用于再select 返回后,array作為源數(shù)據(jù)和fd_set進(jìn)行FD_ISSET判斷。②是select返回后會(huì)把以前加入的但并無(wú)事件發(fā)生的fd清空,則每次開(kāi)始select前都要重新從array取得fd逐一加入(FD_ZERO最先),掃描array的同時(shí)取得fd最大值maxfd,用于select的第一個(gè)參數(shù)。
  • 缺點(diǎn):
    1. 每次調(diào)用select, 都需要手動(dòng)設(shè)置fd集合, 從接口使用角度來(lái)說(shuō)也非常不便。
    2. 每次調(diào)用select,都需要把fd集合從用戶態(tài)拷貝到內(nèi)核態(tài),這個(gè)開(kāi)銷在fd很多時(shí)會(huì)很大。
    3. 同時(shí)每次調(diào)用select都需要在內(nèi)核遍歷傳遞進(jìn)來(lái)的所有fd,這個(gè)開(kāi)銷在fd很多時(shí)也很大。
    4. select支持的文件描述符數(shù)量太小。

    實(shí)例

    #include
    #include
    #include
    #include
    #include
    #include

    conststaticintMAXLINE=1024;
    conststaticintSERV_PORT=10001;

    intmain()
    {
    inti,maxi,maxfd,listenfd,connfd,sockfd;
    /*nready描述字的數(shù)量*/
    intnready,client[FD_SETSIZE];
    intn;
    /*創(chuàng)建描述字集合,由于select函數(shù)會(huì)把未有事件發(fā)生的描述字清零,所以我們?cè)O(shè)置兩個(gè)集合*/
    fd_setrset,allset;
    charbuf[MAXLINE];
    socklen_tclilen;
    structsockaddr_incliaddr,servaddr;
    /*創(chuàng)建socket*/
    listenfd=socket(AF_INET,SOCK_STREAM,0);
    /*定義sockaddr_in*/
    memset(&servaddr,0,sizeof(servaddr));
    servaddr.sin_family=AF_INET;
    servaddr.sin_port=htons(SERV_PORT);
    servaddr.sin_addr.s_addr=htonl(INADDR_ANY);

    bind(listenfd,(structsockaddr*)&servaddr,sizeof(servaddr));
    listen(listenfd,100);
    /*listenfd是第一個(gè)描述字*/
    /*最大的描述字,用于select函數(shù)的第一個(gè)參數(shù)*/
    maxfd=listenfd;
    /*client的數(shù)量,用于輪詢*/
    maxi=-1;
    /*init*/
    for(i=0;iclient[i]=-1;
    FD_ZERO(&allset);
    FD_SET(listenfd,&allset);

    for(;;)
    {
    rset=allset;
    /*只select出用于讀的描述字,阻塞無(wú)timeout*/
    nready=select(maxfd+1,&rset,NULL,NULL,NULL);
    if(FD_ISSET(listenfd,&rset))
    {
    clilen=sizeof(cliaddr);
    connfd=accept(listenfd,(structsockaddr*)&cliaddr,&clilen);
    /*尋找第一個(gè)能放置新的描述字的位置*/
    for(i=0;i{
    if(client[i]<0)
    {
    client[i]=connfd;
    break;
    }
    }
    /*找不到,說(shuō)明client已經(jīng)滿了*/
    if(i==FD_SETSIZE)
    {
    printf("Toomanyclients,overstack.\n");
    return-1;
    }
    FD_SET(connfd,&allset);//設(shè)置fd
    /*更新相關(guān)參數(shù)*/
    if(connfd>maxfd)maxfd=connfd;
    if(i>maxi)maxi=i;
    if(nready<=1)continue;
    elsenready--;
    }

    for(i=0;i<=maxi?;?i++)
    {
    if(client[i]<0)continue;
    sockfd=client[i];
    if(FD_ISSET(sockfd,&rset))
    {
    n=read(sockfd,buf,MAXLINE);
    if(n==0)
    {
    /*當(dāng)對(duì)方關(guān)閉的時(shí)候,server關(guān)閉描述字,并將set的sockfd清空*/
    close(sockfd);
    FD_CLR(sockfd,&allset);
    client[i]=-1;
    }
    else
    {
    buf[n]='\0';
    printf("Socket%dsaid:%s\n",sockfd,buf);
    write(sockfd,buf,n);//Writebacktoclient
    }
    nready--;
    if(nready<=0)break;
    }
    }

    }
    return0;
    }

    poll

    概述

    • poll和select實(shí)現(xiàn)原理基本類似
    • poll只為了解決select的兩個(gè)硬傷:①等待的fd是有上限的,(底層類似鏈表儲(chǔ)存實(shí)現(xiàn),而不是位圖)。②每次要對(duì)關(guān)心的fd進(jìn)行事件重置,(pollfd結(jié)構(gòu)包含了要監(jiān)視的event和發(fā)生的event,使用前后不用初始化fd_set)。

    函數(shù)

    intpoll(structpollfd*fds,nfds_tnfds,inttimeout);

    //pollfd結(jié)構(gòu)
    structpollfd{
    intfd;/*filedescriptor*/
    shortevents;/*requestedevents*/
    shortrevents;/*returnedevents*/
    };
    • 函數(shù)參數(shù):
    參數(shù)說(shuō)明
    fds是一個(gè)poll函數(shù)監(jiān)聽(tīng)的結(jié)構(gòu)列表. 每一個(gè)元素中, 包含了三部分內(nèi)容: 文件描述符, 監(jiān)聽(tīng)的事件集合, 返回的事件集合
    nfds表示fds數(shù)組的長(zhǎng)度
    timeout表示poll函數(shù)的超時(shí)時(shí)間, 單位是毫秒(ms)
    返回——
    > 0表示poll由于監(jiān)聽(tīng)的文件描述符就緒而返回
    == 0表示poll函數(shù)等待超時(shí)
    < 0表示出錯(cuò)

    優(yōu)缺點(diǎn)

    • 優(yōu)點(diǎn):
    1. pollfd結(jié)構(gòu)包含了要監(jiān)視的event和發(fā)生的event,不再使用select“參數(shù)-值”傳遞的方式. 接口使用比 select更方便。
    2. poll并沒(méi)有最大數(shù)量限制 (但是數(shù)量過(guò)大后性能也是會(huì)下降)。
  • 缺點(diǎn):
    1. 和select函數(shù)一樣,poll返回后,需要輪詢pollfd來(lái)獲取就緒的描述符。
    2. 每次調(diào)用poll都需要把大量的pollfd結(jié)構(gòu)從用戶態(tài)拷貝到內(nèi)核中。
    3. 同時(shí)連接的大量客戶端在一時(shí)刻可能只有很少的處于就緒狀態(tài), 因此隨著監(jiān)視的描述符數(shù)量的增長(zhǎng), 其效率也會(huì)線性下降。

    實(shí)例

    #include
    #include
    #include
    #include
    #include
    #include

    #defineMAXLINE1024
    #defineOPEN_MAX16//一些系統(tǒng)會(huì)定義這些宏
    #defineSERV_PORT10001

    intmain()
    {
    inti,maxi,listenfd,connfd,sockfd;
    intnready;
    intn;
    charbuf[MAXLINE];
    socklen_tclilen;
    structpollfdclient[OPEN_MAX];

    structsockaddr_incliaddr,servaddr;
    listenfd=socket(AF_INET,SOCK_STREAM,0);
    memset(&servaddr,0,sizeof(servaddr));
    servaddr.sin_family=AF_INET;
    servaddr.sin_port=htons(SERV_PORT);
    servaddr.sin_addr.s_addr=htonl(INADDR_ANY);

    bind(listenfd,(structsockaddr*)&servaddr,sizeof(servaddr));
    listen(listenfd,10);
    client[0].fd=listenfd;
    client[0].events=POLLRDNORM;
    for(i=1;i{
    client[i].fd=-1;
    }
    maxi=0;

    for(;;)
    {
    nready=poll(client,maxi+1,INFTIM);
    if(client[0].revents&POLLRDNORM)
    {
    clilen=sizeof(cliaddr);
    connfd=accept(listenfd,(structsockaddr*)&cliaddr,&clilen);
    for(i=1;i{
    if(client[i].fd<0)
    {
    client[i].fd=connfd;
    client[i].events=POLLRDNORM;
    break;
    }
    }
    if(i==OPEN_MAX)
    {
    printf("toomanyclients!\n");
    }
    if(i>maxi)maxi=i;
    nready--;
    if(nready<=0)continue;
    }

    for(i=1;i<=maxi;i++)
    {
    if(client[i].fd<0)continue;
    sockfd=client[i].fd;
    if(client[i].revents&(POLLRDNORM|POLLERR))
    {
    n=read(client[i].fd,buf,MAXLINE);
    if(n<=0)
    {
    close(client[i].fd);
    client[i].fd=-1;
    }
    else
    {
    buf[n]='\0';
    printf("Socket%dsaid:%s\n",sockfd,buf);
    write(sockfd,buf,n);//Writebacktoclient
    }
    nready--;
    if(nready<=0)break;//nomorereadabledescriptors
    }
    }
    }
    return0;
    }

    epoll

    概述

    • epoll:是為處理大批量句柄而作了改進(jìn)的poll(真的是大改進(jìn))
    • epoll是IO多路復(fù)用技術(shù),在實(shí)現(xiàn)上維護(hù)了一個(gè)用于返回觸發(fā)事件的Socket的鏈表和一個(gè)記錄監(jiān)聽(tīng)事件的紅黑樹(shù),epoll的高效體現(xiàn)在:
    1. 對(duì)監(jiān)聽(tīng)事件的修改是 logN(紅黑樹(shù))。
    2. 用戶程序無(wú)需遍歷所有的Socket(發(fā)生事件的Socket被放到鏈表中直接返回)。
    3. 內(nèi)核無(wú)需遍歷所有的套接字,內(nèi)核使用回調(diào)函數(shù)在事件發(fā)生時(shí)直接轉(zhuǎn)到對(duì)應(yīng)的處理函數(shù)。

    函數(shù)

    • epoll_create:創(chuàng)建一個(gè)epoll的句柄,用完之后, 必須調(diào)用close()關(guān)閉。
    intepoll_create(intsize);
    • epoll_ctl:它不同于select()是在監(jiān)聽(tīng)事件時(shí)告訴內(nèi)核要監(jiān)聽(tīng)什么類型的事件, 而是在這里先注冊(cè)要監(jiān)聽(tīng)的事件類型。
    intepoll_ctl(intepfd,intop,intfd,structepoll_event*event);

    typedefunionepoll_data
    {
    void*ptr;
    intfd;
    uint32_tu32;
    uint64_tu64;
    }epoll_data_t;

    structepoll_event
    {

    uint32_tevents;
    epoll_data_tdata;
    }EPOLL_PACKED;
    1. events參數(shù)的宏集合:
    EPOLLIN :表示對(duì)應(yīng)的文件描述符可以讀(包括對(duì)端SOCKET正常關(guān)閉)。
    EPOLLOUT :表示對(duì)應(yīng)的文件描述符可以寫(xiě)。
    EPOLLPRI :表示對(duì)應(yīng)的文件描述符有緊急的數(shù)據(jù)可讀(這里應(yīng)該表示有帶外數(shù)據(jù)到來(lái))。
    EPOLLERR :表示對(duì)應(yīng)的文件描述符發(fā)生錯(cuò)誤。
    EPOLLHUP :表示對(duì)應(yīng)的文件描述符被掛斷。
    EPOLLET :將EPOLL設(shè)為邊緣觸發(fā)(Edge Triggered)模式, 這是相對(duì)于水平觸發(fā)(Level Triggered)來(lái)說(shuō)的。
    EPOLLONESHOT:只監(jiān)聽(tīng)一次事件, 當(dāng)監(jiān)聽(tīng)完這次事件之后, 如果還需要繼續(xù)監(jiān)聽(tīng)這個(gè)socket的話, 需要再次把這個(gè)socket加入到EPOLL隊(duì)列里
    1. 函數(shù)參數(shù):
    參數(shù)說(shuō)明
    epfdepoll_create()的返回值(epoll的句柄)
    op表示動(dòng)作,用三個(gè)宏來(lái)表示:
    EPOLL_CTL_ADD :注冊(cè)新的fd到epfd中
    EPOLL_CTL_MOD :修改已經(jīng)注冊(cè)的fd的監(jiān)聽(tīng)事件
    EPOLL_CTL_DEL :從epfd中刪除一個(gè)fd
    fd需要監(jiān)聽(tīng)的fd
    event內(nèi)核需要監(jiān)聽(tīng)的事件
    • epoll_wait:收集在epoll監(jiān)控的事件中已經(jīng)發(fā)送的事件
    intepoll_wait(intepfd,structepoll_event*events,intmaxevents,inttimeout);
    參數(shù)說(shuō)明
    epfdepoll_create()的返回值(epoll的句柄)
    events是分配好的epoll_event結(jié)構(gòu)體數(shù)組。epoll將會(huì)把發(fā)生的事件賦值到events數(shù)組中 (events不可以是空指針,內(nèi)核只負(fù)責(zé)把數(shù)據(jù)復(fù)制到這個(gè)events數(shù)組中,不會(huì)去幫助我們?cè)谟脩魬B(tài)中分配內(nèi)存)
    maxevents通知內(nèi)核這個(gè)events有多大,這個(gè)maxevents的值不能大于創(chuàng)建epoll_create()時(shí)的size
    timeout超時(shí)時(shí)間 (毫秒,0會(huì)立即返回,-1是永久阻塞)
    返回——
    > 0返回對(duì)應(yīng)I/O上已準(zhǔn)備好的文件描述符數(shù)目
    == 0表示已超時(shí)
    < 0表示失敗

    執(zhí)行流程:

    1. 當(dāng)某一進(jìn)程調(diào)用epoll_create方法時(shí),Linux內(nèi)核會(huì)創(chuàng)建一個(gè)eventpoll結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體中有兩個(gè)成員與epoll的使用方式密切相關(guān)。
    2. 每一個(gè)epoll對(duì)象都有一個(gè)獨(dú)立的eventpoll結(jié)構(gòu)體,用于存放通過(guò)epoll_ctl方法向epoll對(duì)象中添加進(jìn)來(lái)的事件。
    3. 這些事件都會(huì)掛載在紅黑樹(shù)中,如此,重復(fù)添加的事件就可以通過(guò)紅黑樹(shù)而高效的識(shí)別出來(lái)(紅黑樹(shù)的插入時(shí)間效率是lgn,其中n為樹(shù)的高度)。
    4. 而所有添加到epoll中的事件都會(huì)與設(shè)備(網(wǎng)卡)驅(qū)動(dòng)程序建立回調(diào)關(guān)系,也就是說(shuō),當(dāng)響應(yīng)的事件發(fā)生時(shí)會(huì)調(diào)用這個(gè)回調(diào)方法。
    5. 這個(gè)回調(diào)方法在內(nèi)核中叫ep_poll_callback,它會(huì)將發(fā)生的事件添加到rdlist雙鏈表中。
    6. 在epoll中,對(duì)于每一個(gè)事件,都會(huì)建立一個(gè)epitem結(jié)構(gòu)體。
    7. 當(dāng)調(diào)用epoll_wait檢查是否有事件發(fā)生時(shí),只需要檢查eventpoll對(duì)象中的rdlist雙鏈表中是否有epitem元素即可。
    8. 如果rdlist不為空,則把發(fā)生的事件復(fù)制到用戶態(tài),同時(shí)將事件數(shù)量返回給用戶. 這個(gè)操作的時(shí)間復(fù)雜度是O(1)。

    優(yōu)缺點(diǎn)

    • 優(yōu)點(diǎn):
    1. 接口使用方便: 雖然拆分成了三個(gè)函數(shù),但是反而使用起來(lái)更方便高效,不需要每次循環(huán)都設(shè)置關(guān)注的文件描述符,也做到了輸入輸出參數(shù)分離開(kāi)。
    2. 數(shù)據(jù)拷貝輕量: 只在合適的時(shí)候調(diào)用 EPOLL_CTL_ADD 將文件描述符結(jié)構(gòu)拷貝到內(nèi)核中,這個(gè)操作并不頻繁(而select/poll都是每次循環(huán)都要進(jìn)行拷貝)。
    3. 事件回調(diào)機(jī)制: 避免使用遍歷,而是使用回調(diào)函數(shù)的方式,將就緒的文件描述符結(jié)構(gòu)加入到就緒隊(duì)列中,epoll_wait 返回直接訪問(wèn)就緒隊(duì)列就知道哪些文件描述符就緒,這個(gè)操作時(shí)間復(fù)雜度O(1),即使文件描述符數(shù)目很多,效率也不會(huì)受到影響。
    4. 沒(méi)有數(shù)量限制: 文件描述符數(shù)目無(wú)上限。
  • 缺點(diǎn):
    1. 不能跨平臺(tái),epoll 是 Linux 特有的 API,不太容易移植到其他操作系統(tǒng)上

    實(shí)例

    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include

    #defineMAXLINE1024
    #defineOPEN_MAX16//一些系統(tǒng)會(huì)定義這些宏
    #defineSERV_PORT10001

    intmain()
    {
    inti,maxi,listenfd,connfd,sockfd,epfd,nfds;
    intn;
    charbuf[MAXLINE];
    structepoll_eventev,events[20];
    socklen_tclilen;
    structpollfdclient[OPEN_MAX];

    structsockaddr_incliaddr,servaddr;
    listenfd=socket(AF_INET,SOCK_STREAM,0);
    memset(&servaddr,0,sizeof(servaddr));
    servaddr.sin_family=AF_INET;
    servaddr.sin_port=htons(SERV_PORT);
    servaddr.sin_addr.s_addr=htonl(INADDR_ANY);

    bind(listenfd,(structsockaddr*)&servaddr,sizeof(servaddr));
    listen(listenfd,10);

    epfd=epoll_create(256);
    ev.data.fd=listenfd;
    ev.events=EPOLLIN|EPOLLET;
    epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);

    for(;;)
    {
    nfds=epoll_wait(epfd,events,20,500);
    for(i=0;i{
    if(listenfd==events[i].data.fd)
    {
    clilen=sizeof(cliaddr);
    connfd=accept(listenfd,(structsockaddr*)&cliaddr,&clilen);
    if(connfd0)
    {
    perror("connfd);
    exit(1);
    }
    ev.data.fd=connfd;
    ev.events=EPOLLIN|EPOLLET;
    epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);
    }
    elseif(events[i].events&EPOLLIN)
    {
    if((sockfd=events[i].data.fd)0
    )
    continue;
    n=recv(sockfd,buf,MAXLINE,0);
    if(n<=?0)
    {
    close(sockfd);
    events[i].data.fd=-1;
    }
    else
    {
    buf[n]='\0';
    printf("Socket%dsaid:%s\n",sockfd,buf);
    ev.data.fd=sockfd;
    ev.events=EPOLLOUT|EPOLLET;
    epoll_ctl(epfd,EPOLL_CTL_MOD,connfd,&ev);
    }
    }
    elseif(events[i].events&EPOLLOUT)
    {
    sockfd=events[i].data.fd;
    send(sockfd,"Hello!",7,0);

    ev.data.fd=sockfd;
    ev.events=EPOLLIN|EPOLLET;
    epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
    }
    else
    {
    printf("Thisisnotavaible!");
    }
    }
    }
    close(epfd);
    return0;
    }

    總結(jié)

    • selectpoll 是兩種傳統(tǒng)的 I/O 多路復(fù)用技術(shù),它們?cè)试S服務(wù)器應(yīng)用程序同時(shí)監(jiān)控多個(gè)網(wǎng)絡(luò)連接,以便在連接準(zhǔn)備就緒時(shí)進(jìn)行讀寫(xiě)操作。盡管這兩種技術(shù)在處理大量并發(fā)連接時(shí)非常有用,但隨著連接數(shù)的增加,它們的性能會(huì)逐漸下降,因?yàn)樗鼈冃枰诿看握{(diào)用時(shí)遍歷整個(gè)文件描述符集合,這在連接數(shù)非常多時(shí)會(huì)導(dǎo)致效率問(wèn)題。

    • 為了解決這個(gè)問(wèn)題,epoll 作為 selectpoll 的一種改進(jìn)方案,在 Linux 系統(tǒng)中被引入。epoll 提供了一種更為高效的事件驅(qū)動(dòng)模型,它可以顯著提高處理大量并發(fā)連接的性能。與 selectpoll 不同,epoll 不會(huì)對(duì)整個(gè)文件描述符集合進(jìn)行線性遍歷,而是使用一組特殊的數(shù)據(jù)結(jié)構(gòu)來(lái)跟蹤哪些文件描述符已經(jīng)準(zhǔn)備好 I/O 操作。這種機(jī)制使得 epoll 能夠快速地通知應(yīng)用程序哪些連接是活躍的,而無(wú)需對(duì)所有連接進(jìn)行不必要的檢查。

    • epoll 的另一個(gè)優(yōu)點(diǎn)是它能夠處理大量文件描述符而不會(huì)顯著增加資源消耗,這使得它非常適合需要處理成千上萬(wàn)甚至更多并發(fā)連接的高性能網(wǎng)絡(luò)服務(wù)器。因此,在 Linux 系統(tǒng)上,epoll 常被視為 selectpoll 的替代方案,特別是在構(gòu)建高性能網(wǎng)絡(luò)應(yīng)用程序時(shí)。

    - END -


    往期推薦:點(diǎn)擊圖片即可跳轉(zhuǎn)閱讀



    linux--sysfs文件系統(tǒng)



    YY3568 Debian11+RT-Thread混合內(nèi)核部署



    YY3568多核異構(gòu)(Linux+RT-Thread)--啟動(dòng)流程




    原文標(biāo)題:Linux--IO多路復(fù)用(select,poll,epoll)

    文章出處:【微信公眾號(hào):Rice 嵌入式開(kāi)發(fā)技術(shù)分享】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

    聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
    收藏 人收藏

      評(píng)論

      相關(guān)推薦

      多路復(fù)用模擬輸入應(yīng)用中使用ADS8411

      電子發(fā)燒友網(wǎng)站提供《在多路復(fù)用模擬輸入應(yīng)用中使用ADS8411.pdf》資料免費(fèi)下載
      發(fā)表于 10-22 09:32 ?0次下載
      在<b class='flag-5'>多路復(fù)用</b>模擬輸入應(yīng)用中使用ADS8411

      AM17xx引腳多路復(fù)用實(shí)用程序

      電子發(fā)燒友網(wǎng)站提供《AM17xx引腳多路復(fù)用實(shí)用程序.pdf》資料免費(fèi)下載
      發(fā)表于 10-14 10:22 ?0次下載
      AM17xx引腳<b class='flag-5'>多路復(fù)用</b>實(shí)用程序

      多路復(fù)用器應(yīng)用中的防護(hù)

      電子發(fā)燒友網(wǎng)站提供《多路復(fù)用器應(yīng)用中的防護(hù).pdf》資料免費(fèi)下載
      發(fā)表于 09-21 10:47 ?0次下載
      <b class='flag-5'>多路復(fù)用</b>器應(yīng)用中的防護(hù)

      基于帶寬的無(wú)源多路復(fù)用器>Ron

      電子發(fā)燒友網(wǎng)站提供《基于帶寬的無(wú)源多路復(fù)用器>Ron.pdf》資料免費(fèi)下載
      發(fā)表于 09-21 10:46 ?0次下載
      基于帶寬的無(wú)源<b class='flag-5'>多路復(fù)用</b>器>Ron

      電源多路復(fù)用器基礎(chǔ)知識(shí)

      電子發(fā)燒友網(wǎng)站提供《電源多路復(fù)用器基礎(chǔ)知識(shí).pdf》資料免費(fèi)下載
      發(fā)表于 09-21 10:21 ?0次下載
      電源<b class='flag-5'>多路復(fù)用</b>器基礎(chǔ)知識(shí)

      為MCU擴(kuò)展選擇正確的多路復(fù)用

      電子發(fā)燒友網(wǎng)站提供《為MCU擴(kuò)展選擇正確的多路復(fù)用器.pdf》資料免費(fèi)下載
      發(fā)表于 09-18 11:52 ?0次下載
      為MCU擴(kuò)展選擇正確的<b class='flag-5'>多路復(fù)用</b>器

      如何使用多路復(fù)用器處理高壓共模應(yīng)用

      電子發(fā)燒友網(wǎng)站提供《如何使用多路復(fù)用器處理高壓共模應(yīng)用.pdf》資料免費(fèi)下載
      發(fā)表于 09-11 11:34 ?0次下載
      如何使用<b class='flag-5'>多路復(fù)用</b>器處理高壓共模應(yīng)用

      多路復(fù)用器將取代繼電器應(yīng)用說(shuō)明

      電子發(fā)燒友網(wǎng)站提供《多路復(fù)用器將取代繼電器應(yīng)用說(shuō)明.pdf》資料免費(fèi)下載
      發(fā)表于 09-11 10:05 ?0次下載
      <b class='flag-5'>多路復(fù)用</b>器將取代繼電器應(yīng)用說(shuō)明

      電源多路復(fù)用器電路設(shè)計(jì)指南

      電子發(fā)燒友網(wǎng)站提供《電源多路復(fù)用器電路設(shè)計(jì)指南.pdf》資料免費(fèi)下載
      發(fā)表于 07-13 09:33 ?0次下載

      什么是多路復(fù)用器?它有哪些作用和應(yīng)用?

      在現(xiàn)代通信與數(shù)據(jù)處理領(lǐng)域,多路復(fù)用器(Multiplexer,簡(jiǎn)稱MUX)作為一種關(guān)鍵設(shè)備,發(fā)揮著不可替代的作用。它能夠?qū)⒍鄠€(gè)輸入信號(hào)選擇性地合并到一個(gè)輸出信號(hào)中,從而實(shí)現(xiàn)了對(duì)通信信道的高效利用
      的頭像 發(fā)表于 05-23 16:38 ?4296次閱讀

      頻分多路復(fù)用和時(shí)分多路復(fù)用的區(qū)別有哪些

      頻分多路復(fù)用(FDM)和時(shí)分多路復(fù)用(TDM)是兩種主要的多路復(fù)用技術(shù),它們?cè)谕ㄐ畔到y(tǒng)中扮演著至關(guān)重要的角色。
      的頭像 發(fā)表于 05-07 15:24 ?3192次閱讀

      多路復(fù)用技術(shù)主要有幾種類型?它們各有什么特點(diǎn)?

      多路復(fù)用技術(shù)主要有幾種類型?它們各有什么特點(diǎn)? 多路復(fù)用技術(shù)主要有以下幾種類型:進(jìn)程多路復(fù)用、I/O多路復(fù)用、信號(hào)驅(qū)動(dòng)I/O和異步I/O。每種類型都有其特點(diǎn)和應(yīng)用場(chǎng)景。 1. 進(jìn)程
      的頭像 發(fā)表于 03-28 15:36 ?3166次閱讀

      一文詳解多路復(fù)用的類型

      多路復(fù)用最初是在電話中發(fā)展起來(lái)的。多個(gè)信號(hào)被組合在一起,通過(guò)一根電纜發(fā)送。
      的頭像 發(fā)表于 03-05 15:44 ?3711次閱讀
      一文詳解<b class='flag-5'>多路復(fù)用</b>的類型

      多路復(fù)用的原理 為什么要多路復(fù)用多路復(fù)用技術(shù)的應(yīng)用

      在計(jì)算機(jī)網(wǎng)絡(luò)中,多路復(fù)用是一種重要的通信技術(shù),它允許多個(gè)信號(hào)通過(guò)同一個(gè)通信信道進(jìn)行傳輸。
      的頭像 發(fā)表于 03-05 15:09 ?3117次閱讀
      <b class='flag-5'>多路復(fù)用</b>的原理 為什么要<b class='flag-5'>多路復(fù)用</b>?<b class='flag-5'>多路復(fù)用</b>技術(shù)的應(yīng)用

      頻分多路復(fù)用的原理 頻分多路復(fù)用方式的分類

      頻分多路復(fù)用(Frequency-division multiplexing,F(xiàn)DM),是指載波帶寬被劃分為多種不同頻帶的子信道,每個(gè)子信道可以并行傳送一路信號(hào)的一種多路復(fù)用技術(shù)。
      的頭像 發(fā)表于 03-05 14:10 ?1679次閱讀
      頻分<b class='flag-5'>多路復(fù)用</b>的原理 頻分<b class='flag-5'>多路復(fù)用</b>方式的分類
      百家乐常用公式| 水果机规律| 百家乐长龙怎么预判| 百家乐官网双峰县| 百家乐官网游戏全讯网2| 荃湾区| 百乐坊娱乐城官网| 哪里有百家乐代理| 风水24山向| 摩纳哥百家乐官网的玩法技巧和规则 | 百家乐视频游戏双扣| 杨公24山属性| 盛大百家乐官网的玩法技巧和规则| 网上百家乐官网记牌软件| 哪个百家乐官网投注好| 百家乐官网赌博怎么玩| 双城市| 晋江市| 丹棱县| 大发888娱乐城下载最新版| 百家乐破解分| KK百家乐的玩法技巧和规则 | 香港六合彩图库| 九乐棋牌官网| 易发棋牌官网| bet365官方| 大西洋娱乐城开户地址| 大发888怎样存款| 澳门太阳城娱乐城| 大发888充值100元| 大发888出纳柜台 2014| 大发888下载大发888游戏平台| 大发888手机版| 碧桂园太阳城户型图| 大发888注册官方网站| 大发888娱乐城赢钱| 大发888备用a99.com| 大发888完整客户端| 大发888赌场是干什么的| 澳门百家乐规则视频| 百家乐暗红色桌布|