真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

網(wǎng)絡(luò)編程:I/O復(fù)用

I/O多路復(fù)用是在多線程或多進(jìn)程編程中常用技術(shù)。主要是通過select/epoll/poll三個函數(shù)支持的。在此主要對select和epoll函數(shù)詳細(xì)介紹。

創(chuàng)新互聯(lián)公司不只是一家網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司;我們對營銷、技術(shù)、服務(wù)都有自己獨特見解,公司采取“創(chuàng)意+綜合+營銷”一體化的方式為您提供更專業(yè)的服務(wù)!我們經(jīng)歷的每一步也許不一定是最完美的,但每一步都有值得深思的意義。我們珍視每一份信任,關(guān)注我們的做網(wǎng)站、網(wǎng)站設(shè)計質(zhì)量和服務(wù)品質(zhì),在得到用戶滿意的同時,也能得到同行業(yè)的專業(yè)認(rèn)可,能夠為行業(yè)創(chuàng)新發(fā)展助力。未來將繼續(xù)專注于技術(shù)創(chuàng)新,服務(wù)升級,滿足企業(yè)一站式營銷型網(wǎng)站建設(shè)需求,讓再小的成都品牌網(wǎng)站建設(shè)也能產(chǎn)生價值!

select函數(shù)

  • 該函數(shù)運行進(jìn)程指示內(nèi)核等待多個事件中的任何一個發(fā)生,并只有一個或多個事件發(fā)生或經(jīng)歷一段指定的時間后才喚醒它。

  • 調(diào)用select告知內(nèi)核對哪些描述符(就讀、寫或異常條件)感興趣以及等待多長時間。我們感興趣的描述符不局限于套接字,任何描述符都可以使用select來測試。

  • 函數(shù)原型:

    #include#includeint select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set
                *exceptset, const struct timeval *timeout);
                返回:若有就緒描述符則為其數(shù)目,若超時則為0,若出錯則為-1
    • 永遠(yuǎn)等待下去:僅在有一個描述符準(zhǔn)備好I/O時才返回,將其設(shè)為空指針

    • 等待一段固定時間:在有一個描述符準(zhǔn)備好I/O時返回,但是不超過由該參數(shù)所指向的timeval結(jié)構(gòu)中指定的秒數(shù)和微秒數(shù)。

    • 根本不等待:檢查描述符后立即返回,這就是輪詢。為此,該參數(shù)必須指向一個timeval結(jié)構(gòu),但是其中的值必須設(shè)置為0

    • 最后一個參數(shù)timeout,它告知內(nèi)核等待所指定描述符中的任何一個就緒可花多長時間。該參數(shù)有三種可能:

    • 三個參數(shù)readset,writeset,exceptset指定我們要讓內(nèi)核測試讀、寫和異常條件的描述符。

    • 如何給這三個參數(shù)的每一個參數(shù)指定一個或多個描述符值是一個設(shè)計上的問題。select使用描述符集,通常是一個整數(shù)數(shù)組,其中每個整數(shù)中的每一位對應(yīng)一個描述符。舉例來說,假設(shè)使用32位整數(shù),那么該數(shù)組的第一個元素對應(yīng)于描述符0~31,第二個元素對應(yīng)于描述符32~63,以此類推。所有這些實現(xiàn)細(xì)節(jié)都與應(yīng)用程序無關(guān),它們隱藏在名為fd_set的數(shù)據(jù)類型和以下四個宏中:

      void FD_ZERO(fd_set *fdset);    //clear all bits in fdsetvoid FD_SET(int fd, fd_set *fdset);   //turn on the bit for fd in fdsetvoid FD_CLR(int fd, fd_set *fdset);  //turn off the bit for fd in fdsetint FD_ISSET(int fd, fd_set *fdset);  //is the bit for fd on in fdset?

      我們分配一個fd_set數(shù)據(jù)類型的描述符集,并用這些宏設(shè)置或測試該集合中的每一位,也可以用C語言中的賦值語句把它賦值成另外一個描述符集。
      注意:前面所討論的每個描述符占用整數(shù)數(shù)組中的一位的方法僅僅是select函數(shù)的可能實現(xiàn)之一。

    • maxfdp1參數(shù)指定待測試的描述符個數(shù),它的值是待測試的最大描述符加1。描述符0,1,2,...,直到maxfdp1 - 1均被測試。

    • select函數(shù)修改由指針readset,writeset和exceptset所指向的描述符集,因而這三個參數(shù)都是值-結(jié)果參數(shù)。該函數(shù)返回后,我們使用FD_ISSET宏來測試fd_set數(shù)據(jù)類型中的描述符。描述符集內(nèi)任何與未就緒描述符所對應(yīng)的位返回時均清成0.為此,每次重新調(diào)用select函數(shù)時,我們都得再次把所有描述符集內(nèi)所關(guān)心的位均置為1

select返回套接字的“就緒”條件
  • 滿足下列四個條件之一的任何一個時,一個套接字準(zhǔn)備好讀:

    • 該套接字接收緩沖區(qū)中的數(shù)據(jù)字節(jié)數(shù)大于等于套接字接收緩沖區(qū)低水位標(biāo)記的當(dāng)前大小。對于這樣的套接字執(zhí)行讀操作不會阻塞并將返回一個大于0的值(也就是返回準(zhǔn)備好讀入的數(shù)據(jù))。我們使用SO_RECVLOWAT套接字選項設(shè)置套接字的低水位標(biāo)記。對于TCP和UDP套接字而言,其默認(rèn)值為1

    • 該連接的讀半部關(guān)閉(也就是接收了FIN的TCP連接)。對這樣的套接字的讀操作將不阻塞并返回0(也就是返回EOF)

    • 該套接字時一個監(jiān)聽套接字且已完成的連接數(shù)不為0。

    • 其上有一個套接字錯誤待處理。對這樣的套接字的讀操作將不阻塞并返回-1(也就是返回一個錯誤),同時把errno設(shè)置為確切的錯誤條件。這些待處理錯誤也可以通過SO_ERROR套接字選項調(diào)用getsockopt獲取并清除。

  • 下列四個條件的任何一個滿足時,一個套接字準(zhǔn)備好寫:

    • 該套接字發(fā)送緩沖區(qū)中的可用字節(jié)數(shù)大于等于套接字發(fā)送緩沖區(qū)低水位標(biāo)記的當(dāng)前大小,并且或該套接字已連接,或者該套接字不需要連接(如UDP套接字)。這意味著如果我們把這樣的套接字設(shè)置成非阻塞的,寫操作將不阻塞并返回一個正值(如由傳輸層接收的字節(jié)數(shù))。我們使用SO_SNDLOWAT套接字選項來設(shè)置該套接字的低水位標(biāo)記。對于TCP和UDP而言,默認(rèn)值為2048

    • 該連接的寫半部關(guān)閉。對這樣的套接字的寫操作將產(chǎn)生SIGPIPE信號

    • 使用非阻塞式connect套接字已建立連接,或者connect已經(jīng)已失敗告終

    • 其上有一個套接字錯誤待處理。對這樣的套接字的寫操作將不阻塞并返回-1(也就是返回一個錯誤),同時把errno設(shè)置為確切的錯誤條件。這些待處理錯誤也可以通過SO_ERROR套接字選項調(diào)用getsockopt獲取并清除。

  • 如果一個套接字存在帶外數(shù)據(jù)或者仍處于帶外標(biāo)記,那么它有異常條件待處理。

  • 注意:當(dāng)某個套接字上發(fā)生錯誤時,它將由select標(biāo)記為既可讀又可寫

  • 接收低水位標(biāo)記和發(fā)送低水位標(biāo)記的目的在于:允許應(yīng)用進(jìn)程控制在select可讀或可寫條件之前有多少數(shù)據(jù)可讀或有多大空間可用于寫。

  • 任何UDP套接字只要其發(fā)送低水位標(biāo)記小于等于發(fā)送緩沖區(qū)大小(默認(rèn)應(yīng)該總是這種關(guān)系)就總是可寫的,這是因為UDP套接字不需要連接。

poll函數(shù)

  • 函數(shù)原型:

    #includeint poll(struct pollfd *fdarray, unsigned long nfds, int timeout);
    
        返回:若有就緒描述符則為數(shù)目,若超時則為0,若出錯則為-1
  • 第一個參數(shù)是指向一個結(jié)構(gòu)數(shù)組第一個元素的指針。每個數(shù)組元素都是一個pollfd結(jié)構(gòu),用于指定測試某個給定描述符fd的條件。

    struct pollfd{    int fd;    //descriptor to check
        short event;  //events of interest on fd
        short revents;  //events that occurred on fd};

    要測試的條件由events成員指定,函數(shù)在相應(yīng)的revents成員中返回該描述符的狀態(tài)。(每個描述符都有兩個變量,一個為調(diào)用值,另一個為返回結(jié)果,從而避免使用值-結(jié)果參數(shù)。)

  • poll事件

epoll函數(shù)

  • epoll是Linux特有的I/O復(fù)用函數(shù)。它在實現(xiàn)和使用上與select、poll有很大的差異。

    • 首先,epoll使用一組函數(shù)來完成任務(wù),而不是單個函數(shù)。

    • 其次,epoll把用戶關(guān)心的文件描述符上的事件放在內(nèi)核里的一個事件表中,從而無須像select和poll那樣每次調(diào)用都要重復(fù)傳入文件描述符集或事件集。

    • 但epoll需要使用一個額外的文件描述符,來唯一標(biāo)識內(nèi)核中的這個事件表

  • epoll文件描述符使用如下方式創(chuàng)建:

    #includeint epoll_create(int size);

    size參數(shù)完全不起作用,只是給內(nèi)核一個提示,告訴它事件表需要多大。該函數(shù)返回的文件描述符將用作其他所有epoll系統(tǒng)調(diào)用的第一個參數(shù),以指定要訪問的內(nèi)核事件表。

  • 下面的函數(shù)用來操作epoll的內(nèi)核事件表:

    #includeint epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    
        返回:若成功返回0,失敗返回-1,并置errno

    fd參數(shù)是要操作的文件描述符,op參數(shù)則指定操作類型。操作類型有以下三類:

    • EPOLL_CTL_ADD, 往事件表中注冊fd上的事件

    • EPOLL_CTL_MOD, 修改fd上的注冊事件

    • EPOLL_CTL_DEL, 刪除fd上的注冊事件

  • event指定事件,它是epoll_event結(jié)構(gòu)指針類型,epoll_event的定義如下:

    strcut epoll_event{    __uint32_t events;    //epoll事件
        epoll_data_t data;    //用戶數(shù)據(jù)};
    • 其中,events成員描述事件類型。epoll支持的事件類型同poll基本相同。表示epoll事件類型的宏在poll對應(yīng)的宏前加上"E",比如epoll的數(shù)據(jù)可讀事件是EPOLLIN。

    • epoll有兩個額外的事件類型——EPOLLET和EPOLLONESHOT。它們對于epoll的高效運作非常關(guān)鍵。

    • data成員用于存儲用戶數(shù)據(jù),是一個聯(lián)合體:

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

      其中4個成員用得最多的是fd,它指定事件所從屬的目標(biāo)文件描述符。

  • epoll系列系統(tǒng)調(diào)用的主要接口是epoll_wait函數(shù),它在一段超時時間內(nèi)等待一組文件描述符上的事件,其原型如下:

    #includeint epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
    
        返回:若成功返回就緒的文件描述符個數(shù),失敗時返回-1,并置errnoo
  • maxevents參數(shù)指定最多監(jiān)聽多少個事件,它必須大于0

  • event_wait函數(shù)如果檢測到事件,就將所有就緒事件從內(nèi)核事件表(由epfd參數(shù)指定)中復(fù)制到它的第二個參數(shù)events指向的數(shù)組中。這個數(shù)組只用于輸出epoll_wait檢測到的就緒事件,而不像select和poll的數(shù)組參數(shù)那樣既用于傳入用戶注冊的事件,又用于輸出內(nèi)核檢測到的就緒事件。這就極大地提高了應(yīng)用程序索引就緒文件描述符的效率。

  • 下面代碼給出 poll和epoll在使用上的差別:

    //如何索引poll返回的就緒文件描述符int ret = poll(fds, MAX_EVENT_NUMBER, -1);//必須遍歷所有已注冊文件描述符并找到其中的就緒者for(int i = 0; i < MAX_EVENT_NUMBER; ++i){    if(fds[i].revents & POLLIN)  //判斷第 i 個文件描述符是否就緒
        {        int sockfd = fds[i].fd;        //處理sockfd
        }
    }//如何索引epoll返回的文件描述符int ret = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);//僅遍歷就緒的ret個文件描述符for(int i = 0; i < ret; ++i){    int sockfd = events[i].data.fd;    //sockfd肯定就緒,直接處理}
  • LT和ET模式

    • LT(Level Trigger,電平觸發(fā))模式:是默認(rèn)工作模式,在這種模式下的epoll相當(dāng)于一個效率較高的poll。當(dāng)epoll_wait檢測到其上有事件發(fā)生并將此事件通知應(yīng)用程序后,應(yīng)用程序可以不立即處理該事件。這樣,當(dāng)應(yīng)用程序下一次調(diào)用epoll_wait時,epoll_wait還會再次向應(yīng)用程序通告此事件。

    • ET(Edge Trigger,邊沿觸發(fā))模式。對于ET工作模式下的文件描述符,當(dāng)epoll_wait檢測到其上有事件發(fā)生并將此事件通知應(yīng)用程序后,應(yīng)用程序必須立即處理該事件,因為后續(xù)的epoll_wait調(diào)用將不再向應(yīng)用程序通知這一事件。

    • ET模式在很大程度上降低了同一個epoll事件被重復(fù)觸發(fā)的次數(shù)。因此效率要比LT模式高。

    • 每個使用ET模式的文件描述符都應(yīng)該是非阻塞的。如果文件描述符是阻塞的,那么讀或?qū)懖僮鲗驗闆]有后續(xù)的時間而一直處于阻塞狀態(tài)(饑渴狀態(tài))

  • EPOLLONESHOT事件

    • 即使使用ET模式,一個socket上的某個事件還是可能被觸發(fā)多次。這在并發(fā)程序中引起一個問題。比如一個線程(或進(jìn)程)在讀取完某個socket上的數(shù)據(jù)后開始處理這些數(shù)據(jù),而在數(shù)據(jù)的處理過程中該socket上又有新數(shù)據(jù)可讀(EPOLLIN再次被觸發(fā)),此時另外一個線程被喚醒來讀取這些新的數(shù)據(jù)。于是出現(xiàn)了兩個線程同時操作一個socket的場面。這當(dāng)然不是我們期望的。我們期望的是一個socket連接在任一時刻都只被一個線程處理。

    • 對于注冊了EPOLLONESHOT事件的文件描述符,操作系統(tǒng)最多觸發(fā)其上注冊的一個可讀、可寫或異常事件,且只觸發(fā)一次,除非我們使用epoll_ctl函數(shù)重置該文件描述符上的EPOLLONESHOT事件。這樣,當(dāng)一個線程在處理某個socket時,其他線程時不可能有機會操作該socket的。但反過來思考,注冊了EPOLLONESHOT事件的socket一旦被某個線程處理完畢,該線程就應(yīng)該立即重置這個socket上的EPOLLONESHOT事件,以確保這個socket下一次可讀時,其EPOLLIN事件能被觸發(fā),進(jìn)而讓其他工作線程有機會繼續(xù)處理這個socket.


分享標(biāo)題:網(wǎng)絡(luò)編程:I/O復(fù)用
標(biāo)題URL:http://weahome.cn/article/iejpsc.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部