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);
            }

        }

    }

}

猜你喜欢

转载自blog.csdn.net/dengshengli123/article/details/80670374