当前位置: 首页 > news >正文

网站建设玖金手指谷哥十八网站建设版块分类

网站建设玖金手指谷哥十八,网站建设版块分类,资源优化网站排名,曲阜文化建设示范区网站目录 从epoll接口入手 创建epoll模型 用户告诉内核关心的事件 内核告诉用户就绪的事件 epoll的原理 整体思路 如何判断事件是否就绪 事件就绪后如何实现将节点插入就绪队列 从epoll接口入手 本篇文章从epoll的三个接口入手介绍epoll的具体工作原理 创建epoll模型 #in…

目录

从epoll接口入手

创建epoll模型

用户告诉内核关心的事件

内核告诉用户就绪的事件

epoll的原理

整体思路

如何判断事件是否就绪

事件就绪后如何实现将节点插入就绪队列


从epoll接口入手

本篇文章从epoll的三个接口入手介绍epoll的具体工作原理

创建epoll模型

#include <sys/epoll.h>
int epoll_create(int size);

创建一个epoll的句柄,从linux-2.6.8后,size参数是被忽略的,用完之后必须调用close关闭。

返回值实际上就是一个文件描述符

用户告诉内核关心的事件

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

 epoll作为多路转接的方案,依然要解决如何让操作系统知道用户关心哪些文件描述符的哪些事件的问题,这个接口解决的就是这个问题。epfd是epoll_create返回的文件描述符,op表示动作,用三个宏来表示。

EPOLL_CTL_ADD:注册新的 fd 到 epfd 中;
EPOLL_CTL_MOD:修改已经注册的 fd 的监听事件;
EPOLL_CTL_DEL:从 epfd 中删除一个 fd;

fd是用户关心的文件描述符,最后一个参数event告诉内核要监听fd上的哪些事件,再来看看epoll_event的结构

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

events是事件类型,有以下几个宏

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

关于epoll_data这个结构,在写代码时还会细谈

内核告诉用户就绪的事件

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

epfd依然是epoll_create的返回值,events和maxevents确定了一个epoll_event的数组,这个数组包含了哪些文件描述符的哪些事件就绪的信息,timeout是超时时间,返回值与select和poll一样

为0表示超时返回;为-1表示有错误发生,并设置错误码errno;为正数表示在timeout时间内事件就绪的文件描述符个数

epoll的原理

整体思路

有了使用epoll的宏观认识后,再来详细谈谈工作原理

先来看看上面第一个接口所说的创建epoll模型,这个epoll模型到底是什么

以下代码来自linux-2.6.18的源码(使用vscode打开源码根目录可以搜索到下面的代码)

/** This structure is stored inside the "private_data" member of the file* structure and rapresent the main data sructure for the eventpoll* interface.*/
struct eventpoll {/* Protect the this structure access */rwlock_t lock;/** This semaphore is used to ensure that files are not removed* while epoll is using them. This is read-held during the event* collection loop and it is write-held during the file cleanup* path, the epoll file exit code and the ctl operations.*/struct rw_semaphore sem;/* Wait queue used by sys_epoll_wait() */wait_queue_head_t wq;/* Wait queue used by file->poll() */wait_queue_head_t poll_wait;/* List of ready file descriptors */struct list_head rdllist;/* RB-Tree root used to store monitored fd structs */struct rb_root rbr;
};

eventpoll结构体就是epoll模型,重点来看rdllist和rbr两个字段,rbr是一颗红黑树,节点存放了文件描述符和事件的映射关系,里面存放的都是用户传入的的文件描述符,而如果某个文件描述符上的事件就绪了,就会将这个节点插入到rdllist中

rdllist是就绪队列,里面的节点都是已经就绪的文件描述符和对应的事件

所以用户使用epoll_ctl实际就是对这颗红黑树进行增删查改,而一旦有文件描述符就绪了,就会被加入rdllist中,epoll_wait实际上就会得到rdllist中存放的就绪文件描述符的信息。

下面是节点的结构体,可以看到有所属的红黑树的节点的信息,所属的等待队列的节点的信息

实际上,前文所说插入到rdllist并不是复制一个节点插入到rdllist里,而是修改这个节点的rdllink字段,让这个节点属于红黑树的同时,还属于等待队列

struct epitem {/* RB-Tree node used to link this structure to the eventpoll rb-tree */struct rb_node rbn;/* List header used to link this structure to the eventpoll ready list */struct list_head rdllink;/* The file descriptor information this item refers to */struct epoll_filefd ffd;/* Number of active wait queue attached to poll operations */int nwait;/* List containing poll wait queues */struct list_head pwqlist;/* The "container" of this item */struct eventpoll *ep;/* The structure that describe the interested events and the source fd */struct epoll_event event;/** Used to keep track of the usage count of the structure. This avoids* that the structure will desappear from underneath our processing.*/atomic_t usecnt;/* List header used to link this item to the "struct file" items list */struct list_head fllink;/* List header used to link the item to the transfer list */struct list_head txlink;/** This is used during the collection/transfer of events to userspace* to pin items empty events set.*/unsigned int revents;
};

如何判断事件是否就绪

接下来再谈谈epoll如何知道某个文件描述符的事件是否就绪

要解决这个问题,先从下面这个结构体开始

struct file {.../* needed for tty driver, and maybe others */void			*private_data;#ifdef CONFIG_EPOLL/* Used by fs/eventpoll.c to link all the hooks to this file */struct list_head	f_ep_links;spinlock_t		f_ep_lock;
#endif /* #ifdef CONFIG_EPOLL */struct address_space	*f_mapping;
};

重点关注这个private_data

当我们创建tcp或者udp套接字时,返回的实际上是文件描述符,内核通过这个文件描述符找到了对应的struct file结构,而这个结构的private_data字段就会指向套接字

struct socket {socket_state		state;unsigned long		flags;const struct proto_ops	*ops;struct fasync_struct	*fasync_list;struct file		*file;struct sock		*sk;wait_queue_head_t	wait;short			type;
};

可以看到socket中还有一个字段是file,这个file就是socket所属的那个struct file的地址

socket下还有sock结构体

struct sock {/** Now struct inet_timewait_sock also uses sock_common, so please just* don't add nothing before this first member (__sk_common) --acme*/...struct sk_buff_head	sk_receive_queue;struct sk_buff_head	sk_write_queue;struct sk_buff_head	sk_async_wait_queue;...
};

太长了省略了一些字段,sock中实际上是各种网络信息,这里最重要的两个就是接收队列和写队列,实际上就是接收缓冲区和发送缓冲区,至此,只需要判断接收缓冲区是否为空就能判断读事件是否就绪

之前说struct file的private_data字段指向struct socket其实还不够准确

我们在创建套接字时常常会指定创建tcp套接字或者udp套接字,private_data会指向这样的struct tcp_socket或者struct udp_socket,而这两个struct结构体实际上会间接包含strcut sock结构

struct tcp_sock {/* inet_connection_sock has to be the first member of tcp_sock */struct inet_connection_sock	inet_conn;...
};
struct inet_connection_sock {/* inet_sock has to be the first member! */struct inet_sock	  icsk_inet;...
};
struct inet_sock {/* sk and pinet6 has to be the first two members of inet_sock */struct sock		sk;...
};
struct udp_sock {/* inet_sock has to be the first member */struct inet_sock inet;...
};
struct inet_sock {/* sk and pinet6 has to be the first two members of inet_sock */struct sock		sk;...
};

可以看到tcp比udp多套了一个管理连接的结构体 struct inet_connection_sock,这里所展现的tcp_sock和udp_sock是一种继承体系,其中strcut sock可看成是基类,剩下的都是子类

而前面的struct socket结构体中的struct sock字段也可能并不是指向一个struct sock的对象,而是指向一个tcp_sock或者udp_sock对象,在需要用到对应tcp或者udp的方法时进行类型强转

这实际上也有一种多态的思想

事件就绪后如何实现将节点插入就绪队列

内核底层提供了一种回调机制,用于处理事件就绪时进行什么动作,默认这个回调是为空的,在epoll这里,这个回调被设置为当事件就绪后把节点插入就绪队列,于是,可以自动的在事件就绪时将节点插入就绪队列。

epoll的优点

使用方便: 虽然拆分成了三个函数, 但是反而使用起来更方便高效. 不需要每次循环都设置关注的文件描述符, 也做到了输入输出参数分离开


数据拷贝轻量: 只在合适的时候调用 EPOLL_CTL_ADD 将文件描述符结构拷贝到内核中, 这个操作并不频繁(而 select/poll 都是每次循环都要进行拷贝)

事件回调机制: 避免使用遍历, 而是使用回调函数的方式, 将就绪的文件描述符结构加入到就绪队列中, epoll_wait 返回直接访问就绪队列就知道哪些文件描述符就绪. 这个操作时间复杂度 O(1). 即使文件描述符数目很多, 效率也不会受到影响.

没有数量限制: 文件描述符数目无上限.

http://www.dtcms.com/a/555687.html

相关文章:

  • 高端做网站价格做网站用电脑自带的
  • 北大通用具身导航模型探索!NavFoM:跨实体和跨任务的具身导航基础模型
  • 巴士定制网站开发用自己的电脑做主机建网站
  • 国外平面设计教程网站高效的设计公司
  • 互联网金融网站设计php网站里放asp
  • 网站建设及推广外包只知道网站后台怎么做301
  • 微信头像做国旗网站热点事件舆情分析
  • 什么公司时候做网站nike官网宣传片
  • 塘沽做网站公司重庆市建设项目环境影响评价网站
  • 礼品册兑换 网站建设昌邑网页设计
  • Python matplotlib详解:从入门到精通,数据可视化利器
  • 网站建设克隆营销型网站策划建设分为哪几个层次
  • 定州建设局网站西安php网站开发培训班
  • 北京智能网站建设企业众筹网站搭建
  • 山东省建设工会网站网站搭建外贸
  • 网站开发组岗位申请网站就是做网站吗
  • iis建设网站秦皇岛网站制作多少钱
  • 佛山网站建设哪个好外链网站有哪些
  • 怎样开网站个人网站建设思路
  • 深圳网站建设 案例宁波网络营销推广外包公司
  • 青岛网站建设服务平台北京网站建设价格行情
  • 医学院英文网站建设方案做明星同款的网站
  • 滑县做网站wordpress网站设计
  • 手机主页网站推荐有域名自己怎么做网站
  • 课程商城网站模板食品企业网站建设方案
  • 福州免费建站品牌企业手机网站源文件
  • 龙岩做网站开发找哪家百度提交wordpress
  • 网络营销软件网站网站底部设计源码
  • 电子商务查询网站网站设计是干什么的
  • 深圳网站建设推广优化公司山网站建设