1 epoll编程问题

对于数据传输send/recv这些接口的而言;如何判断send数据,对方有没有接收成功?
采用send返回值无意义。,send发送数据只是将数据拷贝到协议栈,而数据的发送是协议栈自动处理的(何时发送也是由自己组装的).
2 粘包分包问题
产生原因:应用程序多次调用send后,只是将数据从用户空间拷贝到内核协议栈。而对于协议栈很容将两次的send包一起发送。
解决方案:a): tcp应用层协议添加长度域
b)每个包加上分割符
c)定长包(比较low,不推荐使用)
3 多个线程,并发的给fd发送数据等问题
有可能会产生这种问题。解决方案:在调用send()前,把fd在epoll的epoll_out事件取消掉,发送完后再将fd添加回来到epoll。
4 send返回-1,打开error值是eagain???
产生原因:sendbuffer是满的
解决方案:检测是否准备就绪(可采用epoll,poll,select等)
5 recv的时候,也返回-1
这是正常现象,recvbuffer无data。
6 常见的误区
epoll性能很高,是因为有内存映射mmap。
epoll性能一定比select,poll更高。当fd比较少时候,epoll效率可能没有select和poll高。

2 epoll如何实现

主要分为四部分:
1 如何管理所有的fd(数据结构)
2 如何实现线程安全
3 协议栈如何与epoll关联
4 ET和LT如何实现

1 数据结构
对于fd分为两种状态:就绪(有data)和空闲(无data);注意fd:0,1,2是系统提供的,增加是从3开始增加的。
对于一款服务器来说,一般情况下就绪的比例一般情况下都比较少,空闲的较多。
空闲集合:采用红黑树存储
就绪集合:采用队列存储
空闲集合需要查找所以采用索引的数据结构:hash,红黑树,b树,跳表等;对于就绪集合为何采用队列呢不采用栈:主要是因为就绪集合数据量相对小,而且无查找,无修改,只需要将数据从用户空间搬到内核空间。epoll_wait一次性也拿不完所以采用先进先出的队列来实现。

主要是由两个数据结构epitem和eventpoll。
epitem是每一个io所对应的事件,epoll_ctl添加EPOLL_CTL_ADD时候就会创建一个;
eventpoll是每个epoll所对应的,epoll_create创建的时候就会创建一个eventpoll;


具体的组织结构如下:
2 线程安全
有些情况下,可能会有多个线程同时操作epoll的时候。
解决方案:加锁
list:存储准备就绪IO
红黑树:用来存储所有的IO。
对于数据结构主要是两个方面insert,remove。
当内核数据准备就绪时候,则会执行epoll_event_callback回调函数,将epitem添加到list中;对于删除,当epoll_wait激活重新运行的时候,将list中的epitem一一拷贝copy到events参数中。
红黑树而言,当应用程序执行epoll_ctl添加EPOLL_CTL_ADD操作时候,将epitem添加到rbtree中;对于删除当epoll_ctl删除EPOLL_CTL_DEL操作的时候,将epitem添加到rbtree中。
线程安全的问题转换成了,对红黑树和就绪队列加锁的问题
红黑树加锁主要两种方式:锁整颗树;锁子树。(两者锁子树相对性能高一点,实现起来就很复杂。)
队列加锁:可以采用自旋锁spinlock或者CAS也行。

List:添加:

pthread_spin_lock(&ep->lock);  // 获取spinlock
epi->rdy = 1; // epitem的rdy设置为1,代表epitem已经在就绪队列中,后续在触发相同时间就只需更改event
LIST_INSERT_HEAD(&ep->rdlist, epi, rdlink); // 添加到list中
ep->rdnum++; // 将eventpoll的rdnum域加1
pthread_spin_unlock(&ep->lock);

删除:

pthread_spin_lock(&ep->lock); // 获取spinlockint cnt = 0;
int num = (ep->rdnum > maxevents ?maxevents:ep->rdnum); // 判断就绪数量与maxevents大小避免event溢出
int i = 0;while(num != 0 &&!LIST_EMPTY(&ep->rdlist)){ // EPOLLET 循环遍历list,判断添加list不能为空struct epitem *epi = LIST_FIRST(&ep->rdlist): // 获取首个结点LIST_REMOVE(epi, rdlink);  //删除首个结点epi->rdy = 0; //将epitem的rdy设置为0,标识epitem不在就绪队列中。memcpy(&events[i++], &epi->event, sizeof(struct epoll_event)); // 拷贝epitem的event到用户空间的eventsnum--; //copy数量加1cnt++;ep->rdnum--; // eventpoll中的rdnum减1
}

避免SMP体系下,多核竞争,此处采用自旋锁,不适合采用睡眠锁。

红黑树添加:

pthread_mutex_lock(&ep->mtx); //获取互斥锁struct epitem tmp;
tmp.sockfd = sockid;
struct epitem *epi = RB_FIND(_epoll_rb_socket, &ep->rbr, &tmp); // 查找sockid的epitem是否存在。存在则不能添加,不存在则可以添加。
if (epi){nty_trace_epoll("rbtree is exist\n");pthread_mutex_unlock(&ep->mtx);return -1;
}// 分配epitem
epi = (struct epitem*)calloc(1, sizeof(struct epitem));
if (!epi){pthread_mutex_unlock(&ep->mtx);errno = -ENOMEM;return -1;
}epi->sockfd = sockid; // sockid赋值
memcpy(&epi->event, event, sizeof(struct epoll_event)); //将设置的event添加到epitem的event域epi = RB_INSERT(_epoll_rb_socket, &ep->rbr, api); //将epitem添加到rbrtree中。
assert(epi == NULL);pthread_mutex_unlock(&ep->mtx); //释放互斥锁。

红黑树删除:

pthread_mutex_lock(&ep->mxt); // 获取互斥锁struct epitem tmp;
tmp.sockid = sockid;
struct epitem *epi = RB_REMOVE(_epoll_rb_socket, &ep->rbr, &tmp); // 删除sockid的结点,如果不存在,则rbtree返回-1
if (!epi){nty_trace_epoll("rbtree is no exist\n");pthread_mutex_unlock(&ep->mtx);return -1;
}free(epi); // 释放epitempthread_mutex_unlock(&ep->mtx);

3 epoll回调(协议栈如何与epoll关联
Epoll 的回调函数何时执行,此部分需要与Tcp的协议栈一起来阐述。Tcp协议栈的时序图如下图所示,epoll从协议栈回调的部分从下图的编号1,2,3,4。具体Tcp协议栈的实现,后续从另外的文章中表述出来。下面分别对四个步骤详细描述
编号1:是tcp三次握手,对端反馈ack后,socket进入rcvd状态。需要将监听socket的event置为EPOLLIN,此时标识可以进入到accept读取socket数据。

编号2:在established状态,收到数据以后,需要将socket的event置为EPOLLIN状态。

编号3:在established状态,收到fin时,此时socket进入到close_wait。需要socket的event置为EPOLLIN。读取断开信息。

编号4:检测socket的send状态,如果对端cwnd>0是可以,发送的数据。故需要将socket置为EPOLLOUT。 所以在此四处添加EPOLL的回调函数,即可使得epoll正常接收到io事件。

如下图:

4 ET与LT
LT(水平触发)与ET(边沿触发)是电子信号里面的概念。不清楚可以man epoll查看的。如下图所示:

比如:event = EPOLLIN | EPOLLLT,将event设置为EPOLLIN与水平触发。只要event为EPOLLIN时就能不断调用epoll回调函数。 比如: event = EPOLLIN | EPOLLET,event如果从EPOLLOUT变化为EPOLLIN的时候,就会触发。在此情形下,变化只发生一次,故只调用一次epoll回调函数。关于水平触发与边沿触发放在epoll回调函数执行的时候,如果为EPOLLET(边沿触发),与之前的event对比,如果发生改变则调用epoll回调函数,如果为EPOLLLT(水平触发),则查看event是否为EPOLLIN,即可调用epoll回调函数。

用户态协议栈之epoll实现相关推荐

  1. 【高阶知识】用户态协议栈之Epoll实现原理

    Epoll 是 Linux IO 多路复用的管理机制.作为现在 Linux 平台高性能网络 IO 必要的组件.内核的实现可以参照:fs/eventpoll.c . 为什么需要自己实现 epoll 呢? ...

  2. 架构决定可扩展性--聊聊用户态协议栈的意义

    在进入这个话题之前先说说通用和专业之间的区别.   举个很好的例子,好比我们个人,绝大部分的人都是"通用"的,而只有极少部分的人是"专业"的.通用的人主要目标是 ...

  3. FD.io VSAP(VPP Stack Acceleration Project),通过FD.io VSAP构建用户态协议栈

    目录 VSAP Scope-使用范围 Releases List of all subpages (used or unused) 通过FD.io VSAP构建用户态协议栈 VSAP https:// ...

  4. 用户态协议栈之tcp/ip设计

    1 解决问题 对于服务器而言,正常的接受一帧Data的过程,客户端先通过网络发送一帧数据到网卡,再经过协议栈,最后通过系统调用叨叨应用程序.具体的流程图如下: 针对上面的两个流程,涉及到两次拷贝(网卡 ...

  5. 用户态协议栈tcp/ip设计

    1 解决问题 对于服务器而言,正常的接受一帧Data的过程,客户端先通过网络发送一帧数据到网卡,再经过协议栈,最后通过系统调用叨叨应用程序.具体的流程图如下: 针对上面的两个流程,涉及到两次拷贝(网卡 ...

  6. 深入浅出用户态协议栈

    一.前言 在讲网络协议栈前,先理解一个数据包在网络传输是一个怎么样的流程,如下图所示. 正常的流程是网卡接收到数据后,把数据copy到协议栈(sk_buff),协议栈把sk_buff数据解析完后再把数 ...

  7. 一文彻底掌握用户态协议栈,一看就懂的

    用户态协议栈 那我们先跟大家解释这个协议栈这个东西啊协议栈这个东西呢或多或少啊各个朋友应该都听过,我们站在一个设计者的角度,站在一个设计者的角度,站在tcpip的个人的角度,我们怎么去设计这个协议的? ...

  8. 100行源代码搞定用户态协议栈丨udp,icmp,arp协议的现实丨网络协议栈丨Linux服务器开发丨C++后端开发丨Linux后台开发

    100行源代码搞定用户态协议栈 视频讲解如下,点击观看: 100行源代码搞定用户态协议栈丨udp,icmp,arp协议的现实丨网络协议栈丨Linux服务器开发丨C++后端开发丨Linux后台开发丨网络 ...

  9. 【Linux服务器开发系列】手写用户态协议栈,udpipeth数据包的封装,零拷贝的实现,柔性数组

    视频教你手写网络协议栈,保证大家能学会,耐心看 1. 用户态协议栈 2. udp/ip/eth数据包的封装 3. 零拷贝的实现 4. 零长数组(柔性数组) [Linux服务器开发系列]手写用户态协议栈 ...

最新文章

  1. oracle12 java_java – 无法使用12c jar创建Eclipse数据源到Oracle 12c.不过11g的作品
  2. Boost:BOOST_ASSERT扩展的用法测试程序
  3. 全球计算机视觉顶会CVPR 2019论文出炉:腾讯优图25篇论文入选
  4. Angular 4.x 事件管理器及自定义EventManagerPlugin
  5. jws 方式表格导出,excel文件导出,rest风格接口实现
  6. [源码]C# to SQL 的翻译器.net 1.1版
  7. POJ1011 Sticks
  8. HDU1573 X问题【扩展欧几里得算法】
  9. LINUX下用C判断一个进程是否活着
  10. 【2021ACM-ICPC亚洲区域赛济南站】C、D、J、K四题超详细题解
  11. 计算机启动方式如何选择USB启动,bios设置usb启动的方法
  12. 魔百盒CM311-1_S905L3芯片_YST代工_红外蓝牙语音_安卓9.0_线刷固件包
  13. 深入浅出ES6的标准内置对象Proxy
  14. html中target四种选择_blank、_parent、_self、_top,分别是什么意思?
  15. bzoj4399 魔法少女LJJ
  16. 真无线蓝牙耳机性价比高?真无线蓝牙耳机性价比排行
  17. 最快速的文件传输软件,解析镭速文件传输软件
  18. github 国内替代产品_2020年7种最佳Github替代品
  19. 乐山市计算机学校蔡老师,各展所长,切磋琢磨——乐山市计算机学校召开班主任经验交流会...
  20. 日历插件---FullCalendar (vue3中实现,常用详细的功能以及样式、有源码)

热门文章

  1. 511遇见易语言流程控制变量循环首
  2. C++ 鼠标模拟程序
  3. Android UI 设计规范,Android高分面试指南
  4. 【KNIME案例】参数化驱动工作流调用业务人员建立的脚本
  5. PMC于浪潮互联网峰会展示领先存储解决方案
  6. 百度地图之添加覆盖物并响应事件
  7. win10摄像头可以用计算机里不显示,win10系统不显示摄像头的解决办法
  8. CentOS上使用docker安装redis
  9. 第一届程序设计竞赛题解(E题)
  10. java实现的聊天程序