关于PJSIP介绍的的一篇很好的文章

如果你对SIP/VoIP技术感兴趣,哪希望你不要错过,如果你对写出堪称优美的Code感兴趣 
,那么你也不可错过

这期间我想分析一下一个实际的协议栈的设计到实现的相关技术,算是自己的一个学习经 
历记录. 
最初选择这个库做分析的原因很简单,文档齐全,其它良好的特征则是慢慢发现的
www.pjsip.org

1. PJSIP简介 
PJSIP的实现是为了能在嵌入式设备上高效实现SIP/VOIP.其主要特征包括:

1).极具移植性.(Extremely portable) 
当前可支持平台包括: 
* Win32/x86 (Win95/98/ME, NT/2000/XP/2003, mingw). 
* arm, WinCE and Windows Mobile. 
* Linux/x86, (user mode and as kernel module(!)). 
* Linux/alpha 
* Solaris/ultra. 
* MacOS X/powerpc 
* RTEMS (x86 and powerpc). 
正移植到: 
* Symbian OS 
2).非常小的足印.(Very small footprint) 
官方宣称编译后的库<150Kb,我在PC上编译后加上strip后大概173Kb,这对于嵌入 
式设备,是个好消息
3).高性能.(High performance) 
这点我们后面可以看看是否如作者宣称的
4).支持众多的特征.(Many features) 
这点可以从http://www.pjsip.org/sip_media_features.htm#sip_features看出.

5).充足的SIP文档.(Extensive SIP documentation) 
这是我最初选择该库的原因,当然不是最终的原因,最终的原因是它的code
2. PJSIP的组成. 
其实说是PJSIP不是特别贴切,这个库实际上是几个部分组成的. 
1).PJSIP - Open Source SIP Stack[开源的SIP协议栈] 
2).PJMEDIA - Open Source Media Stack[开源的媒体栈] 
3).PJNATH - Open Source NAT Traversal Helper Library[开源的NAT-T辅助库] 
4).PJLIB-UTIL - Auxiliary Library[辅助工具库] 
5).PJLIB - Ultra Portable Base Framework Library[基础框架库] 
而在最上层库的目录分为(可以使用tree -d -L 1 查看) 
$TOP/build [包含Makefile] 
$TOP/build.symbian [针对symbian的Makefile] 
$TOP/pjlib [参考上面] 
$TOP/pjlib-util [参考上面] 
$TOP/pjnath [参考上面] 
$TOP/pjmedia [参考上面] 
$TOP/pjsip [参考上面] 
$TOP/pjsip-apps 
$TOP/third_party 
而在每个子目录,可以看到分为: 
bin [编译后产生的二进制文件] 
build [Makefile] 
build/output 
build/wince-evc4 
docs [doxygen的文档,用doxygen docs/doxygen.cfg产生] 
include [头文件] 
lib [编译后产生的库] 
src [源代码]

3. PJLIB简介 
要理解好PJSIP,就不得不先说说PJLIB,PJLIB算的上是这个库中最基础的库,正是这个 
库的优美实现,才让PJSIP变得如此优越。 
PJLIB提供了一系列特征,这是我们下面分析的重点,涉及到: 
1).非动态内存分配[No Dynamic Memory Allocations] 
实现了内存池,获取内存是从与分配的内存池中获取,高性能程序多会自己构造内存池 
,后面我们会解释该内存池的使用以及基本的原理。根据作者的比较,是常规的 malloc( 
)/free()函数的30倍。 
2).OS抽象[Operating System Abstraction] 
实现OS抽象的根本原因在与可移植性,毋庸置疑. 
涉及到: 
a).线程[Threads.] 
b).线程本地存储[Thread Local Storage.] 
c).互斥[Mutexes.] 
d).信号灯[Semaphores.] 
e).原子变量[Atomic Variables.] 
f).临届区[Critical sections.] 
g).锁对象[Lock Objects.] 
h).事件对象[Event Object.] 
i).时间管理[Time Data Type and Manipulation.] 
j).高解析的时间戳[High Resolution Timestamp.] 
等等,这些我们后面分析代码时一一看来
3).低层的网络相关IO[Low-Level Network I/O] 
这涉及到: 
a).Socket抽象[Socket Abstraction.] 
b).网络地址解析[Network Address Resolution.] 
c).实现针对Socket的select API[Socket select() API.] 
4).时间管理[Timer Management] 
这主要涉及到两个部分,一个时定时器的管理,还有就是时间解析的精度(举例说来,就 
是能精确到哪个时间等级,比如 POSIX sleep(),就只能以秒为单位,而使用select()则可 
以实现毫秒级别的计时) 
5).各种数据结构[Various Data Structures] 
主要有: 
a).针对字符串的操作[String Operations] 
b).数组辅助[Array helper] 
c).Hash表[Hash Tabl] 
d).链表[Linked List] 
e).红黑平衡树[Red/Black Balanced Tree] 
6).异常处理[Exception Construct] 
使用的是TRY/CATCH,知道C++/JAVA之类面向对象语言的人看过会宛而一笑
7).LOG机制[Logging Facility] 
很显然,一个良好的程序,好的LOG机制不可少。这能很方便的让你去调试程序,对此我 
是深有体会,任何时候,不要忘记“好的程序,是架构出来的;而能跑的程序,是调试出 
来的:)” 
8).随机数以及GUID的产生[Random and GUID Generation] 
GUID指的是"globally unique identifier",只是一个标识而已,比如说你的省份证, 
算的上是一个GUID,当然,准确说来是“china unique identifier”. 
看了这么多的特征列举,是不是很完备,的确。 
总算是初步列举完了PJLIB的基本特征了,后面我们来说说它的使用与实现: 
4. PJLIB的使用 
有了上述介绍,是不是很想知道这个库的使用,没关系,我们慢慢说来
首先是头文件和编译出来的库的位置,这就不必多说了,除非你没有使用过手动编译的库 
,如果不太了解步骤,google一下,啊
1).为了使用这个库,需要使用: 
#include <pjlib.h> 
当然,也可以选择: 
#include <pj/log.h> 
#include <pj/os.h> 
这种分离的方式,不过,简介其间,还是使用第一种吧:),毕竟,你不需要确认到你所 
需的函数或者数据结构具体到哪个具体的头文件
2).确保在使用PJLIB之前调用 pj_init()来完成PJLIB库使用前说必须的一些初始化. 
这是一个必不可少的步骤. 
~~~~~~~~~~~~~~~~~~~~~~~ 
3).使用PJLIB的一些建议 
作者对使用PJLIB的程序提出了一些建议,包括如下 : 
a).不要使用ANSI C[Do NOT Use ANSI C] 
观点很明确,ANSI C并不会让程序具有最大的移植性,应该使用PJSIP库所提供的响 
应机制来实现你所需要的功能. 
b).使用pj_str_t取代C风格的字符串[Use pj_str_t instead of C Strings] 
原因之一是移植性,之二则是PJLIB内置的pj_str_t相关操作会更快(性能). 
c).从内存池分配内存[Use Pool for Memory Allocations] 
这很明显,如果你知道为什么会使用内存池的话(提示一下,性能以及易用性:)) 
d).使用PJLIB的LOG机制做文字显示[Use Logging for Text Display] 
很明显
还有些关于移植的一些问题,不在我们的讨论范围,如果你需要移植到其它平台或者 
环境,请参考http://www.pjsip.org/pjlib/docs/html/porting_pjlib_pg.htm

5. PJLIB的使用以及原理 
终于开始提及实现原理以及具体的编码了:),前面的列举还真是个琐碎的事情,还是奔主题 
来:). 
5.1快速内存池[Fast Memory Pool] 
前面说过,使用内存池的原因在于性能的考虑,原因是C风格的malloc()以及C++风格的new 
操作在高性能或实时条件下表现并不太好,原因在于性能的瓶颈在于内存碎片问题. 
下面列举其优点与需要主要的问题: 
优点: 
a).不像其它内存池,允许分配不同尺寸的chunks. 
b).快速. 
内存chunks拥有O(1)的复杂度,并且操作仅仅是指针的算术运算,其间不需要使用锁住任 
何互斥量. 
c).有效使用内存. 
除了可能因为内存对齐的原因会浪费很少的内存外,内存的使用效率非常高. 
d).可预防内存泄漏. 
在C/C++程序中如果出现内存泄漏问题,其查找过程哪个艰辛,不足为外人道也:( 
[曾经有次用别人的Code,出现了内存泄漏,在开发板上查找N天,又没工具可在开发板上使 
用,哪个痛苦,想自杀~~~ 
原因很简单,你的内存都是从内存池中获取的,就算你没有释放你获取的内存,只要你记得 
把内存池destroy,那么内存还是会还给系统. 
还有设计带来的一些其它益处,比如可用性和灵活性: 
e).内存泄漏更容易被跟踪. 
这是因为你的内存是在指定的内存池中分配的,只要能很快定位到内存池,内存泄漏的侦 
测就方便多了. 
f).设计上从内存池中获取内存这一操作是非线程安全的. 
原因是设计者认为内存池被上层对象所拥有,线程安全应该由上层对象去保证,这样的话 
,没有锁的问题会让内存分配变得非常的快. 
g).内存池的行为像C++中的new的行为,当内存池获取内存chunks会抛出PJ_NO_MEMORY_EX 
CEPTION异常,当然,因为支持异常处理,也可以使用其它方式让上层程序灵活的定义异常的 
处理. 
[这是异常处理的基本出发点,但是这有大量的争论,原因是这改变了程序的正常流程,谁能 
去保证这种流程是用户所需要的呢,因此C++中的异常处理饱受争议,请酌情使用] 
h). 可以在后端使用任何的内存分配器.默认情况下是使用malloc/free管理内存池的块, 
但是应用程序也可以指定自己的策略(strategy),例如从一个全局存储空间分配内存. 
恩,要知道,任何事务都是两面的(颇为佩服创造出“双赢”这个词的语言天才, 不过,文 
字游戏对于技术人员不能说是件好事情:(),好了,使用时,不要认为这个内存池是哪种"per 
fect"的技术,要记得"任何设计,都是在各种限制条件中的一个折中,对于'戴着镣铐的舞蹈 
',除了'舞蹈',也不要忘记'镣铐'哦",不要忘了告诫: 
告诫[Caveats]: 
a).使用合适的大小来初始化内存池. 
使用内存池时,需要指定一个初始内存池大小, 这个值是内存池的初始值,如果你想要高 
性能,要谨慎选择这个值哦,太大的化会

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值