Linux网络编程---I/O复用模型之epoll

https://blog.csdn.net/men_wen/article/details/53456474

Linux网络编程—I/O复用模型之epoll

1. epoll模型简介

epoll是Linux多路服用IO接口select/poll的加强版,e对应的英文单词就是enhancement,中文翻译为增强,加强,提高,充实的意思。所以epoll模型会显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。

  • epoll把用户关心的文件描述符上的时间放在内核的一个事件表中,无需像select和poll那样每次调用都重复传入文件描述符集。
  • epoll在获取事件的时候,无需遍历整个被监听的文件描述符集合,而是遍历那些被内核IO事件异步唤醒而加入ready队列的描述符集合。

所以,epoll是Linux大规模高并发网络程序的首选模型。

2.epoll模型的API

epoll使用一组函数来完成任务

2.1 函数epoll_create

创建一个epoll句柄,句柄的英文是handle,相通的意思是把手,把柄。

#include <sys/epoll.h>int epoll_create(int size);
//返回值:若成功,返回一个非负的文件描述符,若出错,返回-1。
  • 该函数返回一个文件描述符,用来唯一标示内核中这个事件表,sizeof参数提示内核要监听的文件描述符个数,这与内存大小有关。
  • 返回的文件描述符将是其他所有epoll系统调用的第一个参数,以指定要访问的内核时间表,所以用该返回的文件描述符相当与其他epoll调用的把手、把柄一样。

查看进程能够打开的最大数目的文件描述符

➜  ~ cat /proc/sys/fs/file-max
1215126
//该值与内存大小有关

修改最大文件描述符限制

➜  ~ sudo vim /etc/security/limits.conf
//重启生效

2.2 函数epoll_ctl

该函数用来操作epoll的内核事件表

#include <sys/epoll.h>int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
//返回值:若成功,返回0,若出错返回-1。
  • epfd就是函数epoll_create创建的句柄。
  • op是指定操作类型,有一下三种 
    • EPOLL_CTL_ADD,向epfd注册fd的上的event
    • EPOLL_CTL_MOD,修改fd已注册的event
    • EPOLL_CTL_DEL,从epfd上删除fd的event 
      1. fd是操作的文件描述符
      2. event指定内核要监听事件,它是struct epoll_event结构类型的指针。epoll_event定义如下:
struct epoll_event {uint32_t     events;      /* Epoll events */epoll_data_t data;        /* User data variable */
};
  • vents成员描述事件类型,将以下宏定义通过位或方式组合

    • EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭)
    • POLLOUT:表示对应的文件描述符可以写
    • EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来)
    • EPOLLERR:表示对应的文件描述符发生错误
    • EPOLLHUP:表示对应的文件描述符被挂断;
    • EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的
    • EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
  • data用于存储用户数据,是epoll_data_t结构类型,该结构定义如下:

typedef union epoll_data {void        *ptr;int          fd;uint32_t     u32;uint64_t     u64;
} epoll_data_t;
  • epoll_data_t是一个联合体,fd指定事件所从属的目标文件描述符。ptr可以用来指定fd相关的用户数据,但两者不能同时使用。

2.3 函数epoll_wait

函数epoll_wait用来等待所监听文件描述符上有事件发生

#include <sys/epoll.h>int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
//返回值:若成功,返回就绪的文件描述符个数,若出错,返回-1,时间超时返回0
  • epfd就是函数epoll_create创建的句柄
  • timeout是超时事件,-1为阻塞,0为立即返回,非阻塞,大于0是指定的微妙
  • events是一个 传入传出参数,它是epoll_event结构的指针,用来从内核得到事件的集合
  • maxevents告知内核events的大小,但不能大于epoll_create()时创建的size

3. LT和ET模式

  • LT(Level Triggered,电平触发):LT模式是epoll默认的工作模式,也是select和poll的工作模式,在LT模式下,epoll相当于一个效率较高的poll。 
    • 采用LT模式的文件描述符,当epoll_wait检测到其上有事件发生并将此事件通知应用程序后,应用程序可以不立即处理此事件,当下一次调用epoll_wait是,epoll_wait还会将此事件通告应用程序。
  • ET(Edge Triggered,边沿触发):当调用epoll_ctl,向参数event注册EPOLLET事件时,epoll将以ET模式来操作该文件描述符,ET模式是epoll的高效工作模式. 
    • 对于采用ET模式的文件描述符,当epoll_wait检测到其上有事件发生并将此通知应用程序后,应用程序必须立即处理该事件,因为后续的epoll_wait调用将不在向应用程序通知这一事件。ET模式降低了同意epoll事件被触发的次数,效率比LT模式高。

4. LT和ET的服务端和客户端代码

4.1 服务器端

#include <sys/epoll.h>
#include <fcntl.h>
#include "wrap.h"#define MAX_EVENT_NUM           1024
#define BUFFER_SIZE             10
#define true                    1
#define false                   0int setnonblocking(int fd)
{int old_opt = fcntl(fd, F_GETFD);int new_opt = old_opt | O_NONBLOCK;fcntl(fd, F_SETFD, new_opt);return old_opt;
}//将文件描述符设置为非阻塞的void addfd(int epollfd, int fd, int enable_et)
{struct 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);
}//将文件描述符fd的EPOLLIN注册到epollfd指示的epoll内核事件表中,enable_et表示是否对fd启用ET模式void lt(struct epoll_event *events, int num, int epollfd, int listenfd)
{char buf[BUFFER_SIZE];for(int i = 0; i < num; i++){int sockfd = events[i].data.fd;if(sockfd == listenfd){struct sockaddr_in clientaddr;socklen_t clilen = sizeof(clientaddr);int connfd = Accept(listenfd, (struct sockaddr *)&clientaddr, &clilen);addfd(epollfd, connfd, false);//对connfd使用默认的lt模式}else if(events[i].events & EPOLLIN){//只要socket读缓存中还有未读的数据,这段代码就会触发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(struct epoll_event *event, int num, int epollfd, int listenfd)
{char buf[BUFFER_SIZE];for(int i = 0; i < num; i++){int sockfd = event[i].data.fd;if(sockfd == listenfd){struct sockaddr_in clientaddr;int clilen = sizeof(clientaddr);int connfd = Accept(listenfd, (struct sockaddr *)&clientaddr, &clilen);addfd(epollfd, connfd, true);//多connfd开启ET模式}else if(event[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 start_ser(char *ipaddr, char *port)
{int sock = Socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in serveraddr;bzero(&serveraddr, sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(atoi(port));inet_pton(AF_INET, ipaddr, &serveraddr.sin_addr);Bind(sock, (struct sockaddr *)&serveraddr, sizeof(serveraddr));Listen(sock, 128);return sock;
}int main(int argc, char *argv[])
{int listenfd = start_ser(argv[1], argv[2]);struct epoll_event events[MAX_EVENT_NUM];int epollfd = epoll_create(5);if(epollfd < 0){perr_exit("epoll_create err");}addfd(epollfd, listenfd, true);while(1){int ret = epoll_wait(epollfd, events, MAX_EVENT_NUM, -1);if(ret < 0){printf("epoll failure\n");break;}lt(events, ret, epollfd, listenfd);//lt模式//et(events, ret, epollfd, listenfd);//et模式}Close(listenfd);return 0;
}
//warp.h文件是将socket,bind,listen等函数封装为第一个字母大写的头文件

4.2 客户端

#include "wrap.h"                                                            int main(int argc, char *argv[])
{int connfd;struct sockaddr_in serveraddr;char buf[1024];connfd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&serveraddr, sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(atoi(argv[2]));inet_pton(AF_INET, argv[1], &serveraddr.sin_addr);Connect(connfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));while(fgets(buf, 1024, stdin) != NULL){Write(connfd, buf, strlen(buf));}Close(connfd);return 0;
}

4.3 两种模式结果对比

ET模式

LT模式 
当发送超过缓冲区大小的数据量,LT会多次调用epoll_wait函数接受数据,则打印了多次“event level once”,而ET则是循环读取数据知道读完,打印了一次“event trigger once”。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

POJ 1741tree-点分治入门

学习了一下点分治&#xff0c;如果理解有误还请不吝赐教。 为了快速求得树上任意两点之间距离满足某种关系的点对数&#xff0c;我们需要用到这种算法。 点分治是树上的一种分治算法&#xff0c;依靠树和子树之间的关系进行分治从而降低复杂度。 和其他树上的算法有一些区别…

基于单链表的生产者消费者问题

『生产者与消费者问题分析』「原理」生产者生产产品&#xff0c;消费者消费产品。产品如果被消费者消费完了&#xff0c;同时生产者又没有生产出产品&#xff0c;消费者 就必须等待。同样的&#xff0c;如果生产者生产了产品&#xff0c;而消费者没有去消费&#x…

C++智能指针(一)智能指针的简单介绍

https://blog.csdn.net/nou_camp/article/details/70176949C智能指针 在正式了解智能指针前先看一下下面的一段代码 #include<iostream> using namespace std; class A { public:A():_ptr(NULL), _a(0){}~A(){} public:int* _ptr;int _a; };void test() {A a;int *p1 ne…

聪聪可可-点分治

聪聪和可可是兄弟俩&#xff0c;他们俩经常为了一些琐事打起来&#xff0c;例如家中只剩下最后一根冰棍而两人都想吃、两个人都想玩儿电脑&#xff08;可是他们家只有一台电脑&#xff09;……遇到这种问题&#xff0c;一般情况下石头剪刀布就好了&#xff0c;可是他们已经玩儿…

C++智能指针(二)模拟实现三种智能指针

https://blog.csdn.net/nou_camp/article/details/70186721在上一篇博客中提到了Auto_ptr(C智能指针&#xff08;一&#xff09;)&#xff0c;下面进行模拟实现Auto_ptr 采用类模板实现 #include<iostream> using namespace std; template<class T> class Autoptr …

Prime Distance On Tree-树分治+FFT

题目描述 Problem description. You are given a tree. If we select 2 distinct nodes uniformly at random, what’s the probability that the distance between these 2 nodes is a prime number? Input The first line contains a number N: the number of nodes in this…

C++智能指针(三)总结

https://blog.csdn.net/nou_camp/article/details/70195795 在上一篇博客中&#xff08;C智能指针&#xff08;二&#xff09;&#xff09;模拟实现了三种智能指针。 其中最好的就是shared_ptr,但是这并不代表它就是最完美的&#xff0c;它也有问题&#xff0c;这个问题就是循环…

POJ2114-Boatherds-树分治

题目描述 Boatherds Inc. is a sailing company operating in the country of Trabantustan and offering boat trips on Trabantian rivers. All the rivers originate somewhere in the mountains and on their way down to the lowlands they gradually join and finally th…

c++11 你需要知道这些就够了

https://blog.csdn.net/tangliguantou/article/details/50549751c11新特性举着火把寻找电灯今天我就权当抛砖引玉&#xff0c;如有不解大家一起探讨。有部分内容是引用自互联网上的内容&#xff0c;如有问题请联系我。T&& 右值引用 std::move 右值引用出现之前我们只能…

HDU5977-Garden of Eden-树分治+FWT

题目描述 When God made the first man, he put him on a beautiful garden, the Garden of Eden. Here Adam lived with all animals. God gave Adam eternal life. But Adam was lonely in the garden, so God made Eve. When Adam was asleep one night, God took a rib fro…

C++11新特性学习

https://blog.csdn.net/tennysonsky/article/details/778170481、什么是C11C11标准为C编程语言的第三个官方标准&#xff0c;正式名叫ISO/IEC 14882:2011 - Information technology -- Programming languages -- C。在正式标准发布前&#xff0c;原名C0x。它将取代C标准第二版I…

C++ override 关键字用法

override关键字作用&#xff1a; 如果派生类在虚函数声明时使用了override描述符&#xff0c;那么该函数必须重载其基类中的同名函数&#xff0c;否则代码将无法通过编译。举例子说明struct Base {virtual void Turing() 0;virtual void Dijkstra() 0;virtual void VNeumann…

Gym - 101981I-MagicPotion-最大流

题目描述 There are n heroes and m monsters living in an island. The monsters became very vicious these days, so the heroes decided to diminish the monsters in the island. However, the i-th hero can only kill one monster belonging to the set Mi. Joe, the st…

c++仿函数 functor

https://www.cnblogs.com/decade-dnbc66/p/5347088.html内容整理自国外C教材先考虑一个简单的例子&#xff1a;假设有一个vector<string>&#xff0c;你的任务是统计长度小于5的string的个数&#xff0c;如果使用count_if函数的话&#xff0c;你的代码可能长成这样&#…

HDU4812-D Tree-树分治

题目描述 There is a skyscraping tree standing on the playground of Nanjing University of Science and Technology. On each branch of the tree is an integer (The tree can be treated as a connected graph with N vertices, while each branch can be treated as a v…

成为C++高手之实战项目

https://blog.csdn.net/niu_gao/article/details/51458721 在内存中模拟出一副牌&#xff0c;然后模拟洗牌&#xff0c;发牌等动作。 流程是这样的&#xff1a;构建一副牌保存到一个数组中—洗牌—创建玩家—向玩家发牌–输出每个玩家的牌。 #include <stdio.h> #include…

C++中String类的实现

https://www.cnblogs.com/zhizhan/p/4876093.html原文&#xff1a;http://noalgo.info/382.html String是C中的重要类型&#xff0c;程序员在C面试中经常会遇到关于String的细节问题&#xff0c;甚至要求当场实现这个类。只是由于时间关系&#xff0c;可能只要求实现构造函数、…

Ubuntu软件更新失败

刚安装好Ubuntu以后需要将系统的软件都更新一下&#xff0c;但是遇到一个问题就是下载仓库信息失败&#xff0c;大概是这个样子的错误&#xff1a; 经国遇到这样的问题可以试一下下面这个命令&#xff1a; sudo rm -rf /var/lib/apt/lists/* sudo apt-get update参考网址&…

getsockname函数与getpeername函数的使用

https://www.tuicool.com/articles/V3Aveygetsockname和getpeername函数 getsockname函数用于获取与某个套接字关联的本地协议地址 getpeername函数用于获取与某个套接字关联的外地协议地址 定义如下&#xff1a;[cpp] view plaincopy#include<sys/socket.h> int gets…

Ubuntu根目录空间不足

自己在固态硬盘上安装的Ubuntu&#xff0c;结果只用了一天就显示磁盘空间不足。查看空间以后发现Ubuntu自己安装的时候默认给根目录分配的是10GB,然而我们下载的软件以及环境等一般都安装在根目录空间下&#xff0c;尤其是/usr目录所占的空间很大。 不得已我在网上查找了如何给…