歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Linux高性能服務器編程——I/O復用

Linux高性能服務器編程——I/O復用

日期:2017/3/1 9:42:19   编辑:Linux編程

IO復用

I/O復用使得程序能同時監聽多個文件描述符,通常網絡程序在下列情況下需要使用I/O復用技術:

  1. 客戶端程序要同時處理多個socket

  2. 客戶端程序要同時處理用戶輸入和網絡連接

  3. TCP服務器要同時處理監聽socket和連接socket,這是I/O復用使用最多的場合

  4. 服務器要同時處理TCP請求和UDP請求。比如本章將要討論的會社服務器

  5. 服務器要同時監聽多個端口,或者處理多種服務。

I/O復用雖然能同時監聽多個文件描述符,但它本身是阻塞的。並且當多個文件描述符同時就緒時,如果不采用額外措施,程序就只能按順序依次處理其中的每一個文件描述符,這使得服務器程序看起來像是串行工作。如果要實現並發,只能使用多進程或多線程等變成手段。

《Unix/Linux編程實踐教程》之Shell編程一 http://www.linuxidc.com/Linux/2012-12/75690.htm

《Unix/Linux編程實踐教程》之Shell編程二 http://www.linuxidc.com/Linux/2012-12/75691.htm

《Unix/Linux編程實踐教程》之管道 http://www.linuxidc.com/Linux/2012-12/75692.htm

Unix/Linux編程實踐教程【高清PDF中文版+附錄光盤+代碼】:http://www.linuxidc.com/Linux/2011-08/41374.htm

select系統復用

select系統調用的用途是:在一段指定時間內,幾件套用戶感興趣的文件描述符上的可讀可寫和異常等事件。

#include <sys/select.h>

int select(int nfds, fd_set *readfds,fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

  1. nfds參數指定被監聽的文件描述符的總數。通常被設置為select監聽的所有文件描述符中的最大值加1,因為文件描述符是從0開始計數的

  2. readfds, writefds和exceptfds參數分別指向可讀、可寫和異常等事件對應的文件描述符集合。

    fd_set結構體僅包含一個整形數組,高數組的每個元素的每一位標記一個文件描述符。

    可用如下宏來訪問fd_set結構體中的位:

    voidFD_CLR(int fd, fd_set *set);

    intFD_ISSET(int fd, fd_set *set);

    voidFD_SET(int fd, fd_set *set);

    void FD_ZERO(fd_set*set);

  3. timeout參數用來設置select函數的超時時間。它是一個timeval指針,timeval結構體定義如下:

struct timeval {

longtv_sec;/* seconds */

longtv_usec;/* microseconds */

};

如果給timeout傳遞NULL,則select將一直阻塞,直到某個文件描述符就緒。

select成功時返回就緒文件描述符的總數,如果在超時時間內沒有任何文件描述符就緒返回0,失敗返回-1,並設置errno;如果select在等待期間收到信號,則select立即返回-1,並設置errno為EINTR。

poll系統調用

poll系統調用和select類似,也是在指定時間內倫旭一定數量的文件描述符,以測試其中是否有就緒。poll原型如下:

#include<poll.h>

int poll(structpollfd *fds, nfds_t nfds, int timeout);

1)fds參數是一個pollfd結構類型的數組,它指定所以我們感興趣的文件描述符上發生的刻度、可寫和異常等時間。其結構定義如下:

struct pollfd {

intfd;/* file descriptor */

short events;/* requested events */

short revents;/* returned events */

};

其中fd成員指定文件描述符;events成員告訴poll監聽f上的那些時間,它是一系列時間的按位或;revents成員則由內核修改,以通知應用程序fd上實際發生了哪些事件。

2)nfds參數指定被監聽事件集合的大小。其類型nfds_t定義如下:

typedef unsignedlong int nfds_t;

  1. timeout參數指定poll的超時時間,單位是毫秒。當timeout為-1時,poll調用將永遠阻塞,直到某個事件發生;當為0時,poll調用立即返回。

    poll返回值含義與select相同。

epoll系列系統調用

內核事件表

epoll是Linux特有的I/O復用函數。它在實現和使用上與select、poll有很大差異。首先,epoll使用一組函數來完成任務,而不是單個函數。其次,epoll把用戶關心的文件描述符上的時間放在內核裡的一個時間表中,從而無需向select和poll那樣每次調用都要重復傳入文件描述符集或事件集。但epoll需要使用一個額外的文件描述符,來唯一標識內核中的這個時間表。這個文件描述符使用如下epoll_create函數創建:

#include <sys/epoll.h>

int epoll_create(int size);

size參數給內核一個提示,告訴它時間表需要多大。該函數返回的文件描述符將作用其他所有epoll系統調用的第一個參數,以指定要訪問的內核事件表。

下面的函數用來操作epoll的內核事件表:

#include <sys/epoll.h>

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

fd參數是要操作的文件描述符,op參數則制定操作類型,操作類型有如下3種:

EPOLL_CTL_ADD:往事件表中注冊fd上的事件

EPOLL_CTL_MOD:修改fd上的注冊事件

EPOLL_CTL_DEL:刪除fd上的注冊事件

event參數指定時間,它是epoll_event結構指針類型。epoll_event的定義如下:

struct epoll_event {

uint32_tevents;/* Epoll events */

epoll_data_t data;/* User data variable */

};

其中events成員描述事件類型。data成員用於存儲用戶數據,其類型epoll_data的定義如下:

typedef union epoll_data {

void*ptr;

intfd;

uint32_tu32;

uint64_tu64;

} epoll_data_t;

epoll_data_t是一個聯合體,其中4個成員中使用最多的是fd,它指定事件所叢書的目標文件描述符。

epoll_ctl成功時返回0,失敗時返回-1並設置errno。

epoll_wait函數

epoll系列系統調用的主要接口是epoll_wait函數。它在一段超時時間內等待一組文件描述符上的事件,其原型如下:

#include <sys/epoll.h>

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

該函數成功時返回就緒的文件描述符的個數,失敗是返回-1,並設置errno。

maxevents參數指定最多監聽多少時間,必須大於0.

epoll_wait函數如果檢測到事件,就將所有就緒的事件從內核事件表中復制到它的第二個參數events指向的數組中。這個數組只用於輸出epoll_wait檢測到的就緒時間,而不像select和poll數組那樣即用於傳入用戶注冊的時間,有用於輸出內核檢測到的就緒時間。這就極大的提高了應用程序索引就緒文件描述符的效率。下面的代碼體現了這個差別:

/*如何索引poll返回的就緒文件描述符*/

int ret = poll(fds, MAX_EVENT_NUMBER, -1);

/*必須遍歷所有注冊文件描述符並找到其中的就緒著*/

for(int i=0;i<MAX_EVENT_NUMBER; ++i)

{

if(fds[i].revents & POLLIN)

{

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 socketfd = events[i].data.fd;

/*socket肯定就緒,直接處理*/

}

更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2014-07/103982p2.htm

Copyright © Linux教程網 All Rights Reserved