redis源碼epoll用法

           學一下redis源碼中epoll的使用,由於功力有限不足之處望大家指正。

/*
 * 創建一個新的 epoll 實例,並將它賦值給 eventLoop
 */
static int aeApiCreate(aeEventLoop *eventLoop) {

    aeApiState *state = zmalloc(sizeof(aeApiState));

    if (!state) return -1;

    // 初始化事件槽空間
    state->events = zmalloc(sizeof(struct epoll_event)*eventLoop->setsize);
    if (!state->events) {
        zfree(state);
        return -1;
    }

    // 創建 epoll 實例
    state->epfd = epoll_create(1024); /* 1024 is just a hint for the kernel */
    if (state->epfd == -1) {
        zfree(state->events);
        zfree(state);
        return -1;
    }

    // 賦值給 eventLoop
    eventLoop->apidata = state;
    return 0;
}
/*
 * 事件狀態
 */
typedef struct aeApiState {

    // epoll_event 實例描述符
    int epfd;

    // 事件槽
    struct epoll_event *events;

} aeApiState;
/* State of an event based program 
 *
 * 事件處理器的狀態
 */
typedef struct aeEventLoop {

    // 目前已註冊的最大描述符
    int maxfd;   /* highest file descriptor currently registered */

    // 目前已追蹤的最大描述符
    int setsize; /* max number of file descriptors tracked */

    // 用於生成時間事件 id
    long long timeEventNextId;

    // 最後一次執行時間事件的時間
    time_t lastTime;     /* Used to detect system clock skew */

    // 已註冊的文件事件
    aeFileEvent *events; /* Registered events */

    // 已就緒的文件事件
    aeFiredEvent *fired; /* Fired events */

    // 時間事件
    aeTimeEvent *timeEventHead;

    // 事件處理器的開關
    int stop;

    // 多路複用庫的私有數據
    void *apidata; /* This is used for polling API specific data */

    // 在處理事件前要執行的函數
    aeBeforeSleepProc *beforesleep;

} aeEventLoop;

aeApiCreate函數爲epoll_event分配空間,epoll_create創建一個epoll句柄,參數size用來告訴內核監聽的文件描述符的個數,epoll句柄就是函數的返回值。該返回值保存在aeEventLoop的apidata裏面。

/*
 * 關聯給定事件到 fd
 */
static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
    aeApiState *state = eventLoop->apidata;
    struct epoll_event ee;

    /* If the fd was already monitored for some event, we need a MOD
     * operation. Otherwise we need an ADD operation. 
     *
     * 如果 fd 沒有關聯任何事件,那麼這是一個 ADD 操作。
     *
     * 如果已經關聯了某個/某些事件,那麼這是一個 MOD 操作。
     */
    int op = eventLoop->events[fd].mask == AE_NONE ?
            EPOLL_CTL_ADD : EPOLL_CTL_MOD;

    // 註冊事件到 epoll
    ee.events = 0;
    mask |= eventLoop->events[fd].mask; /* Merge old events */
    if (mask & AE_READABLE) ee.events |= EPOLLIN;
    if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;
    ee.data.u64 = 0; /* avoid valgrind warning */
    ee.data.fd = fd;

    if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1;

    return 0;
}

epoll_ctl是事件註冊函數,註冊要監聽的事件類型。state->epfd是epoll_create的返回值,op表示動作,EPOLL_CTL_ADD:註冊新的fd到epfd中;EPOLL_CTL_MOD:修改已經註冊的fd的監聽事件;EPOLL_CTL_DEL:從epfd中刪除一個fd;第三個參數是需要監聽的fd,第四個參數是告訴內核需要監聽什麼事。通信每過一個階段,監聽事件的狀態也會發生改變,通過epoll_ctl來改變。

typedef union epoll_data {
      void *ptr;
      int fd;
      __uint32_t u32;
      __uint64_t u64;
} epoll_data_t;
  
struct epoll_event {
     __uint32_t events; /* Epoll events */
     epoll_data_t data; /* User data variable */
};
/*
 * 獲取可執行事件
 */
static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
    printf("---------epoll aeApiPoll----------\n");
    aeApiState *state = eventLoop->apidata;
    int retval, numevents = 0;

    // 等待時間
    retval = epoll_wait(state->epfd,state->events,eventLoop->setsize,
            tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1);//epoll_wait用於向用戶進程返回ready list

    // 有至少一個事件就緒?
    if (retval > 0) {
        int j;

        // 爲已就緒事件設置相應的模式
        // 並加入到 eventLoop 的 fired 數組中
        numevents = retval;
        for (j = 0; j < numevents; j++) {
            int mask = 0;
            struct epoll_event *e = state->events+j;

            if (e->events & EPOLLIN) mask |= AE_READABLE;
            if (e->events & EPOLLOUT) mask |= AE_WRITABLE;
            if (e->events & EPOLLERR) mask |= AE_WRITABLE;
            if (e->events & EPOLLHUP) mask |= AE_WRITABLE;

            eventLoop->fired[j].fd = e->data.fd;
            eventLoop->fired[j].mask = mask;
        }
    }
    
    // 返回已就緒事件個數
    return numevents;
}

epoll_wait等待事件的產生參數events用來從內核得到事件的集合,setsize告之內核這個events有多大,這個 maxevents的值不能大於創建epoll_create()時的size,參數tvp是超時時間。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章