一、epoll_create函數
函數聲明:int epoll_create(int size)
該 函數生成一個epoll專用的文件描述符。它實際上是在內核申請一空間,用來存放你想關注的socket fd上是否發生以及發生了什麼事件。size就是你在這個epoll fd上能關注的最大socket fd數。隨你定好了,前提是有足夠的空間。
linux
2. socket 函數
程序員
函數原型 int socket(int domain, int type, int protocol);
應用程序調用socket函數來建立一個可以進行網絡通訊的套接字,即套接口描述文件字,它是一個整數,如同文件描述符同樣,是內核一個I/O結構的索引。經過socket函數,咱們指定一個套接口的協議相關的屬性, 爲進行使用socket api作好準備。
第一個參數指定一個協議簇,也每每被稱爲協議域。系統存在許多能夠的協議簇,常見有AF_INET──指定爲IPv4協議,AF_INET6──指定爲 IPv6,AF_LOCAL──指定爲UNIX 協議域等等。它值都是系統預先定義的宏,系統支持哪些協議咱們纔可使用,不然會調用失敗。協議簇是網絡層的協議。
第二個參數指定一個套接口的類型,套接口可能的類型有:SOCK_STREAM、SOCK_DGRAM、SOCK_SEQPACKET、SOCK_RAW等等,它們 分別代表字節流、數據報、有序分組、原始套接口。這其實是指定內核爲咱們提供的服務抽象,好比咱們要一個字節流。須要注意的,並非每一種協議簇都支持 這裏的全部的類型,因此類型與協議簇要匹配。;
第三個參數指定應用程序所使用的傳輸協議,也就是諸如TCP或UDP協議等等,系統針對每個協議簇與類型提供了一個默認的協議,一般咱們經過把protocol設置爲0來使用這 個默認的值。注意這裏的協議與上面的協議簇是兩個不一樣的概念,前者是指網絡層的協議,因爲它對於到傳輸層會出現許多協議,好比IPv4能夠用來實現TCP 或UDP等等傳輸層協議,因此稱爲協議簇。相應的傳輸層的協議就簡單地稱爲協議。常見的協議有TCP、UDP、SCTP,要指定它們分別使用宏 IPPROTO_TCP、IPPROTO_UPD、IPPROTO_SCTP來指定。
api
該函數若是調用成功就返回新建立的套接字的描述符,若是失敗就返回 INVALID_SOCKET。套接字描述符是一個整數類型的值。每一個進程的進程空間裏都有一個套接字描述符表,該表中存放着套接字描述符和套接字數據結 構的對應關係。該表中有一個字段存放新建立的套接字的描述符,另外一個字段存放套接字數據結構的地址,所以根據套接字描述符就能夠找到其對應的套接字數據結構。每一個進程在本身的進程空間裏都有一個套接字描述符表可是套接字數據結構都是在操做系統的內核緩衝裏。數組
3. fcntl函數服務器
函數原型:網絡
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);數據結構
功能:改變一個已打開的文件的屬性,能夠從新設置讀、寫、追加、非阻塞等標誌(這些標誌稱爲File併發
這個函數和open 同樣,也是用可變參數實現的,可變參數的類型和個數取決於前面的cmd 參數。dom
針對第2個參數,int cmd異步
fcntl函數有五種功能:
• 複製一個現存的描述符(cmd=F_DUPFD) 。
• 得到/設置文件描述符標記(cmd=F_GETFD或F_SETFD) 。
• 得到/設置文件狀態標誌(cmd=F_GETFL或F_SETFL) 。
• 得到/設置異步I/O有權(cmd=F_GETOWN或F_SETOWN) 。
• 得到/設置記錄鎖(cmd=F_GETLK,F_SETLK或F_SETLKW)。
將涉及與進程表項中各文件描述符相關聯的文件描述符標誌, 以及每一個文件表項中的文件狀態標誌,
一~複製文件描述符
• F_DUPFD 複製文件描述符filedes,新文件描述符做爲函數值返回。它是還沒有打開的各
描述符中大於或等於第三個參數值(取爲整型值)中各值的最小值。新描述符與 filedes 共享同
一文件表項。可是,新描述符有它本身的一套文件描述符標誌,其 F D _ C L O E X E C
文件描述符標誌則被清除。
• F_GETFD 對應於filedes 的文件描述符標誌做爲函數值返回。當前只定義了一個文件描
述符標誌FD_CLOEXEC。
• F_SETFD 對於filedes 設置文件描述符標誌。新標誌值按第三個參數 (取爲整型值)設置。
應當瞭解不少現存的涉及文件描述符標誌的程序並不使用常數 F D _ C L O E X E C,而是將此
標誌設置爲0(系統默認,在exec時不關閉)或1(在exec時關閉)。
二~文件描述符號,套接口 屬性相關
• F_GETFL 對應於filedes 的文件狀態標誌做爲函數值返回。在說明 open函數時,已說明
了文件狀態標誌 不幸的是,三個存取方式標誌 (O_RDONLY,O_WRONLY,以及O_RDWR)並不各佔1位。(正
如前述,這三種標誌的值各是 0、1和2,因爲歷史緣由。這三種值互斥 — 一個文件只能有這
三種值之一。 )所以首先必須用屏蔽字 O_ACCMODE相與 取得存取方式位,而後將結果與這三種值
相比較。
• F_SETFL 將文件狀態標誌設置爲第三個參數的值 (取爲整型值)。 能夠更改的幾個標誌是:
O_APPEND,O_NONBLOCK,O_SYNC和O_ASYNC。
fcntl的文件狀態標誌共有7個,O_RDONLY,O_WRONLY,O_RDWR,O_APPEND,O_NONBLOCK,O_SYNC和O_ASYNC
三~信號驅動I/O , 帶外數據,設置套接口接受信號的屬主
SIGIO,跟信號驅動I/O有關
SIGURG, 和接受帶外數據有關
• F_GETOWN 取當前接收SIGIO和SIGURG信號的進程ID或進程組ID。12.6.2節將論述這
兩種4.3+BSD異步I/O信號。
• F_SETOWN 設置接收SIGIO和SIGURG信號的進程ID或進程組ID。正的arg指定一個進
程ID,負的arg表示等於arg絕對值的一個進程組ID
三、epoll_ctl函數
函數聲明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
該函數用於控制某個文件描述符上的事件,能夠註冊事件,修改事件,刪除事件。
參數:epfd:由 epoll_create 生成的epoll專用的文件描述符;
op:要進行的操做例如註冊事件,可能的取值EPOLL_CTL_ADD 註冊、EPOLL_CTL_MOD 修
改、EPOLL_CTL_DEL 刪除
fd:關聯的文件描述符;
event:指向epoll_event的指針;
若是調用成功返回0,不成功返回-1
四、epoll_wait函數
函數聲明:int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)
該函數用於輪詢I/O事件的發生;
參數:
epfd:由epoll_create 生成的epoll專用的文件描述符;
epoll_event:用於回傳代處理事件的數組;
maxevents:每次能處理的事件數;
timeout:等待I/O事件發生的超時值;
返回發生事件數。
介紹linux下的epoll(7)方法,其有着良好的就緒事件通知機制。咱們將會使用C來展示一個完整的TCP服務器實現代碼。 Epoll是被linux2.6開始引進的,可是不被其餘的類UNIX系統支持,它提供了一種相似select或poll函數的機制:
1.
Select(2)只可以同時管理FD_SETSIZE
數目的文件描述符
2. poll(2)沒有固定的描述符上限這一限制,可是每次必須遍歷全部的描述符來檢查就緒的描述符,這個過程的時間複雜度爲O(N)。
epoll沒有select這樣對文件描述符上限的限制,也不會像poll那樣進行線性的遍歷。所以epoll處理大併發鏈接有着更高的性能。
Epoll相關操做函數介紹:
1. epoll_create(2) or epoll_create1(2)(有着不一樣的參數值)用來建立epoll實例。
/usr/include/sys/epoll.h
extern int epoll_create (int __size) ;
RETURN:>0, 成功;-1, 出錯
函數描述:
(1) epoll_create返回的是一個文件描述符,也就是說epoll是以特殊文件的方式體現給用戶
(2) __size提示操做系統,用戶可能要使用多少個文件描述符,該參數已經廢棄,填寫一個大於0的正整數
2. epoll_ctl(2)用來增長或移除被epoll所監聽的文件描述符。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
RETURN:0,成功;-1,出錯
函數描述:
(1) epfd爲epoll_create建立的epoll描述符
(2) epoll_ctl函數對epoll進行op類型的操做,op選項爲
EPOLL_CTL_ADD,對fd描述符註冊event事件
EPOLL_CTL_MOD,對fd描述符的event事件進行修改
EPOLL_CTL_DEL,刪除已註冊的event事件
3. epoll_wait(2)用來等待發生在監聽描述符上的事件。它會一直阻塞直到事件發生。
#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
RETURN:>0,發生事件個數;=0,時間到;-1,出錯
函數描述:
epoll_wait與select函數相似,同步地等待事件發生
(1) epfd,標識epoll的文件描述符
(2) events,指向傳入操做系統的一個epoll_event數組
(3) maxevents,表示傳入數組的大小,必須大於0
當有事件發生,Linux會填寫events結構,返回給應用程序。因爲epoll_wait同步等待,有可能被信號中斷,返回EINTR錯誤
更多的函數介紹請參照man。
Epoll的兩種模式:
1. 水平觸發(LT):使用此種模式,當數據可讀的時候,epoll_wait()將會一直返回就緒事件。若是你沒有處理徹底部數據,而且再次在該 epoll實例上調用epoll_wait()才監聽描述符的時候,它將會再次返回就緒事件,由於有數據可讀。ET只支持非阻塞socket。
2. 邊緣觸發(ET):使用此種模式,只能獲取一次就緒通知,若是沒有處理徹底部數據,而且再次調用epoll_wait()的時候,它將會阻塞,由於就緒事件已經釋放出來了。
ET的效能更高,可是對程序員的要求也更高。在ET模式下,咱們必須一次乾淨而完全地處理完全部事件。LT兩種模式的socket都支持。
傳遞給epoll_ctl(2)的Epoll事件結構體以下所示:
typedefunionepoll_data
{
void*ptr;
intfd;
__uint32_t u32;
__uint64_t u64;
}epoll_data_t;
structepoll_event
{
__uint32_t events;/* Epoll events */
epoll_data_t data;/* User data variable */
};
對於每個監聽的描述符,可以關聯一個整形數據或指向用戶數據的指針。
epoll的事件類型:
enum EPOLL_EVENTS
{
EPOLLIN = 0x001,
#define EPOLLIN EPOLLIN
EPOLLPRI = 0x002,
#define EPOLLPRI EPOLLPRI
EPOLLOUT = 0x004,
#define EPOLLOUT EPOLLOUT
EPOLLRDNORM = 0x040,
#define EPOLLRDNORM EPOLLRDNORM
EPOLLRDBAND = 0x080,
#define EPOLLRDBAND EPOLLRDBAND
EPOLLWRNORM = 0x100,
#define EPOLLWRNORM EPOLLWRNORM
EPOLLWRBAND = 0x200,
#define EPOLLWRBAND EPOLLWRBAND
EPOLLMSG = 0x400,
#define EPOLLMSG EPOLLMSG
EPOLLERR = 0x008,
#define EPOLLERR EPOLLERR
EPOLLHUP = 0x010,
#define EPOLLHUP EPOLLHUP
EPOLLRDHUP = 0x2000,
#define EPOLLRDHUP EPOLLRDHUP
EPOLLONESHOT = (1 << 30),
#define EPOLLONESHOT EPOLLONESHOT
EPOLLET = (1 << 31)
#define EPOLLET EPOLLET
};
– EPOLLIN,讀事件
– EPOLLOUT,寫事件
– EPOLLPRI,帶外數據,與select的異常事件集合對應
– EPOLLRDHUP,TCP鏈接對端至少寫寫半關閉
– EPOLLERR,錯誤事件
– EPOLLET,設置事件爲邊沿觸發
– EPOLLONESHOT,只觸發一次,事件自動被刪除
epoll在一個文件描述符上只能有一個事件,在一個描述符上添加多個事件,會產生EEXIST的錯誤。一樣,刪除epoll的事件,只需描述符就夠了
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
這裏有一個比較重要的問題:從epoll_wait返回的events中,該如何知道是哪一個描述符上的事件:在註冊epoll事件的時候,必定要填寫epoll_data,不然咱們將分不清觸發的是哪一個描述符上的事件。