天天看點

從零實作加密隧道(三):epoll 用法

int epoll_create(int size);

  • 作用:建立一個 epoll 的句柄。
  • 參數:size 用來告訴核心監聽的數目大小,核心會根據 size 的大小去申請對應的記憶體。
  • 傳回值:成功則傳回新的 epoll 對象的檔案描述符,這個檔案描述符用于後續的 epoll 操作。如果不需要使用這個描述符,使用 close 關閉。失敗傳回 -1 。

int epoll_create1(int flags);

  • 作用:建立一個 epoll 的句柄。
  • 參數:flags的值是 0,epoll_create1()等同于epoll_create()。但是不再根據 size 大小申請記憶體。而是動态申請記憶體。flasg 還可以使用其他值。
  • 傳回值:成功傳回檔案描述符。失敗傳回 -1 。

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

  • 作用:事件注冊函數。我個人的了解就是将要監測的目标 fd 添加進 epfd 中。
  • 參數:epfd 是 epoll_create 的傳回值。op:添加事件的類型選項。fd:目标檔案描述符(需要監聽的目标)。event 告訴核心需要監聽什麼事。
  • 傳回值:成功傳回 0 ,失敗傳回 -1 。

op 宏(添加事件的類型選項)有以下幾種:

EPOLL_CTL_ADD:在 epfd 中注冊新的 fd ,對這個 fd 要監聽的是 event。
EPOLL_CTL_MOD:改變已注冊到 epfd 的 fd 的監聽事件。即改成新的 event。
EPOLL_CTL_DEL:從指定的 epfd 中 删除 fd 檔案描述符。在這種模式中 event 是被忽略的,并且為可以等于 NULL 。
           

struct epoll_event(需要監聽的事)結構如下:

struct epoll_event {
    uint32_t     events;      /* Epoll events */
    epoll_data_t data;        /* User data variable */
};
           

events 宏有以下幾種,可以取他們的并:

EPOLLIN :表示對應的檔案描述符執行讀時;

EPOLLOUT:表示對應的檔案描述符執行寫時;

EPOLLPRI:表示對應的檔案描述符有緊急的資料可讀(這裡應該表示有帶外資料到來);

EPOLLERR:表示對應的檔案描述符發生錯誤;

EPOLLHUP:表示對應的檔案描述符被挂斷;

EPOLLET: 将EPOLL設為邊緣觸發(Edge Triggered)模式,這是相對于水準觸發(Level Triggered)來說的。

EPOLLONESHOT:隻監聽一次事件,當監聽完這次事件之後,如果還需要繼續監聽這個socket的話,需要再次把這個socket加入到EPOLL隊列裡

epoll_data 的結構如下:

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

一般用法是這樣的:

struct epoll_event e;
e.data.fd = socket_fd;
e.events = EPOLLIN | EPOLLOUT;
epoll_ctl(events_fd, EPOLL_CTL_MOD, socket_fd, &e);
           

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

  • 作用:等待 epfd 中的事件。類似于select()調用。
  • 參數:events 用來從核心得到事件的集合。maxevents 告知核心有多少個 events,必須要大于0。timeout 是用來制定epoll_wait 會阻塞多少毫秒的。

關于 events 的用法一般是這樣的:

struct epoll_event events[100000]; // 一個數組
// num 告知使用者有多少個事件被監聽到,并且這些事件放在 events 數組裡了
int num = epoll_wait(epfd, events, 100000, 0); 

// 循環檢視 num 個事件
for (int i = 0; i < num; ++i) {
    if (events[i].events & (EPOLLIN | EPOLLPRI)) {
        // 表示對應的檔案描述符執行讀;即,events[i].data.fd 執行讀
        // ...
    } else if (events[i].events & EPOLLOUT) {
        // 表示對應的檔案描述符執行寫;即,events[i].data.fd 執行寫
        // ...
    } else{
        // ...
    }
}
           

參考:

https://www.jianshu.com/p/ee381d365a29

繼續閱讀