声明:本文为原创博文,转载请注明出处。

句柄(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)结构的设计剖析的更多相关文章

  1. Nodejs事件引擎libuv源码剖析之:高效线程池(threadpool)的实现

    声明:本文为原创博文,转载请注明出处. Nodejs编程是全异步的,这就意味着我们不必每次都阻塞等待该次操作的结果,而事件完成(就绪)时会主动回调通知我们.在网络编程中,一般都是基于Reactor线程 ...

  2. Nodejs事件引擎libuv源码剖析之:请求(request)结构的设计剖析

    声明:本文为原创博文,转载请注明出处.         在libuv中,请求(request)代表一个用户向libuv发出的指令,比如uv_connect_s就表示一个tcp的连接请求.uv_work ...

  3. Nodejs事件引擎libuv源码剖析之:高效队列(queue)的实现

     声明:本文为原创博文,转载请注明出处. 在libuv中,有一个只使用简单的宏封装成的高效队列(queue),现在我们就来看一下它是怎么实现的. 首先,看一下queue中最基本的几个宏: typede ...

  4. libuv源码分析前言

    Libevent,libev,libuv三者的区别所在? libevent提供了全套解决方案(事件库,非阻塞IO库,http库,DNS客户端),然而libevent使用全局变量,导致非线程安全.它的w ...

  5. Cocos2d-X3.0 刨根问底(七)----- 事件机制Event源码分析

    这一章,我们来分析Cocos2d-x 事件机制相关的源码, 根据Cocos2d-x的工程目录,我们可以找到所有关于事件的源码都存在放在下图所示的目录中. 从这个event_dispatcher目录中的 ...

  6. spark 源码分析之十八 -- Spark存储体系剖析

    本篇文章主要剖析BlockManager相关的类以及总结Spark底层存储体系. 总述 先看 BlockManager相关类之间的关系如下: 我们从NettyRpcEnv 开始,做一下简单说明. Ne ...

  7. Android View 事件分发机制 源码解析 (上)

    一直想写事件分发机制的文章,不管咋样,也得自己研究下事件分发的源码,写出心得~ 首先我们先写个简单的例子来测试View的事件转发的流程~ 1.案例 为了更好的研究View的事件转发,我们自定以一个My ...

  8. Android查缺补漏(View篇)--事件分发机制源码分析

    在上一篇博文中分析了事件分发的流程及规则,本篇会从源码的角度更进一步理解事件分发机制的原理,如果对事件分发规则还不太清楚的童鞋,建议先看一下上一篇博文 <Android查缺补漏(View篇)-- ...

  9. React事件杂记及源码分析

    前提 最近通过阅读React官方文档的事件模块,发现了其主要提到了以下三个点  调用方法时需要手动绑定this  React事件是一种合成事件SyntheticEvent,什么是合成事件?  事件属性 ...

随机推荐

  1. 初试WIX加SQL LocalDB

    最近有个项目需要生成一个自动打包安装App和数据库的MSI文件,经同事推荐WIX,于是乎就试了一试.遇到了一些问题觉得有分享的价值,所以写篇博客记录一下 :) 使用感觉: WIX特点:功能很强大,用X ...

  2. "Becoming Functional" 阅读笔记+思维导图

    <Becoming Functional>是O'Reilly公司今年(2014)7月发布的一本薄薄的小册子,151页,介绍了函数式编程的基本概念.全书使用代码范例都是基于JVM的编程语言, ...

  3. 11g新特性:Health Monitor Checks

    一.什么是Health Monitor ChecksHealth Monitor Checks能够发现文件损坏,物理.逻辑块损坏,undo.redo损坏,数据字典损坏等等.Health Monitor ...

  4. 启动/关闭oracle服务有三种方式

    启动oracle服务有三种方式: 1 从控制面板 2 使用MS-DOS命令 3 通过Oracle Administration Assistant for WindowsNT -通过控制面板启动ora ...

  5. SSDB图形界面管理工具:phpssdbadmin安装部署

    环境: 14.04.1-Ubuntu 1.安装Nginx apt-get install nginx ubantu安装完Nginx后,文件结构大致为: 所有的配置文件都在 /etc/nginx下: 启 ...

  6. nginx 虚拟主机基于端口的搭建

    首先配置nginx.conf [root@localhost conf]# cat nginx.confworker_processes 1;user nginx nginx;error_log /a ...

  7. 基于UDP的网络编程

    与TCP编程相比较,UDP缺少了connect().listen()及accept()函数,这是由于UDP协议无连接的特性,不用维护TCP的连接.断开等状态. UDP编程框图 API函数 socket ...

  8. 通过cmd完成FTP上传文件操作

    一直使用 FileZilla 这个工具进行相关的 FTP 操作,而在某一次版本升级之后,发现不太好用了,连接老是掉,再后来完全连接不上去. 改用了一段时间的 Web 版的 FTP 工具,后来那个页面也 ...

  9. IOS错误Could not produce class with ID

    运行环境 Unity 5.3.5f1 (IL2CPP)编译IOS版本 XCode Version 7.2.1 (7C1002) Mac OS X 10.11.3 (15D21) (Mac mini) ...

  10. 如何用卷积神经网络CNN识别手写数字集?

    前几天用CNN识别手写数字集,后来看到kaggle上有一个比赛是识别手写数字集的,已经进行了一年多了,目前有1179个有效提交,最高的是100%,我做了一下,用keras做的,一开始用最简单的MLP, ...