一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

三、I/O模型

 sven_ 2013-09-13

數(shù)據(jù)結(jié)構(gòu)

  1. [include/types/fd.h]  
  2. /* info about one given fd */  
  3. struct fdtab {  
  4.     struct {  
  5.         int (*f)(int fd);          /* read/write function */  
  6.         struct buffer *b;         /* read/write buffer */  
  7.     } cb[DIR_SIZE];  
  8.     void *owner;               /* the session (or proxy) associated with this fd */  
  9.     struct {                    /* used by pollers which support speculative polling */  
  10.         unsigned char e;        /* read and write events status. 4 bits*/  
  11.         unsigned int s1;         /* Position in spec list+1. 0=not in list. */  
  12.     } spec;  
  13.     unsigned short flags;         /* various flags precising the exact status of this fd */  
  14.     unsigned char state;          /* the state of this fd */  
  15.     unsigned char ev;            /* event seen in return of poll() : FD_POLL_* */  
  16. };  
  17.   
  18. struct poller {  
  19.     void   *private;                 /* any private data for the poller */  
  20.     int REGPRM2 (*is_set)(const int fd, int dir); /* check if <fd> is being polled for dir <dir> */  
  21.     int  REGPRM2    (*set)(const int fd, int dir);    /* set   polling on <fd> for <dir> */  
  22.     int  REGPRM2    (*clr)(const int fd, int dir);       /* clear polling on <fd> for <dir> */  
  23.     int  REGPRM2 (*cond_s)(const int fd, int dir); * set   polling on <fd> for <dir> if unset */  
  24.     int  REGPRM2 (*cond_c)(const int fd, int dir); /* clear polling on <fd> for <dir> if set */  
  25.     void REGPRM1    (*rem)(const int fd);      /* remove any polling on <fd> */  
  26.     void REGPRM1    (*clo)(const int fd);      /* mark <fd> as closed */  
  27.         void REGPRM2   (*poll)(struct poller *p, int exp);   /* the poller itself */  
  28.     int  REGPRM1   (*init)(struct poller *p);            /* poller initialization */  
  29.     void REGPRM1   (*term)(struct poller *p);            /* termination of this poller */  
  30.     int  REGPRM1   (*test)(struct poller *p);            /* pre-init check of the poller */  
  31.     int  REGPRM1   (*fork)(struct poller *p);            /* post-fork re-opening */  
  32.     const char   *name;                                  /* poller name */  
  33.     int    pref;                           /* try pollers with higher preference first */  
  34. };  
  35.   
  36. [src/fd.c]  
  37. struct fdtab *fdtab = NULL;     /* array of all the file descriptors */  
  38. struct fdinfo *fdinfo = NULL;   /* less-often used infos for file descriptors */  
  39. int maxfd;                      /* # of the highest fd + 1 */  
  40. int totalconn;                  /* total # of terminated sessions */  
  41. int actconn;                    /* # of active sessions */  
  42.   
  43. struct poller pollers[MAX_POLLERS];  
  44. struct poller cur_poller;  
  45. int nbpollers = 0;  

在看到fdtabpoller的結(jié)構(gòu)體時,然后查看ev_epoll.c的時候可能會奇怪為什么會設(shè)置成這樣。但是如果先查看ev_sepoll.c的話可能很多疑惑都沒有了。

sepoll

Haproxy中,作者在epoll上將模型推進至sepoll(我不知道是否在此之前就有人提出或者使用這種方法),從理論上來說,這種模型的總體效率應(yīng)該比epoll更好,雖然說它是基于epoll的,因為它能夠減少較多與epoll相關(guān)的昂貴的系統(tǒng)調(diào)用。

sepoll,作者在代碼注釋中稱為speculative I/O。Sepoll的原理就是,對于剛accept完的套接字描述符,一般都是直接能夠讀取導(dǎo)數(shù)據(jù)的;對于connect完的描述符,一般都是可寫的;即使是對于在傳輸數(shù)據(jù)的鏈接,它也是能提升效率的,因為假設(shè)對于某一條鏈接的某端已經(jīng)處于epoll的等待隊列中,那么另一端也是需要做出反應(yīng)的,要么發(fā)送數(shù)據(jù),要么接收數(shù)據(jù),這依賴于(讀/寫)緩沖區(qū)的水位。

當然,作者也描述了sepoll的缺點,那就是這可能會導(dǎo)致在epoll隊列中的可用事件缺少而變得饑餓(starve the polled events)(我對此處饑餓的理解是,有足夠資源的時候不給予需要的人;poll本來就是用于處理多個描述符專用,假設(shè)只處理幾個描述符,那么poll根本就提升不了多少性能,因為它本身也是系統(tǒng)調(diào)用,因此需要保持poll隊列含有一定數(shù)量的fd,否則就是出現(xiàn)饑餓情況),作者說實驗證明,當epoll隊列出現(xiàn)饑餓的情況時,壓力會轉(zhuǎn)到spec I/O上面,此時由于每次去讀取或者寫入,但是都失敗,陷入惡性循環(huán),會嚴重的降低系統(tǒng)性能(spec list描述符較多,一直輪詢肯定會導(dǎo)致性能問題)。用于解決此問題的方法,可以通過減少epoll一次處理的事件來解決這個問題(對spec list的不能使用這個方法,因為實驗顯示,spec list中2/3的fd是新的,只有1/3的fd是老的)。作者說這是基于以下兩點事實,第一,對于位于spec listfd,不能也將它們注冊在epoll中等待;第二是,即使在系統(tǒng)壓力非常大的時候,我們基本上也不會同時對同一個fd進行讀與寫的流操作。作者所說的后面一個事實我認為是這樣的,對于客戶端,一個請求都是將請求數(shù)據(jù)發(fā)送完成之后,后端才會對其進行響應(yīng);對于服務(wù)器,都是接收玩請求之后,才會發(fā)回響應(yīng)數(shù)據(jù)。

作者說第一個事實意味著在饑餓期間,poll等待隊列中不會有超過一半的fd。否則的話,說明spec list中的fdpoll list少,那么也就沒有饑餓情況。第二個事實意味著我們只對最大數(shù)量描述符的一半事件感興趣(每個描述符要么讀,要么寫)。

減少poll list一次處理的數(shù)量用于解決poll list饑餓的情況,可以這么理解,假設(shè)每個fd經(jīng)過一次讀和一次寫之后就被銷毀,那么對于第二個事實,在進行讀的時候,poll listfd不會減少,影響不大,但是在寫的時候,由于讀與寫都已經(jīng)完成了,那么可能這一次會導(dǎo)致大量的fd被移除,而補充又跟不上,這就可能會導(dǎo)致饑餓;但是由于第一個事實限制每次可處理的最大數(shù)量,那么一次讀寫完成被撤掉的fd數(shù)量就減少了,而且把poll list中的fd分成了兩部分,錯開了它們移出poll list的時間,減少了一次被移除的fd數(shù)量,那么就應(yīng)該能夠使后續(xù)的fd補充跟上。

那么對于fd本來就不多,導(dǎo)致poll list分配到的很少導(dǎo)致的饑餓怎么辦?此時由于fd不多,spec listfd也不多,,對性能的影響不是很大,基本上忽略了。

作者最后說明,如果我們能夠在負載高峰時段保證poll list擁有maxsock/2/2數(shù)量的事件,這意味著我們應(yīng)該給poll list分配maxsock/4的事件,就不會受饑餓的影響。Maxsock/2/2來源作者沒有明確說明,不過從上面的的解釋來看,第一除2應(yīng)該是表示如果poll list如果有不小于maxsock/2的fd,那么就不會受饑餓的影響;第二個除2暫時還不能確定,假如是根據(jù)第二個事實來的,那也不是很合理,因為一個sock肯定包含兩個事件,一次處理只做一個事件的話,那么時間數(shù)量也是和sock數(shù)量本身一樣的。

接下來看看sepoll的處理流程。

  1. [src/ev_sepoll.c]  
  2. #define FD_EV_IN_SL 1  
  3. #define FD_EV_IN_PL 4  
  4.   
  5. #define FD_EV_IDLE  0  
  6. #define FD_EV_SPEC  (FD_EV_IN_SL)  
  7. #define FD_EV_WAIT  (FD_EV_IN_PL)  
  8. #define FD_EV_STOP  (FD_EV_IN_SL|FD_EV_IN_PL)  
  9.   
  10. /* Those match any of R or W for Spec list or Poll list */  
  11. #define FD_EV_RW_SL (FD_EV_IN_SL | (FD_EV_IN_SL << 1))  
  12. #define FD_EV_RW_PL (FD_EV_IN_PL | (FD_EV_IN_PL << 1))  
  13. #define FD_EV_MASK_DIR  (FD_EV_IN_SL|FD_EV_IN_PL)  
  14.   
  15. #define FD_EV_IDLE_R    0  
  16. #define FD_EV_SPEC_R    (FD_EV_IN_SL)  
  17. #define FD_EV_WAIT_R    (FD_EV_IN_PL)  
  18. #define FD_EV_STOP_R    (FD_EV_IN_SL|FD_EV_IN_PL)  
  19. #define FD_EV_MASK_R    (FD_EV_IN_SL|FD_EV_IN_PL)  
  20.   
  21. #define FD_EV_IDLE_W    (FD_EV_IDLE_R << 1)  
  22. #define FD_EV_SPEC_W    (FD_EV_SPEC_R << 1)  
  23. #define FD_EV_WAIT_W    (FD_EV_WAIT_R << 1)  
  24. #define FD_EV_STOP_W    (FD_EV_STOP_R << 1)  
  25. #define FD_EV_MASK_W    (FD_EV_MASK_R << 1)  
  26.   
  27. #define FD_EV_MASK  (FD_EV_MASK_W | FD_EV_MASK_R)  

從以上宏定義可以看出,對于位于spec list的讀寫事件分別對應(yīng)的最低兩位;對于位于poll list的讀寫事件位于第三、四位。

  1. [src/ev_sepoll.c]_do_poll()  
  2. REGPRM2 static void _do_poll(struct poller *p, int exp)  
  3. {  
  4.     static unsigned int last_skipped;  
  5.     static unsigned int spec_processed;  
  6.     int status, eo;  
  7.     int fd, opcode;  
  8.     int count;  
  9.     int spec_idx;  
  10.     int wait_time;  
  11.     int looping = 0;  
  12.   
  13.  re_poll_once:  
  14.     /* Here we have two options : 
  15.      * - either walk the list forwards and hope to match more events 
  16.      * - or walk it backwards to minimize the number of changes and 
  17.      *   to make better use of the cache. 
  18.      * Tests have shown that walking backwards improves perf by 0.2%. 
  19.      */  

首先處理的是位于spec list的fd,作者說從后面遍歷spec list能夠提高0.2%的效率,這是因為spec list總是把最新的fd存儲在最后,而對于最新的fd,基本上很可能是直接可讀或者可寫的。

  1. [src/ev_sepoll.c]  
  2.     status = 0;  
  3.     spec_idx = nbspec;  
  4.     while (likely(spec_idx > 0)) {  
  5.         int done;  
  6.   
  7.         spec_idx--;  
  8.         fd = spec_list[spec_idx];  
  9.         eo = fdtab[fd].spec.e;  /* save old events */  
  10.   
  11.         if (looping && --fd_created < 0) {  
  12.             /* we were just checking the newly created FDs */  
  13.             break;  
  14.         }  

拿到fd,然后根據(jù)fdfdtab中拿到對應(yīng)的信息。如果這是第二次處理循環(huán),只是為了檢查由于listen fd進行accept之后新創(chuàng)建的fd,因此作者專門使用一個變量fd_created用于記錄新創(chuàng)建的fd數(shù)量,當新的fd處理完成之后,直接跳出循環(huán)了。

  1. [src/ev_sepoll.c]_do_poll()  
  2.         /* 
  3.          * Process the speculative events. 
  4.          * 
  5.          * Principle: events which are marked FD_EV_SPEC are processed 
  6.          * with their assigned function. If the function returns 0, it 
  7.          * means there is nothing doable without polling first. We will 
  8.          * then convert the event to a pollable one by assigning them 
  9.          * the WAIT status. 
  10.          */  

作者說明規(guī)則是處理標志了FD_EV_SPEC事件的,并且調(diào)用他們指定的函數(shù),如果函數(shù)返回0,那么表示現(xiàn)在沒有任何事可做,我們應(yīng)該先對其進行一個poll等待先。

  1. [src/ev_sepoll.c]_do_poll()  
  2. #ifdef DEBUG_DEV  
  3.         if (fdtab[fd].state == FD_STCLOSE) {  
  4.             fprintf(stderr,"fd=%d, fdtab[].ev=%x, fdtab[].spec.e=%x, .s=%d, idx=%d\n",  
  5.                 fd, fdtab[fd].ev, fdtab[fd].spec.e, fdtab[fd].spec.s1, spec_idx);  
  6.         }  
  7. #endif  
  8.         done = 0;  
  9.         fdtab[fd].ev &= FD_POLL_STICKY;  
  10.         if ((eo & FD_EV_MASK_R) == FD_EV_SPEC_R) {  
  11.             /* The owner is interested in reading from this FD */  
  12.             if (fdtab[fd].state != FD_STERROR) {  
  13.                 /* Pretend there is something to read */  
  14.                 fdtab[fd].ev |= FD_POLL_IN;  
  15.                 if (!fdtab[fd].cb[DIR_RD].f(fd))  
  16.                     fdtab[fd].spec.e ^= (FD_EV_WAIT_R ^ FD_EV_SPEC_R);  
  17.                 else  
  18.                     done = 1;  
  19.             }  
  20.         }  
  21.         else if ((eo & FD_EV_MASK_R) == FD_EV_STOP_R) {  
  22.             /* This FD was being polled and is now being removed. */  
  23.             fdtab[fd].spec.e &= ~FD_EV_MASK_R;  
  24.         }  
  25.   
  26.         if ((eo & FD_EV_MASK_W) == FD_EV_SPEC_W) {  
  27.             /* The owner is interested in writing to this FD */  
  28.             if (fdtab[fd].state != FD_STERROR) {  
  29.                 /* Pretend there is something to write */  
  30.                 fdtab[fd].ev |= FD_POLL_OUT;  
  31.                 if (!fdtab[fd].cb[DIR_WR].f(fd))  
  32.                     fdtab[fd].spec.e ^= (FD_EV_WAIT_W ^ FD_EV_SPEC_W);  
  33.                 else  
  34.                     done = 1;  
  35.             }  
  36.         }  
  37.         else if ((eo & FD_EV_MASK_W) == FD_EV_STOP_W) {  
  38.             /* This FD was being polled and is now being removed. */  
  39.             fdtab[fd].spec.e &= ~FD_EV_MASK_W;  
  40.     }  

對于位于spec fd的讀事件,當函數(shù)返回0時,去掉FD_EV_SPEC_R事件,轉(zhuǎn)為FD_EV_SPEC_WAIT_R事件,表示這個描述符應(yīng)該放入poll等待隊列。函數(shù)返回不為0,那么表示此次spec處理時成功的,那么依然將其留在spec隊列中,記錄成功標志。在處理相應(yīng)事件的時候還用fdtab[fd].ev記錄下了相應(yīng)fd被處理的事件。

對于被標志為停止了的fd,那么將其相應(yīng)的讀事件全部清空。

寫事件的處理與讀事件的處理相同。

  1. [src/ev_sepoll.c]_do_poll()  
  2.         status += done;  
  3.         /* one callback might already have closed the fd by itself */  
  4.         if (fdtab[fd].state == FD_STCLOSE)  
  5.         continue;  

前面只要讀或者寫成功,那么表示此次的spec處理是成功的,因此對其進行數(shù)量統(tǒng)計,當然有可能對應(yīng)的fd在其相應(yīng)的讀或者寫函數(shù)中已經(jīng)關(guān)閉,那么以下的事情就沒必要做了。

  1. [src/ev_sepoll.c]_do_poll()  
  2.         /* Now, we will adjust the event in the poll list. Indeed, it 
  3.          * is possible that an event which was previously in the poll 
  4.          * list now goes out, and the opposite is possible too. We can 
  5.          * have opposite changes for READ and WRITE too. 
  6.          */  
  7.         if ((eo ^ fdtab[fd].spec.e) & FD_EV_RW_PL) {  
  8.             /* poll status changed*/  
  9.             if ((fdtab[fd].spec.e & FD_EV_RW_PL) == 0) {  
  10.                 /* fd removed from poll list */  
  11.                 opcode = EPOLL_CTL_DEL;  
  12.             }  
  13.             else if ((eo & FD_EV_RW_PL) == 0) {  
  14.                 /* new fd in the poll list */  
  15.                 opcode = EPOLL_CTL_ADD;  
  16.             }  
  17.             else {  
  18.                 /* fd status changed */  
  19.                 opcode = EPOLL_CTL_MOD;  
  20.             }  
  21.   
  22.             /* construct the epoll events based on new state */  
  23.             ev.events = 0;  
  24.             if (fdtab[fd].spec.e & FD_EV_WAIT_R)  
  25.                 ev.events |= EPOLLIN;  
  26.   
  27.             if (fdtab[fd].spec.e & FD_EV_WAIT_W)  
  28.                 ev.events |= EPOLLOUT;  
  29.   
  30.             ev.data.fd = fd;  
  31.             epoll_ctl(epoll_fd, opcode, fd, &ev);  
  32.         }  

對于此處的表達式結(jié)果,結(jié)合以上三種情況即可知道其結(jié)果。首先是對于done的情況,此時o^fdtab[fd].spec.e==0,所以不會進入分支;接著是對于函數(shù)返回值為0的情況,這種情況下,FD_EV_SPEC的事件被清除,FD_EV_POLL的事件被設(shè)置,因此結(jié)果為不為0,會進入分支,進入分支后,易知內(nèi)部分支會進入第二分支,也就是將fd加到epoll中;第三種是FD_EV_STOP類型導(dǎo)致事件被清空,計算結(jié)果不為0,進入分支,由于spec.e被清零,因此進入第一個分支,也就是從epoll list中移除fd。

在進行操作判斷之后,然后對poll listfd進行相應(yīng)的操作。

  1. [src/ev_sepoll.c]_do_poll()  
  2.         if (!(fdtab[fd].spec.e & FD_EV_RW_SL)) {  
  3.             /* This fd switched to combinations of either WAIT or 
  4.              * IDLE. It must be removed from the spec list. 
  5.              */  
  6.             release_spec_entry(fd);  
  7.             continue;  
  8.         }  
  9.     }  

在對poll list更新之后,還需要檢查fd新的事件中是否已經(jīng)不再包含spec的事件,如果是,那么需要將fdfdtab中移除。至此spec的循環(huán)處理已經(jīng)結(jié)束。

總結(jié)一下上面的流程。從后往前遍歷spec list,根據(jù)對fd有興趣的事件調(diào)用相應(yīng)函數(shù)進行數(shù)據(jù)的輸入和輸出(所有的fd都是非阻塞形式的),如果調(diào)用成功,那么相應(yīng)的fd仍然保留于spec list中,并統(tǒng)計在spec中成功處理的fd數(shù)量;若失敗,那么需要將其放入poll list去等待,因為在等待數(shù)據(jù)到來之前在spec list中并不能做什么;如果描述符已經(jīng)被停止使用,那么將會從poll list或者spec list中移除。

  1. [src/ev_sepoll.c]_do_poll()  
  2.     /* It may make sense to immediately return here if there are enough 
  3.      * processed events, without passing through epoll_wait() because we 
  4.      * have exactly done a poll. 
  5.      * Measures have shown a great performance increase if we call the 
  6.      * epoll_wait() only the second time after speculative accesses have 
  7.      * succeeded. This reduces the number of unsucessful calls to 
  8.      * epoll_wait() by a factor of about 3, and the total number of calls 
  9.      * by about 2. 
  10.      * However, when we do that after having processed too many events, 
  11.      * events waiting in epoll() starve for too long a time and tend to 
  12.      * become themselves eligible for speculative polling. So we try to 
  13.      * limit this practise to reasonable situations. 
  14.     */  
  15.   
  16.     spec_processed += status;  
  17.   
  18.     if (looping) {  
  19.         last_skipped++;  
  20.         return;  
  21.     }  
  22.   
  23.     if (status >= MIN_RETURN_EVENTS && spec_processed < absmaxevents) {  
  24.         /* We have processed at least MIN_RETURN_EVENTS, it's worth 
  25.          * returning now without checking epoll_wait(). 
  26.          */  
  27.         if (++last_skipped <= 1) {  
  28.             tv_update_date(0, 1);  
  29.             return;  
  30.         }  
  31.     }  
  32.     last_skipped = 0;  

如果是第二次處理,也就是再回來處理新建的fd,那將last_skipped++并返回,這是為什么呢?因為之前作者描述過,一次對poll隊列處理的數(shù)量減少點,既然要減少,之前做過一次了,那么這次就不再檢查了。

last_skipped是用來標志當處理數(shù)量符合最小可返回數(shù)量時是否返回,如果本次返回是由于第二次處理而導(dǎo)致返回,那么下次出現(xiàn)處理數(shù)量達到最小可返回數(shù)量時不再返回。

如果spec處理成功的數(shù)量超過最小可以返回的數(shù)量并且spec_proc處理的數(shù)量不超過poll list最大的事件數(shù),那么要是之前沒設(shè)置跳過標志則返回。

第一次__do_poll循環(huán),并且已處理數(shù)量不足以返回,那么將下次跳過標志清空。

以下流程除了最后的判斷是否新建了fd而決定是否跳轉(zhuǎn)到__do_poll開始再做一次spec處理之外,其他的流程和其他的I/O模型基本上是一致,因此對于其他的I/O模型不再解釋。

  1. [src/ev_sepoll.c]_do_poll()  
  2.     if (nbspec || status || run_queue || signal_queue_len) {  
  3.         /* Maybe we have processed some events that we must report, or 
  4.          * maybe we still have events in the spec list, or there are 
  5.          * some tasks left pending in the run_queue, so we must not 
  6.          * wait in epoll() otherwise we will delay their delivery by 
  7.          * the next timeout. 
  8.          */  
  9.         wait_time = 0;  
  10.     }  
  11.     else {  
  12.         if (!exp)  
  13.             wait_time = MAX_DELAY_MS;  
  14.         else if (tick_is_expired(exp, now_ms))  
  15.             wait_time = 0;  
  16.         else {  
  17.             wait_time = TICKS_TO_MS(tick_remain(now_ms, exp)) + 1;  
  18.             if (wait_time > MAX_DELAY_MS)  
  19.                 wait_time = MAX_DELAY_MS;  
  20.         }  
  21. }  

要是之前的處理沒有能夠返回,那么接下來就需要真正的對epoll進行處理了,但是在處理之前則需要計算epoll_wait調(diào)用應(yīng)該等待的時間。

如果spec隊列還有fd(事件)存在,或者是spec已經(jīng)有處理成功需要回去報告,或者是任務(wù)可執(zhí)行隊列有需要執(zhí)行的任務(wù),或者是信號隊列有未決信號需要處理,那么對于epoll_wait的操作使用無阻塞的。

如果沒有設(shè)置超時時間,那么將等待時間設(shè)置為程序允許的最大值。

如果給定的超時時間已經(jīng)到期,那么對epoll_wait的調(diào)用也是無阻塞。

如果給定的超時時間還沒到,那么計算余下的時間,如果余下的時間比程序允許的最大值還大那么將其設(shè)置為程序允許的最大值。

  1. [src/ev_sepoll.c]_do_poll()  
  2.     /* now let's wait for real events. We normally use maxpollevents as a 
  3.      * high limit, unless <nbspec> is already big, in which case we need 
  4.      * to compensate for the high number of events processed there. 
  5.      */  
  6.     fd = MIN(absmaxevents, spec_processed);  
  7.     fd = MAX(global.tune.maxpollevents, fd);  
  8.     fd = MIN(maxfd, fd);  
  9.     /* we want to detect if an accept() will create new speculative FDs here */  
  10.     //從此處可以看出,listen fd是放在epoll等待隊列中的。  
  11.     fd_created = 0;  
  12.     spec_processed = 0;  
  13.     status = epoll_wait(epoll_fd, epoll_events, fd, wait_time);  
  14.     tv_update_date(wait_time, status);  

對于wait使用的數(shù)量,作者說明一般是使用maxpollevents作為限制的,除非spec list已經(jīng)非常大了,那么才需要對其大處理量進行補償。

epoll_wait返回之后需要對時間進行更新,如果是超時返回,那么需要將等待時間加上,否則根據(jù)返回值適當調(diào)整。

  1. [src/ev_sepoll.c]_do_poll()  
  2.     for (count = 0; count < status; count++) {  
  3.         int e = epoll_events[count].events;  
  4.         fd = epoll_events[count].data.fd;  
  5.   
  6.         /* it looks complicated but gcc can optimize it away when constants 
  7.          * have same values. 
  8.          */  
  9.         DPRINTF(stderr, "%s:%d: fd=%d, ev=0x%08x, e=0x%08x\n",  
  10.             __FUNCTION__, __LINE__,  
  11.             fd, fdtab[fd].ev, e);  
  12.   
  13.         fdtab[fd].ev &= FD_POLL_STICKY;  
  14.         fdtab[fd].ev |=   
  15.             ((e & EPOLLIN ) ? FD_POLL_IN  : 0) |  
  16.             ((e & EPOLLPRI) ? FD_POLL_PRI : 0) |  
  17.             ((e & EPOLLOUT) ? FD_POLL_OUT : 0) |  
  18.             ((e & EPOLLERR) ? FD_POLL_ERR : 0) |  
  19.             ((e & EPOLLHUP) ? FD_POLL_HUP : 0);  
  20.           
  21.         if ((fdtab[fd].spec.e & FD_EV_MASK_R) == FD_EV_WAIT_R) {  
  22.             if (fdtab[fd].state == FD_STCLOSE || fdtab[fd].state == FD_STERROR)  
  23.                 continue;  
  24.             if (fdtab[fd].ev & (FD_POLL_IN|FD_POLL_HUP|FD_POLL_ERR))  
  25.                 fdtab[fd].cb[DIR_RD].f(fd);  
  26.         }  
  27.   
  28.         if ((fdtab[fd].spec.e & FD_EV_MASK_W) == FD_EV_WAIT_W) {  
  29.             if (fdtab[fd].state == FD_STCLOSE || fdtab[fd].state == FD_STERROR)  
  30.                 continue;  
  31.             if (fdtab[fd].ev & (FD_POLL_OUT|FD_POLL_ERR))  
  32.                 fdtab[fd].cb[DIR_WR].f(fd);  
  33.         }  
  34.     }  
  35.   
  36.     if (fd_created) {  
  37.         /* we have created some fds, certainly in return of an accept(), 
  38.          * and they're marked as speculative. If we can manage to perform 
  39.          * a read(), we're almost sure to collect all the request at once 
  40.          * and avoid several expensive wakeups. So let's try now. Anyway, 
  41.          * if we fail, the tasks are still woken up, and the FD gets marked 
  42.          * for poll mode. 
  43.          */  
  44.   
  45.         looping = 1;  
  46.         goto re_poll_once;  
  47.     }  
  48. }  

spec list的處理一樣,對于出現(xiàn)的事件會在fdtab[fd].ev中保存下來。

epoll的相應(yīng)事件處理完成之后。因為讀事件中包括accept,因此可能創(chuàng)建了新的鏈接。如果創(chuàng)建了新的fd,那么可以轉(zhuǎn)回去直接用spec對他們進行處理,這第二次輪詢只處理新建的fd

epoll_wait調(diào)用之前將fd_created置為了0,那么是什么地方對其進行更改呢?是在poller的fd_set函數(shù)中,fd_set函數(shù)會在event_accept()函數(shù)中調(diào)用。后者源代碼位于src/client.c中。

Poller的初始化

之前看完了poller的處理流程,那么看看poller是如何初始化的。

  1. [src/fd.c]  
  2. int init_pollers()  
  3. {  
  4.     int p;  
  5.     struct poller *bp;  
  6.   
  7.   
  8.     do {  
  9.         bp = NULL;  
  10.         for (p = 0; p < nbpollers; p++)  
  11.             if (!bp || (pollers[p].pref > bp->pref))  
  12.                 bp = &pollers[p];  
  13.   
  14.         if (!bp || bp->pref == 0)  
  15.             break;  
  16.   
  17.         if (bp->init(bp)) {  
  18.             memcpy(&cur_poller, bp, sizeof(*bp));  
  19.             return 1;  
  20.         }  
  21.     } while (!bp || bp->pref == 0);  
  22.     return 0;  
  23. }  

很簡單的代碼,僅僅是遍歷poller全局數(shù)組pollers來查找pref值最大的一個poller,并將其設(shè)置為cur_poller。

那么pollers的值如何來的呢?通過查看ev_*.c的代碼可知,每一個文件均有如下函數(shù),

  1. __attribute__((constructor))  
  2. static void _do_register(void)  
  3. {  
  4.     ...  
  5. }  

這是使用了GCC的特性。GCC編譯之后的代碼將會在main函數(shù)運行之前將帶有此特性的函數(shù)先運行。因此,pollers數(shù)組就是通過每個I/O模型的_do_register函數(shù)來初始化的。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    国产精品内射视频免费| 日韩精品中文在线观看| 国产一区麻豆水好多高潮| 色偷偷亚洲女人天堂观看 | 不卡视频免费一区二区三区| 国产精品制服丝袜美腿丝袜| 人人妻在人人看人人澡| 国产又粗又猛又爽色噜噜| 又大又长又粗又黄国产| 亚洲精品av少妇在线观看| 麻豆一区二区三区在线免费| 日韩精品你懂的在线观看| 日本免费一级黄色录像| 国产一区二区三中文字幕| 少妇激情在线免费观看| 日韩特级黄片免费在线观看| 日本黄色美女日本黄色| 午夜精品一区二区av| 成人三级视频在线观看不卡| 成在线人免费视频一区二区| 久久热在线视频免费观看| 永久福利盒子日韩日韩| 亚洲第一香蕉视频在线| 国产在线视频好看不卡| 亚洲最新中文字幕在线视频| 97人妻人人揉人人躁人人| 欧美日韩综合在线精品| 亚洲视频偷拍福利来袭| 国产精品日韩欧美一区二区| 亚洲淫片一区二区三区| 亚洲日本加勒比在线播放 | 插进她的身体里在线观看骚| 久久99爱爱视频视频| 国产精品日韩欧美一区二区| 欧美在线观看视频三区| 亚洲中文字幕视频在线播放| 午夜福利网午夜福利网| 91日韩欧美国产视频| 亚洲日本中文字幕视频在线观看 | 久久一区内射污污内射亚洲| 在线观看中文字幕91|