1. 程式人生 > 實用技巧 >epoll 實現 I/O 複用

epoll 實現 I/O 複用

epoll

#include <sys/epoll.h>

int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

epoll 的核心觀念就是 epoll instance (epoll 例項)

可以把它看成是包含兩個連結串列的容器

●想要監聽的檔案描述符的連結串列

準備好了的檔案描述符連結串列

epoll_create

int epoll_create(int size);
int epoll_create1(int flags)

使用 epoll_create 建立 epoll instance 並返回指向它的檔案描述符,引數 size 不僅沒用還不能為 0

使用 epoll_create1(0) 相當於正確使用 epoll_create。

出錯返回 -1

epoll_ctl

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

用於對監聽的那條連結串列進行增,刪,改操作。

epfd 是 epoll instace 的檔案描述符

op 可能以下值

EPOLL_CTL_ADD    增加要監聽的檔案描述符
EPOLL_CTL_MOD
EPOLL_CTL_DEL
●fd 想要監聽的檔案描述符

●event 的結構體內容如下

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 */ };

uint32_t events 可能是以下值

EPOLLIN   有資料可讀

EPOLLOUT  可以寫資料

在增加描述符 fd 時,把 data.fd 設定成 fd

epoll_tcl 成功返回 0 ,失敗返回 -1

epoll_wait

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

等待描述符就修,會將準備好的檔案描述符儲存在結構體陣列中,其地址由 events 返回

最多 maxevents 個準備好的描述符

timeout 負數代表 一直等待

使用 epoll 替代 poll

源程式見另一篇隨筆

#include <stdio.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <poll.h>
#include <errno.h>
#include <sys/epoll.h>
#include <stdlib.h>

#define MAXLINE 1024
#define MAX_EVENTS 10000

int main(int argc, char *argv[])
{
    char buf[MAXLINE];
    int listenfd, clientfd;

    int i, nread;

    struct sockaddr_in sock;
    socklen_t socklen;

    int epollfd, nready;
    struct epoll_event ev, events[MAX_EVENTS];


    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if(listenfd < 0) {
        perror("socket error:");
        return -1;
    }

    memset(&sock, 0, sizeof(sock));
    sock.sin_family = AF_INET;
    sock.sin_addr.s_addr = htonl(INADDR_ANY);
    sock.sin_port = htons(18888);
    socklen = sizeof(sock);
    if(bind(listenfd, (struct sockaddr *)&sock, socklen) != 0) {
        perror("bind error:");
        return -1;
    }
    if(listen(listenfd, 1024) != 0) {
        perror("listen error\n");
        return -1;
    }

    epollfd = epoll_create1(0);
    if(epollfd < 0) {
        perror("epoll_create1 error:");
        return -1;
    }

    ev.events = EPOLLIN;
    ev.data.fd = listenfd;
    if(epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1) {
        perror("epoll_ctl errpr");
        return -1;
    }

    for(;;) {
        nready = epoll_wait(epollfd, events, MAX_EVENTS, -1);
        if(nready == -1) {
            perror("epoll_wait error: ");
            return -1;
        }
        for(i = 0; i < nready; i++) {
            if(events[i].data.fd == listenfd) {
                clientfd = accept(listenfd, NULL, NULL);
                if(clientfd < 0) {
                    perror("accept error: ");
                    return -1;
                }
                ev.events = EPOLLIN;
                ev.data.fd = clientfd;
                if(epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1) {
                    perror("epoll_ctl error: ");
                    return -1;
                }
            }
            else {
                nread = read(events[i].data.fd, buf, MAXLINE - 1);
                if(nread < -1) {
                    perror("read error: ");
                    return -1;
                }
                else if(nread == 0) {
                    if(epoll_ctl(epollfd, EPOLL_CTL_DEL, events[i].data.fd, &events[i]) == -1) {
                        perror("epoll_ctl error: ");
                        return -1;
                    }
                    continue;
                }
                buf[nread] = '\0';
                printf("%s\n", buf);
            }
        }
    }
    return 0;
}

short events;