• 学习使用epoll The time is passing ITeye技术网站


    学习使用epoll - The time is passing - ITeye技术网站

    epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。

     

     

    一、epoll的优点

    支持一个进程打开大数目的socket描述符

    IO效率不随FD数目增加而线性下降

     

    二、epoll的使用

    epoll2种工作方式:LTET。 

    LTlevel triggered,水平触发是缺省的工作方式,并且同时支持blockno-block socket.在这种做法中,

    内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任操作,

    内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型

    的代表。 

     

    ET edge-triggered,边缘触发是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未

    就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文

    件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在

    发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。但是

    请注意,如果一直不对这个fdIO操作(从而导致它再次变成未就绪),内核不会发送更多的通知

    only once

     

    epoll相关的系统调用有3epoll_create, epoll_ctlepoll_wait。在头文件<sys/epoll.h>

     

    1. int epoll_create(int size);

    创建一个epoll句柄,即图中的epfd, 用来监听事件, size用来告诉内核这个监听的数目一共有多大。

    这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽

     

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

    参数op是操作类型, 使用这个方法完成3种操作:

    EPOLL_CTL_ADD:注册新的fdepfd中;
    EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
    EPOLL_CTL_DEL:从epfd中删除一个fd

    (1) 注册新事件

    C代码  收藏代码
    1. struct epoll_event ev;  
    2. ev.data.fd = fd;  
    3. ev.events = EPOLLIN;  
    4. epoll_cntl(epfd, EPOOL_CTL_ADD, fd, &ev);  
     

     

    (2) 修改监听事件

     

    C代码  收藏代码
    1. struct epoll_event *a_event = get_a_event()   
    2. struct epoll_event ev;  
    3. ev.data.fd = a_event->data.fd;  
    4. ev.events = a_event->events | EPOLLOUT;  
    5. epoll_cntl(epfd, EPOOL_CTL_MOD, a_event->data.fd, &ev);  
     

     

    (3) 删除事件

     

    C代码  收藏代码
    1. struct epoll_event *a_event = get_a_event()   
    2. struct epoll_event ev;  
    3. ev.data.fd = a_event->data.fd;  
    4. epoll_cntl(epfd, EPOOL_CTL_DEL, a_event->data.fd, &ev);  
     

     

    3种操作都使用了一个ev变量, 这个变量用来关联fd和它的监听事件, 是临时的, 可以反复使用.

    ev是一个struct epoll_event结构体, 结构如下:

    1. typedef union epoll_data {  
    2.    void *ptr;  
    3.    int fd;  
    4.    __uint32_t u32;  
    5.    __uint64_t u64;  
    6. } epoll_data_t;  
    7.   
    8. struct epoll_event {  
    9.    __uint32_t events; /* Epoll events */  
    10.    epoll_data_t data; /* User data variable */  
    11. };  

    events可以是以下几个宏的集合:
    EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
    EPOLLOUT:表示对应的文件描述符可以写;
    EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
    EPOLLERR:表示对应的文件描述符发生错误;
    EPOLLHUP:表示对应的文件描述符被挂断;
    EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。

    注意多个socket可以设置不同的触发模式
    EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里


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

    等待事件的产生, 把产生的事件存放到events数组里, 如图中所示, 调用epoll_wait后, 

    fd 1和 fd 3和fd k产生了事件, 把它们分别存放到events[0], events[1], events[2]
    参数epfdepoll_create()函数返回的epoll句柄。

    参数eventsstruct epoll_event结构指针,用来从内核得到事件的集合

    参数 maxevents内核这个events有多大

    参数 timeout: 等待时的超时时间,以毫秒为单位。

    返回值:成功时,返回需要处理的事件数目。调用失败时,返回0,表示等待超时。

     

     

    三 epoll实例 -- 模拟HTTP服务器 

     

     

    C代码  收藏代码
    1. #include <sys/socket.h>  
    2. #include <sys/wait.h>  
    3. #include <netinet/in.h>  
    4. #include <netinet/tcp.h>  
    5. #include <sys/epoll.h>  
    6. #include <sys/sendfile.h>  
    7. #include <sys/stat.h>  
    8. #include <unistd.h>  
    9. #include <stdio.h>  
    10. #include <stdlib.h>  
    11. #include <string.h>  
    12. #include <strings.h>  
    13. #include <fcntl.h>  
    14. #include <errno.h>   
    15.   
    16. #define MAX_EVENTS 10  
    17. #define PORT 8080  
    18.   
    19. //设置socket连接为非阻塞模式  
    20. void setnonblocking(int sockfd) {  
    21.     int opts;  
    22.   
    23.     opts = fcntl(sockfd, F_GETFL);  
    24.     if(opts < 0) {  
    25.         perror("fcntl(F_GETFL)\n");  
    26.         exit(1);  
    27.     }  
    28.     opts = (opts | O_NONBLOCK);  
    29.     if(fcntl(sockfd, F_SETFL, opts) < 0) {  
    30.         perror("fcntl(F_SETFL)\n");  
    31.         exit(1);  
    32.     }  
    33. }  
    34.   
    35. int main(){  
    36.     struct epoll_event ev, events[MAX_EVENTS];  
    37.     int addrlen, listenfd, conn_sock, nfds, epfd, fd, i, nread, n;  
    38.     struct sockaddr_in local, remote;  
    39.     char buf[BUFSIZ];  
    40.   
    41.     //创建listen socket  
    42.     if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {  
    43.         perror("sockfd\n");  
    44.         exit(1);  
    45.     }  
    46.     bzero(&local, sizeof(local));  
    47.     local.sin_family = AF_INET;  
    48.     local.sin_addr.s_addr = htonl(INADDR_ANY);;  
    49.     local.sin_port = htons(PORT);  
    50.     if( bind(listenfd, (struct sockaddr *) &local, sizeof(local)) < 0) {  
    51.         perror("bind\n");  
    52.         exit(1);  
    53.     }  
    54.     listen(listenfd, 20);  
    55.   
    56.     epfd = epoll_create(MAX_EVENTS);  
    57.     if (epfd == -1) {  
    58.         perror("epoll_create");  
    59.         exit(EXIT_FAILURE);  
    60.     }  
    61.   
    62.     ev.events = EPOLLIN;  
    63.     ev.data.fd = listenfd;  
    64.     if (epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev) == -1) {  
    65.         perror("epoll_ctl: listen_sock");  
    66.         exit(EXIT_FAILURE);  
    67.     }  
    68.   
    69.     for (;;) {  
    70.         nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);  
    71.         if (nfds == -1) {  
    72.             perror("epoll_pwait");  
    73.             exit(EXIT_FAILURE);  
    74.         }  
    75.   
    76.         for (i = 0; i < nfds; ++i) {  
    77.             fd = events[i].data.fd;  
    78.             if (fd == listenfd) {  
    79.                 conn_sock = accept(listenfd,  
    80.                         (struct sockaddr *) &remote, &addrlen);  
    81.                 if (conn_sock == -1) {  
    82.                     perror("accept");  
    83.                     exit(EXIT_FAILURE);  
    84.                 }  
    85.                 setnonblocking(conn_sock);  
    86.                 ev.events = EPOLLIN | EPOLLET;  
    87.                 ev.data.fd = conn_sock;  
    88.                 if (epoll_ctl(epfd, EPOLL_CTL_ADD, conn_sock,  
    89.                             &ev) == -1) {  
    90.                     perror("epoll_ctl: add");  
    91.                     exit(EXIT_FAILURE);  
    92.                 }  
    93.                 continue;  
    94.             }    
    95.             if (events[i].events & EPOLLIN) {  
    96.                 n = 0;  
    97.                 while ((nread = read(fd, buf + n, BUFSIZ-1)) > 0) {  
    98.                     n += nread;  
    99.                 }  
    100.                 buf[n] = '\0';  
    101.   
    102.                 ev.data.fd = fd;  
    103.                 ev.events = events[i].events | EPOLLOUT;  
    104.                 if (epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev) == -1) {  
    105.                     perror("epoll_ctl: mod");  
    106.                 }  
    107.             }  
    108.             if (events[i].events & EPOLLOUT) {  
    109.                 sprintf(buf, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\nHello World", 11);  
    110.                 n = strlen(buf);  
    111.                 if (write(fd, buf, n) < n) {  
    112.                     perror("write");  
    113.                 }  
    114.                 close(fd);  
    115.             }  
    116.         }  
    117.     }  
    118.   
    119.     return 0;  
    120. }  
     

    运行程序后, 打开浏览器:

  • 相关阅读:
    Ubuntu16.04 安装Teamviewer
    Redis 中的事务
    apache rewrite .htaccess 站点内容重定向实例
    PHP_EOL常量
    PHP 设计模式之适配器模式
    MYSQL优化
    php设计模式之简单工厂模式
    php设计模式之单例模式
    PHP设计模式之策略模式
    PHP 设计模式之观察者模式 (转载)
  • 原文地址:https://www.cnblogs.com/lexus/p/2855592.html
Copyright © 2020-2023  润新知