博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Nodejs事件引擎libuv源码剖析之:句柄(handle)结构的设计剖析
阅读量:7186 次
发布时间:2019-06-29

本文共 6357 字,大约阅读时间需要 21 分钟。

hot3.png

前言

      libuv的工作建立在用户表达对特定事件的兴趣。这通常通过创造对应I/O设备,定时器,进程等的handle来实现。handle是不透明的数据结构,其中对应的类型uv_TYPE_t中的type指定了handle的使用目的。

      handle代表了持久性对象。在异步的操作中,相应的handle上有许多与之关联的request。request是短暂性对象(通常只维持在一个回调函数的时间),通常对映着handle上的一个I/O操作。request用来在初始函数和回调函数之间,传递上下文。例如uv_udp_t代表了一个udp的socket,然而,对于每一个向socket的写入的完成后,都会向回调函数传递一个uv_udp_send_t

      handle可以通过下面的函数设置:

uv_TYPE_init(uv_loop_t *, uv_TYPE_t *)

      句柄(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[2];                                                      \ //句柄队列节点  union {                                                                     \    int fd;                                                                   \ //绑定的真实资源索引    void* reserved[4];                                                        \  } 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[2];                                                      \ //句柄队列节点  union {                                                                     \    int fd;                                                                   \ //绑定的真实资源索引    void* reserved[4];                                                        \  } u;                                                                        \                                                                              \  uv_handle_t* next_closing;                                                  \ //下一个要被关闭的句柄  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源码剖析之:事件循环(loop)结构的设计剖析

Nodejs事件引擎libuv源码剖析之:跨平台系统调用(syscall)实现原理

Nodejs事件引擎libuv源码剖析之:文件(fs)实现原理

Nodejs事件引擎libuv源码剖析之:管道(pipe)实现原理

Nodejs事件引擎libuv源码剖析之:网络(net)实现原理

Nodejs事件引擎libuv源码剖析之:定时器(timer)实现原理

转载于:https://my.oschina.net/fileoptions/blog/1068810

你可能感兴趣的文章
jquery制作论坛或社交网站的每天打卡签到特效
查看>>
RT-thread内核之线程调度算法
查看>>
Vue.js学习笔记(介绍)
查看>>
L1-2. 点赞【求多组数据中出现次数最多的】
查看>>
在商城系统开发时遇到商品的多级分类,为增强扩展性,子类可以任意添加,此类问题数据库如何设计...
查看>>
PYTHON-字符编码&文件处理-练习
查看>>
iOS 实现简单的移动UIView代码实例
查看>>
LCIS 最长公共上升子序列问题DP算法及优化
查看>>
设计模式(一)单例模式
查看>>
auto,register,static实例
查看>>
log4net 开启内部调试
查看>>
Shell脚本开发过程中遇到的问题处理
查看>>
30421发展
查看>>
一些关于Gulp和NodeJS Stream的理解
查看>>
配置java环境
查看>>
我在csdn有一个新家啦
查看>>
LINQ to SQL语句之Union All/Union/Intersect和Top/Bottom和Paging和SqlMethods
查看>>
【Python学习笔记】-冒泡排序、插入排序、二分法查找
查看>>
linux进程管理之进程创建(三)
查看>>
DS第五章学习小结
查看>>