Nginx源码分析:epoll

epoll简介 :     用于支持高并发的事件机制
优点 :    1、支持打开一个很大个数的socket连接数(linux socket连接默认是2048),        最大可以达到最大文件数目( cat /proc/sys/fs/file-max, 大小和系统内存相关)
        ps : apache使用的就是多进程方式(一个进程最大支持2048 socket连接,通过增多进程来实现)
    2、IO效率不随FD的增多而降低        select/poll会随着socket集合的增大而变慢,因为每次调用都会线性扫描全部的集合,效率呈线性下降        epoll只扫描活跃的soket,根据每个fd上的callback回调
    3、使用mmap加速内核和用户空间的消息传递

    4、内核微调

触发方式 :    1、边缘触发    2、水平触发

相关函数使用 :
    是创建size大小的文件描述符    epoll_create(int size);    
    等待事件的发生    epoll_wait(int epollfd,struct epoll_event *events,int maxevent,int timeout);
    用来向内核注册,删除,修改一个文件描述符的    epoll_ctl(int epollfd,int op,struct epoll_event *events);

例子 :#include <iostream>#include <sys/socket.h>#include <sys/epoll.h>#include <netinet/in.h>#include <arpa/inet.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h>
#define MAXLINE 10#define OPEN_MAX 100#define LISTENQ 20#define SERV_PORT 5555#define INFTIM 1000
void setnonblocking(int sock){    int opts;    opts = fcntl(sock, F_GETFL);    if(opts < 0)    {        perror("fcntl(sock,GETFL)");        exit(1);    }    opts = opts | O_NONBLOCK;    if(fcntl(sock, F_SETFL, opts) < 0)    {        perror("fcntl(sock,SETFL,opts)");        exit(1);    }}
int main(){    int i, maxi, listenfd, connfd, sockfd, epfd, nfds;    ssize_t n;    char line[MAXLINE];    socklen_t clilen;    //声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件    struct epoll_event ev, events[20];    //生成用于处理accept的epoll专用的文件描述符    epfd = epoll_create(256);
    struct sockaddr_in clientaddr;    struct sockaddr_in serveraddr;    listenfd = socket(AF_INET, SOCK_STREAM, 0);    //把socket设置为非阻塞方式    setnonblocking(listenfd);    //设置与要处理的事件相关的文件描述符    ev.data.fd = listenfd;    //设置要处理的事件类型    ev.events = EPOLLIN | EPOLLET;    //注册epoll事件    epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);
    bzero(&serveraddr, sizeof(serveraddr));    serveraddr.sin_family = AF_INET;
    char *local_addr = "200.200.200.204";    inet_aton(local_addr, &(serveraddr.sin_addr)); //htons(SERV_PORT);    serveraddr.sin_port = htons(SERV_PORT);    bind(listenfd, (sockaddr *)&serveraddr, sizeof(serveraddr));    listen(listenfd, LISTENQ);
    maxi = 0;    for ( ; ; )    {        //等待epoll事件的发生        nfds = epoll_wait(epfd, events, 20, 500);        //处理所发生的所有事件        for(i = 0; i < nfds; ++i)        {            if(events[i].data.fd == listenfd)            {
                connfd = accept(listenfd, (sockaddr *)&clientaddr, &clilen);                if(connfd < 0)                {                    perror("connfd<0");                    exit(1);                }                setnonblocking(connfd);
                char *str = inet_ntoa(clientaddr.sin_addr);                std::cout << "connect from " < _u115 ? tr << std::endl;                //设置用于读操作的文件描述符                ev.data.fd = connfd;                //设置用于注测的读操作事件                ev.events = EPOLLIN | EPOLLET;                //注册ev                epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);            }            else if(events[i].events & EPOLLIN)            {                if ( (sockfd = events[i].data.fd) < 0) continue;                if ( (n = read(sockfd, line, MAXLINE)) < 0)                {                    if (errno == ECONNRESET)                    {
                        close(sockfd);                        events[i].data.fd = -1;                    }                    else                        std::cout << "readline error" << std::endl;                }                else if (n == 0)                {                    close(sockfd);                    events[i].data.fd = -1;                }                //设置用于写操作的文件描述符                ev.data.fd = sockfd;                //设置用于注测的写操作事件                ev.events = EPOLLOUT | EPOLLET;                //修改sockfd上要处理的事件为EPOLLOUT                epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);            }            else if(events[i].events & EPOLLOUT)            {                sockfd = events[i].data.fd;                write(sockfd, line, n);                //设置用于读操作的文件描述符                ev.data.fd = sockfd;                //设置用于注测的读操作事件                ev.events = EPOLLIN | EPOLLET;                //修改sockfd上要处理的事件为EPOLIN                epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);            }
        }
    }
}
文章来源: Nginx源码分析:epoll

人吐槽 人点赞

猜你喜欢

发表评论

用户名: 密码:
验证码: 匿名发表

你可以使用这些语言

查看评论:Nginx源码分析:epoll