IO 多路復(fù)用(todo)

IO多路復(fù)用介紹

  • IO 多路復(fù)用是一種同步IO模型,實(shí)現(xiàn)一個線程可以監(jiān)視多個文件句柄娄帖;
  • 一旦某個文件句柄就緒也祠,就能夠通知應(yīng)用程序進(jìn)行相應(yīng)的讀寫操作;
  • 沒有文件句柄就緒就會阻塞應(yīng)用程序近速,交出CPU诈嘿。
  • IO多路復(fù)用的三種實(shí)現(xiàn)方式 select/poll/epoll

epoll

epoll的全稱是eventpoll,它是基于event事件進(jìn)行實(shí)現(xiàn)的数焊,是linux特有的I/O復(fù)用函數(shù)永淌。

  • 有點(diǎn)
    文件描述符沒有限制(使用鏈表存儲)
  • 三個核心函數(shù)
    epoll_create(建立一個epoll對象)
    epoll_ctl(epoll的事件注冊函數(shù))
    epoll_wait(等待事件的產(chǎn)生,類似于select()調(diào)用)

int epoll_create(int size)

在內(nèi)核中創(chuàng)建epoll實(shí)例并返回一個epoll文件描述符佩耳。 在最初的實(shí)現(xiàn)中遂蛀,調(diào)用者通過 size 參數(shù)告知內(nèi)核需要監(jiān)聽的文件描述符數(shù)量。如果監(jiān)聽的文件描述符數(shù)量超過 size, 則內(nèi)核會自動擴(kuò)容干厚。而現(xiàn)在 size 已經(jīng)沒有這種語義了李滴,但是調(diào)用者調(diào)用時 size 依然必須大于 0螃宙,以保證后向兼容性。

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

向 epfd 對應(yīng)的內(nèi)核epoll 實(shí)例添加所坯、修改或刪除對 fd 上事件 event 的監(jiān)聽谆扎。

  • epfd:epoll_create的返回值
  • op:表示動作
    EPOLL_CTL_ADD:添加新的事件到文件描述符。
    EPOLL_CTL_MOD: 修改文件描述符上的事件類型
    EPOLL_CTL_DEL: 刪除一個事件

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

等待事件
當(dāng) timeout 為 0 時芹助,epoll_wait 永遠(yuǎn)會立即返回堂湖。而 timeout 為 -1 時,epoll_wait 會一直阻塞直到任一已注冊的事件變?yōu)榫途w状土。當(dāng) timeout 為一正整數(shù)時无蜂,epoll 會阻塞直到計(jì)時 timeout 毫秒終了或已注冊的事件變?yōu)榫途w。因?yàn)閮?nèi)核調(diào)度延遲蒙谓,阻塞的時間可能會略微超過 timeout 毫秒斥季。

  • demo
#include<stdio.h>
#include<arpa/inet.h>
#include<sys/epoll.h>
#include<unistd.h>
#include<ctype.h>
#define MAXLEN 1024
#define SERV_PORT 8000
#define MAX_OPEN_FD 1024

int main(int argc,char *argv[])
{
    int  listenfd,connfd,efd,ret;
    char buf[MAXLEN];
    struct sockaddr_in cliaddr,servaddr;
    socklen_t clilen = sizeof(cliaddr);
    struct epoll_event tep,ep[MAX_OPEN_FD];

    listenfd = socket(AF_INET,SOCK_STREAM,0);

    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);
    bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
    listen(listenfd,20);
    // 創(chuàng)建一個epoll fd
    efd = epoll_create(MAX_OPEN_FD);
    tep.events = EPOLLIN;tep.data.fd = listenfd;
    // 把監(jiān)聽socket 先添加到efd中
    ret = epoll_ctl(efd,EPOLL_CTL_ADD,listenfd,&tep);
    // 循環(huán)等待
    for (;;)
    {
        // 返回已就緒的epoll_event,-1表示阻塞,沒有就緒的epoll_event,將一直等待
        size_t nready = epoll_wait(efd,ep,MAX_OPEN_FD,-1);
        for (int i = 0; i < nready; ++i)
        {
            // 如果是新的連接,需要把新的socket添加到efd中
            if (ep[i].data.fd == listenfd )
            {
                connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&clilen);
                tep.events = EPOLLIN;
                tep.data.fd = connfd;
                ret = epoll_ctl(efd,EPOLL_CTL_ADD,connfd,&tep);
            }
            // 否則,讀取數(shù)據(jù)
            else
            {
                connfd = ep[i].data.fd;
                int bytes = read(connfd,buf,MAXLEN);
                // 客戶端關(guān)閉連接
                if (bytes == 0){
                    ret =epoll_ctl(efd,EPOLL_CTL_DEL,connfd,NULL);
                    close(connfd);
                    printf("client[%d] closed\n", i);
                }
                else
                {
                    for (int j = 0; j < bytes; ++j)
                    {
                        buf[j] = toupper(buf[j]);
                    }
                    // 向客戶端發(fā)送數(shù)據(jù)
                    write(connfd,buf,bytes);
                }
            }
        }
    }
    return 0;
}
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 */
} __attribute__ ((__packed__));

原理

image.png

image.png

可以看到

  • epoll_create 創(chuàng)建了一個對象,這個對象包含一個event_poll線程池(紅黑樹)和rdlist就緒對列表(雙向鏈表)
  • epoll_ctl 執(zhí)行epoll_ctl()時,如果增加socket句柄累驮,則檢查在紅黑樹中是否存在酣倾,存在立即返回,不存在則添加到樹干上谤专,然后向內(nèi)核注冊回調(diào)函數(shù)躁锡,用于當(dāng)中斷事件來臨時向準(zhǔn)備就緒鏈表中插入數(shù)據(jù)
  • epoll_wait 執(zhí)行epoll_wait()時立刻返回準(zhǔn)備就緒鏈表里的數(shù)據(jù)即可。

當(dāng)事件就緒后置侍,就被加入到 rdlist(就緒鏈表)中稚铣。epoll_wait 檢查是否有事件發(fā)生時,僅僅需要檢查 rdlist 中是否有數(shù)據(jù)即可墅垮。

ep_poll_callback

ep_poll_callback函數(shù)核心功能是將被目標(biāo)fd的就緒事件到來時,將fd對應(yīng)的epitem實(shí)例添加到就緒隊(duì)列耕漱。當(dāng)應(yīng)用調(diào)用epoll_wait()時算色,內(nèi)核會將就緒隊(duì)列中的事件報(bào)告給應(yīng)用

參考

https://www.modb.pro/db/250807
https://www.cnblogs.com/tangxin-blog/p/5470791.html
https://sites.uclouvain.be/SystInfo/usr/include/sys/epoll.h.html
https://zhuanlan.zhihu.com/p/389407114

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市螟够,隨后出現(xiàn)的幾起案子灾梦,更是在濱河造成了極大的恐慌,老刑警劉巖妓笙,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件若河,死亡現(xiàn)場離奇詭異,居然都是意外死亡寞宫,警方通過查閱死者的電腦和手機(jī)萧福,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辈赋,“玉大人鲫忍,你說我怎么就攤上這事膏燕。” “怎么了悟民?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵坝辫,是天一觀的道長。 經(jīng)常有香客問我射亏,道長近忙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任智润,我火速辦了婚禮及舍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘做鹰。我一直安慰自己击纬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布钾麸。 她就那樣靜靜地躺著更振,像睡著了一般。 火紅的嫁衣襯著肌膚如雪饭尝。 梳的紋絲不亂的頭發(fā)上肯腕,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機(jī)與錄音钥平,去河邊找鬼实撒。 笑死,一個胖子當(dāng)著我的面吹牛涉瘾,可吹牛的內(nèi)容都是我干的知态。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼立叛,長吁一口氣:“原來是場噩夢啊……” “哼负敏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起秘蛇,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤其做,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后赁还,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體妖泄,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年艘策,在試婚紗的時候發(fā)現(xiàn)自己被綠了蹈胡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖审残,靈堂內(nèi)的尸體忽然破棺而出梭域,到底是詐尸還是另有隱情,我是刑警寧澤搅轿,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布病涨,位于F島的核電站,受9級特大地震影響璧坟,放射性物質(zhì)發(fā)生泄漏既穆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一雀鹃、第九天 我趴在偏房一處隱蔽的房頂上張望幻工。 院中可真熱鬧,春花似錦黎茎、人聲如沸囊颅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽踢代。三九已至,卻和暖如春嗅骄,著一層夾襖步出監(jiān)牢的瞬間胳挎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工溺森, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留慕爬,地道東北人。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓屏积,卻偏偏與公主長得像医窿,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子炊林,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評論 2 345