epoll实现 IO复用

1、epoll实现 IO复用

epoll的提出--》它所支持的文件描述符上限是系统可以最大打开的文件的数目;eg:1GB机器上,这个上限10万个左右。 每个fd上面有callback(回调函数)函数,只有活跃的fd才有主动调用callback,不需要轮询。 注意: Epoll处理高并发,百万级,不关心底层怎样实现,只需要会调用就可以。

函数接口

#include <sys/epoll.h>
int epoll_create(int size); 
功能:创建红黑树根节点
 参数:size:不作为实际意义值 >0 即可
返回值:成功时返回epoll文件描述符,失败时返回-1

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:控制epoll属性
    epfd:epoll_create函数的返回句柄。7、】=
    op:表示动作类型。有三个宏 来表示:
            EPOLL_CTL_ADD:注册新的fdepfd
            EPOLL_CTL_MOD:修改已注册fd的监听事件
            EPOLL_CTL_DEL:从epfd中删除一个fd
    Fd:需要监听的fd
            event:告诉内核需要监听什么事件
            EPOLLIN:表示对应文件描述符可读
            EPOLLOUT:可写
            EPOLLPRI:有紧急数据可读;
            EPOLLERR:错误;
            EPOLLHUP:被挂断;
            EPOLLET:触发方式,边缘触发;(默认使用边缘触发)
             ET模式:表示状态的变化;
返回值:成功时返回0,失败时返回-1

typedef union epoll_data 
{
      void* ptr;(无效)
      int fd;
      uint32_t u32;
      uint64_t u64;
epoll_data_t;
struct epoll_event 
{
    uint32_t events; / * Epoll事件* /
    epoll_data_t data; / *用户数据变量* /
};
//等待事件到来
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
功能:等待事件的产生,类似于select的用法
     epfd:句柄;
     events:用来保存从内核得到事件的集合;
     maxevents:表示每次能处理事件最大个数;
     timeout:超时时间,毫秒,0立即返回,-1阻塞
成功时返回发生事件的文件描述个数,失败时返回-1

select,poll都属于 同步IO机制(轮询)

epoll属于异步IO机制(不轮询): 

epoll的提出--》它所支持的文件描述符上限是系统可以最大打开的文件的数目;

                       eg:1GB机器上,这个上限10万个左右。

每个fd上面有callback(回调函数)函数,只有产生事件的fd才有主动调用callback,不需要轮询。

注意:

  Epoll处理高并发,百万级

  1. 红黑树: 是特殊的二叉树(每个节点带有属性),Epoll怎样能监听很多个呢?首先创建树的根节点,每个节点都是一个fd以结构体的形式存储(节点里面包含了一些属性,callback函数)
  2. 就绪链表: 当某一个文件描述符产生事件后,会自动调用callback函数,通过回调callback函数来找到链表对应的事件(读时间还是写事件)。

2、epoll特点

  1. 监听的最大的文件描述符没有个数限制(取决与你自己的系统 1GB - 10万个左右)
  2. 异步I/O,epoll当有事件产生被唤醒之后,文件描述符主动调用callback(回调函数)函数直接拿到唤醒的文件描述符,不需要轮询,效率高

3.epoll不需要重新构造文件描述符表,只需要从用户空间向内核空间拷贝一次数据即可.

3、epoll的流程: 

Epoll的使用:

1.创建红黑树 和 就绪链表         

2.添加文件描述符和事件信息到树上

3.阻塞等待事件的产生,一旦产生事件,则进行处理

4.根据链中准备处理的文件描述符 进行处理

epoll 要使用一组函数:  epoll_create 创建红黑树 和 就序列表

                                      epoll_ctl   添加文件描述符和事件到树上 / 从树上删除

                                     epoll_wait  等待事件产生

1)epoll_create 创建红黑树以及链表
头文件:#include <sys/epoll.h>
声明:int epoll_create(int size); 
功能:创建红黑树根节点(创建epoll实例) , 同时也会创建就绪链表
返回值:成功时返回一个实例(二叉树句柄),失败时返回-1
2)epoll_ctl 控制epoll属性

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

功能:控制epoll属性,比如给红黑树添加节点

参数: 1. epfd:   epoll_create函数的返回句柄。//一个标识符
     2. op:表示动作类型,有三个宏:			        
                EPOLL_CTL_ADD:注册新的fd到epfd中
			      EPOLL_CTL_MOD:修改已注册fd的监听事件
			      EPOLL_CTL_DEL:从epfd中删除一个fd
     3. 要操作的文件描述符
     4. 结构体信息: 
 typedef union epoll_data 
{
     int fd;      //要添加的文件描述符
     uint32_t u32;  typedef unsigned int
     uint64_t u64;   typedef unsigned long int
} epoll_data_t;

struct epoll_event 
{
   uint32_t events; 事件
   epoll_data_t data; //共用体(看上面)
};

	  关于events事件:
			 EPOLLIN:  表示对应文件描述符可读
		    EPOLLOUT: 可写
			 EPOLLPRI:有紧急数据可读;
		    EPOLLERR:错误;
		    EPOLLHUP:被挂断;
			 EPOLLET:触发方式,边缘触发;(默认使用边缘触发)
			 ET模式:表示状态的变化;
           NULL: 删除一个文件描述符使用,无事件
           
返回值:成功:0, 失败:-1


3)epoll_wait等待事件产生
声明: int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

功能:等待事件产生
   内核会查找红黑树中有事件响应的文件描述符, 并将结构体放入就绪链表
    就绪链表中的内容, 执行epoll_wait会同时复制到第二个参数events

参数: 	epfd:句柄;
		events:用来保存从就绪链表中响应事件的集合;
		maxevents:  表示每次在链表中拿取响应事件的个数;
		timeout:超时时间,毫秒,0立即返回  ,-1阻塞	

返回值: 成功: 实际从链表中拿出的数目     失败时返回-1


练习epoll实现服务器端

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/epoll.h>
int main(int argc, char const *argv[])
{
    if (argc < 2)
    {
        printf("plase input <ip><port>\n");
        return -1;
    }
    //1.创建套接字,用于链接
    int sockfd;
    int acceptfd;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        return -1;
    }
    printf("sockfd:%d\n", sockfd);
    //2.绑定 ip+port 填充结构体
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;                   //协议族ipv4
    saddr.sin_port = htons(atoi(argv[1]));        //端口号,htons将无符号短整数hostshort从主机字节顺序到网络字节顺序。
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0"); //ip地址,转化为16进制表示
    socklen_t len = sizeof(saddr);                //结构体大小
    //bind绑定ip和端口
    if (bind(sockfd, (struct sockaddr *)&saddr, len) < 0)
    {
        perror("bind err");
        return -1;
    }
    printf("bind success\n");
    //3.启动监听,把主动套接子变为被动套接字
    if (listen(sockfd, 1) < 0)
    {
        perror("listen err");
        return -1;
    }
    printf("listen success\n");
   
    
    //引入epoll
    //创建结构体变量
    struct epoll_event event;
    struct epoll_event revents[32];//存放epoll_wait拿取的内容
    //1.创建树
    int epfd=epoll_create(1);
    // if (epfd = epoll_creat(1) < 0)
    // {
    //     perror(" creat err");
    //     return -1;
    // }
    //2.将关心的文件描述符添加到树上
    //标准输入上树
    event.events = EPOLLIN | EPOLLET;
    event.data.fd = 0;
    epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &event);
    //套接字上树
    event.data.fd = sockfd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event);

    char buf[64] = {0};//字符数组
    //3.在链表中取事件

    while (1)
    {
        //3.阻塞 等待文件描述符产生事件
        int ret = epoll_wait(epfd, revents, 10, -1);
        if (ret < 0)
        {
            perror(" epoll_wait err");
            return -1;
        }
        //4.根据文件描述符 进行处理
        for (int i = 0; i < ret; i++)
        {
            if (revents[i].data.fd == 0)
            {
                //5.执行操作
                fgets(buf, sizeof(buf), stdin);
                if (buf[strlen(buf)] == '\0') //去掉fgets补的'\n'
                {
                    buf[strlen(buf) - 1] = '\0';
                }
                    send(event.data.fd, buf, sizeof(buf), 0); //发送

            }
            else if (revents[i].data.fd == sockfd)
            {
                int acceptfd = accept(sockfd, (struct sockaddr *)&saddr, &len);
                if (acceptfd < 0)
                {
                    perror("accept is err:");
                    return -1;
                }
                printf("fd: %d ip: %s   port: %d is connect\n",acceptfd, inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));

                event.data.fd = acceptfd;//新的acceptfd上树
                event.events = EPOLLIN | EPOLLET;
                epoll_ctl(epfd, EPOLL_CTL_ADD, acceptfd, &event);
            }
            else
            {
                int recvbyte = recv(revents[i].data.fd, buf, sizeof(buf), 0);
                if (recvbyte < 0)
                {
                    perror("recv is err:");
                    return -1;
                }
                else if (recvbyte == 0)
                {
                    printf("client is exit\n");
                    close(revents[i].data.fd);
                    epoll_ctl(epfd, EPOLL_CTL_DEL, revents[i].data.fd, NULL);
                }
                else
                {
                    printf("%d : %s\n", revents[i].data.fd, buf);
                }
            }
        }
    }
    close(sockfd);
    close(acceptfd);
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/126706.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Python爬虫】网页抓取实例之淘宝商品信息抓取

之前我们已经说过网页抓取的相关内容 上次我们是以亚马逊某网页的产品为例 抓取价格、品牌、型号、样式等 该网页上价格、品牌、型号、样式等 都只有一个 如果网页上的目标内容 根据不同规格有多个 又该怎么提取呢&#xff1f; ▼如下图所示 当机身颜色、套餐、存储容量…

python3.8.10虚拟环境安装talib总报平台不匹配

目录 环境&#xff1a; 需求&#xff1a; 问题&#xff1a; 概述 过程及解决 解决方案总结 环境&#xff1a; 操作系统&#xff1a;window10、64位 开发工具&#xff1a;pycharm python版本&#xff1a;python3.8.10 需求&#xff1a; 在python3.8.10的虚拟环境中安…

神经网络遗传算法函数极值寻优

大家好&#xff0c;我是带我去滑雪&#xff01; 对于未知的非线性函数&#xff0c;仅仅通过函数的输入和输出数据难以寻找函数极值&#xff0c;这一类问题可以通过神经网络结合遗传算法求解&#xff0c;利用神经网络的非线性拟合能力和遗传算法的非线性寻优能力寻找函数极值。 …

国内外PLC的差异化对比

在聊PLC的市场格局和国产发展现状之前&#xff0c;我们先来简单了解一下PLC的作用。所谓PLC&#xff0c;你可以把它当成是一台小型电脑&#xff0c;只不过这台电脑是专用于工业领域&#xff0c;用来控制各种机械或生产的过程。比如说我们身上穿的衣服&#xff0c;都是由机器缝制…

合成数据在医疗保健行业的案例研究

从机器人辅助手术到医学成像技术&#xff0c;人工智能在医疗保健领域的应用正在迅速改变医疗保健行业&#xff0c;并改善服务成本和服务质量。例如&#xff0c;埃森哲表示&#xff0c;到 150 年&#xff0c;人工智能临床健康应用每年可以为美国医疗保健行业节省 2026 亿美元。 …

Ubuntu22.04 下 NFS 相关问题与完整配置(客户机 MacOS)

categories: [Linux-Shell] tags: Linux NFS 写在前面 最近折腾一下 NFS, 先白嫖一顿华子云的 1 个月服务器, 2C4G 感觉不错了, 但NFS 配置起来还是有点难度, 主要还是随机分配的端口配置方面比较恶心. server环境: 华为云 2C4G Ubuntu22.04 client环境: MacOS M1 with brew …

个人网厅——销户

目录 需求文档 公积金销户类 controller层 service层 service层实现类 1.验证 &#xff08;个人账户&#xff09; 2.提交&#xff08;添加&#xff09; controller层 service层 service层实现类 3.分页查询 controller层 service层 service层实现类 4. 详情查询…

2.【自动驾驶与机器人中的SLAM技术】左乘模型推导ESKF

目录 1. 证明题 证明&#xff1a;若某个高斯随机变量为零均值&#xff0c;协方差为对角线矩阵且大小相同&#xff08;各向同性&#xff09;&#xff0c;那么在乘任意旋转矩阵以后&#xff0c;其均值仍为零&#xff0c;且协方差不变&#xff1b; 2. 代码实现运动方程将F矩阵…

layui table合并相同的列

table.render({elem: #samples,url: /index/Develorderss/samplelists?od_idod_id //数据接口,page: { //支持传入 laypage 组件的所有参数&#xff08;某些参数除外&#xff0c;如&#xff1a;jump/elem&#xff09; - 详见文档layout: [prev, page, next, count,skip,limit]…

链表OJ题(1)

今天讲解两道链表OJ题目。 1.链表的中间节点 给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回第二个中间结点。 示例 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[3,4,5] 解释&#xff1a;链表只有一个…

mac 安装使用svn教程

mac 安装使用svn教程 一、安装Homebrew 要在Mac OS上安装SVN&#xff0c;首先需要安装Homebrew。Homebrew是一个流行的包管理器&#xff0c;因此我们将使用它来安装SVN。 /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"…

区块链多链数字钱包开发

随着区块链技术的不断发展&#xff0c;多链数字钱包的开发逐渐成为热门领域。多链数字钱包是一种可以支持多种区块链网络的数字钱包&#xff0c;用户可以使用它来存储、管理和转移不同的数字资产。本文将探讨多链数字钱包的开发背景、市场需求、技术实现和未来趋势等方面。 一、…

redisson中的分布式锁二

公平锁&#xff08;Fair Lock&#xff09; 基于Redis的Redisson分布式可重入公平锁也是实现了java.util.concurrent.locks.Lock接口的一种RLock对象。同时还提供了异步&#xff08;Async&#xff09;、反射式&#xff08;Reactive&#xff09;和RxJava2标准的接口。它保证了当…

YB1205B S0T23开关式异步升压具恒压恒流LED驱动器

YB1205B S0T23开关式异步升压具恒压恒流LED驱动器 产品简介&#xff1a; YB1205B是一种输入电压范围宽(0.85.5V),可调恒定电流和限定电流两种模式来驱动白光LED而设计的升压型DCDC变换器。采用变频模式&#xff0c;逐周期限流&#xff0c;使输入输出电流随电源电压降低均匀变…

微服务之Nacos注册管理

文章目录 一、Nacos安装步骤1.安装地址2.安装版本3.目录说明4.端口配置5.启动 二、Nacos服务注册1.Nacos依赖2.客户端修改配置文件3.启动效果图4.总结 三、Nacos服务集群属性1.服务跨集群调用问题2.服务集群属性3.总结 四、Nacos根据集群负载均衡1.修改配置文件2.设置集群服务类…

【C语法学习】20 - 文件访问顺序

文章目录 0 前言1 文件位置指示符2 rewind()函数2.1 函数原型2.2 参数2.3 返回值2.4 使用说明 3 ftell()函数3.1 函数原型3.2 参数3.3 返回值 4 fseek()函数4.1 函数原型4.2 参数4.3 返回值 5 示例5.1 示例15.2 示例2 0 前言 C语言文件访问分为顺序文件访问和随机文件访问。 …

Kotlin库实现多线程爬取数据

由于字数限制&#xff0c;以下是一个简化版的爬虫程序示例&#xff0c;使用了Kotlin的网络库kotlinx.coroutines和kotlinx.html。这个程序会爬取一个简单的Python多线程跑数据的网页&#xff0c;并打印出结果。 import kotlinx.coroutines.* import kotlinx.html.* import java…

oracle-sql语句解析类型

语句执行过程&#xff1a;1. 解析(将sql解析成执行计划) 2.执行 3.获取数据(fetch) 1. shared pool的组成。 share pool是一块内存池。 主要分成3块空间。free&#xff0c; library(库缓存&#xff0c;缓存sql以及执行计划)&#xff0c;row cache(字典缓存) select * from v…

振南技术干货集:C语言的一些“骚操作”及其深层理解(10)

注解目录 第二章《c语言的一些“操作”及其深层理解》 一、字符串的实质就是指针 &#xff08;如何将 35 转为对应的十六进制字符串”0X23”&#xff1f;&#xff09; 二 、转义符\ &#xff08;打入字符串内部的“奸细”。&#xff09; 三、字符串常量的连接 &#xff…

WebSocket在node端和客户端的使用

摘要 如果想要实现一个聊天的功能&#xff0c;就会想到使用WebSocket来搭建。那如果没有WebSocet的时候&#xff0c;我们会以什么样的思路来实现聊天功能呢&#xff1f; 假如有一个A页面 和 B页面进行通信&#xff0c;当A发送信息后&#xff0c;我们可以将信息存储在文件或者…