Nodejs事件引擎libuv源码剖析之:句柄(handle)结构的设计剖析
声明:本文为原创博文,转载请注明出处。
句柄(handle)代表一种对持有资源的索引,句柄的叫法在window上较多,在unix/linux等系统上大多称之为描述符,为了抽象不同平台的差异,libuv使用统一的结构封装了不同平台的实现,接下来就看看这个抽象的过程。由于句柄的实现和系统平台有很大关系,本文只针对unix平台作源码分析。
一、抽象的开始----封装、继承、多态
libuv是用纯c语言写的(排除里面有几处内联汇编的用法),怎么还有继承呢?继承不都是c++、java、python等这些更高级语言才有的特性吗?不错,类似c++这些高级语言,从语言层面就支持了面向对象的三大特性:继承、封装与多态,c语言作为一门历史悠久、简洁高效的语言,虽然没有从语言层次提供复杂的对象管理机制,但是通过巧妙的设计也可以写出面向对象的思想,这在linux内核中体现的淋漓尽致,比如在内核的驱动部分,我们通常在编写一个字符设备驱动程序时,一定会操作的一个结构体:file_operations(定义在下方),就在一个struct中实现了方法和属性的封装,相应的还有其他结构定义充分的利用了“组合”来实现面向对象的“继承”特性。
struct file_operations {
struct module *owner;
loff_t(*llseek) (struct file *, loff_t, int);
ssize_t(*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t(*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
ssize_t(*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t(*aio_write) (struct kiocb *, const char __user *, size_t,
loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int,
unsigned long);
..............
}
总结一下,经过上面的论述,用c语言实现面向对象编程,无外乎两种方法:在struct中通过“组合”实现面向对象的封装特性,通过“函数指针”来实现对象方法的封装,通过工厂方法也可以实现“伪多态”的效果。
有了上面的理论基础,来看看libuv是怎么运用这些特性的吧。
二、抽象基类
libuv中,所有的handle都会有一个共同的抽象基类(这里所说的抽象基类,只是一种称呼,不要和c++与java中的概念混淆),他就是uv_handle_s,下面来看一下它的定义。
/* 所有句柄的抽象基类. */
struct uv_handle_s {
UV_HANDLE_FIELDS
};
其中,将UV_HANDLE_FIELDS宏展开之后,再次列出:
struct uv_handle_s {
/* public */ \
void* data; \ //句柄携带的数据
/* read-only */ \
uv_loop_t* loop; \ //句柄绑定的事件循环
uv_handle_type type; \ //句柄类型
/* private */ \
uv_close_cb close_cb; \ //句柄close时的回调
void* handle_queue[]; \ //句柄队列节点
union { \
int fd; \ //绑定的真实资源索引
void* reserved[]; \
} u; \
UV_HANDLE_PRIVATE_FIELDS \
};
在正式介绍其成员之前,先将宏UV_HANDLE_PRIVATE_FIELDS也展开(它是一个和平台相关的宏):
struct uv_handle_s {
/* public */ \
void* data; \ //句柄携带的数据
/* read-only */ \
uv_loop_t* loop; \ //句柄绑定的事件循环
uv_handle_type type; \ //句柄类型
/* private */ \
uv_close_cb close_cb; \ //句柄close时的回调
void* handle_queue[]; \ //句柄队列节点
union { \
int fd; \ //绑定的真实资源索引
void* reserved[]; \
} u; \
\
15 uv_handle_t* next_closing; \ //下一个要被关闭的句柄
16 unsigned int flags; \ //句柄标识
};
以上就是unix平台下的uv_handle_s结构定义,从其成员定义可以看出所有句柄的共性是什么。首先,void *类型的data成员可以用来传递任何类型的数据,其上面的注释“public”表示该数据可被用户层访问,而标有“provite”字样的属性(成员),则表示不是暴露给用户使用的,它们只会在libuv内部使用。接下来,有一个uv_loop_t *的指针,从字面上可以看出,这是一个事件循环的指针,在上一篇论述线程池时,已经提及过Reactor线程模型,其中事件循环(loop)就是一个Reactor实例,主要提供了事件的注册、注销、dispatch事件的功能,如果你熟悉libevent,它就类似于eventbase,如果你熟悉java中的netty,它就类似于eventloop,好了,这里的loop指针表示这个句柄(handle)是被绑定在哪个事件循环上的;uv_handle_type表示这个句柄的类型,它的取值可以有:UV_TCP、UV_NAMED_PIPE、UV_TTY、UV_UDP、UV_POLL等等;close_cb表示该句柄在关闭时调用的回调函数;handle_queue,作为一个QUEUE节点,会挂载在绑定的loop循环中的handle_queue队列上;u是一个联合体,表示句柄绑定的真实的资源索引(真实的句柄或者描述符),但是我们知道,libuv抽象出来的句柄并不一定都有真实的物理资源对应,比如定时器句柄就不没有一个对应的描述符,因此此时可以使用reserved来占位;next_closing用来将要被关闭的句柄串接成单向链表,该链表会挂载在绑定的loop上的closing_handles指针上。flags,表示该句柄的状态,可以为UV_CLOSING、UV_CLOSED、UV_STREAM_READING、UV_STREAM_SHUTTING、UV_STREAM_SHUT、UV_STREAM_READABLE、UV_STREAM_WRITABLE、UV_STREAM_BLOCKING、UV_STREAM_READ_PARTIAL、UV_STREAM_READ_EOF、UV_TCP_NODELAY、UV_TCP_KEEPALIVE、UV_TCP_SINGLE_ACCEPT、UV_HANDLE_IPV6、UV_UDP_PROCESSING。
至此,所有句柄的抽象基类基本上说清楚了,所以其他类型的句柄都是这个基类的直接或者间接子类,那么libuv都定义了哪些句柄类型呢?在uv.h中,可以看到如下定义:
/* Handle types. */
typedef struct uv_loop_s uv_loop_t;
typedef struct uv_handle_s uv_handle_t;
typedef struct uv_stream_s uv_stream_t;
typedef struct uv_tcp_s uv_tcp_t;
typedef struct uv_udp_s uv_udp_t;
typedef struct uv_pipe_s uv_pipe_t;
typedef struct uv_tty_s uv_tty_t;
typedef struct uv_poll_s uv_poll_t;
typedef struct uv_timer_s uv_timer_t;
typedef struct uv_prepare_s uv_prepare_t;
typedef struct uv_check_s uv_check_t;
typedef struct uv_idle_s uv_idle_t;
typedef struct uv_async_s uv_async_t;
typedef struct uv_process_s uv_process_t;
typedef struct uv_fs_event_s uv_fs_event_t;
typedef struct uv_fs_poll_s uv_fs_poll_t;
typedef struct uv_signal_s uv_signal_t;
句柄的类型大概上分为两种:普通句柄和流句柄,普通的句柄就类似于信号、文件、定时器等,流式句柄比如代表一个tcp连接、pipe连接以及控制台连接等。为了表达清楚他们之间的关系,我现在以图示的形式画了一张伪UML图。

Nodejs事件引擎libuv源码剖析之:句柄(handle)结构的设计剖析的更多相关文章
- Nodejs事件引擎libuv源码剖析之:高效线程池(threadpool)的实现
声明:本文为原创博文,转载请注明出处. Nodejs编程是全异步的,这就意味着我们不必每次都阻塞等待该次操作的结果,而事件完成(就绪)时会主动回调通知我们.在网络编程中,一般都是基于Reactor线程 ...
- Nodejs事件引擎libuv源码剖析之:请求(request)结构的设计剖析
声明:本文为原创博文,转载请注明出处. 在libuv中,请求(request)代表一个用户向libuv发出的指令,比如uv_connect_s就表示一个tcp的连接请求.uv_work ...
- Nodejs事件引擎libuv源码剖析之:高效队列(queue)的实现
声明:本文为原创博文,转载请注明出处. 在libuv中,有一个只使用简单的宏封装成的高效队列(queue),现在我们就来看一下它是怎么实现的. 首先,看一下queue中最基本的几个宏: typede ...
- libuv源码分析前言
Libevent,libev,libuv三者的区别所在? libevent提供了全套解决方案(事件库,非阻塞IO库,http库,DNS客户端),然而libevent使用全局变量,导致非线程安全.它的w ...
- Cocos2d-X3.0 刨根问底(七)----- 事件机制Event源码分析
这一章,我们来分析Cocos2d-x 事件机制相关的源码, 根据Cocos2d-x的工程目录,我们可以找到所有关于事件的源码都存在放在下图所示的目录中. 从这个event_dispatcher目录中的 ...
- spark 源码分析之十八 -- Spark存储体系剖析
本篇文章主要剖析BlockManager相关的类以及总结Spark底层存储体系. 总述 先看 BlockManager相关类之间的关系如下: 我们从NettyRpcEnv 开始,做一下简单说明. Ne ...
- Android View 事件分发机制 源码解析 (上)
一直想写事件分发机制的文章,不管咋样,也得自己研究下事件分发的源码,写出心得~ 首先我们先写个简单的例子来测试View的事件转发的流程~ 1.案例 为了更好的研究View的事件转发,我们自定以一个My ...
- Android查缺补漏(View篇)--事件分发机制源码分析
在上一篇博文中分析了事件分发的流程及规则,本篇会从源码的角度更进一步理解事件分发机制的原理,如果对事件分发规则还不太清楚的童鞋,建议先看一下上一篇博文 <Android查缺补漏(View篇)-- ...
- React事件杂记及源码分析
前提 最近通过阅读React官方文档的事件模块,发现了其主要提到了以下三个点 调用方法时需要手动绑定this React事件是一种合成事件SyntheticEvent,什么是合成事件? 事件属性 ...
随机推荐
- 16-static和extern关键字2-对变量的作用
上一讲介绍了static和extern对函数的作用,static用来定义一个内部函数,不允许其他文件访问:extern用来定义和声明一个外部函数,允许其他文件访问.static和extern对变量也有 ...
- KeySpaceNotification 键空间通知
KeySpaceNotification 键空间通知 1.Redis键淘汰机制简介 在Redis中,内存的大小是有限的,所以为了防止内存饱和,需要实现某种键淘汰策略.主要有两种方法,一种是当Redis ...
- ASP.NET MVC 3 网站优化总结(六)压缩 HTML
压缩 html 可以去除代码中无用的空格等,这样可提高网站的加载速度并节省带宽.今天就让我们看看在 ASP.NET MVC 3 怎么实现 html 压缩,我们可通过实现 ActionFilter 来完 ...
- sublime 相关配置
安转插件:案例格式化HTML代码,需要安装插件,具体安装步骤如下: 1.打开菜单->首选项->插件控制,输入 install package 2.等待程序进入插件管理功能,再输入插件名称: ...
- mongodb 3.x 之实用新功能窥看[1] ——使用TTLIndex做Cache处理
mongodb一直都在不断的更新,不断的发展,那些非常好玩也非常实用的功能都逐步加入到了mongodb中,这不就有了本篇对ttlindex的介绍, 刚好我们的生产业务场景中就有这个一个案例... 一: ...
- linux ssh远程免登陆
一.备份: 操作之前先将/root/.ssh/下的known_hosts备份成known_hosts.bak
- mysql查询本周、月、季度、年
#查询本周记录 select * from product_process where WEEKOFYEAR(update_time)=WEEKOFYEAR(now()); #查询本月数据 selec ...
- HttpsURLConnection 利用keepAlive特性进行优化一例
最近项目中,遇到一个报错: java.lang.OutOfMemoryError: unable to create new native thread 报错的场景是:一个消息的群发,群里总共有50多 ...
- URL_MODEL 2 不能访问 在APACHE服务器上的访问方式上去除index.php
thinkphp URL_MODEL=2,访问链接http://i.cnblogs.com/Online/index.html 报错: Not Found The requested URL /on ...
- ascii、unicode、utf、gb等编码详解
很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物.他们看到8个开关状态是好的,于是他们把这称为"字节".再后来,他们又做了一些可以处理这 ...