epoll两种模式(lt/et)复习

news/2024/7/9 17:19:49 标签: epoll, lt, et

0x01 缘由

    最近又在复习一些基础知识,现在看看epoll的相关知识点。

0x02 介绍

    epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。epoll除了提供select/poll那种IO事件的水平触发(Level Triggered)外,还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。(摘自百度百科)
    两种模式(简单理解):
    LT(水平触发): 对于采用LT工作模式的文件描述符,当epoll_wait检测到其上有事件发生并将此事件通知应用程序后,应用程序可以不立即处理该事件,当应用程序下一次调用epoll_wait时,epoll_waite还会再次向应用程序通告此事件,直到该时间被处理。
    ET(边缘触发):对于采用ET工作模式的文件描述符,当epoll_wait检测到其上有事件发生并将此事件通知应用程序后,应用程序必须立即处理该事件,因后续的epoll_wait调用将不再向应用程序通知这件事。
0x03 示例和测试
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <pthread.h>

#define MAX_EVENT_NUMBER 1024
#define BUFFER_SIZE 10

int setnonblocking( int fd )
{
    int old_option = fcntl( fd, F_GETFL );
    int new_option = old_option | O_NONBLOCK;
    fcntl( fd, F_SETFL, new_option );
    return old_option;
}

void addfd( int epollfd, int fd, bool enable_et )
{
    epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN;
    if( enable_et )
    {
        event.events |= EPOLLET;
    }
    epoll_ctl( epollfd, EPOLL_CTL_ADD, fd, &event );
    setnonblocking( fd );
}

void lt( epoll_event* events, int number, int epollfd, int listenfd )
{
    char buf[ BUFFER_SIZE ];
    for ( int i = 0; i < number; i++ )
    {
        int sockfd = events[i].data.fd;
        if ( sockfd == listenfd )
        {
            struct sockaddr_in client_address;
            socklen_t client_addrlength = sizeof( client_address );
            int connfd = accept( listenfd, ( struct sockaddr* )&client_address, &client_addrlength );
            addfd( epollfd, connfd, false );
        }
        else if ( events[i].events & EPOLLIN )
        {
            printf( "event trigger once\n" );
            memset( buf, '\0', BUFFER_SIZE );
            int ret = recv( sockfd, buf, BUFFER_SIZE-1, 0 );
            if( ret <= 0 )
            {
                close( sockfd );
                continue;
            }
            printf( "get %d bytes of content: %s\n", ret, buf );
        }
        else
        {
            printf( "something else happened \n" );
        }
    }
}

void et( epoll_event* events, int number, int epollfd, int listenfd )
{
    char buf[ BUFFER_SIZE ];
    for ( int i = 0; i < number; i++ )
    {
        int sockfd = events[i].data.fd;
        if ( sockfd == listenfd )
        {
            struct sockaddr_in client_address;
            socklen_t client_addrlength = sizeof( client_address );
            int connfd = accept( listenfd, ( struct sockaddr* )&client_address, &client_addrlength );
            addfd( epollfd, connfd, true );
        }
        else if ( events[i].events & EPOLLIN )
        {
            printf( "event trigger once\n" );
            while( 1 )
            {
                memset( buf, '\0', BUFFER_SIZE );
                int ret = recv( sockfd, buf, BUFFER_SIZE-1, 0 );
                if( ret < 0 )
                {
                    if( ( errno == EAGAIN ) || ( errno == EWOULDBLOCK ) )
                    {
                        printf( "read later\n" );
                        break;
                    }
                    close( sockfd );
                    break;
                }
                else if( ret == 0 )
                {
                    close( sockfd );
                }
                else
                {
                    printf( "get %d bytes of content: %s\n", ret, buf );
                }
            }
        }
        else
        {
            printf( "something else happened \n" );
        }
    }
}

int main( int argc, char* argv[] )
{
    if( argc <= 2 )
    {
        printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi( argv[2] );

    int ret = 0;
    struct sockaddr_in address;
    bzero( &address, sizeof( address ) );
    address.sin_family = AF_INET;
    inet_pton( AF_INET, ip, &address.sin_addr );
    address.sin_port = htons( port );

    int listenfd = socket( PF_INET, SOCK_STREAM, 0 );
    assert( listenfd >= 0 );

    ret = bind( listenfd, ( struct sockaddr* )&address, sizeof( address ) );
    assert( ret != -1 );

    ret = listen( listenfd, 5 );
    assert( ret != -1 );

    epoll_event events[ MAX_EVENT_NUMBER ];
    int epollfd = epoll_create( 5 );
    assert( epollfd != -1 );
    addfd( epollfd, listenfd, true );

    while( 1 )
    {
        int ret = epoll_wait( epollfd, events, MAX_EVENT_NUMBER, -1 );
        if ( ret < 0 )
        {
            printf( "epoll failure\n" );
            break;
        }
    
        lt( events, ret, epollfd, listenfd );
        //et( events, ret, epollfd, listenfd );
    }

    close( listenfd );
    return 0;
}


编译两个程序:
    gcc -o lt main.cpp
    gcc -o et main.cpp
运行:
    lt模式:
    lt="" style="display:inline-block; position:relative; padding:0px; margin-right:0px; margin-bottom:0px; margin-left:0px; vertical-align:text-bottom; max-width:100%" />et/20180108163925729?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcGFuZ3llbWVuZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" align="middle" alt="" />
    et/20180108163943195?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcGFuZ3llbWVuZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" align="middle" alt="" />lt="" style="display:inline-block; position:relative; padding:0px; margin-right:0px; margin-bottom:0px; margin-left:0px; vertical-align:text-bottom; max-width:100%" />
    两次事件触发。buffer中的数据做两次触发读出。
    et模式:
    et/20180108164020474?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcGFuZ3llbWVuZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" align="middle" alt="" />
et/20180108164042034?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcGFuZ3llbWVuZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" align="middle" alt="" />
lt="" style="display:inline-block; position:relative; padding:0px; margin-right:0px; margin-bottom:0px; margin-left:0px; vertical-align:text-bottom; max-width:100%" />
lt="" style="display:inline-block; position:relative; padding:0px; margin-right:0px; margin-bottom:0px; margin-left:0px; vertical-align:text-bottom; max-width:100%" />
    仅触发了一次事件,将buffer中的数据全部读出。

0x04 总结

    lt模式和et模式,两种模式的差异,是内核事件的处理方式的不同。

http://www.niftyadmin.cn/n/1460668.html

相关文章

mysql in group by_mysql isn't in GROUP BY

今天安装新环境使用group by 语句查询报错isnt in GROUP BY!如图:以前时候也没有报啊~ 想到一定是新环境mysql版本过高了配置文件不一样了!isnt in GROUP BY报错翻译:意思要分组的列不在select的字段中(*这里重点 group by *号不代…

复习--友元

## 为什么要有友元? 实际开发过程中没有用过,这里只做文字和代码层面的理解和学习。 友元的作用:可以让对应的友元函数和类可以访问当前类的私有变量和保护类变量。 类的友元函数是定义在类外部,但有权访问类的所有私有&#x…

mysql lock not wait_关于MySQL的lock wait timeout exceeded解决方案

关于MySQL出现lock wait timeout exceeded; try restarting transaction 的解决方案。一、问题抛出在做查询语句时,MySQL 抛出了这样的异常:MySQL server error report:Array([0] > Array([message] > MySQL Query Error)[1] > Array([sql] >…

CentOS7忘记root密码,重置root密码

描述 当root密码忘了之后,可以通过重启系统,在开机时重新设置密码。 注:测试版本为CentOS Linux release 7.6.1810 (Core),其他版本可能有些区别。 重置密码 1、重启系统,在开机过程中,出现以下界面时按e键,进入编辑 按e键之后进入如下界面: 2、按方向键下键↓…

复习--重载

## 什么是重载 C重载分为:函数重载、操作符重载 函数重载:函数名相同参数不同 操作符重载:对一些常规操作进行重载,这样方便对象之间的操作。 反例:返回值不同,不能作为函数重载的差异 ## 重载的用法 …

yum安装nginx php mysql_yum安装nginx+PHP+Mysql

#mkdir /var/www/yum_repo1、nginx安装:在http://nginx.org/en/linux_packages.html#stable中下载CentOSX对应版本的rpm文件把下载的nginx-release-centos-5-0.el5.ngx.noarch.rpm拷贝到yum_repo目录下#rpm -Uvh nginx-release-centos-5-0.el5.ngx.noarch.rpm#yum i…

如何在 Kali Linux 上安装 SSH 服务

目的 我们的目的是 Kali Linux 上安装 SSH(安全 shell)。 要求 你需要有特权访问你的 Kali Linux 安装或者 Live 系统。 困难程度 很容易! 惯例 #- 给定命令需要以 root 用户权限运行或者使用sudo命令 $- 给定命令以常规权限用户运行…

Kali Linux网络配置

Kali Linux拥有强大的网络功能,所使用的网络配置文件与其他的Linux发行版也不同,Kali Linux属于Debian Linux 1、基本的网络命令 ifconfig命令用于显示当前主机中状态为“激活”的网络接口信息。 ifconfig -a命令用于显示当前主机中所有网络接口信息&…