Epoll编程笔记

UNIX环境高级编程-读书笔记-epoll epoll 说明:epoll是linux内核为了处理大批量的文件描述符而做了改进的poll,是linux系 多路复用IO接口的selet/poll的增强版本,能够显著的提高程序在大量的并发链接中 只有少量活跃的情况下系统的CPU利用率。特点: 1.支持一个进程打开最大数量的socket 的描述符 1G内存的机器一般可以最多10 万左右的描述符 2.IO效率不会随着fd数目的增减线性的下 阅读详情

个人作品,未经允许禁止转载!!!

解决IO问题的编程模型及特点

1.阻塞等待

缺点:一个fd等待时,其他fd无法被监听

2.多线程阻塞等待

缺点:一个fd占用一个线程,线程过多,效率很低

3.非阻塞、忙轮询

缺点:无效轮询占用大量CPU时间

4.Select

缺点:能够打开的fd数量受内核限制,如需修改,需要编译内核。效率较低,每次需要遍历fd数组,复杂度和最大fd数量相同

5.Epoll

优点:用红黑树管理fd,每次返回可以读写或者发生其他事件的fd,大部分情况下效率高于select。

缺点:Linux系统的API。当每个连接都非常活跃时,效率会低于select

Epoll API(Doc地址:epoll Document)

1.头文件:

#include <sys/epoll.h>

2.创建Epoll

//size是Epoll最大监听的fd的数量,对应内部红黑树的最大节点数
//返回一个epoll的描述符 epoll fd
int epoll_create(int size);

3.设置Epoll

/*
epfd为epoll描述符
op为需要的操作,可以是EPOLL_CTL_ADD,EPOLL_CTL_MOD,EPOLL_CTL_DEL,对应添加,修改,删除
fd为需要监听的描述符,可以是文件描述符、Socket描述符等
*event定义为:
struct epoll_event {
 __uint32_t events; /* epoll 事件 */
 epoll_data_t data; /* 用户传递的数据 */
}
其中,events为监听的事件,可以是:
EPOLLIN 可读
EPOLLOUT 可写
EPOLLRDHUP socket另一端关闭连接
POLLPRI 预料之外的错误,很少用,参见https://man7.org/linux/man-pages/man2/poll.2.html
EPOLLERR 发生错误,默认开启,无法关闭,所以也没必要传入
EPOLLHUP 描述符被挂起
EPOLLET 边沿触发事件。默认情况下是水平触发
等

typedef union epoll_data {
    void *ptr;
    int fd;
    uint32_t u32;
    uint64_t u64;
} epoll_data_t;
data是一个结构体,可以是用户自行传入的任意数据的指针(这是实现Reactor模式的关键)
*/
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

4.监听Epoll事件

/*
其中
epfd是epoll描述符
epoll_event在上一步中有解释,这里传入一个数组,数组大小必须大于epoll_create中传入的最大大小
maxevents是最大监听数量,和epoll_create保持一致
timeout代表超时事件
-1为永久阻塞,直到至少有一个fd返回事件
0为立即返回
大于0时,为阻塞事件,超时返回
*/
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

5.Epoll编程常用步骤

#define MAX_FD 1024
//创建 epoll
int epfd = epoll_crete(MAX_FD);

//将监听的socket添加进 epoll 中
epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &event);

while (1) {
    //阻塞等待 epoll 中 的fd 触发
    int active_cnt = epoll_wait(epfd, events, 1000, -1);
    # 对返回的fd进行
    for (i = 0 ; i < active_cnt; i++) {
        //如果监听的fd触发,那么accept请求,并将fd加入epoll
        if (evnets[i].data.fd == listen_fd) {
        }
        //如果其他fd触发,则进行读写
        else if (events[i].events & EPOLLIN) {
        }
        else if (events[i].events & EPOLLOUT) {
        }
    }
}

Epoll回显服务器
服务器端

#include <stdio.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <unistd.h>

#define SERVER_PORT 9998
#define MAX_PENDING_CONNECTION 1024
#define EPOLL_MAX_FD (1024*1024)
#define MAX_BUFFER_SIZE (1024*1024)

using namespace std;
bool running = true;
int main(int argc, char* argv[]){
    int32_t listen_fd = 0, epoll_fd = 0;
    sockaddr_in server_addr{};

    // 创建监听套接字
    listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    // 绑定端口
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(SERVER_PORT);
    bind(listen_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
    // 监听
    listen(listen_fd, MAX_PENDING_CONNECTION);

    // 创建epoll
    epoll_fd = epoll_create(EPOLL_MAX_FD);
    if (epoll_fd < 0){
        perror("Epoll create failed");
        return -1;
    }
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = listen_fd;
    // 将监听套接字加入epoll
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &event) < 0){
        perror("Epoll CTL add listen fd");
        return -1;
    }
    struct epoll_event *events = new struct epoll_event[EPOLL_MAX_FD];
    int client_fd, active_fd;
    sockaddr_in client_addr{};
    char buffer[MAX_BUFFER_SIZE];
    while(running){
        // 等待epoll事件触发
        active_fd = epoll_wait(epoll_fd, events ,EPOLL_MAX_FD, -1);

        // 遍历epoll
        for (int i=0; i<active_fd; i++){
            // 如果是连接事件,则连接并将新的fd加入到epoll监听read事件
            if (events[i].data.fd == listen_fd){
                socklen_t clientlen = sizeof(client_addr);
                client_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &clientlen);
                if (client_fd < 0){
                    perror("Cannot accept");
                    continue;
                }
                event = {};
                event.events = EPOLLIN;
                event.data.fd = client_fd;
                epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event);
            }
            // 如果是读事件,读取并且发回去
            else if (events[i].events & EPOLLIN){
                client_fd = events[i].data.fd;
                int n = read(client_fd, buffer, MAX_BUFFER_SIZE);
                if (n < 0){
                    perror("read");
                    continue;
                }
                else if (n == 0){
                    event = {};
                    event.events = EPOLLIN | EPOLLOUT;
                    event.data.fd = client_fd;
                    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, &event);
                    close(client_fd);
                }
                else{
                    buffer[n] = '\0';
                    printf("Get Data -> %s", buffer);
                    write(client_fd, buffer, n);
                }
            }
            // 套接字可写事件,没有监听,也不处理
            else if (events[i].events & EPOLLOUT){
                printf("Send Data -> Epoll out end! \n");
            }
        }
    }
    close(epoll_fd);
    close(listen_fd);
    return 0;
}

客户端

#include <iostream>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <cstring>
#include <unistd.h>

#define MAX_BUFFER_SIZE (1024*1024)
#define SERVER_PORT 9998
int main(int argc, char* argv[]) {
    int sockfd;
    char buffer[MAX_BUFFER_SIZE];

    struct sockaddr_in server_addr{};
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
        perror("socket");
        return -1;
    }
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    if (inet_pton(AF_INET, "0.0.0.0", &server_addr.sin_addr) <= 0){
        perror("inet_pton");
        return -1;
    }

    if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0){
        perror("connect");
        return -1;
    }

    int opts = 0;
    opts = fcntl(sockfd, F_GETFL);
    opts = opts | O_NONBLOCK;
    fcntl(sockfd, F_SETFL);

    char input[1024];

    while(fgets(input, 1024, stdin) != nullptr){
        printf("Sending -> %s", input);
        int n = send(sockfd, input, strlen(input), 0);
        if (n < 0){
            perror("send");
            continue;
        }
        usleep(500000);
        n = read(sockfd, buffer, MAX_BUFFER_SIZE);
        if (n <= 0){
            perror("read");
            return -1;
        }
        else{
            buffer[n] = '\0';
            printf("Recv <- %s", buffer);
            continue;
        }
    }
    close(sockfd);
    return 0;
}

运行服务器和客户端后,从终端向客户端输入即可看到现象。

Linux epoll 学习笔记:高效事件驱动编程 epoll 是 Linux 内核提供的一种事件通知机制,用于高效地监听大量文件描述符上的事件。与传统的事件驱动机制(如 select 和 poll)相比,epoll 在处理大量并发连接时具有更好的性能和扩展性。epoll 通过在内核中创建一个事件表来存储待监听的文件描述符和相应的事件状态。应用程序通过系统调用将文件描述符添加到事件表中,并指定感兴趣的事件类型(如读、写、错误等)。当文件描述符上发生指定的事件时,内核会通知应用程序,从而实现事件驱动的编程模型。 阅读详情

相关推荐

RS-485调试踩坑实录:MAX13487数据回环问题排查与修复(附电路图)

本文深入解析了RS-485通信中MAX13487芯片的数据回环问题,提供了详细的排查过程和解决方案。通过分析芯片特性和自动换向机制,揭示了RO引脚配置不当是导致问题的关键原因,并给出硬件(添加上拉电阻)和软件(调整MCU配置)两种修复方案,附有优化设计建议和实际调试技巧。

play7的博客 1295

epoll_wait详解

epoll_pwait,等待epoll文件描述符上的I / O事件。 #include <sys / epoll.h> int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout); epoll_wait()系统调用等待文件描述符epfd引用的epoll实例上的事件。事件所指向的存储区域将包含可供调用者使用的事件。 epoll_wait()最多返回最大事件。 maxevents...

zhoumuyu_yu的博客 6万+

从ADC校准到精准测量:避开PY32F030小信号采样的那些坑

本文深入探讨了PY32F030微控制器进行ADC小信号采样时面临的挑战与解决方案。从硬件设计、软件滤波算法到系统级校准策略,详细分析了如何避开常见陷阱,实现精准测量,特别是在25mV以下小信号场景中的应用实践。

ujm567890的博客 754

嵌入式 Linux网络编程(五)——epoll机制

嵌入式 Linux网络编程(五)——epoll机制一、epoll简介epoll是在2.6内核中提出的,是select和poll的增强版本。epoll更加灵活,没有描述符限制,使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中。1、epoll函数#include <sys/epoll.h>int epoll...

weixin_33757911的博客 493

AI编程助手Cursor如何革新经济学研究:从计量模型到可视化全流程实战

在数据科学和人工智能技术蓬勃发展的今天,如何高效地将前沿AI工具融入专业领域的研究工作流,成为提升科研效率的关键。其核心原理在于通过自然语言交互,将研究者的专业问题转化为机器可执行的指令,从而实现人机协作的范式转移。这一技术的核心价值在于大幅降低非计算机背景研究者的编程门槛,将精力从繁琐的代码实现中解放出来,更聚焦于学科本身的思想与逻辑。尤其在经济学、金融学等高度依赖数据分析和计量建模的领域,AI编程助手能够快速处理数据清洗、模型估计、可视化及结果解释等任务。具体到应用场景,研究者可以利用结构化提示词工程,

weixin_42531579的博客 296

软件测试工程师笔试题

这里写目录标题全套软件测试面试笔试题(附答案)二、选折三、添空四、简答(资料是搜集整理的,感谢前辈的解题)无1.区别阶段评审的与同行评审2.什么是软件测试3. 简述集成测试的过程4 怎么做好文档测试5 白盒测试有几种方法6 系统测试计划是否需要同行审批,为什么7Alpha 测试与 beta 的区别8 比较负载测试,容量测试和强度测试的区别。性能测试从使用方便的角度来说,9 测试结束的标准是什么?10 描述软件测试活动的生命周期?11 软件的缺陷等级应如何划分? 软件测试工程师笔试题(附答案) 全套软件测试面

m0_46446002的博客 2548

Nginx源码分析 - Event事件篇 - Epoll事件模块(19)

这一篇主要讲解一下epoll事件模型。Nginx支持多种事件模型:epoll/pool/select/kqueue等。epoll比较常用,我们也比较熟悉。Nginx的epoll事件模块在/event/module/ngx_epoll_module.c

自娱自乐的代码人 1万+

手把手教你用CV5200模块搭建无人机高清图传系统(含实测数据)

本文提供了一份基于CV5200模块搭建远距离无人机高清图传系统的完整实战指南。文章详细解析了系统架构、硬件连接、软件配置与优化,并分享了实地飞行测试数据。核心在于利用CV5200的LR-WiFi私有协议,在视距下实现数公里级的稳定高清视频传输,为DIY爱好者和行业应用提供了高性价比的解决方案。

day7的博客 717

Linux编程之epoll

现在有这么一个场景:我是一个很忙的大老板,我有100个手机,手机来信息了,我的秘书就会告诉我“老板,你的手机来信息了。”我很生气,我的秘书就是这样子,每次手机来信息就只告诉我来信息了,老板赶紧去看。但是她从来不把话说清楚:到底是哪个手机来信息啊!我可有100个手机啊!于是,我只能一个一个手机去查看,来确定到底是哪几个手机来信息了。这就是IO复用中select模型的缺点!老板心想,要是秘书能把来信息...

weixin_34014555的博客 286

Ubuntu更新后VirtualBox报错rc=-1908?手把手教你修复Kernel driver not installed

本文详细解析了Ubuntu更新后VirtualBox报错rc=-1908的原因,并提供了完整的修复方案。通过重新安装VirtualBox、处理DKMS内核模块以及解决Secure Boot问题,帮助用户快速恢复虚拟机功能。特别针对Kernel driver not installed错误,给出了从诊断到修复的详细步骤。

weixin_42519781的博客 338

《AgentScope-Java 深入浅出教程》第2章 环境搭建与第一个智能体

创建// 【导入说明】/*** 时间相关工具类* 工具类是普通的 Java 类,通过注解声明为工具。* 智能体可以根据用户需求自动调用这些工具。*/// 【工具定义】获取当前时间@Tool(name = "get_current_time", // 工具名称:LLM 看到的名称description = "获取指定时区的当前时间" // 工具描述:帮助 LLM 理解何时使用// 【参数定义】// @ToolParam 的 name 属性是必需的!

步子哥的博客 417

epoll系统调用

epoll_create函数 epoll是Linux特有的I/O复用函数。它在实现上与select和poll有很大差异。epoll使用一组函数来完成任务,而select使用单个函数来完成。epoll把用户关心的文件描述符上的事件放在内核里的一个事件表中,从而无须向select和poll那样每次调用都要重复传入文件描述符集或者事件集。但是eopll需要使用一个额外的文件创建描述符,来唯一标示内核中这个事件表。这个文件描述符使用如下epoll_create函数来创建: #include <sys/e

站在树顶端设计的男人 638

模型泛化能力提升:领域自适应与迁移学习

模型泛化能力提升:领域自适应与迁移学习​ ,人工智能,计算机视觉,大模型,AI,在机器学习和深度学习的应用中,模型的泛化能力是一个关键问题。泛化能力指的是模型在新的、未见过的数据上表现良好的能力。然而,实际情况中,训练数据和测试数据往往来自不同的领域,存在分布差异,这会导致模型的泛化能力下降。领域自适应与迁移学习正是为了解决这一问题而发展起来的重要技术。

不踩坑,不知道坑有多深;不总结,踩过的坑白踩。 3万+

【网络编程】epoll 笔记

一、最大连接数 1、select select在单进程中最多同时监听1024个fd;要想实现百万并发需要一千个进程,并且性能会很差、内存消耗巨大。所以select只适用于连接数在一千个以下的场景。 2、epoll epoll本身不限制连接数,但是连接数会受到系统限制。 1)文件句柄限制 Linux系统限制单进程可以打开文件的数量默认是1024,可以使用ulimit -n命令查看。 $ ulimit...

郭老二 1637

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

Socket编程之epoll的学习笔记

1.内核事件表 epoll是Linux特有的I/O复用函数,首先,epoll使用一组函数来完成任务,而不是单个函数.其次,epoll把用户关心的文件描述符上的事件放在内核里的一个事件表中,epoll需要使用一个额外的文件描述符,来唯一标识内核的这个事件表. #include&lt;sys/epoll.h&gt; int epoll_create(int size) size只是告诉内核,事件表需...

dream0130__的博客 308

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

网络编程笔记——select、poll、epoll

五种IO模型简介 阻塞式IO,非阻塞式IO,IO复用,信号驱动式IO,异步IO为五种UNIX\LINUX下的IO模型。 阻塞式IO。默认情况下所有套接字都为阻塞,当进程调用IO系统调用时(如read,recvfrom),会从用户态转换成内核态,准备从内核的缓存区中将数据复制到进程的缓冲区,如果数据没有准备好(即没有到达套接字),则一直阻塞直到数据准备好然后进行复制(或者发生错误返回)。...

haha与长颈鹿的博客 510

linux网络编程学习笔记——epoll

文章目录epoll描述epoll突破单进程文件描述符1024的限制epoll常用基础API接口int epoll_create(int size)int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)int epoll_wait(int epfd, struct epoll_event *events, int maxe...

qq_37863891的博客 745

《Linux C编程实战》笔记:epoll多路复用

epoll是 Linux 下提供的一种,全称是event poll,用于处理大量文件描述符的可读写事件,是select和poll的。:创建一个 epoll 实例;:注册、修改、删除感兴趣的文件描述符;:等待发生事件的文件描述符。epoll 支持和两种模式,更灵活高效。EPOLLET那到底哪些因素是提高性能的更大障碍?是调用select函数后常见的针对所有文件描述符对象的循环语句?还是每次需要传递的监视对象信息?只看代码的话很容易认为是循环。但相比于循环语句,更大的障碍是每次传递监视对象信息。

ouliten的博客 917

TCPIP网络编程学习笔记——epoll

本文对比了epoll和select两种I/O多路复用技术。首先指出多进程方法的资源消耗问题,说明select通过监视套接字实现并发处理,但存在每次清空观察集合和循环检测的性能问题。epoll作为改进方案被提出,解决了select的缺点,但移植性较差。文章详细介绍了epoll的使用方法,包括创建epoll实例、设置事件和等待事件等操作。最后解释了条件触发和边缘触发的区别,以及设置边缘触发的方法。整体概括了网络编程中epoll机制的核心概念和实际应用。

m0_57222081的博客 316

《TCP/IP网络编程》阅读笔记--epoll的使用

边缘触发中输入缓冲收到数据时仅注册 1 次该事件,即使输入缓冲中还留有数据,也不会再进行注册;① EPOLL_CTL_ADD: 将文件描述符注册到 epoll 例程;② EPOLL_CTL_DEL: 从 epoll 例程中删除文件描述符;epoll_create: 创建保存 epoll 文件描述符的空间;③ EPOLL_CTL_MOD: 更改注册的文件描述符的关注事件;条件触发和边缘触发的区别在于发生事件的时间点;epoll 默认以条件触发的方式工作;epoll_ctl: 向空间注册并注销文件描述符;

牵一只蜗牛去散步 604

Linux C编程笔记----多路复用之epoll

文章目录1.epoll的相关API1.1 epoll_create1.2 epoll_ctl1.3 epoll_wait epoll因为具有比select、poll更高的效率而被广泛使用,epoll的使用也比他们更加复杂,epoll有效的利用了中断,来阻塞或者唤醒进程的方式有效减少了操作大量文件描述符的开销。 1.epoll的相关API #include <sys/epoll.h> int epoll_create(int size); int epoll_ctl(int epfd, int o

qq_44664843的博客 388

Linux网络编程——epoll学习笔记

epoll是在2.6内核中提出的,是之前的select和poll的增强版本。相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。

MR_Allen_Lwx的博客 436

【linux网络编程基础】Select,Poll,Epoll学习笔记

多路I/O复用三种方式的学习笔记

Enchanted to meet U 270

《TCP/IP网络编程》学习笔记 | Chapter 17:优于 select 的 epoll

《TCP/IP网络编程》学习笔记 | Chapter 17:优于 select 的 epoll

ProgramNovice的博客 927

学习笔记:网络编程之epoll的粗浅理解(c/c++)

再给出struct epoll_event的数据机构。

qq_62922995的博客 729

(网络编程笔记):高并发服务器模型 - epoll

多路IO-epoll 将检测文件描述符的变化委托给内核去处理,然后内核将发生变化的文件描述符对应的事件返回给应用程序。 函数介绍 int epoll_create(int size); 函数说明: 创建一颗epoll树,返回一个根节点。 参数说明: size: 最大节点数,此参数在linux 2.6.8已被忽略, 但必须传递一个大于0的数。 返回值: 成功: 返回一个大于0的文件描述符,代表整个树的树根. 失败: 返回-1,并设置errno值. int ep...

baidu_41388533的博客 491
上一篇: Libevent安装
下一篇: Libevent基础之 Reactor模式
WilliamCode
博客等级 码龄11年 118粉丝 105原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值