epoll_event结构

struct epoll_event
{
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
} __attribute__ ((__packed__)); typedef union epoll_data
{
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;

epoll_data是一个联合体,有的网络库使用了fd字段,比如redis,

有的使用了u32,比如libiop,之前一直纠结epoll的工作原理

个人认为在epoll_wait之后内核会自动移动epoll_event队列的内容。

因为epoll_wait返回就绪的文件描述符数量,之后我们采用循环从0到n

从epoll_event队列中取出对应第i个epoll_event结构,通过epoll_data

中的fd或者u32回调找到用户自己封装的事件回调单元,调用对应的回调

函数。

举一个例子假设epoll_event队列中有1000个文件描述符,第一次调用epoll_wait返回

5,那么表示队列前五个元素就绪了,如果不处理第三个就绪事件,其他的都处理。

第二次调用epoll_wait会返回8,那么这8个epoll_event也是按顺序排列的。

所以认为epoll_wait这个函数做了内部的优化排序,返回给用户按顺序拍好的内存

看下epoll_wait 函数参数

#include <sys/epoll.h>

int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
int epoll_pwait(int epfd, struct epoll_event *events,
int maxevents, int timeout,
const sigset_t *sigmask);

第二个参数表示epoll_event那个队列的首地址,第三个参数表示epoll_event队列的大小。

我查看了下manpage对于epoll的说明

The epoll_wait() system call waits for events on the epoll() instance referred to by the file descriptor epfd. The memory area pointed to by events will contain the events that will be available for the caller. Up to maxevents are returned by epoll_wait(). The maxevents argument must be greater than zero.The timeout argument specifies the minimum number of milliseconds that epoll_wait() will block

大体意思是epoll_wait 是系统等待处理  epfd所指向的 epoll实例。指向epoll_events的内存是可以被用户访问的。

epoll_wait最多返回maxevents大小。而且maxevents必须大于0。timeout 这个参数表示epoll_wait阻塞的最小

毫秒数。

The data of each returned structure will contain the same data the user set with an epoll_ctl() (EPOLL_CTL_ADD,EPOLL_CTL_MOD) while the events member will contain the returned event bit field.

可以通过EPOLL_CTL_ADD或者EPOLL_CTL_MOD更改event的事件属性。

比如epoll_ctl(state->epfd,EPOLL_CTL_DEL,fd,&ee);

下面是对返回值的说明

When successful, epoll_wait() returns the number of file descriptors ready for the requested I/O, or zero if no file descriptor became ready during the requested timeout milliseconds. When an error occurs, epoll_wait() returns - and errno is set appropriately

当成功时epoll_wait返回就绪的I/O描述符个数,0表示超时,-1表示出错。

查看man手册给我们的一个例子

#define MAX_EVENTS 10
struct epoll_event ev, events[MAX_EVENTS];
int listen_sock, conn_sock, nfds, epollfd; /* Code to set up listening socket, 'listen_sock',
(socket(), bind(), listen()) omitted */ epollfd = epoll_create1();
if (epollfd == -) {
perror("epoll_create1");
exit(EXIT_FAILURE);
} ev.events = EPOLLIN;
ev.data.fd = listen_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -) {
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
} for (;;) {
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -);
if (nfds == -) {
perror("epoll_wait");
exit(EXIT_FAILURE);
} for (n = ; n < nfds; ++n) {
if (events[n].data.fd == listen_sock) {
conn_sock = accept(listen_sock,
(struct sockaddr *) &addr, &addrlen);
if (conn_sock == -) {
perror("accept");
exit(EXIT_FAILURE);
}
setnonblocking(conn_sock);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = conn_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
&ev) == -) {
perror("epoll_ctl: conn_sock");
exit(EXIT_FAILURE);
}
} else {
do_use_fd(events[n].data.fd);
}
}
}

同样是采用epoll_wait后返回的大小进行轮询,每次epoll_events回传,保证就绪的事件排在前边,

这样比select效率高,因为select存在set传入和传出,epoll不需要传入,只需要用户开辟一段连续

空间,并且将首地址告诉epoll_wait即可。并且需要轮询所有文件描述符,而不是只针对就绪的轮询。

对于网络库,常常会封装一个供用户使用的EventBase 结构,例如libiop中

struct tag_iop_t
{
int id; /*对应的id*/
io_handle_t handle; /*关联的句柄*/
int iop_type; /*对象类型:0:free,1:io,2:timer*/
int prev; /*上一个对象*/
int next; /*下一个对象*/
unsigned int events; /*关注的事件*/
int timeout; /*超时值*/
iop_event_cb evcb; /*事件回调*/
void *arg; /*用户指定的参数,由用户负责释放资源*/
void *sys_arg; /*系统指定的参数,系统自动释放资源*/
/*以下字段对定时器无用*/
dbuf_t *sbuf; /*发送缓存区*/
dbuf_t *rbuf; /*接收缓存区*/
iop_time_t last_dispatch_time; /*上次调度的时间*/
};

例如redis中

typedef struct aeFileEvent {
int mask; /* one of AE_(READABLE|WRITABLE) */ //文件事件类型 读/写
aeFileProc *rfileProc;
aeFileProc *wfileProc;
void *clientData;
} aeFileEvent;

我们大都会开辟一段内存空间, 例如

tag_iop_t * ioplist = malloc(sizeof(struct tag_iop_t) * size );

当调用epoll_wait成功之后会返回n个就绪事件,从0到n遍历,

对于第i个epoll_events[i],怎么找到应用层的数据节点

tag_iop_t呢?

常用的做法就是将节点的标号存在

epoll_data 的 u32字段,
或者 将关联的socketfd存在
epoll_data的 fd 字段

通过如下伪代码可以实现类似的调用
int socketFd = epoll_events[i].epoll_data.fd;
ioplist[socketFd].callBack;
//或者根据节点回调 int node = epoll_events[i].epoll_data.u32;
ioplist[node].callBack; //至于采用何种方式回调
//关键在于我们之前如何绑定iop的 //用socket下标绑定回调函数
ioplist[fd].callBack = ...; //用node 序号绑定
//node是用户自己管理的从0到maxnum的数字
ioplist[node].fd = fd;
ioplist[node].callBack = ...;
这些都是读过一些网络库自己的理解,如果有什么好的建议
请大家告诉我,一起进步吧。
这是我的公众号:

epoll的一些细节和注意事项的更多相关文章

  1. Python面向对象基础:编码细节和注意事项

    在前面,我用了3篇文章解释python的面向对象: 面向对象:从代码复用开始 面向对象:设置对象属性 类和对象的名称空间 本篇是第4篇,用一个完整的示例来解释面向对象的一些细节. 例子的模型是父类Em ...

  2. Servlet的部署开发细节以及注意事项

    学习servlet最困难的我感觉还是配置,一開始是非常麻烦的.为了较好的学习,一開始还是以手动开发我认为比較好,可是真的有点把握给搞晕了,尤其是部署servlet方面非常麻烦,这里做一下简单的总结,前 ...

  3. [Linux]I/O多路复用和epoll

    首先我们来定义流的概念,一个流可以是文件,socket,pipe等等可以进行I/O操作的内核对象. 不管是文件,还是套接字,还是管道,我们都可以把他们看作流. 之后我们来讨论I/O的操作,通过read ...

  4. python网络编程——IO多路复用之epoll

    1.内核EPOLL模型讲解     此部分参考http://blog.csdn.net/mango_song/article/details/42643971博文并整理 首先我们来定义流的概念,一个流 ...

  5. 我读过的最好的epoll讲解--转自”知乎“

    首先我们来定义流的概念,一个流可以是文件,socket,pipe等等可以进行I/O操作的内核对象. 不管是文件,还是套接字,还是管道,我们都可以把他们看作流. 之后我们来讨论I/O的操作,通过read ...

  6. [转载]我读过最好的Epoll模型讲解

    转载来自:http://blog.csdn.net/mango_song/article/details/42643971 首先我们来定义流的概念,一个流可以是文件,socket,pipe等等可以进行 ...

  7. epoll 知识总结

    poll/select/epoll 对比 http://www.cnblogs.com/apprentice89/p/3234677.html    ---有待继续学习 http://blog.chi ...

  8. epoll讲解

    首先我们来定义流的概念,一个流可以是文件,socket,pipe等等可以进行I/O操作的内核对象.       不管是文件,还是套接字,还是管道,我们都可以把他们看作流.       之后我们来讨论I ...

  9. epoll讲解--转自”知乎“

    http://my.oschina.net/dclink/blog/287198 首先我们来定义流的概念,一个流可以是文件,socket,pipe等等可以进行I/O操作的内核对象. 不管是文件,还是套 ...

随机推荐

  1. Linux内核学习笔记(1)-- 进程管理概述

    一.进程与线程 进程是处于执行期的程序,但是并不仅仅局限于一段可执行程序代码.通常,进程还要包含其他资源,像打开的文件,挂起的信号,内核内部数据,处理器状态,一个或多个具有内存映射的内存地址空间及一个 ...

  2. Wampserver 修改根目录

    wampserver 默认根目录在 www 文件夹下 修改根目录方法如下: 1. 在打算存放项目或代码的位置新建文件夹(我建在了C:/MyProject) 2. 打开 httpd.conf 文件(该文 ...

  3. React Native 【学习总结】-【常用命令】

    前言 刚接触RN,相信很多人无从下手,不知道下一步要干什么,能干什么,本次学习围绕这个问题,将RN的常用命令总结一下,帮助你快速上手 架构理解 光知道命令的作用,远远不够,如果知道命令背后的意义,才能 ...

  4. 王者荣耀交流协会final冲刺第五次scrum会议

    成员王超,高远博,冉华,王磊,王玉玲,任思佳,袁玥全部到齐,王磊拍照. master:高远博 2.时间跨度 2017年12月5日 18:00 - 18:31,总计31分钟 3.地点 一食堂二楼沙发座椅 ...

  5. 20162328蔡文琛 Bag类

    在刚刚开始着手这个作业时,想的是使用for循环来自己写出add等方法来,但是在看过API后知道了Arraylist这个java已有的列表类,于是就只用ArrayList的方法很快的就做了出来.在进行B ...

  6. 第二次程序+PSP0级

    第二周,老师接着上次的程序有对四则运算的程序,做出来一些要求,这次要求可以控制乘除法,有无括号,控制输出方式,控制结果有无负数,有无余数. 我在对原先的程序分析了一下,发现我原先的程序可扩展性特别差, ...

  7. VUE AXIOS 跨域问题

    背景: 后台跨域使用通配符:context.Response.Headers.Add("Access-Control-Allow-Origin", "*"); ...

  8. 4th 课堂SCRM会议旁观记录

    项目名称:基于C#的连连看设计 小组名称:待定 小组成员:张政.张金生.武志远.李权 Master:张政 项目已完成部分: 现阶段已经实现了一定的功能,可以运行使用,进行第一关的游戏. 今天计划要完成 ...

  9. 用友 SAP 金蝶 季报

    用友 2018Q3季报 营收:.42亿 营收收入同比增长:42.36% 净利润:.35万 净利润同比增长率:113.83% 销售毛利率:66.88% 销售净利率:19.29% 用友2017财年年报 营 ...

  10. linux设置时区和自动同步时间

    1.设置时区 编辑 /etc/sysconfig/clock 修改 ZONE="Asia/Shanghai" 然后  cp  /usr/share/zoneinfo/Asia/Sh ...