现在的位置: 首页 > 综合 > 正文

Partysip的插件技术研究

2013年10月25日 ⁄ 综合 ⁄ 共 5600字 ⁄ 字号 评论关闭

Partysip的插件技术研究
Partysip框架优化方案

 

介绍:

       本文是《Partysip框架优化计划》的一部分,着重研究Partysip插件技术,并在此基础上尝试优化。

       整体上说Partysip虽然没有使用OO的思想去设计,但是还是尽量“封装”独立的函数操作,所以研究和理解还是比较方便,对于每个声明结构体都会有一组相关的函数对其操作,这些操作大部分是名字上的差异,其执行操作是对结构体内变量赋值,修改操作,以及结构体的initfree操作,试想如果Partysip采用OO设计思想进行设计,将是非常容易理解。

       对于Partysip插件,我们是按照程序启动时引入插件的过程来理解,并逐步理解插件的实现和调用,在文章最后给出实现一个插件所需要完成的基本操作。现在假设我们已经具有插件,这里我们所说的插件其实就是按照Partysip框架格式定义的动态链接库(dll)。

插件的加载过程

       Partysip中插件加载在main.c文件中的main_load_plugins()函数内实现,在加载Partysip的插件之前,Partysip框架已经做了一些初始化,关于这方面的知识,以后会再讲。在main_load_plugins函数内,大部分代码我们不用考虑,加载插件的主要函数是psp_plugin_load函数,通过循环调用psp_plugin_load函数实现对所有插件的加载,该函数在psp_plugin.h文件内声明,在psp_plugin.c文件内定义。

关于插件操作其他函数也是在这两个文件内声明和定义,后面将详细介绍psp_plugin.h内的函数。在psp_plugin_load函数调用ppl_dso_load函数(在ppldso.h文件内定义),在ppl_dso_load函数内调用系统函数LoadLibraryEx加载动态链接库,这样我们就把插件“插入”Partysip中,这和在程序中引入一个其他dll是没有区别的。Partysip经过诸多转换后,会把dll的句柄保存在ppl_dso_handle_t类型的结构体内,在XXX_plugin(XXX是插件的名称,每个插件都有这样的一个全局结构体)结构体内有一个ppl_dso_handle_t类型指针指向这个变量,最后资源释放时也是通过ppl_dso_handle_t这个结构体进行资源释放,释放时是通过全局结构XXX_plugin来释放的,这个结构体在psp_plugin_take_ownership函数内注册,Partysip在加载控件,主要通过XXX_plugin这个结构体,调用plugin_init函数,关于这个函数的作用以后会提到。

       加载动态链接库dll(插件)成功后,我们回到psp_plugin_load函数,这个函数内声明两个重要变量

1dso_handle,变量类型为ppl_dso_handle_t,他的原型为

struct ppl_dso_handle_t
{

        void *handle;
        const char *errormsg;

};

是用来记录加载dll的句柄和错误信息的(如果加载失败)。

2sym,变量类型为ppl_dso_handle_sym_t,他的原型是typedef void *ppl_dso_handle_sym_t;,这个类型更多是基于将来扩展考虑而这样定义的。当加载dll后用dso_handle记录加载dll的句柄信息和错误信息。然后通过调用ppl_dso_sym函数,获取dll中的特定结构体的指针(我们做插件时,必须创建一个这样的全局结构体变量,结构体变量名必须为插件名+ _plugin,比如UDP插件中声明udp_plugin结构体)。结构体指针的类型为psp_plugin_t。这个结构体的主要作用是记录插件的相关信息,我们在后面还将详细介绍这个结构体。获取这个结构指针后,再次分别调用ppl_dso_sym (&sym, dso_handle, "plugin_init")ppl_dso_sym (&sym, dso_handle, "plugin_start")ppl_dso_sym (&sym, dso_handle, "plugin_release"),分别获取加载插件中的plugin_init,plugin_start,plugin_release三个函数地址(后面我们将详细介绍其作用),通过这个函数(*plug)->plugin_init (name_config)调用插件的plugin_init函数,这个函数内做插件的相关注册操作,具体操作参考下边的函数讲解。以上各个过程中如果出现错误,马上调用ppl_dso_unload函数,通过FreeLibrary函数释放插件。在PartysipOsip中,函数返回值为0,表示执行函数成功,-1表示失败。

    3:关于线程,在Partysip框架优化计划》一文中提到,Partysip会为每个插件创建一个线程的观点是不正确的。经过仔细分析Partysip框架,Partysip会创建三个线程(启动参数中包含i选项,i表示为interface,用户界面的意思,主界面线程,加上这个线程Partysip一共创建4个线程。如果Osip库采用多线程方式编译,osip至少还会创建4个线程)。这三个线程分别为mythread_resolvmythread_tlpmythread_sfp。这三个线程分别监听三个管道,Partysip为了通用向并没有使用windows下的CreatePipe等函数,而是创建了从10500开始的三个TCP端口,当有消息到来时(比如q,退出)通过这几个端口来传输。关闭这三个线程的作用。

制作插件

Partysip插件就是满足Partysip框架要求的动态链接库(dll),满足Partysip框架的插件至少要满足一下五点

1:插件内定义全局变量XXX_pluginXXX为插件名称),此变量类型为psp_plugin_t

这个结构体原型为

struct psp_plugin_t

{
        int plug_id;//
加载后的插件ID
 
      char *name; //
插件名字
        char *version; //
插件版本
        char *description; //
插件描述信息   

#define PLUGIN_TLP 0x01
#define PLUGIN_IMP 0x02
#define PLUGIN_UAP 0x04
#define PLUGIN_SFP 0x16
        int flag;
        /*... */
        int owners;         /* number of attached plugins */
        ppl_dso_handle_t *dso_handle; //
加载插件后的插件句柄
        int (*plugin_init) (); //
以下为三个回调函数,分别执行初始化,开始,释放数据操作。
        int (*plugin_start) ();
        int (*plugin_release) ();
};

这个结构体的作用是记录Partysip插件的相关信息,比如插件的名字,插件的ID,插件的版本,插件的描述信息,插件的三个回调函数,插件的句柄等。变量XXX_plugin中的一些成员变量会在插件加载的Partysip框架中修改。比如ownersdso_handleplugin_init等,下面是UDP插件udp_plugin的例子

psp_plugin_t PSP_PLUGIN_DECLARE_DATA udp_plugin = {
  0,                /* uninitialized */
  "Udp plugin",
  "0.5.1",
  "plugin for receiving and sending UDP message",
  PLUGIN_TLP,
  0,                /* number of owners is always 0 at the begining */
  NULL,             /* future place for the dso_handle */
  &plugin_init,
  &plugin_start,
  &plugin_release
};

2:声明plugin_init 函数,其声明原型为
PSP_PLUGIN_DECLARE (int) plugin_init (char *name_config)

PSP_PLUGIN_DECLARE
是一个宏,声明为

#define PSP_PLUGIN_DECLARE(type) __declspec(dllimport) type __stdcall
表示plugin_init为一个导出函数。名字,函数参数,返回值不能修改。

Plugin_int函数的主要作用是,初始化插件内部的一些操作,变量的申请,系统参数的获取操作,系统资源的获取,还有一个重要作用就是,注册回调函数(hook机制),涉及到回调函数执行的优先级别等信息。参数name_config传递配置文件路径的字符串,这个函数在加载完成插件后调用。

3:声明plugin_start函数,函数原型为PSP_PLUGIN_DECLARE (int) plugin_start ()
调用完成plugin_init之后,在合适的位置执行此函数,这个函数目前只是作为保留函数使用,一般都是只是return -1

 

4:声明plugin_release 函数,函数原型为PSP_PLUGIN_DECLARE (int) plugin_release ()
函数的作用,主要用于卸载插件时,调用这个函数,释放由plugin_init中申请的内存空间,关闭系统资源等。

5:声明注册hook函数
插件中hook函数执行顺序(psp_plug)

/** run this hook first, before ANYTHING */
#define PSP_HOOK_REALLY_FIRST   (-10)
/** run this hook first */
#define PSP_HOOK_FIRST      0
/** run this hook somewhere */
#define PSP_HOOK_MIDDLE     10
/** run this hook after every other hook which is defined*/
#define PSP_HOOK_LAST       20
/** run this hook last, after EVERYTHING */
#define PSP_HOOK_REALLY_LAST    30
#define PSP_HOOK_FINAL      40

 

/* How to call plugins:
   1: detect which method or response code is used
   2: call the list of method in the PSP_HOOK_REALLY_FIRST list
   3: call the list of method in the PSP_HOOK_FIRST
   4: ...
   5: ...

   So: For performance reason, this list should be ready to use:
   table[0] for any REQUEST (other than INVITE)
   table[1] for INVITE
   table[2] for REGISTER
   table[3] for BYE
   ...

   table[1] contains all the hook related to INVITE:
   for irp:
*/

 

/* HOW PLUGINS ARE IMPLEMENTED:
   1: The core layer is asked to load a plugin named "$name"
   2: Do a dlopen of the shared library
   3: Do dlsym to access some methods:
       plugin_configure
       -> used to init internal state of plugin
       -> plugin store its "func_tab" in the *_plugin structure
       ....
*/

小结:

    以上这些只是基于Partysip框架的基本了解,由于刚接触Partysip难免有错误,或者理解偏差的地方,欢迎大家指出。

部分缩写词说明(部分)

缩写

原组成词

说明

备注

psp

Partysip

pspPartysip缩写

 

tlp

Transport layer Processing

tlp传输层处理

Transport layer Processing
(
传输层处理)

sfp

State Full Processing

sfp全状态处理

State Full Processing

(全状态处理)

ict

Invite Client Transaction

ict客户端邀请事务

Invite Client Transaction

(邀请客户端事务)

nict

Non-Invite Client Transaction

Nict非客户端要求事务

Non Invite Client Transaction(非邀请客户端事务)

ist

Invite Server Transaction

Ist服务器端要求事务

Invite Server Transaction
(
邀请服务器端事务)

nist

Non Invite Server Transaction

Nist非服务器端要求事务

Non Invite Server Transaction(非邀请服务器端事务)

snd

Send

 

 

inc

 

 

 

rcv

receive

 

 

抱歉!评论已关闭.