Linux下Epoll簡介

weixin_34119545發表於2013-04-23

什麼是epoll?簡單來講,就是替代select的一種方法。如果不知到select是什麼,就不用往下看了。

為什麼要替換掉select?大家說,原因有兩個:

1)Select有連結數量限制,epoll沒有。

2)select在處理大兩併發時效率低。

關於第一個原因,搜了下結果如下:

我本人也曾經在專案中用過select和epoll,對於select,感觸最深的是linux下select最大數目限制(windows 下似乎沒有限制),每個程式的select最多能處理FD_SETSIZE個FD(檔案控制程式碼),
如果要處理超過1024個控制程式碼,只能採用多程式了。
常見的使用slect的多程式模型是這樣的: 一個程式專門accept,成功後將fd通過unix socket傳遞給子程式處理,父程式可以根據子程式負載分派。曾經用過1個父程式+4個子程式 承載了超過4000個的負載。
這種模型在我們當時的業務執行的非常好。epoll在連線數方面沒有限制,當然可能需要使用者呼叫API重現設定程式的資源限制。

第二個原因,作者在部落格裡也做了介紹。select最終呼叫是net/ipv4/tcp.c,一直在做輪尋;而epoll則是等待事件發生時,告訴程式現在的socket是可讀還是可寫。

對上面內容哦你感興趣的可以戳這裡:http://www.cppblog.com/feixuwu/archive/2010/07/10/119995.html

瞭解就這麼多,很可能是錯的,今天只是敲了下demo程式碼,暫時放這裡。

//server.c
#include    <stdio.h>
#include    <string.h>
#include    <fcntl.h>
#include    <sys/epoll.h>
#include    <sys/socket.h>
#include    <netinet/in.h>

#define MAX_SOCKET 10000

void
add_event(int epfd, int fd, struct epoll_event *event)
{
    epoll_ctl(epfd, EPOLL_CTL_ADD, fd, event);
}

void
mod_event(int epfd, int fd, struct epoll_event *event)
{
    epoll_ctl(epfd, EPOLL_CTL_MOD, fd, event);
}

void
del_event(int epfd, int fd, struct epoll_event *event)
{
    epoll_ctl(epfd, EPOLL_CTL_DEL, fd, event);
}

int
init_listen(int epfd, int port)
{
    int listenfd = socket(AF_INET, SOCK_STREAM,  0);
    fcntl(listenfd, F_SETFL, O_NONBLOCK);

    struct epoll_event event;
    event.data.fd = listenfd;
    event.events = EPOLLIN | EPOLLET;
    add_event(epfd, listenfd, &event);

    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(struct sockaddr_in));
    serveraddr.sin_family = AF_INET;
    inet_aton("127.0.0.1",&(serveraddr.sin_addr));
    serveraddr.sin_port = htons(port);
    bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr_in));
    listen(listenfd, 10);
    return listenfd;
}

void
accept_conn(int epfd, int listenfd)
{
    printf("connection begin...\n");
    socklen_t length;
    struct sockaddr_in clientaddr;
    memset( &clientaddr, 0, sizeof(struct sockaddr_in));
    int fd = accept(listenfd, (struct sockaddr *)&clientaddr, &length);
    fcntl(fd, F_SETFL, O_NONBLOCK);
    printf("connected...\n");

    char *clientip =inet_ntoa(clientaddr.sin_addr);
    if(clientip == NULL) printf("error!\n");
    unsigned short clientport = ntohs(clientaddr.sin_port);
//    printf("client connected: %s:%d\n",clientip, clientport);

    struct epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET;
    add_event(epfd, fd, &event);
}

void
recv_data(int epfd, int fd)
{
    char buffer[1024];
    memset(buffer, 0, sizeof(buffer));

    ssize_t count = read(fd, buffer, sizeof(buffer));
    if (count <= 0){
        close(fd);
        return;
    }

    printf("fd %d recv: %s \n", fd, buffer);

    struct epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLOUT | EPOLLET;
    mod_event(epfd, fd, &event);
}

void
send_data(int epfd, int fd)
{
    char buffer[1024];
    static int i=0;
    memset(buffer, 0, sizeof(buffer));
    i++;
    sprintf(buffer, "hello fd %d,i=%d", fd,i);
    ssize_t count = write(fd, buffer, strlen(buffer));
    if (count <= 0){
        close(fd);
        return;
    }

    printf("fd %d send: %s\n", fd, buffer);

    struct epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET;
    mod_event(epfd, fd, &event);
}

int
main()
{
    int port = 12345;
    int epfd = epoll_create(MAX_SOCKET);
    int listenfd = init_listen(epfd, port);
    printf("fd %d listen: %d\n", listenfd, port);

    while (1) {
        printf("epoll_wait.. \n");
        struct epoll_event events[20];
        int fds = epoll_wait(epfd, events, 20, 5000);
        printf("epoll_wait fds:%d\n", fds);

        int i;
        for (i = 0; i < fds; i++){
            int fd = events[i].data.fd;
            if (fd == listenfd){
                printf("accept_conn...\n");
                accept_conn(epfd, listenfd);
                continue;
            }
            if (events[i].events & EPOLLIN) {
                printf("recv_data fd: %d\n", fd);
                recv_data(epfd, fd);
            }
            if (events[i].events & EPOLLOUT) {
                printf("send_data fd; %d\n", fd);
                send_data(epfd, fd);
            }
        }
    }
    close(epfd);
}

再貼個client:

/client
#include    <sys/types.h>
#include    <sys/socket.h>
#include    <stdio.h>
#include    <netinet/in.h>
#include    <arpa/inet.h>
#include    <unistd.h>
#include    <stdlib.h>


int
main()
{
    int sockfd;
    int len;
    int result;
    char ch[20]={'1','2','3'};

    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in address;
    address.sin_family = AF_INET;
//    strcpy(address.sun_path, "server_socket");
    inet_aton("127.0.0.1",&(address.sin_addr));
    len = sizeof(address);
    address.sin_port = htons(12345);

    result = connect(sockfd, (struct sockaddr *)&address, len);

    if(result == -1) {
        perror("oops; connect failed");
        exit(1);
    }

    write(sockfd, ch, 4);
    read(sockfd, ch, 5);
    printf("char from server = %s \n", ch);
    close(sockfd);
    exit(0);
}

偶得空閒,只為對epool有個感性認識。

相關文章