zoukankan      html  css  js  c++  java
  • linux epoll用法

    epoll 是 linux 特有的 I/O 复用函数。它是把用户关心的文件描述符事件放在内核的一个事件列表中,故而,无须像select和poll一样每次调用都重复传入文件描述符或事件集。但是, epoll 需要一个额外的文件描述符,来唯一标识内核中的这个事件表。这个文件描述符由 epoll_create 函数来创建:

           #include <sys/epoll.h>
    
           int epoll_create(int size);
    

      size 参数现在是被忽略的,但是,为了兼容性,需要传入一个大于0的数。

    epoll_ctl 函数来操作epoll的内核事件表:

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

      epfd是epoll_create返回的文件描述符,op指定操作类型,有如下三种:

    /* Valid opcodes ( "op" parameter ) to issue to epoll_ctl().  */
    #define EPOLL_CTL_ADD 1 /* Add a file descriptor to the interface.  */
    #define EPOLL_CTL_DEL 2 /* Remove a file descriptor from the interface.  */
    #define EPOLL_CTL_MOD 3 /* Change file descriptor epoll_event structure.  */
    

      event 参数指定事件,它是 epoll_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 */
               };
    

      events 是成员描述符事件类型。事件类型也定义在 sys/epoll.h 文件中

    enum EPOLL_EVENTS
      {
        EPOLLIN = 0x001,
    #define EPOLLIN EPOLLIN
        EPOLLPRI = 0x002,
    #define EPOLLPRI EPOLLPRI
        EPOLLOUT = 0x004,
    #define EPOLLOUT EPOLLOUT
        EPOLLRDNORM = 0x040,
    #define EPOLLRDNORM EPOLLRDNORM
        EPOLLRDBAND = 0x080,
    #define EPOLLRDBAND EPOLLRDBAND
        EPOLLWRNORM = 0x100,
    #define EPOLLWRNORM EPOLLWRNORM
        EPOLLWRBAND = 0x200,
    #define EPOLLWRBAND EPOLLWRBAND
        EPOLLMSG = 0x400,
    #define EPOLLMSG EPOLLMSG
        EPOLLERR = 0x008,
    #define EPOLLERR EPOLLERR
        EPOLLHUP = 0x010,
    #define EPOLLHUP EPOLLHUP
        EPOLLRDHUP = 0x2000,
    #define EPOLLRDHUP EPOLLRDHUP
        EPOLLEXCLUSIVE = 1u << 28,
    #define EPOLLEXCLUSIVE EPOLLEXCLUSIVE
        EPOLLWAKEUP = 1u << 29,
    #define EPOLLWAKEUP EPOLLWAKEUP
        EPOLLONESHOT = 1u << 30,
    #define EPOLLONESHOT EPOLLONESHOT
        EPOLLET = 1u << 31
    #define EPOLLET EPOLLET
      };
    

      data 是 epoll_data_t 联合体类型。可以用fd 表示文件描述符,或者用ptr指针指向更多的用户数据。

    epoll 系列系统调用的主要接口是epoll_wait函数,它在一段超时时间内等待一组文件描述符上的事件,定义:

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

      成功时,返回就绪文件描述符的个数,失败返回-1,并设置errno

    其中,timeout指定超时时间,单位毫秒。-1表示永远等待直到有文件描述符就绪。

    maxevents 指定最多监听多少个事件,它必须大于0

    epoll_wait 函数如果检测到时间,就将事件从内核事件表中复制到第二个参数events指向的数组中。这个数组只输出epoll_wait检测到的就绪事件。

     

    epoll 对文件描述符的操作有两种模式:LT(level trigger 电平触发)和 ET(edge trigger 边沿触发)。LT是默认的工作模式。在这种模式下,文件描述符会一直被检测到直到应用程序处理它。ET模式下,文件描述符就绪,被通知给应用程序,之后,就不再通知该同一事件了。ET模式降低了同一个epoll时间被重复触发的次数,因此效率较高。

     

    EPOLLONESHOT事件

    对于注册了EPOLLONESHOT事件的文件描述符,操作系统最多触发其上注册的一个可读、可写、异常事件,且只触发一次,除非我们使用 epoll_ctl 函数重置该文件描述符上注册的 EPOLLONESHOT 事件。这样就不会用多并发的问题。

     

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>
    #include <sys/epoll.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
    #include <fcntl.h>
    #include <signal.h>
    
    #include <map>
    #include <string>
    
    using namespace std;
    
    #define CLIENTSIZE 5000
    #define BUFSIZE 4000
    
    
    int createSocket()
    {
        struct sockaddr_in servaddr;
        int listenfd = -1;
    
        if (-1 == (listenfd = socket(PF_INET, SOCK_STREAM, 0)))
        {
            fprintf(stderr, "socket: %d, %s
    ", errno, strerror(errno));
            exit(1);
        }
    
        int reuseaddr = 1;
        if (-1 == setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)))
        {
            fprintf(stderr, "setsockopt: %d, %s
    ", errno, strerror(errno));
            exit(1);
        }
    
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = PF_INET;
        servaddr.sin_port = htons(8008);
        inet_pton(PF_INET, "0.0.0.0", &servaddr.sin_addr);
    
        if (-1 == bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)))
        {
            fprintf(stderr, "bind: %d, %s
    ", errno, strerror(errno));
            exit(1);
        }
    
        if (-1 == listen(listenfd, 5))
        {
            fprintf(stderr, "listen: %d, %s
    ", errno, strerror(errno));
            exit(1);
        }
    
        return listenfd;
    }
    
    int setnoblock(int fd)
    {
        int oldopt = fcntl(fd, F_GETFL);
        int newopt = oldopt | O_NONBLOCK;
        fcntl(fd, F_SETFL, newopt);
        return oldopt;
    }
    
    void ErrExit(const char* reason)
    {
        fprintf(stderr, "%s: %d, %s
    ", reason, errno, strerror(errno));
        exit(1);
    }
    
    void addsig(int sig, void (*handler)(int))
    {
        int olderrno = errno;
        struct sigaction ac;
    
        memset(&ac, 0, sizeof(ac));
        ac.sa_handler = handler;
        ac.sa_flags |= SA_RESTART;
        sigfillset(&ac.sa_mask);
        if (-1 == sigaction(sig, &ac, NULL))
        {
            ErrExit("sigaction");
        }
        errno = olderrno;
    }
    
    void addfd(int epfd, int fd)
    {
        struct epoll_event ev;
    
        ev.events = EPOLLIN | EPOLLET | EPOLLERR;
        ev.data.fd = fd;
        if (-1 == epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev))
        {
            ErrExit("epoll_ctl");
        }
        setnoblock(fd);
    }
    
    void delfd(int epfd, int fd)
    {
        struct epoll_event ev;
    
        ev.data.fd = fd;
        if (-1 == epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev))
        {
            ErrExit("epoll_ctl");
        }
    }
    
    int main(int argc, char const *argv[])
    {
        int listenfd = createSocket();
        int epfd = -1;
        map<int, string> mapdata;
    
        if (-1 == (epfd = epoll_create(CLIENTSIZE)))
        {
            ErrExit("epoll_create");
        }
    
        struct epoll_event evs[CLIENTSIZE];
        addfd(epfd, listenfd);
    
        while (1)
        {
            int connnum = epoll_wait(epfd, evs, CLIENTSIZE, -1);
            for (int i = 0; i < connnum; ++i)
            {
                if (evs[i].events & EPOLLERR)
                {
                    printf("%d exit
    ", evs[i].data.fd);
                    delfd(epfd, evs[i].data.fd);
                    close(evs[i].data.fd);
                    mapdata.erase(evs[i].data.fd);
                }
                else if (evs[i].data.fd == listenfd && (evs[i].events & EPOLLIN))
                {
                    struct sockaddr_in client;
                    socklen_t len = sizeof(client);
                    int cfd = accept(listenfd, (struct sockaddr*)&client, &len);
                    if (cfd == -1)
                    {
                        ErrExit("accept");
                    }
                    printf("get connection: %d
    ", cfd);
                    addfd(epfd, cfd);
                }
                else if (evs[i].events & EPOLLIN)
                {
                    char buf[BUFSIZE] = {0};
                    int len = recv(evs[i].data.fd, buf, BUFSIZE-1, 0);
                    if (len > 0)
                    {
                        mapdata[evs[i].data.fd] = buf;
                        evs[i].events &= (~EPOLLIN);
                        evs[i].events |= EPOLLOUT;
                        if (-1 == epoll_ctl(epfd, EPOLL_CTL_MOD, evs[i].data.fd, &evs[i]))
                        {
                            ErrExit("epoll_ctl");
                        }
                    }
                    else if (len == 0)
                    {
                        printf("%d exit
    ", evs[i].data.fd);
                        delfd(epfd, evs[i].data.fd);
                        close(evs[i].data.fd);
                        mapdata.erase(evs[i].data.fd);
                    }
                    else
                    {
                        ErrExit("recv");
                    }
                }
                else if (evs[i].events & EPOLLOUT)
                {
                    if (send(evs[i].data.fd, mapdata[evs[i].data.fd].c_str(), mapdata[evs[i].data.fd].size(), 0) < 0)
                    {
                        if (errno == 104)
                        {
                            continue;
                        }
                        ErrExit("send");
                    }
                    evs[i].events &= (~EPOLLOUT);
                    evs[i].events |= EPOLLIN;
                    if (-1 == epoll_ctl(epfd, EPOLL_CTL_MOD, evs[i].data.fd, &evs[i]))
                    {
                        ErrExit("epoll_ctl");
                    }
                }
            }
        }
    
        close(listenfd);
        close(epfd);
        
        return 0;
    }
    

      

  • 相关阅读:
    jquery省市联动,根据公司需求而写
    jquery.easyui使用详解,和遇到的问题,提供大家在使用的时候少走弯路(二)
    div内容滚动,无缝滚动
    使用CSS修改HTML5 input placeholder颜色( 转载 )
    如何编写规范,灵活,稳定,高质量的HTML和css代码
    div+css实现未知宽高元素垂直水平居中
    原生JavaScript实现的addclass,removeclass,hasclass,toggleclass,getbyclass
    JS判断上传图片格式是否正确
    文字超出限制字数后隐藏
    JS判断输入框值是否为空
  • 原文地址:https://www.cnblogs.com/zuofaqi/p/9638098.html
Copyright © 2011-2022 走看看